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。

        
  1. <uses-permissionandroid:name="android.permission.CAMERA"/>
  2. <uses-featureandroid:name="android.hardware.camera"/>
  3. <uses-feature
  4. android:name="android.hardware.camera.autofocus"
  5. android:required="false"/>

在以上的<uses-permission>属性中,android:name表示权限名称为CAMERA。<uses-feature>标签表示该应用程序运行所需使用的硬件或软件特性。<uses-feature>属性中的android:required表示应用程序的正常运行是否必须依赖于硬件特性,比如autofocus。设定为false,表示不一定需要此硬件特性也能正常运行;反之,true表示该硬件特性是正常运行的必需条件。

2)定义应用程序自定义壁纸权限SET_WALLPAPER。需要说明的是,此权限属于Normal权限级别,定义一个新壁纸一般不会影响到敏感信息。

        
  1. <uses-permission
  2. android:name="android.permission.SET_WALLPAPER"/>

3)定义应用程序具备读取短信权限READ_SMS。此权限属于Dangerous权限级别,因为应用程序一旦具备了读取短信的权限,就可以访问到短信中的敏感信息,具有一定的危险性。如果应用程序没有该权限,直接访问受保护的API,将抛出SecurityException。

        
  1. <uses-permission
  2. 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函数的代码实现如下:

        
  1. privatePackageparsePackage(Resourcesres,XmlResourceParserparser,intflags,String[]outError)throwsXmlPullParserException,IOException
  2. {
  3. ……
  4. elseif(tagName.equals("uses-permission"))
  5. {
  6. sa=res.obtainAttributes(attrs,
  7. com.android.internal.R.styleable.AndroidManifestUsesPermission);
  8. Stringname=sa.getNonResourceString(
  9. com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
  10. sa.recycle();
  11. //检测是否已经保存了该请求权限信息,
  12. //如果没有保存就将其保存至pkg.requestedPermissions
  13. if(name!=null&&!pkg.requestedPermissions.contains(name))
  14. {
  15. pkg.requestedPermissions.add(name.intern());
  16. pkg.requestedPermissionsRequired.add(Boolean.TRUE);
  17. }
  18. XmlUtils.skipCurrentTag(parser);
  19. }

解析完<uses-permission>标签后,grantPermissionsLP函数将根据parsePackage保留的“android.permission.***”定义为应用程序分配具有相应权限的组ID。应用程序拥有此组ID,在启动之后,便具备了其申请的权限。相关代码片段如下:

        
  1. privatevoidgrantPermissionsLP(PackageParser.Packagepkg,booleanreplace)
  2. {
  3. ……
  4. if(allowed){
  5. if(!gp.grantedPermissions.contains(perm))
  6. {
  7. changedPermission=true;
  8. gp.grantedPermissions.add(perm);
  9. //这里把相应的组都保存到了gids中
  10. gp.gids=appendInts(gp.gids,bp.gids);
  11. }elseif(!ps.haveGids)
  12. gp.gids=appendInts(gp.gids,bp.gids);
  13. }
  14. ……
  15. }

应用程序获取组ID之后,将调用ActivityManagerService类的startProcessLocked方法启动应用程序。应用程序在启动之后便具备了组ID中所有的权限。ActivityManagerService类代码位于frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中。

其中startProcessLocked方法启动应用程序的代码实现如下:

        
  1. privatefinalvoidstartProcessLocked(ProcessRecordapp,StringhostingType,StringhostingNameStr)
  2. {
  3. ...
  4. try{
  5. //获取组ID,保存到gids
  6. gids=mContext.getPackageManager().getPackageGids(
  7. app.info.packageName);
  8. }
  9. ...
  10. //这里获取前面保存的gids,并启动
  11. intpid=Process.start("android.app.ActivityThread",
  12. mSimpleProcessManagement?app.processName:
  13. null,uid,uid,gids,debugFlags,null);
  14. }

以上代码显示了startProcessLocked启动应用程序之后创建了一个新的进程android.app.ActivityThread,所传入的参数包括组gid,用户uid。创建新进程,利用JNI调用forkAndSpecializeCommon函数创建一个SystemServer进程,其源代码位于dalvik/vm/native/dalvik_system_Zygote.c中。forkAndSpecializeCommon的主要代码如下:

        
  1. staticpid_tforkAndSpecializeCommon(constu4*args,boolisSystemServer)
  2. {
  3. ......
  4. pid=fork();//创建新进程
  5. if(pid==0)
  6. {
  7. setgroupsIntarray(gids);//设置进程的所有组
  8. setrlimitsFromArray(rlimits);
  9. setgid(gid);//设置进程的组ID
  10. setuid(uid);//设置进程的用户ID
  11. }
  12. ...
  13. }

在这里真正实现了设置进程的组ID和用户ID,通过fork创建的子进程调用setgroups Intarray设置该进程所属的组,这样应用程序就拥有了该组的权限,并且可以通过setgid及setuid确定应用程序的gid及uid值。

通过代码层面的分析可见,Android权限机制从权限设置到执行的过程是相当严谨的。应用程序在应用层的AndroidManifest.xml中所申请的权限将会在Android系统启动时,经过解析后,逐步映射到内核层的组ID和用户ID,最终由内核层的setgid()和setuid()函数设置后才能执行。


更多相关文章

  1. 如何在android style文件中使用自定义属性
  2. Android(安卓)Weekly - 第 173 期
  3. 自定义ViewGroup实现瀑布流效果
  4. Android解决异常: startRecording() called on an uninitialized
  5. 【android】AIDL传递自定义类型参数
  6. android Service Binder交互通信实例详解
  7. android 自定义view 初识
  8. Android(安卓)自定义View实现仿微信语音界面
  9. Android复杂自定义Listview实现

随机推荐

  1. Android面试题总结(一)
  2. android studio 怎么取消默认AppcompatAc
  3. android SDK 快速更新配置
  4. 再说Android中实现全屏的方法
  5. Android(安卓)OpenGL ES 开发教程 从入门
  6. (Android)用Socket的小例子
  7. Android Recovery的汉化 显示中文
  8. Android Service用法讲解与实例
  9. Android 10.0 Andorid.bp 动态编译模块
  10. Android 4.x 获取存储卡路径的方式