Документы по платформе приложений Android/Tuts

Может ли кто-нибудь указать мне на учебные пособия, связанные с разработкой платформы приложений для Android? Здесь я особенно говорю о «Структура приложений» (второй слой сверху в архитектуре Android), а не о разработке приложений.

Меня интересует: что происходит после того, как любое приложение вызывает API системы/фреймворка? Как ОС проверяет, есть ли у этого приложения конкретное разрешение? Какой компонент в «инфраструктуре приложения» обрабатывает эту проверку? Какие классы Java за это отвечают?

Я хотел бы поиграть с этими классами Java и сделать некоторые наблюдения.

P.S.: Я предполагаю, что модель разрешений реализована на уровне «структуры приложения». Поправьте меня, если я ошибаюсь.


person user1010    schedule 24.01.2012    source источник


Ответы (1)


Насколько мне известно, ресурсы для разработки фреймворка ограничены, большая часть того, что доступно, разбросана по разным блогам и спискам рассылки. Для начала я бы порекомендовал сайт проекта с открытым исходным кодом source.android.com. Он содержит ограниченную документацию о том, как что-то делать, но, по крайней мере, обеспечивает настройку для работы с проектом с открытым исходным кодом. Кроме того, есть официальные списки рассылки, связанные с разработкой на уровне платформы и фреймворка. Различные ROM-проекты также могут содержать полезную информацию на таких сайтах, как вики Cyanogenmod.

Затем, чтобы ответить на ваш конкретный вопрос о том, как разрешения реализованы в рамках. Нет специального компонента, который обрабатывает проверки, каждый поставщик услуг в структуре должен выполнить проверку разрешений, прежде чем разрешать вызов службы. В такой проверке участвуют две важные части: менеджер пакетов на системном сервере и механизм IPC Binder. Диспетчер пакетов — это компонент ОС, отвечающий за установку приложений. Это проанализирует файл AndroidManifest.xml при установке, запросит у пользователя разрешения и сохранит реестр разрешений, которыми обладает конкретное приложение. Это основано на идее, что каждое приложение работает со своим собственным идентификатором пользователя Linux. Для каждого uid есть список разрешений.

Вторая часть — это механизм межпроцессного взаимодействия Binder. Binder — это объектно-ориентированный способ выполнения IPC, но он также реализует некоторые функции безопасности. Самый важный из них, связанный с разрешениями, заключается в том, что он позволяет принимающей стороне вызова IPC проверять uid вызывающего абонента. Служба, защищенная разрешением, будет иметь интерфейс Binder и будет делать две вещи для каждого полученного запроса. Сначала он вызовет связыватель, чтобы получить uid вызывающего абонента, а затем вызовет системный сервер, предоставив uid и разрешение, чтобы проверить, было ли оно предоставлено. Если проверка прошла успешно, она будет продолжена и выполнит вызов службы, в противном случае возникнет исключение безопасности.

Если мы посмотрим на исходный код, начиная с простого вызова службы вибратора. (Все приведенные ниже коды защищены авторскими правами проекта Android с открытым исходным кодом в соответствии с лицензией Apache 2.0).

public void vibrate(long milliseconds, IBinder token) {
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
            != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Requires VIBRATE permission");
    }

Реализация проверки разрешений на уровне фреймворка принадлежит классу Context, а точнее, у нас есть файл ContextImpl.java, в котором

@Override
public int checkCallingOrSelfPermission(String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    return checkPermission(permission, Binder.getCallingPid(),
            Binder.getCallingUid());
}
@Override
public int checkPermission(String permission, int pid, int uid) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    try {
        return ActivityManagerNative.getDefault().checkPermission(
                permission, pid, uid);
    } catch (RemoteException e) {
        return PackageManager.PERMISSION_DENIED;
    }
}

Это вызов через Binder к ActivityManagerService, где мы окажемся:

/**
 * As the only public entry point for permissions checking, this method
 * can enforce the semantic that requesting a check on a null global
 * permission is automatically denied.  (Internally a null permission
 * string is used when calling {@link #checkComponentPermission} in cases
 * when only uid-based security is needed.)
 * 
 * This can be called with or without the global lock held.
 */
public int checkPermission(String permission, int pid, int uid) {
    if (permission == null) {
        return PackageManager.PERMISSION_DENIED;
    }
    return checkComponentPermission(permission, pid, uid, -1, true);
} 
/**
 * This can be called with or without the global lock held.
 */
int checkComponentPermission(String permission, int pid, int uid,
        int owningUid, boolean exported) {
    // We might be performing an operation on behalf of an indirect binder
    // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
    // client identity accordingly before proceeding.
    Identity tlsIdentity = sCallerIdentity.get();
    if (tlsIdentity != null) {
        Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
                + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
        uid = tlsIdentity.uid;
        pid = tlsIdentity.pid;
    }

    // Root, system server and our own process get to do everything.
    if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID) {
        return PackageManager.PERMISSION_GRANTED;
    }
    // If there is a uid that owns whatever is being accessed, it has
    // blanket access to it regardless of the permissions it requires.
    if (owningUid >= 0 && uid == owningUid) {
        return PackageManager.PERMISSION_GRANTED;
    }
    // If the target is not exported, then nobody else can get to it.
    if (!exported) {
        Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
        return PackageManager.PERMISSION_DENIED;
    }
    if (permission == null) {
        return PackageManager.PERMISSION_GRANTED;
    }
    try {
        return AppGlobals.getPackageManager()
                .checkUidPermission(permission, uid);
    } catch (RemoteException e) {
        // Should never happen, but if it does... deny!
        Slog.e(TAG, "PackageManager is dead?!?", e);
    }
    return PackageManager.PERMISSION_DENIED;
}

Вызов диспетчера пакетов checkUidPermission — это то, что выполнит поиск, чтобы сопоставить uid с таблицами предоставленных разрешений. Если вы хотите продолжить отслеживание источника, соответствующий файл — PackageManagerService.java.

Если вы просто проводите исследование, не стесняйтесь погрузиться прямо в код в frameworks/base/ в проекте с открытым исходным кодом. Все файлы, упомянутые выше, находятся там. Следуйте инструкциям по сборке, и вы сможете протестировать свои изменения с помощью эмулятора. Если вы не хотите изменять сами основные файлы фреймворка, взгляните на пример в /device/sample о том, как делать расширения фреймворка. Тем не менее, большинство API-интерфейсов, связанных с разрешениями, доступны на уровне приложения, поэтому вы можете добиться успеха, просто имея приложение, которое предоставляет услугу, и выполняйте собственную проверку разрешений на это.

person BMB    schedule 25.01.2012
comment
Ух ты! Довольно красивое объяснение вы дали здесь!! Спасибо. Кстати, откуда вы столько всего знаете (изучая исходники)? Мне очень интересно все это изучать. - person user1010; 26.01.2012
comment
Я работаю с Android на уровне платформы с момента выхода исходного кода, более 3 лет. Я предполагаю, что мои знания получены из объединения информации из различных списков рассылки, блогов и видео с бесчисленными часами, потраченными на изучение файлов исходного кода. - person BMB; 27.01.2012
comment
Блестящий. У меня аналогичный вопрос. stackoverflow.com/questions/21008264/ - person Bachalo; 10.01.2014