ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
### 应用程序的安装过程 - Package管理服务PackageManagerService在安装一个应用程序的过程中,会对这个应用程序的配置文件AndroidManifest.xml进行解析,以便可以获得它的安装信息。一个应用程序可以配置的安装信息很多,其中,最重要的就是它的组件信息。 - 一个应用程序组件只有在配置文件中被正确地配置之后,才可以被Activity管理服务ActivityManagerService正常地启动起来。 - Package管理服务PackageManagerService在安装一个应用程序时,如果发现它没有与其他应用程序共享同一个Linux用户ID ,那么就会为它分配一个唯一的Linux用户ID,以便它可以在系统中获得合适的运行权限。 - 一个应用程序除了拥有一个Linux用户ID之外,还可以拥有若干个Linux用户组ID ,以便可以在系统中获得更多的资源访问权限,例如,读取联系人信息、使用摄像头、发送短信,以及拨打电话等权限。这些Linux用户组ID也是由Package管理服务PackageManagerService来负责分配 - Package管理服务PackageManagerService在安装一个应用程序时,如果发现它申请了一个特定的资源访问权限,那么就会为它分配一个相应的Linux用户组ID。 **在分析Android应用程序的安装过程中,我们主要关注它们的组件信息的解析过程,以及它们的Linux用户ID和Linux用户组ID的分配过程。** - System进程在启动时,会调用PackageManagerService类的静态成员函数main将系统的Package管理服务PackageManagerService启动起来。 - Package管理服务PackageManagerService在启动过程中,会对系统中的应用程序进行安装,因此,接下来我们就从PackageManagerService类的静态成员函数main开始分析应用程序的安装过程。 - Package管理服务Pac kageManagerSer叫ce安装应用程序的过程一共分为17个步骤 - 下图是step1-6的示意图 ![](https://i.imgur.com/pQ8M6tY.png) #### **Step-1: PackageManagerService.main** PackageManagerService的启动(**不同版本Android系统的PackageManagerService类不一样,但是核心意思不变**) 代码如下 ---------- ~~~ 1 class PackageManagerService extends IPackageManager.Stub { 2 ................. 3 public static final IPackageManager main(Context context, boolean factoryTest) { 4 PackageManagerService m = new PackageManagerService(context, factoryTest); 5 ServiceManager.addService("package", m); 6 return m; 7 } 8 ............. 9 } ~~~ 第4行代码首先将Package管理服务PackageManagerService启动起来,接着第6行代码再将它注册到Service Manager中,这样系统中的其他组件就可以通过Servi ce Manager来获得它的访问接口了。 - Package管理服务Pac kageManagerService在启动过程中,会对系统中的特定目录进行扫描,以便可以对保存在里面的应用程序进行安装,相关的代码如下所示。 ---------- ~~~ 1 package com.android.server.pm; 2 class PackageManagerService extends IPackageManager.Stub { 3 ........ 4 final Settings mSettings;//用来管理应用程序的安装信息的。例如,用来管理Package管理服务 5 //PackageManagerService为应用程序分配的Linux用户ID和Linux用户组ID。 6 ................ 7 public static final IPackageManager main(Context context, boolean factoryTest) { 8 //将Package管理服务PackageManagerService启动起来 9 PackageManagerService m = new PackageManagerService(context, factoryTest); 10 //将它注册到Service Manager中,这样系统中的其他组件就可以通过ServiceManager来获得它的访问接口了 11 ServiceManager.addService("package", m); 12 return m; 13 } 14 ....... 15 16 public PackageManagerService(Context context, boolean factoryTest) { 17 .............. 18 mSettings = new Settings(); 19 .............. 20 synchronized (mInstallLock) { 21 synchronized (mPackages) { 22 .......... 23 File dataDir = Environment.getDataDirectory();//来获得系统的数据目录/data,并且保存在变量dataDir中 24 ............. 25 26 //通过dataDir这个目录来获得目录mDrmAppPrivateInstallDir,对应的系统路径是/data/app-private 27 //保存的是受DRM保护的私有应用程序(DRM的全称为Digital Right Management , 28 //即数字权限管理技术,是用来保护数字内容版权的一种技术 29 mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); 30 ............. 31 32 mRestoredSettings = mSettings.readLP();//恢复上一次的应用程序安装信息 33 ............. 34 //开始扫描和安装保存在特定目录中的应用程序了。 35 36 //调用Environment类的静态成员函数getRootDirectory来获得Android系统目录/system , 37 //接下来再通过这个目录来获得另外一个目录mFrameworkDir ,它对应的系统路径为/system/framework , 38 //里面保存的应用程序是资源型的。 39 //资源型的应用程序是用来打包资源文件的,它们不包含有执行代码。 40 mFrameworkDir = new File(Environment.getRootDirectory(), "framework"); 41 42 mDalvikCacheDir = new File(dataDir, "dalvik-cache"); 43 44 // Find base frameworks (resource packages without code). 45 mFrameworkInstallObserver = new AppDirObserver( 46 mFrameworkDir.getPath(), OBSERVER_EVENTS, true); 47 mFrameworkInstallObserver.startWatching(); 48 scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM 49 | PackageParser.PARSE_IS_SYSTEM_DIR, 50 scanMode | SCAN_NO_DEX, 0); 51 52 // Collect all system packages. 53 //调用Environment类的静态成员函数getRootDirectory来获得系统目录/system, 54 //接着再通过这个目录来获得另外一个目录mSystemAppDir , 55 //它对应的系统路径为/system/app,里面保存的是系统自带的应用程序。 56 mSystemAppDir = new File(Environment.getRootDirectory(), "app"); 57 mSystemInstallObserver = new AppDirObserver( 58 mSystemAppDir.getPath(), OBSERVER_EVENTS, true); 59 mSystemInstallObserver.startWatching(); 60 scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM 61 | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); 62 63 // Collect all vendor packages. 64 //直接构建了一个目录mVendorAppDir,它对应的系统路径为/vendor/app,里面保存的是由设备厂商提供的应用程序。 65 mVendorAppDir = new File("/vendor/app"); 66 mVendorInstallObserver = new AppDirObserver(mVendorAppDir.getPath(), OBSERVER_EVENTS, true); 67 mVendorInstallObserver.startWatching(); 68 scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM 69 | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0); 70 ................ 71 72 //通过dataDir这个目录来获得目录mAppInstallDir,对应的系统路径是/data/app, 73 // /data/app 目录保存的是由用户自己安装的应用程序 74 mAppInstallDir = new File(dataDir, "app"); 75 ................ 76 77 scanDirLI(mAppInstallDir, 0, scanMode, 0); 78 79 mDrmAppInstallObserver = new AppDirObserver( 80 mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); 81 mDrmAppInstallObserver.startWatching(); 82 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, 83 scanMode, 0); 84 85 ............... 86 final boolean regrantPermissions = mSettings.mInternalSdkPlatform 87 != mSdkVersion; 88 if (regrantPermissions) Slog.i(TAG, "Platform changed from " 89 + mSettings.mInternalSdkPlatform + " to " + mSdkVersion 90 + "; regranting permissions for internal storage"); 91 mSettings.mInternalSdkPlatform = mSdkVersion; 92 //调用PackageManagerService类的成员函数updatePermissionsLP来 93 //为申请了特定的资源访问权限的应用程序分配相应的Linux用户组ID。 94 updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions); 95 //调用PackageManagerService的成员变量mSettings的成员函数writeLP 96 //将前面所获到得的应用程序安装信息保存在本地的一个配置文件中, 97 //以便下一次再安装这些应用程序时,可以将需要保持一致的应用程序信息恢复回来。 98 mSettings.writeLP(); 99 ............. 100 Runtime.getRuntime().gc(); 101 } // synchronized (mPackages) 102 } // synchronized (mInstallLock) 103 } 104 105 ................ 106 } ~~~ - PackageManagerService类有一个类型为Settings的成员变量 mSettings,例如,用来管理Package管理服务PackageManagerService为应用程序分配的Linux用户ID和Linux用户组ID。 > Android系统每次启动时,都会重新安装一遍系统中的应用程序,但是有些应用程序信息每次安装都是需要保持一致的,例如,应用程序的Linux用户ID ; 否则,应用程序每次在系统重新启动之后,表现可能都会不一致。因此, Package管理服务PackageManagerService每次在安装完成应用程序之后,都需要将它们的信息保存下来,以便下次安装时可以恢复回来。 - 恢复上一次的应用程序安装信息是通过调用PackageManagerService类的成员变量mSettings的成员函数readLP来实现的, 如第32行代码所示 - 第23行代码调用Environment类的静态成员函数getDataDirectory来获得系统的数据目录/data,并且保存在变量dataDir中 - 接下来第29行和第74行代码通过这个目录来获得另外两个目录mDnnAppP口vatelnstallDir和mApplnstallDir ,它们对应的系统路径分别是/data/app-private和/data/app 。其中,/data/app-private 目录保存的是受DRM保护的私有应用程序,而/data/app 目录保存的是由用户自己安装的应用程序。 - 第40行代码首先调用Environment类的静态成员函数getRootDirectory来获得Android系统目录/system,接下来再通过这个目录来获得另外一个目录mFrameworkDir,它对应的系统路径为/system/framework ,里面保存的应用程序是资源型的。资源型的应用程序是用来打包资源文件的,它们不包含有执行代码。 - 第56行代码同样是首先调用Environment类的静态成员函数getRootDirectory来获得系统目录/system,接着再通过这个目录来获得另外一个目录mSystemAppDir,它对应的系统路径为/system/app,里面保存的是系统自带的应用程序。 - 第65行代码直接构建了一个目录mVendorAppDir,它对应的系统路径为/vendor/app,里面保存的是由设备厂商提供的应用程序。 - PackageManagerService就得到了五个目录,它们分别是/system/framework、/system/app、/vendor/app 、/data/app和/data/app-private。接下来第48行、第60行、第68行、第77行和第82行代码就会分别调用PackageManagerService类的成员函数scanDirLI来安装保存在它们里面的应用程序。 - 第94行代码再调用PackageManagerService类的成员函数updatePermissionsLP来为申请了特定的资源访问权限的应用程序分配相应的Linux用户组ID 。 - 最后,第98行代码调用PackageManagerService的成员变量mSettings的成员函数writeLP将前面所获到得的应用程序安装信息保存在本地的一个配置文件中,以便下一次再安装这些应用程序时,可以将需要保持一致的应用程序信息恢复回来。 - 由于Android系统每次启动时,都会重新安装一遍系统中的应用程序,但是有些应用程序信息每次安装都是需要保持一致的,例如,应用程序的Linux用户ID;否则,应用程序每次在系统重新启动之后,表现可能都会不一致。因此,Package管理服务PackageManagerService每次在安装完成应用程序之后,都需要将它们的信息保存下来,以便下次安装时可以恢复回来。 #### Settings类 - 先Settings类的成员函数readLP的实现,然后分析PackageManagerService类的成员函数scanDirLI和updatePermissionsLP的实现,最后分析Settings类的成员函数writeLP的实现。 #### **Step2: Settings.readLP** ---------- ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ........ 4 5 private static final class Settings { 6 //下面这两个文件都是用来保存上一次的应用程序安装信息的,其中,后一个文件是用来备份前一个文件的。 7 private final File mSettingsFilename;//指向了系统中的文件/data/system/packages.xml 8 private final File mBackupSettingsFilename;//指向了系统中的文件/data/system/packages-backup.xml , 9 //在文件/data/system/packages-backup.xml和文件/data/system/packages.xml中, 10 //每一个标签值为“package”的xml元素都是用来描述上一次安装的一个应用程序的信息的 11 ............. 12 13 Settings() { 14 File dataDir = Environment.getDataDirectory(); 15 File systemDir = new File(dataDir, "system"); 16 ............. 17 mSettingsFilename = new File(systemDir, "packages.xml"); 18 mBackupSettingsFilename = new File(systemDir, "packages-backup.xml"); 19 mPackageListFilename = new File(systemDir, "packages.list"); 20 } 21 ............. 22 23 void writeLP() { 24 ................ 25 26 mPastSignatures.clear(); 27 28 try { 29 if (str == null) { 30 .............. 31 //文件/data/system/packages-backup.xml不存在。就会以文件/data/system/packages.xml的内容作为上一次的应用程序安装信息。 32 str = new FileInputStream(mSettingsFilename); 33 } 34 //由于文件/data/system/packages -backup皿il和文件/data/system/packages .xml的内容都是xml格式的, 35 //因此,就创建一个XmlPullParser对象parser来解析保存在它们里面的应用程序安装信息。 36 XmlPullParser parser = Xml.newPullParser(); 37 parser.setInput(str, null); 38 39 int type; 40 ................... 41 42 int outerDepth = parser.getDepth(); 43 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 44 && (type != XmlPullParser.END_TAG 45 || parser.getDepth() > outerDepth)) { 46 .............. 47 48 String tagName = parser.getName(); 49 //上一次安装应用程序时保存下来的信息比较多,不过我们主要关注与应用程序的Linux用户ID相关的信息,如下所示。 50 if (tagName.equals("package")) { 51 //调用Settings类的成员函数readPackageLP来解析它的内容,以便可以获得上一次安装这个应用程序时所分配给它的Linux用户ID 52 readPackageLP(parser); 53 } 54 ............... 55 //在文件/data/system/packages-backup.xml和文件/data/system/packages.xml中, 56 //每一个标签值为“shared-user”的xml元素都是用来描述上一次安装应用程序时所分配的一个共享Linux用户的, 57 //调用Settings类的成员函数readSharedUserLP来解析它的内容,以便可以获得上一次安装应用程序时所分配的共享Linux用户ID。 58 59 60 else if (tagName.equals("shared-user")) { 61 readSharedUserLP(parser);// 62 } 63 ............... 64 65 } 66 67 str.close(); 68 69 } catch(XmlPullParserException e) { 70 Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); 71 } catch(java.io.IOException e) { 72 Slog.w(TAG, "Unable to write package manager settings, current changes will be lost at reboot", e); 73 } 74 if (mSettingsFilename.exists()) { 75 if (!mSettingsFilename.delete()) { 76 Log.i(TAG, "Failed to clean up mangled file: " + mSettingsFilename); 77 } 78 } 79 } 80 ............. 81 82 boolean readLP() { 83 FileInputStream str = null; 84 //if语句首先检查文件/data/system/packages-backup.xml是否存在。如果存在,那么接下来第24行代码就会以它的内容作为上一次的应用程序安装信息; 85 if (mBackupSettingsFilename.exists()) { 86 try { 87 str = new FileInputStream(mBackupSettingsFilename); 88 ............. 89 } 90 } catch (java.io.IOException e) { 91 // We'll try for the normal settings file. 92 } 93 } 94 ........... 95 } 96 .............. 97 98 } ~~~ - Settings类的成员变量mSettingsFilename和mBackupSettingsFilename分别指向了系统中的文件/data/system/packages.xml和/data/system/packages-backup.xml ,这两个文件都是用来保存上一次的应用程序安装信息的,其中,后一个文件是用来备份前一个文件的。 - 第85行的if语句首先检查文件/data/system/packages-backup.xml是否存在。如果存在,那么接下来第87行代码就会以它的内容作为上一次的应用程序安装信息;否则,第32行代码就会以文件/data/system/packages.xml的内容作为上一次的应用程序安装信息。 - 由于文件/data/system/packages-backup皿il和文件/data/system/packages.xml的内容都是xml格式的,因此,第36行和第37行代码就创建一个XmlPullParser对象parser来解析保存在它们里面的应用程序安装信息。上一次安装应用程序时保存下来的信息比较多,不过我们主要关注与应用程序的Linux用户ID相关的信息,如第50行到第62行代码所示。 - 在文件/data/system/packages-backup.xml和文件/data/system/packages.xml中,每一个标签值为"package"的xml元素都是用来描述上一次安装的一个应用程序的信息的,第52行代码调用Settings类的成员函数readPackageLP来解析它的内容,以便可以获得上一次安装这个应用程序时所分配给它的Linux用户ID - 在文件/data/system/packages-backup.xml和文件/data/system/packages.xml 中,每一个标签值为"shared-user"的xml元素都是用来描述上一次安装应用程序时所分配的一个共享Linux用户的,第61行代码调用Settings类的成员函数readSharedUserLP来解析它的内容,以便可以获得上一次安装应用程序时所分配的共享Linux用户ID 。 - 接下来,我们首先分析Settings类的成员函数readPackageLP的实现,然后再分析Settings类的成员函数readSharedUserLP的实现。 #### **Step3: Settings.readPackageLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ........... 7 8 private final ArrayList<PendingPackage> mPendingPackages 9 = new ArrayList<PendingPackage>(); 10 ........... 11 12 private void readPackageLP(XmlPullParser parser) 13 throws XmlPullParserException, IOException { 14 String name = null; 15 String realName = null; 16 String idStr = null; 17 String sharedIdStr = null; 18 ............. 19 try { 20 name = parser.getAttributeValue(null, "name"); 21 realName = parser.getAttributeValue(null, "realName"); 22 idStr = parser.getAttributeValue(null, "userId"); 23 uidError = parser.getAttributeValue(null, "uidError"); 24 sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); 25 ............... 26 int userId = idStr != null ? Integer.parseInt(idStr) : 0; 27 ............... 28 if (name == null) { 29 reportSettingsProblem(Log.WARN, 30 "Error in package manager settings: <package> has no name at " 31 + parser.getPositionDescription()); 32 } else if (codePathStr == null) { 33 reportSettingsProblem(Log.WARN, 34 "Error in package manager settings: <package> has no codePath at " 35 + parser.getPositionDescription()); 36 } else if (userId > 0) { 37 packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr), 38 new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode, 39 pkgFlags); 40 ................ 41 } else if (sharedIdStr != null) { 42 userId = sharedIdStr != null 43 ? Integer.parseInt(sharedIdStr) : 0; 44 if (userId > 0) { 45 packageSetting = new PendingPackage(name.intern(), realName, 46 new File(codePathStr), new File(resourcePathStr), 47 nativeLibraryPathStr, userId, versionCode, pkgFlags); 48 packageSetting.setTimeStamp(timeStamp); 49 packageSetting.firstInstallTime = firstInstallTime; 50 packageSetting.lastUpdateTime = lastUpdateTime; 51 mPendingPackages.add((PendingPackage) packageSetting); 52 ............ 53 } else { 54 ............. 55 } 56 } else { 57 ............ 58 } 59 } catch (NumberFormatException e) { 60 ............... 61 } 62 if (packageSetting != null) { 63 ................ 64 } else { 65 XmlUtils.skipCurrentTag(parser); 66 } 67 } 68 ................ 69 70 } 71 72 ............... 73 } ~~~ - 上一步,我们曾讲到---》在文件/data/system/packages-backup.xml和文件/data/system/packages.xml中,每一个标签值为“package”的xml元素都是用来描述上一次安装的一个应用程序的信息的。而标签值等于“package”的xml元素有三个属性name、userld和shared Userld ,其中,属性name用来描述上一次安装的一个应用程序的Package名称;而属性userld和sharedUserld用来描述这个应用程序所使用的独立Linux用户ID和共享Linux用户ID。第20行、第22行和第24行代码分别将这三个属性的值解析出来,并且保存在变量name、idStr和sharedldStr中。 - 上一次安装的每一个应用程序的Package名称都是必须存在的;否则,第29行到第31行代码就会调用PackageManagerService类的成员函数reportSettingsProhlem来报告一个错误。 - 第36行的if语句检查Package名称等于name 的应用程序在上一次安装时是否被分配了一个独立的Linux用户ID 。如果是,那么接下来第37行到第39行代码就会调用Settings类的成员函数addPackageLP将这个应用程序上一次安装时被分配的Linux用户ID保留下来。 - 第41行的if语句检查前面所获得的变量sharedIdStr的值是否不等于null。如果不等于null,那么就说明上一次安装Package名称等于name的应用程序时,Package管理服务PackageManagerService并没有给它分配一个独立的Linux用户ID,而是让它与其他的应用程序共事同一个Linux用户ID 。在这种情况下, Package管理服务PackageManagerService不能马上为这个Package名称等于name的应用程序保留它上次所使用的Linux用户ID,因为这个Linux用户ID不属于它所有,换句话来说,就是这个Linux用户ID可能是一个无效的Linux用户ID 。接下来第45行到第49行代码首先会创建PendingPackage对象,接着再将这个PendingPackage对象保存在Settings类的成员变量mPendingPackages中,用来描述一个Linux用户ID还未确定的应用程序。在接下来的Step6 中,我们就会看到,等到Package管理服务PackageManagerService解析完成上一次的应用程序安装信息中的共享Linux用户信息之后,才能确认保存在Settings类的成员变量mPendingPackages中的应用程序上次所使用的一个共享Linux用户ID是否还是有效的。如果还是有效,那么Package管理服务PackageManagerService才会为它们保留它们上一次所使用的Linux用户ID 。 #### **Step 4 : Settings. addPackageLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ........... 7 private final HashMap<String, PackageSetting> mPackages = 8 new HashMap<String, PackageSetting>(); 9 ............ 10 11 PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath, 12 String nativeLibraryPathString, int uid, int vc, int pkgFlags) { 13 PackageSetting p = mPackages.get(name); 14 if (p != null) { 15 if (p.userId == uid) { 16 return p; 17 } 18 reportSettingsProblem(Log.ERROR, 19 "Adding duplicate package, keeping first: " + name); 20 return null; 21 } 22 p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, 23 vc, pkgFlags); 24 p.userId = uid; 25 if (addUserIdLP(uid, p, name)) { 26 mPackages.put(name, p); 27 return p; 28 } 29 return null; 30 } 31 32 .............. 33 } 34 35 ............... 36 } ~~~ - 在Package管理服务PackageManagerService中,每一个应用程序的安装信息都是使用一个PackageSetting对象来描述的。这些PackageSetting对象被保存在Settings类的成员变量mPackages所描述的一个HashMap中,并且是以它们所描述的应用程序的Package名称为关键字的。 - 从前面的Step 3可以知道,参数name用来描述一个应用程序的Package名称,第13行和第14行代码首先在Settings类的成员变量mPackages中检查是否已经存在一个与它所对应的PackageSetting对象。如果存在,那么接下来第15行的if语句再检查这个PackageSetting对象的成员变量userId是否等于参数uid的值。如果等于,那么就说明Package管理服务PackageManagerService已经为Package名称等于name的应用程序分配过所要求的一个Linux用户ID了,因此,接下来第16行代码就直接返回了。 - 如果Settings类的成员变量mPackages中不存在与参数name所对应的一个PackageSetting对象,即第14行的币吾句为false,那么接下来第22行到第28行代码就会为Package名称等于name的应用程序分配参数uid所描述的一个Linux用户ID 。 - 第22行和第23行代码首先创建了一个PackageSetting对象p ,用来描述Package名称等于name的应用程序的安装信息;接着第24行代码将这个PackageSetting对象p的成员变量userId的值设置为参数uid 的值,以便可以表示Package名称等于name的应用程序所使用的Linux用户ID的值为uid;最后第25行代码调用Settings类的成员函数addUserldLP在系统中保留值为uid 的Linux用户ID 。如果保留成功,那么第26行代码还会将前面所创建的一个PackageSetting对象p保存在Settings类的成员变量mPackages中,以便可以表示Package管理服务PackageManagerService已经为Package名称等于name的应用程序分配过Linux用户ID了。 - 接下来,我们继续分析Settings类的成员函数addUserldLP是如何在系统中保留一个指定的Linux用户ID的,它的实现如下所示。 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ............... 7 private final ArrayList<Object> mUserIds = new ArrayList<Object>(); 8 private final SparseArray<Object> mOtherUserIds = 9 new SparseArray<Object>(); 10 .............. 11 12 private boolean addUserIdLP(int uid, Object obj, Object name) { 13 if (uid >= FIRST_APPLICATION_UID + MAX_APPLICATION_UIDS) { 14 return false; 15 } 16 17 if (uid >= FIRST_APPLICATION_UID) { 18 int N = mUserIds.size(); 19 final int index = uid - FIRST_APPLICATION_UID; 20 while (index >= N) { 21 mUserIds.add(null); 22 N++; 23 } 24 if (mUserIds.get(index) != null) { 25 reportSettingsProblem(Log.ERROR, 26 "Adding duplicate user id: " + uid 27 + " name=" + name); 28 return false; 29 } 30 mUserIds.set(index, obj); 31 } else { 32 if (mOtherUserIds.get(uid) != null) { 33 reportSettingsProblem(Log.ERROR, 34 "Adding duplicate shared id: " + uid 35 + " name=" + name); 36 return false; 37 } 38 mOtherUserIds.put(uid, obj); 39 } 40 return true; 41 } 42 ......... 43 44 } 45 46 ............... 47 } ~~~ - 在Android系统中,大于或者等于FIRST_APPLICATION_UID并且小于(FIRST_APPLICATION_UID+ MAX_APPLICATION_UIDS)的Linux用户ID是保留给应用程序使用的,而小于FIRST_APPLICATION_UID的Linux用户ID是保留给特权用户使用的。FIRST_APPLICATION_UID和MAX_APPLICATION_UIDS的值分别定义为10000和1000,从这里就可以看出,系统最多可以分配1000个Linux用户ID给应用程序使用。虽然小于FIRST_APPLICATION_UID 的Linux用户ID不能作为应用程序的Linux用户ID ,但是它们却可以以共享的方式被应用程序使用。例如,如果一个应用程序想要修改系统的时间,那么它就可以申请与名称为“android.uid.system”的特权用户共享同一个Linux用户ID(1000),即在配置文件中将它的android:sharedUserld属性值设置为“android.uid.system”。 - Settings类的成员变量mUserids是一个类型为ArrayList的列表,用来维护那些已经分配给应用程序使用的Linux用户ID, 即大于或者等于FIRST_APPLICATION_UID 的Linux用户ID。如果保存在这个列表中的第index个位置的一个Object对象的值不等于null ,那么就说明值为(FIRST_APPLICATION_UID+ index)的Linux用户ID已经被分配了。 - Settings类的另外一个成员变量mOtherUserlds是一个类型为SparseArray的稀疏数组,用来维护那些已经分配给特权用户使用的Linux用户ID,即小于FIRST_APPLICATION_UID的Linux用户ID。如果一个小于FIRST_APPLICATION_UID的Linux用户ID已经被分配了,那么它在这个稀疏数组中所对应的一个Object对象的值就不等于null。 >[info] **注意**: 虽然保存在Settings类的成员变量mUserlds和mOtherUserlds中的对象的类型为Object ,但是它们实际上指向的要么是一个PackageSetting对象,要么是一个SharedUserSetting对象。其中,PackageSetting对象所描述的Linux用户D是独立的,而SharedUserSetting对象所描述的Linux 用户ID是共享的。 - 第13行的if语句首先检查要保留的Linux用户ID的值是否大于或者等于(FIRST_APPLICATION_UID+MAX_APPLICATION_UIDS)。如果是,那么就说明要保留的是一个非法的Linux用户ID ,因此,接下来第14行代码就直接返回一个false值给调用者。 - 第17行的if语句接着检查要保留的Linux用户ID的值是否大于或者等于FIRST_APPLICATION_UID。如果是,那么接下来第18行到第30行代码就会将它添加到Setting类的成员变量mUserIds中;否则,第32行到第38行代码就会将它添加到Setting类的成员变量mOtherUserIds中。 - 这一步执行完成之后,返回到前面的Step 2 中,即Settings类的成员函数readLP中,接下来就会调用Settings类的成员函数readSharedUserLP来解析上一次的应用程序安装信息中的共事Linux用户信息。 #### **Step 5: Settings.readSharedUserLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ................. 7 private void readSharedUserLP(XmlPullParser parser) 8 throws XmlPullParserException, IOException { 9 String name = null; 10 String idStr = null; 11 int pkgFlags = 0; 12 SharedUserSetting su = null; 13 try { 14 name = parser.getAttributeValue(null, "name"); 15 idStr = parser.getAttributeValue(null, "userId"); 16 int userId = idStr != null ? Integer.parseInt(idStr) : 0; 17 if ("true".equals(parser.getAttributeValue(null, "system"))) { 18 pkgFlags |= ApplicationInfo.FLAG_SYSTEM; 19 } 20 if (name == null) { 21 reportSettingsProblem(Log.WARN, 22 "Error in package manager settings: <shared-user> has no name at " 23 + parser.getPositionDescription()); 24 } else if (userId == 0) { 25 reportSettingsProblem(Log.WARN, 26 "Error in package manager settings: shared-user " 27 + name + " has bad userId " + idStr + " at " 28 + parser.getPositionDescription()); 29 } else { 30 if ((su=addSharedUserLP(name.intern(), userId, pkgFlags)) == null) { 31 reportSettingsProblem(Log.ERROR, 32 "Occurred while parsing settings at " 33 + parser.getPositionDescription()); 34 } 35 } 36 } catch (NumberFormatException e) { 37 reportSettingsProblem(Log.WARN, 38 "Error in package manager settings: package " 39 + name + " has bad userId " + idStr + " at " 40 + parser.getPositionDescription()); 41 }; 42 .......... 43 } 44 ............. 45 46 } 47 48 ............... 49 } ~~~ - 在文件/data/system/packages-backup.xml或者文件/data/syatem/packages.xml中,每一个标签值等于“shared-user”的xml元素都是用来描述上一次安装应用程序时所分配的一个共享Linux用户的。这个xml元素有三个属性name 、userId和system,其中,属性name和userId描述一个共享Linux用户的名称和ID值;而属性system用来描述这个共享Linux用户ID是分配给一个系统类型的应用程序使用的,还是分配给一个用户类型的应用程序使用的。 * 第14行和第15行代码分别将上一次安装应用程序时所分配的一个共享Linux用户的名称和ID值取出来,并且保存在变量name和idStr中;接着第16行代码再将变量idStr的值转换为一个整数,并且保存 在变量userId中。 * 第17行的if语句检查值等于userId的共享Linux用户ID是否是分配给一个系统类型的应用程序使用的。如果是,那么接下来第18行代码就会将变量pkgFlags的ApplicationInfo.FLAG_SYSTEM位的值设置为1。 * 由于标签值等于“shared-user”的xml元素的属性name和userId是要求存在的,因此,接下来第25行代码就会调用Settings类的成员函数addSharedUserLP在系统中为名称等于name的共享Linux用户保留一个值为userId的Linux用户ID。 #### **Step 6: Settings. addSharedUserLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ............... 7 private final HashMap<String, SharedUserSetting> mSharedUsers = 8 new HashMap<String, SharedUserSetting>(); 9 ............ 10 11 SharedUserSetting addSharedUserLP(String name, int uid, int pkgFlags) { 12 SharedUserSetting s = mSharedUsers.get(name); 13 if (s != null) { 14 if (s.userId == uid) { 15 return s; 16 } 17 reportSettingsProblem(Log.ERROR, 18 "Adding duplicate shared user, keeping first: " + name); 19 return null; 20 } 21 s = new SharedUserSetting(name, pkgFlags); 22 s.userId = uid; 23 if (addUserIdLP(uid, s, name)) { 24 mSharedUsers.put(name, s); 25 return s; 26 } 27 return null; 28 } 29 ............. 30 31 } 32 ............... 33 } ~~~ - 在Package管理服务PackageManagerService中,每一个共享Linux用户都是使用一个SharedUserSettin对象来描述的。这些ShardUserSetting对象被保存在Settings类的成员变量mSharedUsers所描述的一个HashMap中,并且是以它们所描述的共享Linux用户的名称为关键字的。 * 从前面的Step 5可以知道,参数name用来描述一个共享Linux用户的名称,第12行和第13行代码首先在Settings类的成员变量mSharedUsers 中检查是否已经存在一个与它所对应的SharedUserSetting对象。如果存在,那么接下来第14行的if语句再检查这个SharedUserSetting对象的成员变量userId的值是否等于参数uid的值。如果等于,那么就说明Package管理服务PackageManagerService已经为名称等于name的共享Linux用户分配过所要求的一个Linux用户ID了,因此,接下来第15行代码就直接返回了。 * 如果Settings类的成员变量mSharedUsers 中不存在与参数name所对应的一个SharedUserSetting对象,即第13行的if语句为false ,那么接下来第20行到第25行代码就会为名称等于name的共享Linux用户分配参数uid所描述的一个Linux用户ID。 * 第20行代码首先创建了一个SharedUserSetting对象s,用来描述名称等于name的共享Linux用户;接着第21行代码再将这个SharedUserSetting对象s的成员变量userId的值设置为参数uid的值,以便可以表示名称等于name的共享Linux用户所使用的Linux用户ID 的值为uid;最后第22行代码调用Settings类的成员函数addUserldLP在系统中保留一个值为uid的Linux用户ID 。如果保留成功,那么第23行代码还会将前面所创建的一个SharedUserSetting对象s保存在Settings类的成员变量mSharedUsers中,以便可以表示Package管理服务PackageManagerService已经为名称等于name的共享Linux用户分配过Linux用户ID了。 >[info] **注意**:第22行代码在调用Settings类的成员函数addUserIdLP在系统中保留一个Linux用户ID时,传进去的第二参s的类型为SharedUserSetting。这时候在Settings类的成员变量mUserIds中,与参数uid所对应的一个Object对象的类型就为SharedUserSetting。这样Package管理服务PackageManagerService就可以知道值为uid的Linux用户ID是分配给一个共享Linux户使用的。 - 这一步执行完成之后,再次返回到前面的Step 2中,即Settings类的成员函数readLP中,这时候Package管理服务PackageManagerS ervice就获得上-次安装应用程序时所分配的共享Linux用户信息了。 * 在前面的Step 3中提到,如果上一次安装的一个应用程序指定了要与其他应用程序共享同一个Linux用户ID,那么Package管理服务PackageManagerService需要在解析完成上一次的应用程序安装信息中的共享Linux用户信息之后,再为它们保留它们上一次所使用的Linux用户ID。这些未保留Linux用户ID 的应用程序分别被封装成一个PendingPackage对象,并且保存在Settings类的成员变量mPendingPackages中。 * 现在既然Package管理服务PackageManagerService已经获得了上一次安装应用程序时所分配的共享Linux用户信息,接下来我们就继续分析Settings类的成员函数readLP是如何为保存在Settings类的成员变量mPendingPackages中的应用程序保留它们上一次所使用的Linux用户ID的。 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ........ 4 5 private static final class Settings { 6 7 boolean readLP() { 8 .............. 9 10 int N = mPendingPackages.size(); 11 for (int i=0; i<N; i++) { 12 final PendingPackage pp = mPendingPackages.get(i); 13 Object idObj = getUserIdLP(pp.sharedId); 14 if (idObj != null && idObj instanceof SharedUserSetting) { 15 PackageSetting p = getPackageLP(pp.name, null, pp.realName, 16 (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, 17 pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true); 18 ............ 19 p.copyFrom(pp); 20 } else if (idObj != null) { 21 ............. 22 23 } else { 24 ............... 25 26 } 27 } 28 mPendingPackages.clear(); 29 ............ 30 31 } 32 ........... 33 34 } 35 ............. 36 } ~~~ - 第11行到第22行的for循环依次遍历保存在Settings类的成员变量mPendingPackages 中的每一个PendingPackage对象,以便可以为它们所描述的应用程序保留上次安装时所使用的Linux用户ID。 * 对于保存在Settings类的成员变量mPendingPackages中的每一个PendingPackage对象pp来说,如果在Settings类的成员变量mUserIds或者mOtherUserIds中存在一个与它的成员变量userId所对应的Object对象,并且这个Object对象的实际类型为SharedUserSetting,即第14行的if语句为true,那么就说明PendingPackage对象pp所描述的一个应用程序上一次所使用的一个Linux用户ID是有效的。因此,接下来第15行到第19行代码就会调用Settings类的成员函数getPackageLP来为它保留它上一次安装时所使用的一个Linux用户ID,即为它创建一个PackageSetting对象,并且保存在Settings类的成员变量mPackages中。在接下来的Step 12中,我们再分析Settings类的成员函数getPackageLP的实现。 * 这一步执行完成之后,返回到前面的Step 1中,即PackageManagerService类的构造函数中,这时候Package管理服务PackageManagerService就将上一次的应用程序安装信息恢复完成了。接下来第7步到第13步就会继续调用PackageManagerService类的成员函数scanDirLI来安装保存在目录/system/framework、/system/app、/vendor/app 、/data/app和/data/app-private中的应用程序,这个过程如图16-2所示。 ![](https://box.kancloud.cn/e8f1066aaabd742cef9475072b4ed2e8_639x579.jpg) #### **Step 7 : PackageManagerService.scanDirLI** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private void scanDirLI(File dir, int flags, int scanMode, long currentTime) { 6 String[] files = dir.list(); 7 if (files == null) { 8 Log.d(TAG, "No files in app dir " + dir); 9 return; 10 } 11 12 if (false) { 13 Log.d(TAG, "Scanning app dir " + dir); 14 } 15 16 int i; 17 for (i=0; i<files.length; i++) { 18 File file = new File(dir, files[i]); 19 if (!isPackageFilename(files[i])) { 20 // Ignore entries which are not apk's 21 continue; 22 } 23 PackageParser.Package pkg = scanPackageLI(file, 24 flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); 25 // Don't mess around with apps in system partition. 26 if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && 27 mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { 28 // Delete the apk 29 Slog.w(TAG, "Cleaning up failed install of " + file); 30 file.delete(); 31 } 32 } 33 } 34 ............... 35 } ~~~ - 第17行到第32行的for循环依次检查保存在参数dir所描述的一个目录中的每一个文件。如果这些文件的名称是以".apk"为后缀的,即第11行的if语句为true,那么接下来第23行和第24行代码就会调用PackageManagerService类的成员函数scanPackageLI来对它们进行解析。每解析完成一个apk文件之后,PackageManagerService类的成员函数scanPackageLI就会返回一个Package对象pkg给调用者。 * 如果PackageManagerService类的成员函数scanPackageLI发现它所解析的apk文件并不是一个真正的应用程序文件,那么它就会将PackageManagerService类的成员变量mLastScanError的值设置为PackageManager.INSTALL_FAILED_INVALID_APK ,并且返回一个值为null的Package对象pkg给调用者。在这种情况下,如果参数flags的第PackageParser.PARSE_IS_SYSTEM位的值等于0 ,即参数dir所描述的一个目录不是一个系统目录,那么第30行代码就会将刚才所解析的文件删除。从前面的Step l可以知道,目录/data/app和/data/app-private均不是系统目录,因此,当保存在它里面的一个apk文件不是一个真正的应用程序文件时,这个文件就会被删除。 * 接下来,我们继续分析PackageManagerService类的成员函数scanPackageLI的实现,以便可以了解它是如何解析一个应用程序文件的。 #### **Step 8 : PackageManagerService.scanPackageLI** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private PackageParser.Package scanPackageLI(File scanFile, 6 int parseFlags, int scanMode, long currentTime) { 7 mLastScanError = PackageManager.INSTALL_SUCCEEDED; 8 String scanPath = scanFile.getPath(); 9 parseFlags |= mDefParseFlags; 10 PackageParser pp = new PackageParser(scanPath); 11 pp.setSeparateProcesses(mSeparateProcesses); 12 final PackageParser.Package pkg = pp.parsePackage(scanFile, 13 scanPath, mMetrics, parseFlags); 14 ........... 15 16 return scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime); 17 } 18 ............... 19 } ~~~ - 第8行的代码首先得到要解析的应用程序文件所对应的路径名称,接着第10行到第13行代码再以这个路径名称为参数来创建一个PackageParser对象,并且调用这个PackageParser对象的成员函数parsePackage来对它所描述的一个应用程序文件进行解析。 * PackageParser类的成员函数parsePackage成功地解析完成一个应用程序文件之后,就会返回一个Package对象给PackageManagerService类的成员函数scanPackageLI,后者接着会调用PackageManagerService类的另外一个重载版本的成员函数scanPackageLI来对这个Package对象所描述的一个应用程序文件进行安装,以便可以获得它的组件信息,以及为它分配的Linux用户ID等,如第16行代码所示。 * 接下来,我们首先分析PackageParser类的成员函数parsePackage的实现,然后分析PackageManagerService类的另外一个重载版本的成员函数scanPackageLI的实现。 #### **Step 9: PackageParser.parsePackage** ~~~ 1 package android.content.pm; 2 3 public class PackageParser { 4 ............ 5 6 public Package parsePackage(File sourceFile, String destCodePath, 7 DisplayMetrics metrics, int flags) { 8 mParseError = PackageManager.INSTALL_SUCCEEDED; 9 10 mArchiveSourcePath = sourceFile.getPath(); 11 ............. 12 13 XmlResourceParser parser = null; 14 AssetManager assmgr = null; 15 boolean assetError = true; 16 try { 17 assmgr = new AssetManager(); 18 int cookie = assmgr.addAssetPath(mArchiveSourcePath); 19 if(cookie != 0) { 20 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); 21 assetError = false; 22 } else { 23 Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); 24 } 25 } catch (Exception e) { 26 Log.w(TAG, "Unable to read AndroidManifest.xml of " 27 + mArchiveSourcePath, e); 28 } 29 .............. 30 31 String[] errorText = new String[1]; 32 Package pkg = null; 33 Exception errorException = null; 34 try { 35 // XXXX todo: need to figure out correct configuration. 36 Resources res = new Resources(assmgr, metrics, null); 37 pkg = parsePackage(res, parser, flags, errorText); 38 } catch (Exception e) { 39 errorException = e; 40 mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; 41 } 42 ............... 43 44 parser.close(); 45 assmgr.close(); 46 47 // Set code and resource paths 48 pkg.mPath = destCodePath; 49 pkg.mScanPath = mArchiveSourcePath; 50 51 pkg.mSignatures = null; 52 53 return pkg; 54 } 55 56 ............ 57 58 } ~~~ - 第10行代码首先将参数sourceFile所描述的一个应用程序文件的路径名称保存在PackageParser类的成员变量mArchiveSourcePath中,接着第13行到第41行的代码再对这个应用程序进行解析。 * Android应用程序文件是一个以“.apk”为后缀名的归档文件,它里面包含了一个配置文件AndroidManifest.xml。通过解析这个配置文件,我们就可以获得一个应用程序的所有信息。因此,解析一个Android应用程序文件的过程其实就是解析包含在它里面的一个配置文件AndroidManifest.xml 的过程。 * 第17行到第24行代码首先获得保存在要解析的Android应用程序文件中的配置文件AndroidManifest.xml,接着第36行和第37行代码调用PackageParser类的另外一个重载版本的成员函数parsePackage来对这个AndroidManifest.xml文件进行解析,以便可以获得对应的Android应用程序文件的信息。PackageParser类的另外一个重载版本的成员函数parsePackage的实现如下所示。 **frameworks/base/core/java/android/content/pm/PackageParser . java** ~~~ 1 package android.content.pm; 2 public class PackageParser { 3 ............ 4 5 private Package parsePackage( 6 Resources res, XmlResourceParser parser, int flags, String[] outError) 7 throws XmlPullParserException, IOException { 8 AttributeSet attrs = parser; 9 .............. 10 11 String pkgName = parsePackageName(parser, attrs, flags, outError); 12 .............. 13 int type; 14 15 final Package pkg = new Package(pkgName); 16 boolean foundApp = false; 17 18 TypedArray sa = res.obtainAttributes(attrs, 19 com.android.internal.R.styleable.AndroidManifest); 20 .............. 21 22 String str = sa.getNonConfigurationString( 23 com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); 24 if (str != null && str.length() > 0) { 25 ............... 26 27 pkg.mSharedUserId = str.intern(); 28 pkg.mSharedUserLabel = sa.getResourceId( 29 com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); 30 } 31 sa.recycle(); 32 ............ 33 34 while ((type=parser.next()) != parser.END_DOCUMENT 35 && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { 36 ............ 37 38 String tagName = parser.getName(); 39 if (tagName.equals("application")) { 40 if (foundApp) { 41 if (RIGID_PARSER) { 42 outError[0] = "<manifest> has more than one <application>"; 43 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 44 return null; 45 } else { 46 Log.w(TAG, "<manifest> has more than one <application>"); 47 XmlUtils.skipCurrentTag(parser); 48 continue; 49 } 50 } 51 52 foundApp = true; 53 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) { 54 return null; 55 } 56 } else if (tagName.equals("permission-group")) { 57 if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) { 58 return null; 59 } 60 } else if (tagName.equals("permission")) { 61 if (parsePermission(pkg, res, parser, attrs, outError) == null) { 62 return null; 63 } 64 } else if (tagName.equals("permission-tree")) { 65 if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) { 66 return null; 67 } 68 } else if (tagName.equals("uses-permission")) { 69 sa = res.obtainAttributes(attrs, 70 com.android.internal.R.styleable.AndroidManifestUsesPermission); 71 72 // Note: don't allow this value to be a reference to a resource 73 // that may change. 74 String name = sa.getNonResourceString( 75 com.android.internal.R.styleable.AndroidManifestUsesPermission_name); 76 77 sa.recycle(); 78 79 if (name != null && !pkg.requestedPermissions.contains(name)) { 80 pkg.requestedPermissions.add(name.intern()); 81 } 82 83 XmlUtils.skipCurrentTag(parser); 84 85 } 86 87 ............ 88 89 return pkg; 90 } 91 ............. 92 93 } ~~~ * 参数res和parser分别指向了一个Resources对象和一个XmlResourceParser对象,它们均是用来访问一个xml文件的内容的。从前面的调用过程可以知道,这个xml文件就是我们所需要解析的应用程序配置文件AndroidManifest.xml。 * 第10行代码首先调用PackageParser类的成员函数parsePackageName来获得要解析的应用程序的Package名称,接着第15行代码再以这个Package名称为参数来创建一个Package对象pkg,以便可以用来保存即将要解析的应用程序的配置信息。 * 我们可以在一个AndroidManifest.xml文件中配置一个应用程序的很多信息,不过这里我们只关心与共享Linux用户ID 、Linux用户组ID和应用程序组件相关的配置信息,这些配置信息分别保存在manifest、uses-pe1mission和application标签中。 * 第18行到第23行代码用来解析manifest标签中的android:sharedUserId属性。如果我们设置了这个属性,那么就表示正在解析的应用程序要与其他应用程序共享同一个Linux用户ID。在这种情况下,第27行代码就会将这个属性值提取出来,并且将它保存在前面所创建的一个Package对象pkg的成员变量mSharedUserId中。 * 第34行到第85行的while循环用来解析uses-permission和application标签,这两个标签均为manifest标签里面的一个子标签。 - 一个uses-permission标签对应一个资源访问权限。在后面的Step15中,我们就会看到,一个资源访问权限又是与一个Linux用户组ID相对应的,即如果一个应用程序申请了某一个资源访问权限,那么它就会获得一个对应的Linux用户组ID 。 - 一个应用程序可以同时申请多个资源访问权限,即可以在它的配置文件中设置多个uses-permission标签。这些uses-permission标签有一个属性name,用来描述它所对应的一个资源访问权限的名称。第69行到第85行代码将正在解析的应用程序所申请的资源访问权限的名称保存在前面所创建的一个Package对象pkg的成员变量requestedPermissions中。 - 每一个AndroidManifest皿nl文件都必须有一个application标签,用来描述与应用程序组件相关的信息。第53行代码调用PackageParser类的成员函数parseApplication来解析这个标签的内容,以便可以获得正在解析的应用程序的组件配置信息。 接下来,我们就继续分析PackageParser类的成员函数parseApplication的实现。 #### **Step 10:PackageParser.parseApplication** ~~~ 1 package android.content.pm; 2 public class PackageParser { 3 .......... 4 5 private boolean parseApplication(Package owner, Resources res, 6 XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) 7 throws XmlPullParserException, IOException { 8 ............ 9 10 int type; 11 while ((type=parser.next()) != parser.END_DOCUMENT 12 && (type != parser.END_TAG || parser.getDepth() > innerDepth)) { 13 if (type == parser.END_TAG || type == parser.TEXT) { 14 continue; 15 } 16 17 String tagName = parser.getName(); 18 if (tagName.equals("activity")) { 19 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false); 20 if (a == null) { 21 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 22 return false; 23 } 24 25 owner.activities.add(a); 26 27 } else if (tagName.equals("receiver")) { 28 Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true); 29 if (a == null) { 30 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 31 return false; 32 } 33 34 owner.receivers.add(a); 35 36 } else if (tagName.equals("service")) { 37 Service s = parseService(owner, res, parser, attrs, flags, outError); 38 if (s == null) { 39 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 40 return false; 41 } 42 43 owner.services.add(s); 44 45 } else if (tagName.equals("provider")) { 46 Provider p = parseProvider(owner, res, parser, attrs, flags, outError); 47 if (p == null) { 48 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; 49 return false; 50 } 51 52 owner.providers.add(p); 53 54 } 55 ......... 56 57 } 58 59 return true; 60 } 61 ............. 62 63 } ~~~ - 在应用程序配置文件AndroidManifest.xml中,分别使用activity、receiver、service和provider标签来描述一个应用程序的Activity、BroadcastReceiver、Service和ContentProvider组件的配置信息,它们均是application标签里面的一个子标签。 - 第11行到第57行的while循环分别对activity、receiver、service和provider标签进行解析,以便可以获得正在解析的应用程序的Activity、BroadcastReceiver、Service和ContentProvider组件的配置信息,这些组件的配置信息分别保存在参数owner所描述的一个Package对象的成员变量activities、receivers、services和providers中。 - 这一步执行完成之后,返回到前面的Step 8中,即PackageManagerService类的成员函数scanPackageLI中,这时候Package管理服务PackageManagerService就解析完成一个应用程序了。接下来它再调用PackageManagerService类的另外一个重载版本的成员函数scanPackageLI,以便可以获得前面所解析的应用程序的组件配置信息,以及为这个应用程序分配Linux用户ID。 #### **Step 11: PackageManagerService.scanPackageLI** 在分析这个函数之前,我们首先解释PackageManagerService类的五个成员变量mPackages、mActivities、mReceivers、mServices和mProvidersByComponent的作用 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ........... 4 5 // Keys are String (package name), values are Package. This also serves 6 // as the lock for the global state. Methods that must be called with 7 // this lock held have the prefix "LP". 8 final HashMap<String, PackageParser.Package> mPackages = 9 new HashMap<String, PackageParser.Package>(); 10 11 // All available activities, for your resolving pleasure. 12 final ActivityIntentResolver mActivities = 13 new ActivityIntentResolver(); 14 15 // All available receivers, for your resolving pleasure. 16 final ActivityIntentResolver mReceivers = 17 new ActivityIntentResolver(); 18 19 // All available services, for your resolving pleasure. 20 final ServiceIntentResolver mServices = new ServiceIntentResolver(); 21 22 // Keys are String (provider class name), values are Provider. 23 final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent = 24 new HashMap<ComponentName, PackageParser.Provider>(); 25 ............. 26 27 } ~~~ - 系统中所有已经安装了的应用程序都是使用-个Package对象来描述的。这些Package对象保存在PackageManagerService类的成员变量mPackages所描述的一个HashMap中,并且是以这些Package对象所描述的应用程序的Package名称为关键字的。 * 系统中每一个已经安装了的应用程序都包含了一系列的Activity、BroadcastReceiver、Service和ContentProvider组件,这些组件的配置信息分别保存在PackageManagerService类的成员变量mActivities、mReceivers、mServices和mProvidersByComponent中。 * 接下来,我们就开始分析PackageManagerService类的另外一个重载版本的成员函数scanPackageLI的实现,如下所示。 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 .............. 4 5 private PackageParser.Package scanPackageLI(PackageParser.Package pkg, 6 int parseFlags, int scanMode, long currentTime) { 7 ............... 8 9 SharedUserSetting suid = null; 10 PackageSetting pkgSetting = null; 11 12 .............. 13 14 synchronized (mPackages) { 15 ............... 16 17 if (pkg.mSharedUserId != null) { 18 suid = mSettings.getSharedUserLP(pkg.mSharedUserId, 19 pkg.applicationInfo.flags, true); 20 if (suid == null) { 21 Slog.w(TAG, "Creating application package " + pkg.packageName 22 + " for shared user failed"); 23 mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; 24 return null; 25 } 26 .............. 27 } 28 ................. 29 30 // Just create the setting, don't add it yet. For already existing packages 31 // the PkgSetting exists already and doesn't have to be created. 32 pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile, 33 destResourceFile, pkg.applicationInfo.nativeLibraryDir, 34 pkg.applicationInfo.flags, true, false); 35 ................ 36 37 } 38 39 ................ 40 41 synchronized (mPackages) { 42 // We don't expect installation to fail beyond this point, 43 if ((scanMode&SCAN_MONITOR) != 0) { 44 mAppDirs.put(pkg.mPath, pkg); 45 } 46 // Add the new setting to mSettings 47 mSettings.insertPackageSettingLP(pkgSetting, pkg); 48 // Add the new setting to mPackages 49 mPackages.put(pkg.applicationInfo.packageName, pkg); 50 ............... 51 52 int N = pkg.providers.size(); 53 StringBuilder r = null; 54 int i; 55 for (i=0; i<N; i++) { 56 PackageParser.Provider p = pkg.providers.get(i); 57 p.info.processName = fixProcessName(pkg.applicationInfo.processName, 58 p.info.processName, pkg.applicationInfo.uid); 59 mProvidersByComponent.put(new ComponentName(p.info.packageName, 60 p.info.name), p); 61 ............... 62 63 } 64 if (r != null) { 65 if (Config.LOGD) Log.d(TAG, " Providers: " + r); 66 } 67 68 N = pkg.services.size(); 69 r = null; 70 for (i=0; i<N; i++) { 71 PackageParser.Service s = pkg.services.get(i); 72 s.info.processName = fixProcessName(pkg.applicationInfo.processName, 73 s.info.processName, pkg.applicationInfo.uid); 74 mServices.addService(s); 75 ................. 76 77 } 78 if (r != null) { 79 if (Config.LOGD) Log.d(TAG, " Services: " + r); 80 } 81 82 N = pkg.receivers.size(); 83 r = null; 84 for (i=0; i<N; i++) { 85 PackageParser.Activity a = pkg.receivers.get(i); 86 a.info.processName = fixProcessName(pkg.applicationInfo.processName, 87 a.info.processName, pkg.applicationInfo.uid); 88 mReceivers.addActivity(a, "receiver"); 89 .............. 90 91 } 92 if (r != null) { 93 if (Config.LOGD) Log.d(TAG, " Receivers: " + r); 94 } 95 96 N = pkg.activities.size(); 97 r = null; 98 for (i=0; i<N; i++) { 99 PackageParser.Activity a = pkg.activities.get(i); 100 a.info.processName = fixProcessName(pkg.applicationInfo.processName, 101 a.info.processName, pkg.applicationInfo.uid); 102 mActivities.addActivity(a, "activity"); 103 ................ 104 105 } 106 ................. 107 108 } 109 110 return pkg; 111 } 112 ............ 113 114 115 } ~~~ 这个函数主要是完成以下6个工作。 1. 为参数pkg所描述的一个应用程序分配Linux用户ID,如第17行到第34行代码所示。 2. 将参数pkg所指向的一个Package对象保存在PackageManagerService类的成员变量mPackages中,如第49行代码所示。 3. 将参数pkg所描述的一个应用程序的ContentProvider组件配置信息、保存在PackageManagerService类的成员变量mProvidersByComponent中,如第52行到第63行代码所示。 4. 将参数pkg所描述的一个应用程序的Service组件配置信息、保存在PackageManagerService类的成员变量mServices中,如第68行到第77行代码所示。 5. 将参数pkg所描述的一个应用程序的BroadcastReceiver组件配置信息保存在PackageManagerService类的成员变量mReceivers中,如第82行到第91行代码所示。 6. 将参数pkg所描述的一个应用程序的Activity组件配置信息保存在PackageManagerService类的成员变量mActivities中,如第96行到第105行代码所示。 第2个到第6个工作都比较直观,接下来我们主要分析第1个工作的处理过程,即为一个应用程序分配一个Linux用户ID的过程。 第17行的if语句首先检查参数pkg所描述的一个应用程序是否指定了要与其他应用程序共享同一个Linux用户ID 。如果指定了,那么接下来第18行和第19行代码就会调用PackageManagerService类的成员变量mSettings所指向的一个Settings对象的成员函数getSharedUserLP来获得这个被共享的Linux用户,并且保存在变量suid中。 Settings类的成员函数getSharedUserLP的实现如下所示。 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ............. 7 8 SharedUserSetting getSharedUserLP(String name, 9 int pkgFlags, boolean create) { 10 SharedUserSetting s = mSharedUsers.get(name); 11 if (s == null) { 12 if (!create) { 13 return null; 14 } 15 s = new SharedUserSetting(name, pkgFlags); 16 if (MULTIPLE_APPLICATION_UIDS) { 17 s.userId = newUserIdLP(s); 18 } else { 19 s.userId = FIRST_APPLICATION_UID; 20 } 21 Log.i(TAG, "New shared user " + name + ": id=" + s.userId); 22 // < 0 means we couldn't assign a userid; fall out and return 23 // s, which is currently null 24 if (s.userId >= 0) { 25 mSharedUsers.put(name, s); 26 } 27 } 28 29 return s; 30 } 31 .......... 32 33 } 34 35 ............... 36 } ~~~ - 参数name用来描述一个共享Linux用户的名称,而参数create用来描述当系统中不存在名称等于name的共享Linux用户时,是否需要新创建一个。 * 在前面的Step 6中提到,系统中的所有共享Linux用户都是使用一个SharedUserSetting对象来描述的,并且以它们的名称为关键字保存在Settings类的成员变量mSharedUsers中。因此,第10行代码首先以参数name为关键字在里面查找是否存在一个对应的SharedUserSetting对象。如果存在,即第11行的if语句为false,那么第29行代码就将这个SharedUserSetting对象返回给调用者;否则,接下来第12行到第26行代码就会检查是否需要使用参数name来创建一个对应的SharedUserSetting对象,以便可以返回给调用者。 * 第12行的if语句首先检查参数create的值是否等于true。如果不等于true,那么接下来第13行代码就会直接返回一个null值给调用者,表示找不到名称等于name的共享Linux用户;否则,第15行到第26行代码就会首先使用参数name来创建一个SharedUserSetting对象,即创建一个名称等于name的共享Linux用户,然后调用Settings类的成员函数newUserIdLP来为这个SharedUserSetting对象分配一个Linux用户ID,最后再将这个SharedUserSetting对象保存在Settings类的成员变量mSharedUsers中。在接下来的Step 13中,我们再分析Settings类的成员函数newUserIdLP的实现。 >[info] **注意**:MULTIPLE_APPLICATION_UIDS是PackageManagerService类的一个静态成员变量,它的值被设置为true ,表示要为系统中的每一个应用程序分配一个不同的Linux用户ID,但是不包括那些要求与其他应用程序共享同一个Linux用户ID 的应用程序。如果它的值等于false ,那么就表示系统中的所有应用程序都共用一个值为FIRST_APPLICATION_UID 的Linux用户ID。 回到PackageManagerService类的成员函数scanPackageLI中,接下来第32行到第34行代码就会继续调用Settings类的成员函数getPackageLP来为参数pkg所描述的一个应用程序分配一个Linux用户ID 。 #### **Step 12: Settings. getPackageLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ............. 7 8 private final HashMap<String, PackageSetting> mPackages = 9 new HashMap<String, PackageSetting>(); 10 ............. 11 12 PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage, 13 String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, 14 String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) { 15 final String name = pkg.packageName; 16 PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath, 17 resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add); 18 return p; 19 } 20 ............ 21 22 private PackageSetting getPackageLP(String name, PackageSetting origPackage, 23 String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, 24 String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) { 25 PackageSetting p = mPackages.get(name); 26 if (p != null) { 27 .................... 28 29 if (p.sharedUser != sharedUser) { 30 .............. 31 32 p = null; 33 } else { 34 ............... 35 36 } 37 } 38 if (p == null) { 39 if (!create) { 40 return null; 41 } 42 if (origPackage != null) { 43 // We are consuming the data from an existing package. 44 p = new PackageSetting(origPackage.name, name, codePath, resourcePath, 45 nativeLibraryPathString, vc, pkgFlags); 46 ............ 47 48 p.copyFrom(origPackage); 49 p.signatures = s; 50 p.sharedUser = origPackage.sharedUser; 51 p.userId = origPackage.userId; 52 p.origPackage = origPackage; 53 mRenamedPackages.put(name, origPackage.name); 54 name = origPackage.name; 55 // Update new package state. 56 p.setTimeStamp(codePath.lastModified()); 57 } else { 58 p = new PackageSetting(name, realName, codePath, resourcePath, 59 nativeLibraryPathString, vc, pkgFlags); 60 p.setTimeStamp(codePath.lastModified()); 61 p.sharedUser = sharedUser; 62 if (sharedUser != null) { 63 p.userId = sharedUser.userId; 64 } else if (MULTIPLE_APPLICATION_UIDS) { 65 // Clone the setting here for disabled system packages 66 PackageSetting dis = mDisabledSysPackages.get(name); 67 if (dis != null) { 68 ............... 69 70 p.userId = dis.userId; 71 ............ 72 73 } else { 74 // Assign new user id 75 p.userId = newUserIdLP(p); 76 } 77 } else { 78 p.userId = FIRST_APPLICATION_UID; 79 } 80 } 81 ............. 82 83 if (add) { 84 addPackageSettingLP(p, name, sharedUser); 85 } 86 } 87 return p; 88 } 89 ............ 90 91 } 92 ............. 93 } ~~~ 第15行代码首先将Package对象pkg的成员变量packageName的值保存在变量name中,接着第16行和第17行代码再调用Settings类的另外一个重载版本的成员函数getPackageLP来为Package名称等于name的应用程序分配一个Linux用户ID 。 Settings类的另外一个重载版本的成员函数getPackageLP的实现如上面的代码中所示。 在前面的Step 4 中提到,系统中的所有应用程序的安装信息都使用一个PackageSetting对象来描述,并且保存在Settings类的成员变量mPackages所描述的一个HashMap 中。 >[info] **注意**: 这些安装信息有可能是来自于全新安装的应用程序的,还有可能是来自于上一次安装的应用程序的。 * 参数name用来描述一个应用程序的Package名称,第25行代码以它为关键字在Settings类的成员变量mPackages中查找是否存在一个对应PackageSetting对象p。如果存在,那么接下来第29行的还语句就会检查这个Paci吨eSetting对象p是否是用来描述一个与其他应用程序共享同一个Linux用户ID的应用程序的安装信息的。如果是,那么第29行的if语句还需要继续检查这个PackageSetting对象p的成员变量sharedUser所描述的一个共享Linux用户是否与参数sharedUser所描述的一个SharedUserSetting对象相同。如果不相同,即第29行的if语句为true,那么这个PackageSetting对象p就不能用来描述Package名称等于name的应用程序的安装信息。因此,第32行代码会将它的值重新设置为null,以便接下来可以为Package名称等于name的应用程序创建一个PackageSetting对象。 * 第38行代码检查前面所获得的一个PackageSetting对象p的值是否等于null。如果不等于null,那么接下来第87行代码就会直接将它返回给调用者;否则,第39行到第85行代码就会检查是否需要为Package名称等于name的应用程序创建一个PackageSetting对象,以便可以返回给调用者。 * 第39行的if语句检查参数create的值是否等于true。如果不等于true,那么就说明不需要为Package名称等于name的应用程序创建一个PackageSetting对象。因此,第40行代码就会直接返回一个null给调用者;否则看,第42行到第85行代码就会为Package名称等于name的应用程序创建一个PackageSetting对象。 * 第42行的if语句检查参数origPackage的值是否不等于null。如果不等于null,那么就说明Package名称等于name 的应用程序在系统中有一个旧的版本。在这种情况下,接下来第44行到第52行代码就会为这个旧版本的应用程序的Package名称以及Linux用户ID创建一个新的PackageSetting对象p。 * 如果参数origPackage的值等于null,即第42行的if语句为false,那么就说明Package名称等于name的应用程序是一个全新安装的应用程序。在这种情况下,接下来第58行到第79行代码就会全部使用函数的参数来为Package名称等于name的应用程序创建一个新的PackageSetting对象P。 * 第62行的if与语句检查Package名称等于name的应用程序是否指定了要与其他的应用程序共享同一个Linux用户ID。如果指定了,那么参数sharedUser就指向了被共享的Linux用户。因此,第63行代码就会将它的Linux用户ID设置为前面所创建的PackageSetting对象p的成员变量userId的值,以便可以用来描述Package名称等于name的应用程序的Linux用户ID 。 * 如果Package名称等于name的应用程序没有指定要与其他的应用程序共享同一个Linux用户ID,即第45行的if语句为false,那么第64行的if语句就会检查PackageManagerService类的静态成员变量MULTIPLE_APPLICATION_ UIDS的值是否等于true 。如果等于true,那么接下来第66行到第77行代码就会为Package名称等于name的应用程序分配一个新的Linux用户ID;否则,第77行代码就会简单地将Package名称等于name的应用程序的Linux用户ID设置为FIRST_APPLICATION_UID 。 * 第67行的if语句检查Package名称等于name的应用程序是否是一个禁用的系统应用程序。如果是,那么就不需要为Package名称等于nam e的应用程序分WC-个新的Linux用户ID了,直接使用它原来的Linux用户ID即可,如第70行代码所示;否则,第75行代码就会调用Settings类的成员函数newUserldLP来分配一个新的Linux用户ID,并且保存在新创建的PackageSetting对象p的成员变量userId中,以便可以用来描述Package名称等于name的应用程序的Linux用户ID 。 * 第83行的if语句检查参数add 的值是否等于true。如果等于true,那么第84行代码就会调用Settings类的成员函数addPackageSettingLP将新创建的PackageSetting对象p保存在Settings类的成员变量mPackages中,以便可以表示它描述的应用程序已经成功地安装在系统中了。 * 接下来,我们继续分析Settings类的成员函数newUserldLP的实现,以便可以了解它是如何为一个新安装的应用程序分配一个Linux用户ID 的。 #### **Step 13 : Settings.newUserldLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............... 4 5 private static final class Settings { 6 ........... 7 8 private int newUserIdLP(Object obj) { 9 // Let's be stupidly inefficient for now... 10 final int N = mUserIds.size(); 11 for (int i=0; i<N; i++) { 12 if (mUserIds.get(i) == null) { 13 mUserIds.set(i, obj); 14 return FIRST_APPLICATION_UID + i; 15 } 16 } 17 18 // None left? 19 if (N >= MAX_APPLICATION_UIDS) { 20 return -1; 21 } 22 23 mUserIds.add(obj); 24 return FIRST_APPLICATION_UID + N; 25 } 26 ........... 27 28 29 } 30 31 ........... 32 33 } ~~~ * 在前面的Step 4中提到,Settings类的成员变量mUserIds用来保存系统中所有已经分配的Linux用户ID,它是一个类型为ArrayList的列表。如果保存在这个列表中的第1个位置的一个Object对象的值等于null,那么就说明值等于(FIRST_APPLICATION_UID+i)的Linux用户ID尚未分配出去,因此,我们就可以将它分配给新安装的应用程序使用,如第11行到第16行的for循环所示。 * 如果不能在Settings类的成员变量mUserIds中找到一个空闲的Linux用户ID,并且系统已经分配出去的Linux用户ID的数量还没有达到最大值(MAX_APPLICATION_UIDS)即第19行的if语句为false,那么接下来第23行和第24行代码就会将值等于(FIRST_APPLICATION_UID+N)的Linux用户ID分配给新安装的应用程序使用,其中,变量N表示之前已经分配出去的Linux用户ID的数量。 * 这一步执行完成之后,一个应用程序就成功地安装到系统中了。返回到前面的Step l中,即PackageManagerService类的构造函数中,当系统中的所有应用程序都安装完成之后,接下来第14步到第17步就会调用PackageManagerService类的成员函数updatePermissionsLP来为前面所安装的应用程序分配Lin山用户组ID,即授予它们所申请的资源访问权限,以及调用Settings类的成员函数writeLP将这些应用程序的安装信息保存在本地文件中。这个过程如图16-3所示。 ![](https://box.kancloud.cn/7a0b1d5876fc678c46ea130aa90ff082_709x742.jpg) #### **Step 14: PackageManagerService. updatePermissionsLP** ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............. 4 5 final HashMap<String, PackageParser.Package> mPackages = 6 new HashMap<String, PackageParser.Package>(); 7 ............ 8 9 private void updatePermissionsLP(String changingPkg, 10 PackageParser.Package pkgInfo, boolean grantPermissions, 11 boolean replace, boolean replaceAll) { 12 ................ 13 14 if (grantPermissions) { 15 for (PackageParser.Package pkg : mPackages.values()) { 16 if (pkg != pkgInfo) { 17 grantPermissionsLP(pkg, replaceAll); 18 } 19 } 20 } 21 .............. 22 23 } 24 ............... 25 26 } ~~~ 所有已经安装了的应用程序都保存在PackageManagerService类的成员变量rnPackages 中,当参数grantPermissions的值等于true时,第15行到第19行的for循环就会依次调用PackageManagerService类的成员函数grantPermissionsLP来为它们分配Linux用户组ID,以便它们可以获得所申请的资源访问权限。 #### **Step 15: PackageManagerService. grantPermissionsLP** 在分析这个函数的实现之前,我们首先了解配置文件AndroidManifest.xml中的uses-permission标签和Android应用程序的Linux用户组ID的关系。 假设一个应用程序需要使用照相设备,那么它就需要在它的配置文件AndroidManifest.xml 中添加下面这一行信息: ~~~ <uses-permission android:name = "android..permission.CAMERA” /> ~~~ 从前面的Step 9可以知道, Package管理服务PackageManagerService在解析这个配置文件时,会将这个uses-permission标签中的android:name属性的值“android.permission. CAMERA”取出来,并且保存在一个对应的Package对象的成员变量requestedPermissions所描述的一个资源访问权限列表中。 Package管理服务PackageManagerService在启动时,会调用PackageManagerService类的成员函数 readPermissions来解析保存在设备上的/system/etc/permissions/platfom.xml文件中的内容,这个文件的内容是以xml格式保存的,里面包含了一系列的permission标签,用来描述系统中的资源访问权限列表, 它们的格式如下所示: ~~~ <permission name= "android.permission.CAMERA” > <group gid= "camera ” /〉 </permission> ~~~ * 这个permission标签表示使用名称为“ camera”的Linux用户组来描述名称为“ android.permission.CAMERA”的资源访问权限。知道了一个Linux用户组的名称之后,我们就可以调用由Lin山内核提供的函数ge tgrnam来获得对应的Linux用户组ID ,这样就可以将一个资源访问权限与一个Linux用户组ID关联起来。 * Package管理服务PackageManagerService会为这个文件中的每一千permission标签创建一个BasePermission对象,并且以这个标签中的name属性值作为关键字,将这些BasePermission对象保存在PackageManagerService的成员变量mSettings所指向的一个Settings对象的成员变量mPermissions所描述的一个HashMap中。 * 由于一个permission标签可以包含多个group子标签,即一个资源访问权限名称可以用来描述多个Linux用户组,因此,每一个BasePermission对象内部都有一个gids数组,用来保存所有与它对应的Linux用户组ID 。 * 在设备上的/system/etc/pennissions/platform.xml文件中,还包含了一些全局的group标签。这些全局的group标签与permission标签是同一级的,用来描述系统中所有的应用程序默认都具有的资源访问权限。Package管理服务PackageManagerService会将这些全局的group标签所描述的Linux用户组ID保存在PackageManagerService类的成员变量mGlobalGids所描述的一个数组中。 * 了解了这些背景知识之后,接下来我们就分段来阅读PackageManagerService类的成员函数grantPermissionsLP的代码,如下所示。 ~~~ 1 package com.android.server; 2 class PackageManagerService extends IPackageManager.Stub { 3 ............. 4 5 private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) { 6 final PackageSetting ps = (PackageSetting)pkg.mExtras; 7 if (ps == null) { 8 return; 9 } 10 final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; 11 boolean changedPermission = false; 12 13 if (replace) { 14 ps.permissionsFixed = false; 15 if (gp == ps) { 16 gp.grantedPermissions.clear(); 17 gp.gids = mGlobalGids; 18 } 19 } 20 21 if (gp.gids == null) { 22 gp.gids = mGlobalGids; 23 } 24 25 final int N = pkg.requestedPermissions.size(); 26 for (int i=0; i<N; i++) { 27 String name = pkg.requestedPermissions.get(i); 28 BasePermission bp = mSettings.mPermissions.get(name); 29 if (false) { 30 if (gp != ps) { 31 Log.i(TAG, "Package " + pkg.packageName + " checking " + name 32 + ": " + bp); 33 } 34 } 35 if (bp != null && bp.packageSetting != null) { 36 final String perm = bp.name; 37 boolean allowed; 38 boolean allowedSig = false; 39 if (bp.protectionLevel == PermissionInfo.PROTECTION_NORMAL 40 || bp.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) { 41 allowed = true; 42 } else if (bp.packageSetting == null) { 43 allowed = false; 44 } else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE 45 || bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { 46 allowed = (checkSignaturesLP( 47 bp.packageSetting.signatures.mSignatures, pkg.mSignatures) 48 == PackageManager.SIGNATURE_MATCH) 49 || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures) 50 == PackageManager.SIGNATURE_MATCH); 51 if (!allowed && bp.protectionLevel 52 == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) { 53 if (isSystemApp(pkg)) { 54 if (isUpdatedSystemApp(pkg)) { 55 PackageSetting sysPs = mSettings.getDisabledSystemPkg( 56 pkg.packageName); 57 final GrantedPermissions origGp = sysPs.sharedUser != null 58 ? sysPs.sharedUser : sysPs; 59 if (origGp.grantedPermissions.contains(perm)) { 60 allowed = true; 61 } else { 62 allowed = false; 63 } 64 } else { 65 allowed = true; 66 } 67 } 68 } 69 if (allowed) { 70 allowedSig = true; 71 } 72 } else { 73 allowed = false; 74 } 75 if (false) { 76 if (gp != ps) { 77 Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); 78 } 79 } 80 if (allowed) { 81 if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 82 && ps.permissionsFixed) { 83 if (!allowedSig && !gp.grantedPermissions.contains(perm)) { 84 allowed = false; 85 86 final int NP = PackageParser.NEW_PERMISSIONS.length; 87 for (int ip=0; ip<NP; ip++) { 88 final PackageParser.NewPermissionInfo npi 89 = PackageParser.NEW_PERMISSIONS[ip]; 90 if (npi.name.equals(perm) 91 && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { 92 allowed = true; 93 Log.i(TAG, "Auto-granting " + perm + " to old pkg " 94 + pkg.packageName); 95 break; 96 } 97 } 98 } 99 } 100 if (allowed) { 101 if (!gp.grantedPermissions.contains(perm)) { 102 changedPermission = true; 103 gp.grantedPermissions.add(perm); 104 gp.gids = appendInts(gp.gids, bp.gids); 105 } else if (!ps.haveGids) { 106 gp.gids = appendInts(gp.gids, bp.gids); 107 } 108 } else { 109 Slog.w(TAG, "Not granting permission " + perm 110 + " to package " + pkg.packageName 111 + " because it was previously installed without"); 112 } 113 } else { 114 if (gp.grantedPermissions.remove(perm)) { 115 changedPermission = true; 116 gp.gids = removeInts(gp.gids, bp.gids); 117 Slog.i(TAG, "Un-granting permission " + perm 118 + " from package " + pkg.packageName 119 + " (protectionLevel=" + bp.protectionLevel 120 + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) 121 + ")"); 122 } else { 123 Slog.w(TAG, "Not granting permission " + perm 124 + " to package " + pkg.packageName 125 + " (protectionLevel=" + bp.protectionLevel 126 + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) 127 + ")"); 128 } 129 } 130 } else { 131 Slog.w(TAG, "Unknown permission " + name 132 + " in package " + pkg.packageName); 133 } 134 } 135 136 if ((changedPermission || replace) && !ps.permissionsFixed && 137 ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) || 138 ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){ 139 ps.permissionsFixed = true; 140 } 141 ps.haveGids = true; 142 } 143 ............... 144 145 } ~~~ * 参数pkg所描述的一个应用程序的安装信息保存在它的成员变量mExtras中,第5行代码首先将它取出来,并且保存在PackageSetting对象ps中,以便接下来可以根据这些安装信息来为这个应用程序分配Linux用户组ID * 第8行代码检查参数pkg所描述的一个应用程序是否指定了要与其他应用程序共享同一个Linux用户ID。如果指定了,那么前面所获得的PackageSetting对象ps 的成员变量sharedUser就用来描述被共享的Linux用户。在这种情况下,参数pkg所描述的一个应用程序所获得的资源权访问权限就与它所共享的Linux用户所具有的资源权访问权限相同。第10行代码执行完成之后,参数pkg所描述的一个应用程序所申请的资源访问权限就使用GrantedPennissions对象即来描述。 * 第13行的if语句检查参数replace的值是否等于true。如果等于true,那么第15行的if语句就会继续检查参数pkg所描述的一个应用程序是否指定了要与其他应用程序共享同一个Linux用户ID。如果没有指定,即第15行的面吾句为true,那么接下第16行和第17行代码就会将已经授予给这个应用程序的资源访问权限替换为系统中所有应用程序默认都具有的资源、访问权限。 >[info] **注意**:一个应用程序已经被授予的资源访问权限保存在与它所对应的一个GrantedPermissions对象的成员变量gids所描述的一个数组中。 * 第21行的if语句检查GrantedPermissions对象gp的成员变量gids的值是否等于null 。如果等于null,那么就说明系统尚未为参数pkg所描述的一个应用程序分配过Linux用户组ID。在这种情况下,接下来第22行代码就会将保存在PackageManagerService类的成员变量mGlobalGids中的Linux用户组ID拷贝到GrantedPermissions对象gp的成员变量gids中,以便可以给参数pkg所描述的一个应用程序授予默认的资源访问权限。 *