Android "Application Framework" Документы / Документы - PullRequest
4 голосов
/ 24 января 2012

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

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

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

П.С .: Я предполагаю, что модель разрешений реализована в слое "Framework приложения". Поправь меня, если я ошибаюсь.

1 Ответ

10 голосов
/ 25 января 2012

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

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

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

Если мы посмотрим на исходный код, начнем с простого звонка в службу вибратора. (Весь приведенный ниже код защищен авторским правом Android Open Source Project под лицензией 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-интерфейсов, связанных с разрешениями, доступны на уровне приложений, поэтому вы можете добиться успеха, просто имея приложение, предоставляющее услугу, и выполните свою собственную проверку разрешений.

...