Android应用程序权限机制的源代码分析(2)

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-permission android:name="android.permission.CAMERA" /> 
  2. <uses-feature android: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. private Package parsePackage(Resources res,XmlResourceParser parser, int flags, String[] outError)throwsXmlPullParserException, IOException  
  2. {  
  3.  ……  
  4. else if (tagName.equals("uses-permission"))  
  5. {  
  6.  sa=res.obtainAttributes(attrs,  
  7.   com.android.internal.R.styleable.AndroidManifestUsesPermission);  
  8.  String name = 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);  

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

  
  
  
  
  1. private void grantPermissionsLP(PackageParser.Packagepkg, boolean replace)  
  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.  } else if (!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. private final void startProcessLocked(ProcessRecord app,String hostingType, String hostingNameStr)  
  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);  

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

  
  
  
  
  1. static pid_t forkAndSpecializeCommon(const u4* 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. ...  

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

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


你可能感兴趣的:(Android应用程序权限机制的源代码分析(2))