有人可以指点我与 android 应用程序框架开发相关的教程吗?在这里,我特别谈论 “应用程序框架” (android 架构中的第二层)而不是应用程序开发。

我感兴趣的是:任何应用程序调用系统/框架 API 后会发生什么?操作系统如何检查该应用程序是否具有该特定权限? “应用程序框架”中的哪个组件处理此检查?哪些java类负责它?

我想玩这些 java 类并进行一些观察。

PS:我假设权限模型是在“应用程序框架”层中实现的。如果我错了,请纠正我。

最佳答案

据我所知,框架开发的资源有限,大部分可用资源分布在不同的博客和邮件列表中。首先,我会推荐开源项目站点 source.android.com 。它包含关于如何做事的有限文档,但至少提供了使用开源项目的设置。然后是与平台和框架级开发相关的official mailing lists。不同的 ROM 项目也可能在 Cyanogenmod wiki 等站点上提供有用的信息。

然后回答您关于如何在框架中实现权限的具体问题。没有处理检查的特定组件,框架中的每个服务提供者都需要在允许服务调用通过之前执行权限检查。这种检查涉及到两个重要部分,系统服务器中的包管理器和 Binder IPC 机制。包管理器是处理应用程序安装的操作系统组件。这将在安装时解析 AndroidManifest.xml 文件,提示用户输入权限并维护特定应用程序拥有哪些权限的注册表。这是基于每个应用程序都使用自己的 Linux 用户 ID 运行的想法。每个 uid 都有一个权限列表。

第二部分是Binder进程间通信机制。 Binder 是一种执行 IPC 的面向对象方式,但它也实现了一些安全功能。与权限相关的最重要的一个是它使 IPC 调用的接收端可以检查调用者的 uid。受权限保护的服务将有一个 Binder 接口(interface),并且会为它收到的每个请求做两件事。首先它会调用 binder 来获取调用者的 uid,然后它会调用提供 uid 和权限的系统服务器来检查它是否已被授予。如果检查没问题,它将继续执行服务调用,否则将引发安全异常。

如果我们查看源代码,从对振动器服务的简单调用开始。 (以下所有代码均受 Apache 2.0 许可下的 Android 开源项目的版权保护)。

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 都可以从应用程序级别获得,因此您可能会成功拥有一个提供服务的应用程序并对其进行自己的权限检查。

10-08 02:59