### 應用程序的安裝過程
- 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的示意圖

#### **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所示。

#### **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所示。

#### **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所描述的一個應用程序授予默認的資源訪問權限。
*
- 第一章 準備知識
- 第二章 硬件抽象層
- 第三章 智能指針
- 第四章 Logger日志系統
- 第五章 Binder進程間通信系統
- 第六章 Ashmem匿名共享內存系統
- 第七章 Activity組件的啟動過程
- 7.1 Activity組件應用實例
- 7.2 根Activity組件的啟動過程
- 第八章 Service組件的啟動過程
- 第九章 Android系統廣播機制
- 第十章 Content Provider組件的實現原理
- 第十一章 Zygote和System進程的啟動過程
- 第十二章 Android應用程序進程的啟動過程
- 第十三章 Android應用程序的消息處理機制
- 第十四章 Android應用程序的鍵盤消息處理機制
- 第十五章 Android應用程序線程的消息循環模型
- 第十六章 Android應用程序的安裝和顯示過程
- 16.1 Android應用程序的安裝過程
- 16.2 Android應用程序的顯示過程