AndroidManifest.xml权限的定义方式通过<uses-permission>标签实现,<uses-permission>标签表示Android应用程序被授予的权限。
下面以Camera 应用程序为例具体分析AndroidManifest.xml中的权限定义。Camera应用程序需要使用系统提供的多种特性,在AndroidManifest.xml中定义了访问这些特性所需权限,比如照相(CAMERA)、录制音频(RECORD_AUDIO)、访问GPS定位信息(ACCESS_FINE_LOCATION)、设置壁纸(SET_WALLPAPER)等。下面举例说明如何在AndroidManifest.xml中为Camera应用程序设定权限。
1)定义应用程序有照相功能权限CAMERA。
- <uses-permission android:name="android.permission.CAMERA" />
- <uses-feature android:name="android.hardware.camera" />
- <uses-feature
- android:name="android.hardware.camera.autofocus"
- android:required="false" />
在以上的<uses-permission>属性中,android:name表示权限名称为CAMERA。<uses-feature>标签表示该应用程序运行所需使用的硬件或软件特性。<uses-feature>属性中的android:required表示应用程序的正常运行是否必须依赖于硬件特性,比如autofocus。设定为false,表示不一定需要此硬件特性也能正常运行;反之,true表示该硬件特性是正常运行的必需条件。
2)定义应用程序自定义壁纸权限SET_WALLPAPER。需要说明的是,此权限属于Normal权限级别,定义一个新壁纸一般不会影响到敏感信息。
- <uses-permission
- android:name="android.permission.SET_WALLPAPER"/>
3)定义应用程序具备读取短信权限READ_SMS。此权限属于Dangerous权限级别,因为应用程序一旦具备了读取短信的权限,就可以访问到短信中的敏感信息,具有一定的危险性。如果应用程序没有该权限,直接访问受保护的API,将抛出SecurityException。
- <uses-permission
- android:name="android.permission.READ_SMS" />
(2)Android应用程序权限的执行
AndroidManifest.xml中定义的权限是如何被执行的?这些申请的权限需要映射到底层的用户和组权限,才能够被执行,被应用程序启用。当Android系统启动时,SystemServer会首先启动PackageManagerService安装包服务,PackageManagerService是一个负责处理各种应用程序的安装、卸载、管理等工作的服务。PackageManagerService服务中的parsePackage函数将对AndroidManifest.xml文件进行解析,然后获取应用程序所申请的权限。系统根据所申请的这些权限,授予应用程序相应权限的组ID。同时应用程序在安装时系统已经赋予了它唯一的用户ID,这样一来,拥有用户ID和组ID的应用程序便可正常启动。下面对应用程序权限的执行进行代码层面的分析。
PackageManagerService源代码PackageManagerService.java文件位于frameworks/base/services/ java/com/android/server/目录下。负责对权限进行解析的parsePackage函数由Package ManagerService服务中的installPackageLI函数调用,它所属的PackageParser类的源代码PackageParser.java位于frameworks/base/core/java/android/content/pm/目录下。
parsePackage函数主要是对AndroidManifest.xml中<uses-permission>标签进行解析。在进行解析时,parsePackage会保留“android.permission.***”定义,在解析完成后,会根据保留的权限定义调用grantPermissionsLP函数获取权限对应的组ID。parsePackage函数的代码实现如下:
- private Package parsePackage(Resources res,XmlResourceParser parser, int flags, String[] outError)throwsXmlPullParserException, IOException
- {
- ……
- else if (tagName.equals("uses-permission"))
- {
- sa=res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestUsesPermission);
- String name = sa.getNonResourceString(
- com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
- sa.recycle();
- //检测是否已经保存了该请求权限信息,
- //如果没有保存就将其保存至pkg.requestedPermissions
- if (name != null &&!pkg.requestedPermissions.contains(name))
- {
- pkg.requestedPermissions.add(name.intern());
- pkg.requestedPermissionsRequired.add(Boolean.TRUE);
- }
- XmlUtils.skipCurrentTag(parser);
- }
解析完<uses-permission>标签后,grantPermissionsLP函数将根据parsePackage保留的“android.permission.***”定义为应用程序分配具有相应权限的组ID。应用程序拥有此组ID,在启动之后,便具备了其申请的权限。相关代码片段如下:
- private void grantPermissionsLP(PackageParser.Packagepkg, boolean replace)
- {
- ……
- if (allowed) {
- if (!gp.grantedPermissions.contains(perm))
- {
- changedPermission = true;
- gp.grantedPermissions.add(perm);
- //这里把相应的组都保存到了gids中
- gp.gids = appendInts(gp.gids, bp.gids);
- } else if (!ps.haveGids)
- gp.gids = appendInts(gp.gids, bp.gids);
- }
- ……
- }
应用程序获取组ID之后,将调用ActivityManagerService类的startProcessLocked方法启动应用程序。应用程序在启动之后便具备了组ID中所有的权限。ActivityManagerService类代码位于frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中。
其中startProcessLocked方法启动应用程序的代码实现如下:
- private final void startProcessLocked(ProcessRecord app,String hostingType, String hostingNameStr)
- {
- ...
- try {
- //获取组ID,保存到gids
- gids = mContext.getPackageManager().getPackageGids(
- app.info.packageName);
- }
- ...
- //这里获取前面保存的gids,并启动
- intpid = Process.start("android.app.ActivityThread",
- mSimpleProcessManagement? app.processName :
- null, uid, uid, gids, debugFlags, null);
- }
以上代码显示了startProcessLocked启动应用程序之后创建了一个新的进程android.app.ActivityThread,所传入的参数包括组gid,用户uid。创建新进程,利用JNI调用forkAndSpecializeCommon函数创建一个SystemServer进程,其源代码位于dalvik/vm/native/dalvik_system_Zygote.c中。forkAndSpecializeCommon的主要代码如下:
- static pid_t forkAndSpecializeCommon(const u4* args, boolisSystemServer)
- {
- ......
- pid = fork(); //创建新进程
- if (pid == 0)
- {
- setgroupsIntarray(gids); //设置进程的所有组
- setrlimitsFromArray(rlimits);
- setgid(gid); //设置进程的组ID
- setuid(uid); //设置进程的用户ID
- }
- ...
- }
在这里真正实现了设置进程的组ID和用户ID,通过fork创建的子进程调用setgroups Intarray设置该进程所属的组,这样应用程序就拥有了该组的权限,并且可以通过setgid及setuid确定应用程序的gid及uid值。
通过代码层面的分析可见,Android权限机制从权限设置到执行的过程是相当严谨的。应用程序在应用层的AndroidManifest.xml中所申请的权限将会在Android系统启动时,经过解析后,逐步映射到内核层的组ID和用户ID,最终由内核层的setgid()和setuid()函数设置后才能执行。