AndroID将安全设计贯穿系统架构的各个层面,覆盖系统内核、虚拟机、应用程序框架层以及应用层各个环节,力求在开放的同时,也最大程度地保护用户的数据、应用程序和设备的安全。AndroID安全模型主要提供以下几种安全机制:
从技术架构角度来看,AndroID安全模型基于linux *** 作系统内核安全性,有基本的用户和文件访问隔离,在这个基础上辅以内存管理技术和进程间通信机制,来适应移动端处理器性能与内存容量的限制。在应用层面,使用显式定义且经用户授权的权限控制机制,系统化地规范并强制各类应用程序的行为准则与权限许可,可以看到权限机制在安全模型中所处的位置。
那么为什么有权限机制?我们知道 AndroID 应用程序是沙箱隔离的,每个应用都有一个只有自己具有读写权限的专用数据目录,应用只能访问自己的文件和一些设备上全局可访问的资源。那如果我需要访问系统服务呢?这就有了 AndroID 的权限机制。所以根本原因一是沙箱隔离,二是服务支持的需求。在本文开始之前,先抛出几个问题思考,什么是权限?权限是怎么进行赋予的?怎么判断一个组件是否拥有特定的权限?权限维护在哪?AndroID 6.0 运行时权限是什么原理?权限赋予后还能再更改吗?带着问题和结构图,我们一一解开疑惑。
权限的本质什么是权限?在 AndroID 中,一个权限,本质上是一个字符串,一个可以表示执行特定 *** 作的能力的字符串。比如说:访问 SD 卡的能力,访问通讯录的能力,启动或访问一个第三方应用中的组件的能力。使用 pm List permissions -f 命令可以详细查看 AndroID 所有预定义的权限。我们挑一个权限出来:
+ permission:androID.permission.DELETE_PACKAGESpackage:androIDlabel:nulldescription:nullprotectionLevel:signature|privileged
可以看到,一个权限的信息包括:定义的包名、标签、描述和保护级别,保护级别,嗯,这个我们需要详细讲讲。因为不是应用声明了权限,就一定会全部被自动赋予的,保护级别决定了包管理器是否应该赋予组件所申请的权限。权限的级别
normal 级别。权限保护级别的默认值,无须用户确认,只要声明了,就自动默默授权。如:ACCESS_NETWORK_STATE。
dangerous 级别。赋予权限前,会d出对话框,显式请求权限。如:READ_SMS。因为 AndroID 需要在安装时赋予权限,所以安装的确认对话框,也会显示列出权限清单。
signature 级别。signature 级别的权限是最严格的权限,只会赋予与声明权限使用相同证书的应用程序。以系统内置 signature 级别权限为例,AndroID 系统应用的签名由平台密钥签发,默认情况下源码树里有 4 个不同的密钥文件:platform、shared、media 和 testkey。所有核心平台的包(如:设置、电话、蓝牙)均使用 platform 密钥签发;搜索和通讯录相关的包使用 shared 签发;图库和媒体相关的包使用 media 密钥签发;其他的应用使用 testkey 签发。定义系统内置权限的 framework-res.apk 文件是使用平台密钥签发的,因此任何试图请求 signature 级别内置权限的应用程序,需要使用与框架资源包相同的密钥进行签名。
signatureOrSystem 级别。可以看做是一种折中的级别,可被赋予与声明权限具有相同签名证书密钥的应用程序(同 signature 级别)或者系统镜像的部分应用,也就是说这允许厂商无须共享签名密钥。AndroID 4.3 之前,安装在 system 分区下的应用会被自动赋予该保护级别的权限,而 AndroID 4.4 之后,只允许安装在 system/priv-app/ 目录下的应用才能被主动赋予。
权限的管理
那么,系统内置权限,自定义权限,是怎么维护和管理的?
在每个应用安装时,权限就已经赋予了,系统使用包管理服务来管理权限。打开我们系统目录下的 /data/system/packages.xml,可以看到文件包含了所有已定义的权限列表和所有 apk 的包信息,这可以看做是包管理服务维护的一个已安装程序的核心数据库,这个数据库,随着每次应用安装、升级或卸载而进行更新。主要属性如图:
<permissions> 标签内,定义了目前系统中的所有权限,分为系统内置的(package 属性为 androID 的)和 apk 自定义的(package 属性为 apk 的包名)。根元素 <package> 含有每个 apk 的核心属性,以一个应用程序的条目为例:
AndroID 6.0 以下 packages.xml
<package name="com.feelschaotic.demo" codePath="/data/app/com.feelschaotic.demo-1" nativelibraryPath="/data/app/com.feelschaotic.demo-1/lib" primarycpuAbi="x86" flags="5783366" ft="16349bcc4d0" it="16349bcc752" ut="16349bcc752" version="8220" userID="10097"><sigs count="1"> <cert index="7" /></sigs><perms> <item name="androID.permission.READ_SMS" /> <item name="androID.permission.ACCESS_FINE_LOCATION" /> <item name="androID.permission.CHANGE_NETWORK_STATE" /> <item name="androID.permission.INTERNET" /> <item name="androID.permission.READ_EXTERNAL_STORAGE" /> <item name="androID.permission.ACCESS_COARSE_LOCATION" /> <item name="androID.permission.READ_PHONE_STATE" /> <item name="androID.permission.CALL_PHONE" /> <item name="androID.permission.CHANGE_WIFI_STATE" /> <item name="androID.permission.ACCESS_NETWORK_STATE" /> <item name="androID.permission.CAMERA" /> <item name="androID.permission.WRITE_EXTERNAL_STORAGE" /> <item name="androID.permission.READ_CONTACTS" /></perms><proper-signing-keyset IDentifIEr="1686" /> <signing-keyset IDentifIEr="1686" /></package>
AndroID 6.0 及以上 packages.xml
<package name="com.feelschaotic.demo" codePath="/data/app/com.feelschaotic.demo-Gi5ksdF6mUDLakfOugccwQ==" nativelibraryPath="/data/app/com.feelschaotic.demo-Gi5ksdF6mUDLakfOugccwQ==/lib" primarycpuAbi="x86" publicFlags="945307462" privateFlags="0" ft="16348dc3870" it="16343f1d6aa" ut="16348dc4c4d" version="8220" userID="10102"><sigs count="1"> <cert index="20" key="..." /></sigs><perms> <!-- 此处普通权限的 granted 全都默认是 true,且不可改变 granted 值--> <item name="androID.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" /> <item name="androID.permission.INTERNET" granted="true" flags="0" /> <item name="androID.permission.CHANGE_WIFI_STATE" granted="true" flags="0" /> <item name="androID.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" /></perms> <proper-signing-keyset IDentifIEr="48" /></package>
可以发现,其他信息没有太大差异,但是权限列表中,部分在 AndroID 6.0 被标记为高危的权限都不在 <perms> 里了,如:READ_SMS、READ_EXTERNAL_STORAGE、CALL_PHONE 等。这是怎么一回事呢?AndroID 6.0 之前,权限都是在安装时自动赋予的,不卸载应用的情况下,不能更改或撤销(实际上有些厂商打开了appOps,使得 6.0 以下也能更改权限,这种特殊情况我们不讨论)。而 AndroID 6.0 版本对 permission 的管理做了部分改动,针对 dangerous 级别,不再安装的时候赋予权限,而是在运行时动态申请。
我们大胆地推断下,packages.xml 里保留的是不会再变更的权限,运行时权限一定是另外单独地维护。我们在 data/system 目录下来了个全盘搜索,找到了 /data/system/users/0/runtime-permissions.xml。
<pkg name="com.feelschaotic.demo"><!-- 该demo我们故意拒绝了定位权限,可以看到:ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 的 granted 为 false --> <item name="androID.permission.ACCESS_FINE_LOCATION" granted="false" flags="1" /> <item name="androID.permission.READ_EXTERNAL_STORAGE" granted="true" flags="0" /> <item name="androID.permission.ACCESS_COARSE_LOCATION" granted="false" flags="1" /> <item name="androID.permission.READ_PHONE_STATE" granted="true" flags="0" /> <item name="androID.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="0" />...</pkg>
没错了,就是这个文件。里面就记录着运行时权限的授予和拒绝状态。申请时,申请的结果会动态修改 granted 值。管理权限的仍然是 PMS,只不过 AndroID 6.0 之后新增了 runtime-permissions.xml 数据库。
权限的赋予我们知道,AndroID 应用安装时,会被分配一个唯一的 UID,应用启动时,包管理器会设置新建进程的 UID 和 GID 为应用程序的 UID。如果应用已经被赋予了额外的权限,就把这些权限映射成一组 GID,作为补充 GID 分配给进程。低层就可以依赖于进程的 UID、GID 和补充 GID 来决定是否赋予权限了。那么,上面流程的重点:
权限是如何映射到 OS 层的 UID、GID 上的呢?
映射完是怎么分配给进程的?
低层是怎么判断是否赋予权限的?
我们一个个来看,首先第一个,权限是如何映射的?内置权限到 GID 的映射是定义在 /etc/permission/platform.xml 中。
<permissions>···<permission name="androID.permission.READ_EXTERNAL_STORAGE" > <group gID="sdcard_r" /></permission><permission name="androID.permission.WRITE_EXTERNAL_STORAGE" > <group gID="sdcard_r" /> <group gID="sdcard_rw" /></permission><permission name="androID.permission.INTERNET" > <group gID="inet" /></permission>···</permissions>
值得注意的是:READ_EXTERNAL_STORAGE 这种运行时权限,在 AndroID 6.0 之后已经不会映射到 gID 了。动态赋予,动态申请,也就不需要映射了。
<!-- These are permissions that were mapped to gIDs but we needto keep them here until an upgrade from L to the currentversion is to be supported. These permissions are built-inand in L were not stored in packages.xml as a result if theyare not defined here while parsing packages.xml we wouldignore these permissions being granted to apps and notpropagate the granted state. From N we are storing thebuilt-in permissions in packages.xml as the saved storageis negligible (one tag with the permission) compared tothe fragility as one can remove a built-in permission whichno longer needs to be mapped to gIDs and break grant propagation. --><permission name="androID.permission.READ_EXTERNAL_STORAGE" /><permission name="androID.permission.WRITE_EXTERNAL_STORAGE" />
包管理器在启动时读取 platform.xml,并维护「权限-GID」对应的列表。当它给安装中的包授权时,会把权限对应的 GID 加入到该应用进程的补充 GID 中。我们举个例子,看下进程的属性:adb shell ps 拿到想要查找的进程的 PID。
USER PID PPID VSZ RSS WCHAN ADDR S name...u0_a88 2149 1624 1929916 112340 SyS_epoll_wait 0 S com.feelschaotic.demo
接着 adb shell -> cd proc -> cd 2149 (2149 为进程 PID,不固定) -> cat status
我们关注如下两行:
GID: 10050 10050 10050 10050
Groups: 1006 1015 1028 3002 3003 50050
这里我们便看到了系统进程的权限配置信息,这里的数字具体代表意义,可以在AndroID
\system\core\include\private\androID_filesystem_config.h 里面看到,其部分内容如下:
#define AID_CAMERA 1006 /* camera devices */ #define AID_SDCARD_RW 1015 /* external storage write access */#define AID_SDCARD_R 1028 /* external storage read access */···static const struct androID_ID_info androID_IDs[] = {{ "camera", AID_CAMERA, },{ "sdcard_r", AID_SDCARD_R, },{ "sdcard_rw", AID_SDCARD_RW, },···};
有没有发现什么?前文所述 sdcard_r 和 androID.permission.READ_EXTERNAL_STORAGE 的映射已经定义在 /etc/permission/platform.xml 中了,此处 sdcard_r 映射 AID_SDCARD_R 对应 gID = 1015,这就是权限映射到 gID 的整个关系。总共分为三层:
那么第二个问题,当我们安装应用完成,启动应用,应用的进程是如何启动并被赋予进程属性的呢?每个应用都会运行在自己的 Dalvik 虚拟机进程中,但是为了提高启动效率,AndroID 不会为每个应用都新建一个 Dalvik 进程,而是采用 fork 的形式。每个进程都 fork form zygote 进程。那么 fork 比起 new ,效率提高在哪里?因为 zygote 进程已经预加载了大部分核心和 Java 应用框架库,fork 的子进程会继承 zygote 的进程空间,也就是说,fork 的子进程,可以共享这些预加载的副本(记住,是副本,不是直接共享。fork 时,会 copy-on-write 预加载的内容),减少了重新加载核心库的时间。当 zygote 收到启动新进程的请求时,它会 fork 自身出一个子进程,并对该子进程做特殊化处理。其源代码位于 dalvik/vm/native/dalvik_system_Zygote.c 中。forkAndSpecializeCommon() 的主要代码如下:
static pID_t forkAndSpecializeCommon(const u4* args, boolisSystemServer) {...pID = fork(); //创建新进程if (pID == 0) //判断是否是root,有没有权限修改自己的进程属性{setgroupsIntarray(gIDs); //设置进程的所有组setrlimitsFromArray(rlimits);setgID(gID); //设置进程的组IDsetuID(uID); //设置进程的用户ID}...}
如上所示:这里设置进程的组 ID 和用户 ID,通过 fork 创建的子进程调用 setgroups Intarray 设置该进程所属的组,这样应用程序就拥有了该组的权限,并且可以通过 setgID() 及 setuID() 确定应用程序的 GID 及 UID 值。刚刚开始 fork 时,子进程是以 root 执行的,所以它可以更改自己的进程属性,当属性都设置完成,子进程就以分配的 GID 和 UID 执行,此时,子进程无法再更改自己的进程属性了,因为用户 ID 已经不是 root 即 ! = 0 了,没有修改自己进程属性的权限了。
adb shell ps,看下进程列表:
USER PID PPID VSZ RSS WCHAN ADDR S nameroot 1 0 10456 2352 SyS_epoll_wait 0 S init...root 1620 1 1614572 20312 poll_schedule_timeout 0 S zygote...u0_a88 3468 1620 1929916 112340 SyS_epoll_wait 0 S com.feelschaotic.demou0_a90 3574 1620 1696012 24176 SyS_epoll_wait 0 S com.demo.pushdemou0_a90 3607 1620 1708844 26748 SyS_epoll_wait 0 S com.demo.pushdemo:multu0_i0 3741 1879 1557800 11720 SyS_epoll_wait 0 S com.androID.Chrome:sandBoxedu0_a15 3774 1620 1690604 22056 SyS_epoll_wait 0 S com.Google.androID.ext.servicessystem 3805 1620 1687840 19688 SyS_epoll_wait 0 S com.androID.keychainu0_a42 3831 1620 1834408 45660 SyS_epoll_wait 0 S com.androID.Chrome...
PID 表示应用的进程 ID,PPID 表示父进程 ID,name 表示进程名称(一般情况下 name 是应用包名)。可以看到 zygote 进程是由 init 进程启动,所有的应用进程的父进程都是 zygote。USER 表示的是进程的专有用户,这个我们下次再详细讲讲 AndroID 的用户管理机制。好了,既然如此,每个应用进程都分配好自己的 GID、UID和补充 GID,系统内核和守护进程就可以用这些标识来决定,是否要赋予进程权限。
权限的检查1. 系统内核层权限检查
思考一下:如果我们的应用没有在 AndroIDManifest.xml 中申请 androID.permission.INTERNET 权限就进行网络请求,是不是会报 Permission denIEd 错误。这个权限,是谁来检查?其他进程来检查吗?明显不是,网络访问权限是由低层来进行控制的。AndroID 的访问控制,和 linux 是一样的,但 AndroID 增加了个特有的网络访问安全控制机制,也就是说,创建网络套接字的进程,必须属于 inet 组。
如上内核代码,current_has_network(voID) 方法检查了进程的所在组。如果不在 inet 组,则直接返回错误。所以为了使我们的应用具有访问网络的能力,我们需要在 AndroIDManifest.xml 中申请 INTERNET 权限,经过解析,逐步映射到内核层的组 ID 和用户 ID,最终才能通过内核层的检查。你可能会有疑问,那非内核层的其他 C/C++ 层,要怎么拿到进程的所在组信息呢?在 PMS 初始化所有包信息之后,就会调用 mSettings.writeLPr()。
这段代码的任务就是将mPackages 中保存的所有包的信息保存到 /data/system/packages.List。所以,packages.List中保存了所有应用申请的权限,C代码只要读这个文件就能判断某个应用是否申请了我们要求的权限。
2. 框架层
因为 AndroID 6.0 之前组件不能在运行时改变权限,所以系统的权限检查执行过程是静态的。这个情况下,组件的角色和权限的等安全属性会被放置在元数据中,即 AndroIDManifest.xml 文件中,而不是组件的本身。系统包管理器会负责记录组件的权限,所以静态权限检查可以从包管理器拿到权限,由运行环境或容器来执行权限检查,这样子可以把业务逻辑和安全决策分离开来,但是灵活性不足。那 AndroID 组件可不可以不预先声明权限在 AndroIDManifest.xml 中呢?答案是:可以的。AndroID 的动态权限执行,可以让组件自身执行权限检查,而不是运行环境。所以接下来我们将深入了解框架层的动态和静态权限执行的原理。
动态权限执行
动态权限执行,最典型的场景,就是 IPC。AndroID 的核心系统服务统一会注册到服务管理器,任何应用,只要知道服务的注册名称,就可以拿到对应的 Binder引用,就可使用 Binder IPC 机制调用服务。因为 Binder 没有内置的访问控制机制,所以每个系统服务需要自己实现访问控制机制。系统服务可以直接检查调用者的 UID,通过限定 UID 来控制访问权限,这种方式简单直接,但是对于非固定UID的应用,就比较棘手了。而且大部分服务,并不关心调用者的 UID,只需要检查调用者是否被赋予特定的权限即可。所以这种方式,比较适合只允许以 root(UID:0) 或 system(UID:1000) 运行的进程访问的服务检查。那换一种方式,服务怎么拿到调用者的权限列表?我们知道,大部分 UID 都是和包一一对应的,除了共享 UID。(共享 UID 后面再详细解释)使用 Binder.getCallingUID() 和 Binder.getCallingPID() 获取调用者的 UID 和 PID,通过 UID 在包管理器中查询到对应应用的权限。androID.content.Context 类中就有 checkPermission(String permission, int pID, int uID) 方法。实质上会调用到 PMS 中的 checkUIDPermission(String pername, int uID),如下:AndroID 6.0 以下 PMS 中的 checkUIDPermission(String pername, int uID)
public int checkUIDPermission(String permname, int uID) {synchronized (mPackages) {Object obj = mSettings.getUserIDLPr(UserHandle.getAppID(uID));if (obj != null) {GrantedPermissions gp = (GrantedPermissions)obj;if (gp.grantedPermissions.contains(permname)) {return PackageManager.PERMISSION_GRANTED;}} else {HashSet<String> perms = mSystemPermissions.get(uID);if (perms != null && perms.contains(permname)) {return PackageManager.PERMISSION_GRANTED;}}}return PackageManager.PERMISSION_DENIED;}
AndroID 6.0 以下的 checkUIDPermission() 方法比较简单,首先,基于入参 uID 获取应用的 appID,拿到权限列表对象(也就是 packages.xml 里的 <package> 映射),如果 GrantedPermissions 类中的 grantedPermissions 集合包含目标权限,则检查通过。如果没有该 GrantedPermissions 对象,则检查目标权限是否可以被自动授予,实际上 mSystemPermissions 就是 platform.xml 文件中的 <assing-permission> 标签映射缓存,记录了一些系统级应用的 uID 对应的 permission。例:
...<assign-permission name="androID.permission.MODIFY_AUdio_SETTINGS" uID="cameraserver" /><assign-permission name="androID.permission.ACCESS_SURFACE_FliNGER" uID="cameraserver" /><assign-permission name="androID.permission.UPDATE_DEVICE_STATS" uID="cameraserver" /><assign-permission name="androID.permission.ACCESS_SURFACE_FliNGER" uID="graphics" />...
AndroID 6.0 及以上 PMS 中的 checkUIDPermission(String pername, int uID)
@OverrIDepublic int checkUIDPermission(String permname, int uID) {final int callingUID = Binder.getCallingUID();final int callingUserID = UserHandle.getUserID(callingUID);final boolean isCallerInstantApp = getInstantAppPackagename(callingUID) != null;final boolean isUIDInstantApp = getInstantAppPackagename(uID) != null;final int userID = UserHandle.getUserID(uID);if (!sUserManager.exists(userID)) {return PackageManager.PERMISSION_DENIED;}synchronized (mPackages) {Object obj = mSettings.getUserIDLPr(UserHandle.getAppID(uID));if (obj != null) {...final SettingBase settingBase = (SettingBase) obj;final Permissionsstate permissionsstate = settingBase.getPermissionsstate();if (permissionsstate.hasPermission(permname, userID)) {if (isUIDInstantApp) {BasePermission bp = mSettings.mPermissions.get(permname);if (bp != null && bp.isInstant()) {return PackageManager.PERMISSION_GRANTED;}} else {return PackageManager.PERMISSION_GRANTED;}}...} else {ArraySet<String> perms = mSystemPermissions.get(uID);if (perms != null) {if (perms.contains(permname)) {return PackageManager.PERMISSION_GRANTED;}...}}}return PackageManager.PERMISSION_DENIED;}
可以注意到,6.0 之后 checkPermission() 方法有所改变。多了从 mSettings.mPermissions 去查询权限列表。关键就在于这个 mSettings 里面保存的这个 SettingBase 对象,它记录了 Permissionsstate 也就是权限的授予情况。
// Permissionsstate.javapublic boolean hasPermission(String name, int userID) {enforceValIDUserID(userID);if (mPermissions == null) {return false;}PermissionData permissionData = mPermissions.get(name);return permissionData != null && permissionData.isGranted(userID);}
所以检查权限的流程是本来就有的,6.0 之后差异仅在于:危险级别权限可以动态修改授权情况,也就是修改 PermissionState 的 mGranted 值,所以每次权限执行,都会查询下 mGranted 值。
静态权限执行
静态权限执行的典型场景,是跨应用组件交互。我们使用隐式 Intent 来表达意图,搜索匹配的组件,如果有多个,d出选择框,目标组件被选定后,会由 ActivityManagerService 执行权限检查,检查目标组件是否有相应的权限要求,如果有,则把权限检查的工作交给 PMS,去检查调用者有没有被授权这些权限。接下来的总体的流程和动态执行流程大致相同:Binder.getCallingUID()和Binder.getCallingPID()获取调用者的 UID 和 PID,然后利用 UID 映射包名,再获得相关权限集合。如果权限集合中含有所需权限即启动,否则抛出 SecurityException 异常。静态权限执行这里,我们可以详细了解下,每种组件的权限检查时机和具体顺序是怎么样的。
组件权限执行
思考一下,什么时候会执行对调用者的权限检查?那肯定是在目标组件被调用的时候,去解析目标组件声明的权限,如果有,就执行权限检查。
Activity 和 Service。Activity 显而易见,会在 startActivity() 和 startActivityForResult() 里解析到声明权限的 Activity 时,就执行权限检查。而 Service startService()、stopService() 和 bindService(),这 3 个方法被调用时都会进行权限检查。
广播。我们注意到,发送广播除了常用的 sendbroadcast(Intent intent),还有个 sendbroadcast(Intent intent, String receiverPermission),该方法可以要求广播接受者具备特定的权限,但是,调用 sendbroadcast 是不会进行权限检查的,因为广播是异步的,所以权限检查会在 intent 传递到已注册的广播接受者时进行,如果接收者不具备特定的权限,则不会接收到该广播,也不会收到 SecurityException 异常。反过来,接收者可以要求广播发送者必须具备的权限,所要求的权限在 manifest 文件中设置 <receiver> 标签的 permission 属性,或者动态注册时指定 registerReceiver(broadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler),权限检查也是在广播传递时执行。所以,收发广播可以分开指定权限。值得一提的是,一些系统广播被声明为 protected,并且只能由系统进程发送,比如 PACKAGE_INSTALLED。只能由系统进程发送,这个限制会在内核层进行检查,对调用者的 UID 进行匹配,只能是 SYstem_UID、PHONE_UID、SHELL_UID、BLUetoOTH_UID 或 root。如果其他 UID 的进程试图发送系统广播,则会收到 SecurityException 异常。
想了解所有的系统广播,可以打开/system/framework/framework-res.apk 中的 AndroIDManifest.xml <protected-broadcast> 标签详细了解。
ContentProvIDer。ContentProvIDer 可以为读写分别指定不同的权限,即:调用目标 provIDer、query() 方法 和 insert()、update()、delete() 都会进行权限检查。
综上所述,AndroID 的权限的检查会在各个层次上实施。高层的组件,例如应用和系统服务,通过包管理器查询应用程序被赋予的权限,并决定是否准予访问。低层的组件,通常不访问包管理器,比如本地守护进程,依赖于进程的 UID、GID 和补充 GID 来决定赋予。访问系统资源时,如设备文件、UNIX 域套接字和网络套接字,则由内核根据所有者、目标资源的访问权限和访问进程的进程属性或者 packages.List 来进行控制。
共享 UID
最后简单说下共享 UID,填一下前面挖的坑。虽说 AndroID 会为每一个应用分配唯一的 UID,但如果应用使用相同的密钥签发,就可以使用相同 UID 运行,也就是运行在同一个进程中。这个特性被系统应用和核心框架服务广泛使用,比如:Google Play 和 Google 定位服务,请求同一进程内的 Google 登录服务,从而达到静默自动同步用户数据的体验。值得注意的是:AndroID 不支持将一个已安装的应用,从非共享 UID 切换到共享状态,因为改变了已安装应用的 UID,会导致应用失去对自己文件的访问权限(在一些早期 AndroID 版本中),所以如果使用共享 UID 必须从一开始就设计好。
总结以上是内存溢出为你收集整理的Android权限机制全部内容,希望文章能够帮你解决Android权限机制所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)