Package管理服務PackageManagerService在安裝一個應用程序的過程中,會對這個應用程序的配置文件AndroidManifest.xml進行解析,以便可以獲得它的安裝信息。
Android系統中每一個應用程序都有一個Linux用戶ID,一個應用程序除了擁有一個linux用戶ID之外,還可以擁有若干個Linux用戶組ID,以便可以在系統中獲得更多的資源訪問權限,如讀取聯繫人信息、使用攝像頭等權限。
PMS在安裝一個應用程序時,如果發現它沒有與其他應用程序共享同一個Linux用戶ID,那麼就會爲它分配一個唯一的Linux用戶ID,以便它可以在系統中獲得合適的運行權限。如果發現它申請了一個特定的資源訪問權限,那麼就會爲它分配一個相應的Linux用戶組ID。
分析應用程序的安裝過程,主要是關注它的組件信息的解析過程,以及Linux用戶ID和Linux用戶組ID的分配過程。
System進程在啓動時,會調用PMS類的靜態成員函數main方法將系統的PMS啓動起來。由於PMS在啓動過程中會對系統中的應用程序進行安裝,因此,接下來就從PMS類的main方法開始分析應用程序的安裝過程:
- public static PackageManagerService main(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
- // 實例化PMS
- PackageManagerService m = new PackageManagerService(context, installer,
- factoryTest, onlyCore);
- // 把PMS添加到ServiceManager中
- ServiceManager.addService("package", m);
- return m;
- }
PMS的構造方法完成的主要功能是,掃描android系統中幾個目標文件夾中的apk文件,從而建立合適的數據結構以管理諸如Package信息、四大組件信息、權限信息等各種信息。PMS的工作流程相對簡單,複雜的是其中用於保存各種信息的數據結構和它們之間的關係,以及影響最終結果的策略控制。
由於代碼量較大,採用分段的方法進行分析。
1.掃描目標文件夾之前的準備工作
先看下時序圖:

- final int mSdkVersion = Build.VERSION.SDK_INT;
- final Context mContext;
- final boolean mFactoryTest;
- final boolean mOnlyCore;
- final boolean mLazyDexOpt;
- final DisplayMetrics mMetrics;
- final Settings mSettings;
- // Keys are String (package name), values are Package. This also serves
- // as the lock for the global state. Methods that must be called with
- // this lock held have the prefix "LP".
- @GuardedBy("mPackages")
- final ArrayMap<String, PackageParser.Package> mPackages =
- new ArrayMap<String, PackageParser.Package>();
- private static final int RADIO_UID = Process.PHONE_UID;
- private static final int LOG_UID = Process.LOG_UID;
- private static final int NFC_UID = Process.NFC_UID;
- private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
- private static final int SHELL_UID = Process.SHELL_UID;
-
- public PackageManagerService(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
-
- // 編譯的SDK版本,若沒有定義則APK就無法知道自己運行在Android的哪個版本上
- if (mSdkVersion <= 0) {
- Slog.w(TAG, "**** ro.build.version.sdk not set!");
- }
-
- mContext = context;
- // 是否運行在工廠模式下
- mFactoryTest = factoryTest;
- // 用於判斷是否只掃描系統目錄
- mOnlyCore = onlyCore;
- // 如果此係統是eng版本,則掃描Package後,不對其做dex優化
- mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
- // 用於存儲與顯示屏相關的一些屬性,如屏幕的寬高、分辨率等信息
- mMetrics = new DisplayMetrics();
- // Settings是用來管理應用程序的安裝信息的
- mSettings = new Settings(mPackages);
- // 添加共享用戶
- mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
- ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
- . . .
- }
剛進入構造函數就會遇到第一個較爲複雜的數據結構Settings以及它的addSharedUserLPw方法。先看Settings類的構造方法,主要是創建相關的文件夾並設置文件夾的讀寫權限:
- Settings(Object lock) {
- // Environment.getDataDirectory()獲取到的是/data目錄
- this(Environment.getDataDirectory(), lock);
- }
-
- private final Object mLock;
- private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
- private final File mSystemDir;
- private final File mSettingsFilename;
- private final File mBackupSettingsFilename;
- private final File mPackageListFilename;
- private final File mStoppedPackagesFilename;
- private final File mBackupStoppedPackagesFilename;
-
- Settings(File dataDir, Object lock) {
- mLock = lock;
-
- mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
-
- mSystemDir = new File(dataDir, "system");
- // 創建/data/system文件夾
- mSystemDir.mkdirs();
- // 設置/data/system文件夾的讀寫權限
- FileUtils.setPermissions(mSystemDir.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG
- |FileUtils.S_IROTH|FileUtils.S_IXOTH,
- -1, -1);
- mSettingsFilename = new File(mSystemDir, "packages.xml");
- mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
- mPackageListFilename = new File(mSystemDir, "packages.list");
- FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
-
- // Deprecated: Needed for migration
- mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
- mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
- }
Settings的構造方法會在data目錄下創建system目錄,用來保存多個系統文件。主要有:
packages.xml:記錄系統中所有安裝的應用信息,包括基本信息、簽名和權限。
packages-backup.xml:packages.xml文件的備份。寫文件之前會先備份,寫成功後再刪除掉備份文件,如果寫的時候出現問題,則重啓後再讀取這兩個文件時,如果發現備份文件存在,就會使用備份文件的內容,因爲原文件可能已經損壞了。
packages.list:保存普通應用的數據目錄和uid等信息。
packages-stopped.xml:記錄系統中被強制停止運行的應用信息。系統在強制停止某個應用時,會將應用的信息記錄在該文件中。
packages-stopped-backup.xml:packages-stopped.xml文件的備份文件。
- final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();
-
- // 添加共享用戶
- SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
- // 根據key從map中獲取值
- SharedUserSetting s = mSharedUsers.get(name);
- // 如果值不爲null並且保存的uid和傳遞過來的一致,就直接返回結果。uid不一致則返回null
- if (s != null) {
- if (s.userId == uid) {
- return s;
- }
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared user, keeping first: " + name);
- return null;
- }
- // 若s爲null,則根據傳遞過來的參數新創建對象
- s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = uid;
- // 在系統中保存值爲uid的Linux用戶ID,成功返回true
- if (addUserIdLPw(uid, s, name)) {
- // 保存到map中
- mSharedUsers.put(name, s);
- return s;
- }
- return null;
- }
-
- private final ArrayList<Object> mUserIds = new ArrayList<Object>();
- private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>();
-
- private boolean addUserIdLPw(int uid, Object obj, Object name) {
- // LAST_APPLICATION_UID = 19999
- if (uid > Process.LAST_APPLICATION_UID) {
- return false;
- }
-
- // FIRST_APPLICATION_UID = 10000
- if (uid >= Process.FIRST_APPLICATION_UID) {
- // 獲取數組的長度
- int N = mUserIds.size();
- // 計算目標索引值
- final int index = uid - Process.FIRST_APPLICATION_UID;
- // 如果目標索引值大於數組長度,則在數組索引值之前的位置都添加null的元素
- while (index >= N) {
- mUserIds.add(null);
- N++;
- }
- // 如果數組的目標索引值位置有不爲null的值,說明已經添加過
- if (mUserIds.get(index) != null) {
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate user id: " + uid
- + " name=" + name);
- return false;
- }
- // 把值放在數組的目標索引位置
- mUserIds.set(index, obj);
- } else {
- // 如果uid < 10000,則把對應的值保存在mOtherUserIds變量中
- if (mOtherUserIds.get(uid) != null) {
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared id: " + uid
- + " name=" + name);
- return false;
- }
- mOtherUserIds.put(uid, obj);
- }
- return true;
- }
至此,對Settings的分析就告一段落了。下面繼續分析PMS的構造方法:
- private static final long WATCHDOG_TIMEOUT = 1000*60*10;
-
- public PackageManagerService(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
-
- . . .
-
- // 應用安裝器
- mInstaller = installer;
- // 實例化類優化器
- mPackageDexOptimizer = new PackageDexOptimizer(this);
- mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
-
- mOnPermissionChangeListeners = new OnPermissionChangeListeners(
- FgThread.get().getLooper());
-
- // 獲取當前顯示屏信息
- getDefaultDisplayMetrics(context, mMetrics);
-
- // 獲取系統的初始化變量
- SystemConfig systemConfig = SystemConfig.getInstance();
- mGlobalGids = systemConfig.getGlobalGids();
- mSystemPermissions = systemConfig.getSystemPermissions();
- mAvailableFeatures = systemConfig.getAvailableFeatures();
-
- // 創建一個HandlerThread子類的實例,主要處理應用的安裝和卸載
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
- mHandlerThread.start();
- // 以mHandlerThread線程的looper創建的Handler實例,該Handler運行在mHandlerThread線程
- mHandler = new PackageHandler(mHandlerThread.getLooper());
- // 把mHandler加入到watchdog中
- Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
-
- // /data目錄
- File dataDir = Environment.getDataDirectory();
- // /data/data目錄
- mAppDataDir = new File(dataDir, "data");
- // /data/app目錄,保存的是用戶自己安裝的app
- mAppInstallDir = new File(dataDir, "app");
- mAppLib32InstallDir = new File(dataDir, "app-lib");
- mAsecInternalPath = new File(dataDir, "app-asec").getPath();
- mUserAppDataDir = new File(dataDir, "user");
- // /data/app-parivate目錄,保存的是受DRM保護的私有app
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- mRegionalizationAppInstallDir = new File(dataDir, "app-regional");
-
- // 實例化用戶管理服務,多用戶時使用
- sUserManager = new UserManagerService(context, this,
- mInstallLock, mPackages);
-
- // 通過systemConfig獲取系統中定義的權限,這些權限保存在/system/etc/permissions目錄下的文件中
- ArrayMap<String, SystemConfig.PermissionEntry> permConfig
- = systemConfig.getPermissions();
- for (int i=0; i<permConfig.size(); i++) {
- SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
- // 根據權限名獲取基本權限信息
- BasePermission bp = mSettings.mPermissions.get(perm.name);
- if (bp == null) {
- bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
- // 如果基本權限信息爲null,則根據權限名創建它並保存到mSettings.mPermissions中
- mSettings.mPermissions.put(perm.name, bp);
- }
- if (perm.gids != null) {
- bp.setGids(perm.gids, perm.perUser);
- }
- }
-
- // 通過systemConfig獲取系統的共享庫列表,並保存在mSharedLibraries中
- ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
- for (int i=0; i<libConfig.size(); i++) {
- mSharedLibraries.put(libConfig.keyAt(i),
- new SharedLibraryEntry(libConfig.valueAt(i), null));
- }
-
- // 解析SELinux的策略文件
- mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
-
- // 恢復上一次的應用程序安裝信息,掃描package.xml文件
- mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
-
- // 這裏獲取的customResolverActivity爲空字符串
- String customResolverActivity = Resources.getSystem().getString(
- R.string.config_customResolverActivity);
- if (TextUtils.isEmpty(customResolverActivity)) {
- customResolverActivity = null;
- } else {
- mCustomResolverComponentName = ComponentName.unflattenFromString(
- customResolverActivity);
- }
- . . .
-
- }
下面分析下SystemConfig類的方法:
- static SystemConfig sInstance;
-
- public static SystemConfig getInstance() {
- synchronized (SystemConfig.class) {
- if (sInstance == null) {
- sInstance = new SystemConfig();
- }
- return sInstance;
- }
- }
-
- SystemConfig() {
- // Read configuration from system
- // Environment.getRootDirectory()獲取到的是/system目錄
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "sysconfig"), false);
- // Read configuration from the old permissions dir
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "permissions"), false);
- // Only read features from OEM config
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "sysconfig"), true);
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "permissions"), true);
- }
-
- void readPermissions(File libraryDir, boolean onlyFeatures) {
- // Read permissions from given directory.
- if (!libraryDir.exists() || !libraryDir.isDirectory()) {
- if (!onlyFeatures) {
- Slog.w(TAG, "No directory " + libraryDir + ", skipping");
- }
- return;
- }
- if (!libraryDir.canRead()) {
- Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
- return;
- }
-
- // 遍歷目標文件夾下所有的.xml文件
- File platformFile = null;
- for (File f : libraryDir.listFiles()) {
- // 最後解析platform.xml文件
- if (f.getPath().endsWith("etc/permissions/platform.xml")) {
- platformFile = f;
- continue;
- }
-
- if (!f.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
- continue;
- }
- if (!f.canRead()) {
- Slog.w(TAG, "Permissions library file " + f + " cannot be read");
- continue;
- }
-
- // 解析xml文件,保存到相應的變量中
- readPermissionsFromXml(f, onlyFeatures);
- }
-
- // 最後解析platform.xml文件,該文件的優先級最高
- if (platformFile != null) {
- readPermissionsFromXml(platformFile, onlyFeatures);
- }
- }
-
-
- public int[] getGlobalGids() {
- // 該變量保存xml中group標籤定義的gid
- return mGlobalGids;
- }
-
- public SparseArray<ArraySet<String>> getSystemPermissions() {
- // uid爲索引,存儲一個字符串集合,用於描述指定UID所擁有的權限。解析assign-permission標籤而來
- return mSystemPermissions;
- }
-
- public ArrayMap<String, String> getSharedLibraries() {
- // 解析library標籤而來,key爲庫的名字,value爲庫文件的位置
- return mSharedLibraries;
- }
-
- public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
- // 解析feature標籤而來。key爲feature的字符串描述,value爲FeatureInfo對象
- return mAvailableFeatures;
- }
-
- public ArrayMap<String, PermissionEntry> getPermissions() {
- // 解析permission標籤而來,key爲權限名,value爲PermissionEntry對象。
- return mPermissions;
- }
Android系統每次啓動時,都會重新安裝一遍系統中的應用程序,但是有些應用程序信息每次安裝都是需要保持一致的,如應用程序的Linux用戶ID等。否則應用程序每次在系統重啓後表現可能不一致。因此PMS每次在安裝完成應用程序之後,都需要將它們的信息保存下來,以便下次安裝時可以恢復回來。恢復上一次的應用程序安裝信息是通過Settings類的readLPw方法實現的。下面就分析下該方法掃描packages.xml文件或其備份文件的過程:
- // 讀取應用程序的安裝信息
- boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
- boolean onlyCore) {
- FileInputStream str = null;
- // 先檢查/data/system/packages-backup.xml文件是否存在,
- // 如果存在就將它的內容作爲上一次的應用程序安裝信息
- if (mBackupSettingsFilename.exists()) {
- try {
- str = new FileInputStream(mBackupSettingsFilename);
- mReadMessages.append("Reading from backup settings file\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "Need to read from backup settings file");
- if (mSettingsFilename.exists()) {
- // 備份文件和原文件都存在時,原文件可能已經損壞了,故這裏要刪除掉原文件
- Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
- + mSettingsFilename);
- mSettingsFilename.delete();
- }
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
-
- mPendingPackages.clear();
- mPastSignatures.clear();
- mKeySetRefs.clear();
-
- try {
- // 如果str爲null,說明沒有讀取備份文件,下面要讀取原文件
- if (str == null) {
- if (!mSettingsFilename.exists()) {
- mReadMessages.append("No settings file found\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "No settings file; creating initial state");
- // 如果原文件不存在,根據默認值創建版本信息。
- findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);
- findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);
- return false;
- }
- str = new FileInputStream(mSettingsFilename);
- }
- // 創建解析器
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, StandardCharsets.UTF_8.name());
-
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (type != XmlPullParser.START_TAG) {
- mReadMessages.append("No start tag found in settings file\n");
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "No start tag found in package manager settings");
- Slog.wtf(PackageManagerService.TAG,
- "No start tag found in package manager settings");
- return false;
- }
-
- int outerDepth = parser.getDepth();
- // 開始解析xml文件
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("package")) {
- // 解析標籤爲package的元素,一個應用一個package標籤.
- // 獲取上一次安裝這個應用程序時所分配給它的Linux用戶ID
- readPackageLPw(parser);
- } else if (tagName.equals("permissions")) {
- // 解析系統定義了哪些權限,由哪個包定義
- readPermissionsLPw(mPermissions, parser);
- } else if (tagName.equals("permission-trees")) {
- readPermissionsLPw(mPermissionTrees, parser);
- } else if (tagName.equals("shared-user")) {
- // shared-user標籤是以sharedUserId的名字爲name屬性,然後爲它分配一個userId賦值給userId屬性。
- // 其他應用用到該sharedUserId的,userId都是shared-user標籤中的userId屬性值
- // 解析上一次應用程序安裝信息中的共享Linux用戶信息
- readSharedUserLPw(parser);
- }
- . . .
- else {
- Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- str.close();
-
- } catch (XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
-
- } catch (java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
- }
-
- . . .
-
- // 先看readPackageLPw方法再往下面看
- final int N = mPendingPackages.size();
-
- // 循環遍歷mPendingPackages中的PendingPackage對象,以便爲它們所描述的應用程序保存上一次安裝時所使用的Linux用戶ID
- for (int i = 0; i < N; i++) {
- final PendingPackage pp = mPendingPackages.get(i);
- // 根據uid獲取對應的對象,如果在mUserIds或mOtherUserIds中存在一個與userId對應的Object對象,
- // 且該對象是SharedUserSetting的類型,則說明pp所描述的應用程序上一次所使用的Linux用戶ID是有效的
- Object idObj = getUserIdLPr(pp.sharedId);
- if (idObj != null && idObj instanceof SharedUserSetting) {
- // 爲該應用程序分配一個Linux用戶ID
- PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
- (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
- pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString,
- pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, pp.pkgPrivateFlags,
- null, true /* add */, false /* allowInstall */);
- if (p == null) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Unable to create application package for " + pp.name);
- continue;
- }
- p.copyFrom(pp);
- } else if (idObj != null) {
- String msg = "Bad package setting: package " + pp.name + " has shared uid "
- + pp.sharedId + " that is not a shared uid\n";
- mReadMessages.append(msg);
- PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
- } else {
- String msg = "Bad package setting: package " + pp.name + " has shared uid "
- + pp.sharedId + " that is not defined\n";
- mReadMessages.append(msg);
- PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
- }
- }
- mPendingPackages.clear();
- . . .
-
- mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, "
- + mSharedUsers.size() + " shared uids\n");
-
- return true;
- }
-
- private static final String ATTR_NAME = "name";
- // 解析標籤爲package的元素,可以獲取到上一次安裝這個應用程序時所分配給它的Linux用戶ID
- private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
- String name = null;
- String idStr = null;
- String sharedIdStr = null;
- . . .
- try {
- // 應用程序的包名
- name = parser.getAttributeValue(null, ATTR_NAME);
- // 應用程序所使用的獨立Linux用戶ID
- idStr = parser.getAttributeValue(null, "userId");
- // 應用程序所使用的共享Linux用戶ID
- sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- . . .
-
- int userId = idStr != null ? Integer.parseInt(idStr) : 0;
- . . .
- if (name == null) {
- // 安裝的應用程序包名不能爲null,否則上報錯誤信息
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <package> has no name at "
- + parser.getPositionDescription());
- }
- . . .
- else if (userId > 0) {
- // 檢查該應用是否被分配了一個獨立的uid,如果是就在系統中保存值爲userId的Linux用戶ID
- packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
- new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
- secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
- pkgPrivateFlags);
- . . .
- } else if (sharedIdStr != null) {
- // 如果sharedIdStr不爲null,說明安裝該應用時PMS給它分配了一個共享的uid。此時不能馬上保存該uid,
- // 因爲這個uid不屬於它自己所有,等解析完shared-user節點之後,再爲它保存上一次所使用的Linux用戶ID
- userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
- if (userId > 0) {
- // 創建PendingPackage對象並保存在mPendingPackages中,用來描述一個Linux用戶ID還未確定的應用程序
- packageSetting = new PendingPackage(name.intern(), realName, new File(
- codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
- primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- userId, versionCode, pkgFlags, pkgPrivateFlags);
- . . .
- mPendingPackages.add((PendingPackage) packageSetting);
- . . .
- }
- . . .
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package " + name + " has bad userId "
- + idStr + " at " + parser.getPositionDescription());
- }
- } catch (NumberFormatException e) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package " + name + " has bad userId "
- + idStr + " at " + parser.getPositionDescription());
- }
- }
-
- // 在系統中保存值爲userId的Linux用戶ID
- // 在PMS中,每一個應用程序的安裝信息都是使用一個PackageSetting對象來描述的。這些對象保存在mPackages中。
- PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
- String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags) {
- // 根據包名獲取應用程序的安裝信息
- PackageSetting p = mPackages.get(name);
- if (p != null) {
- // 如果應用程序的安裝信息不爲null,並且uid和目標uid相同則說明PMS已經爲該應用程序分配過uid了
- if (p.appId == uid) {
- return p;
- }
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate package, keeping first: " + name);
- return null;
- }
- // 如果mPackages中不存在與該包名對應的PackageSetting對象,則爲該應用程序分配一個參數uid所描述的Linux用戶ID
- p = new PackageSetting(name, realName, codePath, resourcePath,
- legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags);
- p.appId = uid;
- // 在系統中保存保存值爲uid的Linux用戶ID
- if (addUserIdLPw(uid, p, name)) {
- mPackages.put(name, p);
- return p;
- }
- return null;
- }
-
- // 解析上一次的應用程序安裝信息中的共享Linux用戶信息
- private void readSharedUserLPw(XmlPullParser parser) throws XmlPullParserException,IOException {
- String name = null;
- String idStr = null;
- int pkgFlags = 0;
- int pkgPrivateFlags = 0;
- SharedUserSetting su = null;
- try {
- // 獲取sharedUserId的名字
- name = parser.getAttributeValue(null, ATTR_NAME);
- // 所有共享該sharedUserId名字的應用程序uid
- idStr = parser.getAttributeValue(null, "userId");
- int userId = idStr != null ? Integer.parseInt(idStr) : 0;
- // system屬性用來描述這個共享uid是分配給一個系統類型的應用程序使用的還是分配給一個用戶類型的應用程序使用的。
- if ("true".equals(parser.getAttributeValue(null, "system"))) {
- pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- }
- if (name == null) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: <shared-user> has no name at "
- + parser.getPositionDescription());
- } else if (userId == 0) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: shared-user " + name
- + " has bad userId " + idStr + " at "
- + parser.getPositionDescription());
- } else {
- // 調用addSharedUserLPw方法在系統中爲名稱爲name的共享Linux用戶保存一個值爲userId的Linux用戶
- if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags, pkgPrivateFlags))
- == null) {
- PackageManagerService
- .reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at "
- + parser.getPositionDescription());
- }
- }
- } catch (NumberFormatException e) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Error in package manager settings: package " + name + " has bad userId "
- + idStr + " at " + parser.getPositionDescription());
- }
-
- . . .
- }
-
- // 在系統中爲名稱爲name的共享Linux用戶保存一個值爲userId的Linux用戶
- // 在PMS中,每一個共享Linux用戶都是使用一個SharedUserSetting對象來描述的。這些對象保存在mSharedUsers中。
- SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
- // 根據包名獲取共享應用程序的安裝信息,如果存在並且userId等於參數uid的值,則說明PMS已經爲該應用程序分配過uid了
- SharedUserSetting s = mSharedUsers.get(name);
- if (s != null) {
- if (s.userId == uid) {
- return s;
- }
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Adding duplicate shared user, keeping first: " + name);
- return null;
- }
- // 如果mSharedUsers中不存在與該包名對應的SharedUserSetting對象,則爲該應用程序分配一個參數uid所描述的Linux用戶ID
- s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
- s.userId = uid;
- // 在系統中保存保存值爲uid的Linux用戶ID
- if (addUserIdLPw(uid, s, name)) {
- mSharedUsers.put(name, s);
- return s;
- }
- return null;
- }
-
- // 爲應用程序分配一個Linux用戶ID
- private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
- String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
- UserHandle installUser, boolean add, boolean allowInstall) {
- // 根據name從mPackages中獲取PackageSetting對象
- PackageSetting p = mPackages.get(name);
- UserManagerService userManager = UserManagerService.getInstance();
- if (p != null) {
- . . .
-
- // 判斷已經存在的p對象的sharedUser是否和傳遞來的一致,若不一致說明對象p已不能描述應用的安裝信息,需要重新分配
- if (p.sharedUser != sharedUser) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + name + " shared user changed from "
- + (p.sharedUser != null ? p.sharedUser.name : "<nothing>")
- + " to "
- + (sharedUser != null ? sharedUser.name : "<nothing>")
- + "; replacing with new");
- p = null;
- } else {
- // 判斷當前分配的應用是否是系統應用/私有特權應用
- p.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
- p.pkgPrivateFlags |= pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- }
- }
- // p爲null說明要重新分配Linux用戶ID
- if (p == null) {
- if (origPackage != null) {
- // origPackage不爲null說明該應用程序在系統中有一箇舊版本
- // 根據舊版本的應用程序創建一個新的PackageSetting對象
- p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
- legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
- . . .
- // 把舊安裝包的userId分配給該應用程序
- p.appId = origPackage.appId;
- . . .
- } else {
- // origPackage爲null,說明該應用是一個新安裝的應用程序
- // 根據傳遞過來的參數新建一個PackageSetting對象
- p = new PackageSetting(name, realName, codePath, resourcePath,
- legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
- p.setTimeStamp(codePath.lastModified());
- p.sharedUser = sharedUser;
- // 如果該應用不是系統應用,則新安裝後是停止運行狀態
- if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- List<UserInfo> users = getAllUsers();
- final int installUserId = installUser != null ? installUser.getIdentifier() : 0;
- if (users != null && allowInstall) {
- for (UserInfo user : users) {
- final boolean installed = installUser == null
- || (installUserId == UserHandle.USER_ALL
- && !isAdbInstallDisallowed(userManager, user.id))
- || installUserId == user.id;
- // 設置該應用是停止運行狀態
- p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
- installed,
- true, // stopped,
- true, // notLaunched
- false, // hidden
- null, null, null,
- false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
- writePackageRestrictionsLPr(user.id);
- }
- }
- }
- if (sharedUser != null) {
- // 如果sharedUser不爲null,說明該新安裝應用程序設置了sharedUserId屬性,
- // 則把sharedUserId的userId分配給該應用
- p.appId = sharedUser.userId;
- } else {
- // 如果該新安裝應用程序沒有設置sharedUserId屬性,則需要爲該應用程序分配新的Linux用戶ID
-
- // 根據name值從已經禁用的系統應用程序列表中獲取PackageSetting對象
- PackageSetting dis = mDisabledSysPackages.get(name);
- if (dis != null) {
- // 如果dis不爲null,說明該應用程序是一個已經禁用的系統應用,此時
- // 只需用把它原來的userId分配給它即可
- . . .
- // 把原來的userId分配給該應用程序
- p.appId = dis.appId;
- . . .
- // 在系統中保存保存值爲uid的Linux用戶ID
- addUserIdLPw(p.appId, p, name);
- } else {
- // 如果dis爲null,說明需要爲該新安裝應用程序新分配一個Linux用戶ID
- p.appId = newUserIdLPw(p);
- }
- }
- }
- if (p.appId < 0) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + name + " could not be assigned a valid uid");
- return null;
- }
- if (add) {
- // 調用addPackageSettingLPw方法把p保存到mPackages中,
- // 表示它所描述的應用程序已經成功地安裝在系統中了
- addPackageSettingLPw(p, name, sharedUser);
- }
- } else {
- // p不爲null說明不需要重新分配Linux用戶ID
- if (installUser != null && allowInstall) {
- // The caller has explicitly specified the user they want this
- // package installed for, and the package already exists.
- // Make sure it conforms to the new request.
- List<UserInfo> users = getAllUsers();
- if (users != null) {
- for (UserInfo user : users) {
- if ((installUser.getIdentifier() == UserHandle.USER_ALL
- && !isAdbInstallDisallowed(userManager, user.id))
- || installUser.getIdentifier() == user.id) {
- boolean installed = p.getInstalled(user.id);
- if (!installed) {
- p.setInstalled(true, user.id);
- writePackageRestrictionsLPr(user.id);
- }
- }
- }
- }
- }
- }
- return p;
- }
-
- // 爲該新安裝應用程序新分配一個Linux用戶ID
- private int newUserIdLPw(Object obj) {
- // 獲取之前已經分配出去的Linux用戶ID的數量
- final int N = mUserIds.size();
- for (int i = mFirstAvailableUid; i < N; i++) {
- // 如果第i個對象值爲null,說明值爲Process.FIRST_APPLICATION_UID + i
- // 的Linux用戶ID還未分配出去,這裏就將該值分配給新安裝的應用程序使用
- if (mUserIds.get(i) == null) {
- mUserIds.set(i, obj);
- return Process.FIRST_APPLICATION_UID + i;
- }
- }
-
- // 如果已經沒有值可分配時,則返回-1
- if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
- return -1;
- }
-
- mUserIds.add(obj);
- // 如果不能在mUserIds中找到空閒的Linux用戶ID,且沒有超過分配限制,則把
- // Process.FIRST_APPLICATION_UID + N分配給新安裝應用程序使用
- return Process.FIRST_APPLICATION_UID + N;
- }
到這裏,掃描目標文件夾之前的準備工作就介紹完了。主要是掃描並解析xml文件將上一次的應用程序安裝信息恢復完成。
2.掃描目標文件夾
先看下時序圖:

下面主要是掃描系統中的apk文件了。繼續分析PMS的構造方法:
- private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
-
- public PackageManagerService(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
-
- . . .
- // 記錄開始掃描的時間
- long startTime = SystemClock.uptimeMillis();
-
- // 初始化掃描參數
- final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;
-
- // /system/framework目錄保存的應用程序是資源型的。資源型的應用程序是用來打包資源文件的,不包含有執行代碼
- File frameworkDir = new File(Environment.getRootDirectory(), "framework");
- . . .
- // Collect vendor overlay packages.
- // (Do this before scanning any apps.)
- // 收集供應商提供的覆蓋應用程序
- File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
- scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
-
- // Find base frameworks (resource packages without code).
- scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR
- | PackageParser.PARSE_IS_PRIVILEGED,
- scanFlags | SCAN_NO_DEX, 0);
-
- // Collected privileged system packages./system/priv-app系統自帶應用程序
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR
- | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
-
- // Collect ordinary system packages./system/app目錄保存的是系統自帶的應用程序
- final File systemAppDir = new File(Environment.getRootDirectory(), "app");
- scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
-
- // Collect all vendor packages.供應商提供的應用程序
- File vendorAppDir = new File("/vendor/app");
- try {
- vendorAppDir = vendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
- if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
- mInstaller.moveFiles();
-
- // 刪除各種無效安裝包
-
- if (!mOnlyCore) {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
- SystemClock.uptimeMillis());
- scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
- . . .
-
- }
-
- // Now that we know all the packages we are keeping,
- // read and update their last usage times.
- mPackageUsage.readLP();// 更新各安裝包的上次使用時間
-
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
- SystemClock.uptimeMillis());
- // 輸出掃描用的時間
- Slog.i(TAG, "Time to scan packages: "
- + ((SystemClock.uptimeMillis()-startTime)/1000f)
- + " seconds");
- . . .
- }
上面這段代碼主要是掃描系統中各相關目錄下的apk文件。
- // 掃描給定目錄下的apk文件
- private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
- final File[] files = dir.listFiles();
- if (ArrayUtils.isEmpty(files)) {
- Log.d(TAG, "No files in app dir " + dir);
- return;
- }
-
- Log.d(TAG, "start scanDirLI:"+dir);
- // 使用多線程提高掃描速度
- int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);
- Log.d(TAG, "max thread:" + iMultitaskNum);
- final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(
- MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;
-
- for (File file : files) {
- final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageName(file.getName());
- if (!isPackage) {
- continue;
- }
-
- final File ref_file = file;
- final int ref_parseFlags = parseFlags;
- final int ref_scanFlags = scanFlags;
- final long ref_currentTime = currentTime;
- Runnable scanTask = new Runnable() {
- public void run() {
- try {
- // 解析apk文件
- scanPackageLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK,
- ref_scanFlags, ref_currentTime, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage());
-
- // 解析失敗則刪除無效的apk文件
- if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
- e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file);
- if (ref_file.isDirectory()) {
- mInstaller.rmPackageDir(ref_file.getAbsolutePath());
- } else {
- ref_file.delete();
- }
- }
- }
- }
- };
-
- if (dealer != null)
- dealer.addTask(scanTask);
- else
- scanTask.run();
- }
-
- if (dealer != null)
- dealer.waitAll();
- Log.d(TAG, "end scanDirLI:"+dir);
- }
-
- // 解析apk文件
- private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
- if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
- parseFlags |= mDefParseFlags;
- PackageParser pp = new PackageParser();
- . . .
-
- final PackageParser.Package pkg;
- try {
- // 解析apk文件後返回一個PackageParser.Package對象
- pkg = pp.parsePackage(scanFile, parseFlags);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
- }
- . . .
-
- // Set application objects path explicitly.
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(resourcePath);
- pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
-
- // 對剛解析apk文件返回的pkg對象所描述的apk文件進行安裝,以便獲取它的組件信息,以及爲它分配Linux用戶ID等。
- PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
- | SCAN_UPDATE_SIGNATURE, currentTime, user);
-
- return scannedPkg;
- }
接下來先看PackageParser類的parsePackage方法:
- // 解析apk文件
- public Package parsePackage(File packageFile, int flags) throws PackageParserException {
- if (packageFile.isDirectory()) {
- // 解析文件夾下的所有apk文件
- return parseClusterPackage(packageFile, flags);
- } else {
- // 解析獨立apk文件
- return parseMonolithicPackage(packageFile, flags);
- }
- }
-
- // 解析獨立apk文件
- public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
- if (mOnlyCoreApps) {
- // 根據傳遞來的參數生成一個PackageLite對象,並對它進行是否爲核心應用判斷,若不是則拋異常
- final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
- if (!lite.coreApp) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Not a coreApp: " + apkFile);
- }
- }
-
- final AssetManager assets = new AssetManager();
- try {
- // 解析apk文件
- final Package pkg = parseBaseApk(apkFile, assets, flags);
- pkg.codePath = apkFile.getAbsolutePath();
- return pkg;
- } finally {
- IoUtils.closeQuietly(assets);
- }
- }
-
- private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
- // 解析apk文件
- private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
- throws PackageParserException {
- . . .
-
- Resources res = null;
- XmlResourceParser parser = null;
- try {
- res = new Resources(assets, mMetrics, null);
-
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
-
- final String[] outError = new String[1];
- // 解析AndroidManifest.xml文件
- final Package pkg = parseBaseApk(res, parser, flags, outError);
- if (pkg == null) {
- throw new PackageParserException(mParseError,
- apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
- }
- . . .
- return pkg;
-
- } catch (PackageParserException e) {
- throw e;
- } catch (Exception e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to read manifest from " + apkPath, e);
- } finally {
- IoUtils.closeQuietly(parser);
- }
- }
-
- // 解析AndroidManifest.xml文件
- private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
- String[] outError) throws XmlPullParserException, IOException {
- . . .
- try {
- // 解析apk的包名
- Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
- pkgName = packageSplit.first;
- splitName = packageSplit.second;
- } catch (PackageParserException e) {
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
- return null;
- }
-
- final Package pkg = new Package(pkgName);
- boolean foundApp = false;
-
- // 解析apk的版本號、versionName、等
- . . .
- // 解析manifest標籤中的android:sharedUserId屬性
- String str = sa.getNonConfigurationString(
- com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
- if (str != null && str.length() > 0) {
- . . .
- // 把該屬性值賦值給mSharedUserId
- pkg.mSharedUserId = str.intern();
- . . .
- }
- . . .
- sa.recycle();
- . . .
-
- // 循環解析manifest的各個子標籤
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("application")) {
- . . .
- // 解析application標籤,該標籤用來描述與應用程序組件相關的信息
- if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
- return null;
- }
- } else if (tagName.equals("uses-permission")) {
- // 解析uses-permission標籤,一個標籤對應一個資源訪問權限
- if (!parseUsesPermission(pkg, res, parser, attrs)) {
- return null;
- }
- }
- . . .
- else {
- Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
- + " at " + mArchiveSourcePath + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
- . . .
- return pkg;
- }
-
- // 解析application標籤
- private boolean parseBaseApplication(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
- throws XmlPullParserException, IOException {
- // 解析application標籤的基本信息,圖標、應用名等
- . . .
-
-
- final int innerDepth = parser.getDepth();
- int type;
- // 解析application的子標籤
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- // 解析四大組件的標籤,把相應配置信息都存放在owner的相應列表中
- if (tagName.equals("activity")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
- owner.baseHardwareAccelerated);
- . . .
- owner.activities.add(a);
- } else if (tagName.equals("receiver")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
- . . .
- owner.receivers.add(a);
- } else if (tagName.equals("service")) {
- Service s = parseService(owner, res, parser, attrs, flags, outError);
- . . .
- owner.services.add(s);
- } else if (tagName.equals("provider")) {
- Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
- . . .
- owner.providers.add(p);
- }
- . . .
- else {
- . . .
- }
- }
- . . .
- return true;
- }
到這裏,PMS就解析完一個應用程序了,下面調用PMS類的scanPackageLI方法以便可以獲得前面所解析的應用程序的組件配置信息,以及爲這個應用程序分配Linux用戶ID:
- // 獲得前面所解析的應用程序的組件配置信息,以及爲這個應用程序分配Linux用戶ID
- private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- boolean success = false;
- try {
- final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
- currentTime, user);
- success = true;
- return res;
- } finally {
- if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
- }
- }
- }
-
- // 獲得前面所解析的應用程序的組件配置信息,以及爲這個應用程序分配Linux用戶ID
- private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- final File scanFile = new File(pkg.codePath);
- . . .
-
- SharedUserSetting suid = null;
- PackageSetting pkgSetting = null;
-
- // 爲參數pkg所描述的應用程序分配Linux用戶ID
- // writer
- synchronized (mPackages) {
- if (pkg.mSharedUserId != null) {
- // 如果該應用有sharedUserId屬性,則從mSettings中獲取要爲它分配的共享uid
- suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, true);
- // 判斷該共享uid是否合法
- . . .
- }
- . . .
-
- // 爲應用程序分配一個Linux用戶ID
- pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
- destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
- pkg.applicationInfo.primaryCpuAbi,
- pkg.applicationInfo.secondaryCpuAbi,
- pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
- user, false);
- . . .
- }
- . . .
-
- // writer
- synchronized (mPackages) {
- . . .
- // 把pkg所指向的一個Package對象保存在mSettings中
- mPackages.put(pkg.applicationInfo.packageName, pkg);
- . . .
-
- // 把參數pkg所描述的應用程序的Content Provider組件配置信息保存在mProviders中
- int N = pkg.providers.size();
- StringBuilder r = null;
- int i;
- for (i=0; i<N; i++) {
- PackageParser.Provider p = pkg.providers.get(i);
- p.info.processName = fixProcessName(pkg.applicationInfo.processName,
- p.info.processName, pkg.applicationInfo.uid);
- mProviders.addProvider(p);
- . . .
- }
-
- // 把參數pkg所描述的應用程序的Service組件配置信息保存在mServices中
- N = pkg.services.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Service s = pkg.services.get(i);
- s.info.processName = fixProcessName(pkg.applicationInfo.processName,
- s.info.processName, pkg.applicationInfo.uid);
- mServices.addService(s);
- . . .
- }
-
- // 把參數pkg所描述的應用程序的廣播接收者組件配置信息保存在mReceivers中
- N = pkg.receivers.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Activity a = pkg.receivers.get(i);
- a.info.processName = fixProcessName(pkg.applicationInfo.processName,
- a.info.processName, pkg.applicationInfo.uid);
- mReceivers.addActivity(a, "receiver");
- . . .
- }
-
- // 把參數pkg所描述的應用程序的Activity組件配置信息保存在mActivities中
- N = pkg.activities.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Activity a = pkg.activities.get(i);
- a.info.processName = fixProcessName(pkg.applicationInfo.processName,
- a.info.processName, pkg.applicationInfo.uid);
- mActivities.addActivity(a, "activity");
- . . .
- }
-
- . . .
- }
-
- return pkg;
- }
通過調用getPackageLPw方法爲該應用程序分配uid後,一個應用程序就成功地安裝到系統中了。到這裏PMS構造方法的掃描目標文件夾的工作就完成了。
3.掃描之後的工作
先看下時序圖:

當系統安裝完所有應用程序後,PMS的構造方法就會調用updatePermissionsLPw方法爲前面所安裝的應用程序分配Linux用戶組ID,即授予它們所申請的資源訪問權限,以及調用Settings類的writeLPr方法將這些應用程序的安裝信息保持到本地文件中:
- public PackageManagerService(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
-
- . . .
- // 爲申請了特定的資源訪問權限的應用程序分配相應的Linux用戶組ID
- updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
- . . .
-
- // 將前面獲取到的應用程序安裝信息保存在本地的一個配置文件中,以便下一次再安裝這些應用程序時
- // 可以將需要保持一致的應用程序信息恢復回來
- mSettings.writeLPr();
- . . .
- // Now after opening every single application zip, make sure they
- // are all flushed. Not really needed, but keeps things nice and
- // tidy.
- Runtime.getRuntime().gc();
-
- // Expose private service for system components to use.
- LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
-
-
- // are all flushed. Not really needed, but keeps things nice and
- // tidy.
- Runtime.getRuntime().gc();
-
- // Expose private service for system components to use.
- LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
-
- }
-
- // 爲前面所安裝的應用程序分配Linux用戶組ID
- private void updatePermissionsLPw(String changingPkg,
- PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
- . . .
-
- // Now update the permissions for all packages, in particular bold">new PackageManagerInternalImpl());
-
- }
-
- // 爲前面所安裝的應用程序分配Linux用戶組ID
- private void updatePermissionsLPw(String changingPkg,
- PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
- . . .
-
- // Now update the permissions for all packages, in particular
- // replace the granted permissions of the system packages.
- if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
- for (PackageParser.Package pkg : mPackages.values()) {
- if}