Android-6.0之PMS解析中篇1

本文轉載於:http://www.iloveandroid.net/2016/06/20/Android_PackageManagerService-2/


本篇文章主要介紹PMS掃描和解析APK文件。

繼續分析PMS的構造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);

創建PackageHandler對象,建立PackageHandler的消息循環,用於處理apk的安裝請求。

爲”/data”目錄下的子目錄生成文件對象:

1
/data/data
/data/app
/data/app-lib
/data/user
/data/app-private

創建用戶管理服務UserManagerService:

繼續PMS構造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Propagate permission configuration in to package manager.
    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);
            mSettings.mPermissions.put(perm.name, bp);
        }
        if (perm.gids != null) {
            bp.setGids(perm.gids, perm.perUser);
        }
    }

    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));
    }

    mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();

作用是將前面從/system/etc/permission裏面讀取到的permission的name和對應的gid放入到bp中,然後保存在mSettings的mPermissions中.

也要把從/system/etc/permission中讀取到的shared library 放到PMS的變量mSharedLibraries中去。

繼續

1
2
3
4
5
6
7
8
9
10
11
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
        mSdkVersion, mOnlyCore);

String customResolverActivity = Resources.getSystem().getString(
        R.string.config_customResolverActivity);
if (TextUtils.isEmpty(customResolverActivity)) {
    customResolverActivity = null;
} else {
    mCustomResolverComponentName = ComponentName.unflattenFromString(
            customResolverActivity);
}

這裏首先調用Settings的readLPw函數去解析packages.xml和packages-backup.xml保存的安裝列表信息,並把解析的pakcages信息添加到相應的數據結構中。

這裏我們先假設這是Android設備第一次開機,所有packages.xml和packages-backup.xml文件都還不存在。所以Settings的readLPw函數會直接返回。

繼續:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
long startTime = SystemClock.uptimeMillis(); // 獲取當前時間

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
       startTime);

// Set flag to monitor and not change apk file paths when
// scanning install directories.
final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING | SCAN_INITIAL;//設置掃描模式

final ArraySet<String> alreadyDexOpted = new ArraySet<String>();// 存儲已經優化的文件

/**
* Add everything in the in the boot class path to the
* list of process files because dexopt will have been run
* if necessary during zygote startup.
*/
final String bootClassPath = System.getenv("BOOTCLASSPATH");//獲取BOOTCLASSPATH環境變量的值
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");//獲取SYSTEMSERVERCLASSPATH環境變量的值

if (bootClassPath != null) {
   String[] bootClassPathElements = splitString(bootClassPath, ':');
   for (String element : bootClassPathElements) {
       alreadyDexOpted.add(element); //將BOOTCLASSPATH中的指明的文件加入已經優化的文件列表中
   }
} else {
   Slog.w(TAG, "No BOOTCLASSPATH found!");
}

if (systemServerClassPath != null) {
   String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
   for (String element : systemServerClassPathElements) {
       alreadyDexOpted.add(element);//將SYSTEMSERVERCLASSPATH中的文件加入已經優化的文件列表中
   }
} else {
   Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
}

該段代碼主要是把BOOTCLASSPATH和SYSTEMSERVERCLASSPATH裏面的文件添加到alreadyDexOpted這個HashSet中,因爲它們在zygote啓動時已經進過Dex優化了。

繼續

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets =
        getDexCodeInstructionSets(
                allInstructionSets.toArray(new String[allInstructionSets.size()]));

/**
 * Ensure all external libraries have had dexopt run on them.
 */
if (mSharedLibraries.size() > 0) {
    // NOTE: For now, we're compiling these system "shared libraries"
    // (and framework jars) into all available architectures. It's possible
    // to compile them only when we come across an app that uses them (there's
    // already logic for that in scanPackageLI) but that adds some complexity.
    for (String dexCodeInstructionSet : dexCodeInstructionSets) {
        for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
            final String lib = libEntry.path;
            if (lib == null) {
                continue;
            }

            try {
                int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                    alreadyDexOpted.add(lib);
                    mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                }
            } catch (FileNotFoundException e) {
                Slog.w(TAG, "Library not found: " + lib);
            } catch (IOException e) {
                Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
                        + e.getMessage());
            }
        }
    }
}

這裏先獲取當前Android設備的abi列表,也就是armeabi,armeabi-v7a,arm64-v8a等等信息。

然後在每種abi情況下,利用DexFile.getDexOptNeeded檢查該library是否已經執行過dexopt了。

NO_DEXOPT_NEEDED :if the apk/jar is already up to date.

DEX2OAT_NEEDED: if dex2oat should be called on the apk/jar file.

PATCHOAT_NEEDED:if patchoat should be called on the apk/jar file to patch the odex file along side the apk/jar.

SELF_PATCHOAT_NEEDED if selfpatchoat should be called on the apk/jar file to patch the oat file in the dalvik cache.

當結果不爲NO_DEXOPT_NEEDED表明,該library需要dexopt.通過mInstaller的dexopt進行dexopt操作。

繼續:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
File frameworkDir = new File(Environment.getRootDirectory(), "framework");//"/system/framework"

// Gross hack for now: we know this file doesn't contain any
// code, so don't dexopt it to avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");

// Gross hack for now: we know this file is only part of
// the boot class path for art, so don't dexopt it to
// avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");

/**
 * There are a number of commands implemented in Java, which
 * we currently need to do the dexopt on so that they can be
 * run from a non-root shell.
 */
String[] frameworkFiles = frameworkDir.list();
if (frameworkFiles != null) {
    // TODO: We could compile these only for the most preferred ABI. We should
    // first double check that the dex files for these commands are not referenced
    // by other system apps.
    for (String dexCodeInstructionSet : dexCodeInstructionSets) {
        for (int i=0; i<frameworkFiles.length; i++) {
            File libPath = new File(frameworkDir, frameworkFiles[i]);
            String path = libPath.getPath();
            // Skip the file if we already did it.
            if (alreadyDexOpted.contains(path)) {
                continue;
            }
            // Skip the file if it is not a type we want to dexopt.
            if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
                continue;//跳過那些非apk和jar的文件
            }
            try {
                int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                    mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                }
            } catch (FileNotFoundException e) {
                Slog.w(TAG, "Jar not found: " + path);
            } catch (IOException e) {
                Slog.w(TAG, "Exception reading jar: " + path, e);
            }
        }
    }
}

1
/system/framework/framework-res.apk
/system/framework/core-libart.jar

這兩個文件加入已優化列表alreadyDexOpted中去。

然後搜索/system/framework中那些還沒有dexopt的jar或者apk文件,進行dexopt操作。

繼續:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
inal VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
        mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
    Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
    while (pkgSettingIter.hasNext()) {
        PackageSetting ps = pkgSettingIter.next();
        if (isSystemApp(ps)) {
            mExistingSystemPackages.add(ps.name);
        }
    }
}

如果是升級系統時,進行的處理,假設沒有進行系統升級,則忽略這段代碼。

在接下來的處理中,就會遇到PMS的一個非常重要的函數:scanDirLI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
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.
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.
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);

// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

依次調用scanDirLI方法對下列的目錄中的apk文件進行掃描。

1
/vendor/overlay
/system/framework
/system/priv-app
/system/app
/vendor/app
/oem/app

scanDirLI方法代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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;// 會從傳入的dir中,遍歷所有的文件,如果沒有文件,則直接返回
        }

        if (DEBUG_PACKAGE_SCANNING) {
            Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                    + " flags=0x" + Integer.toHexString(parseFlags));
        }

        for (File file : files) {// 如果有文件存在的話,就會進行遍歷

          // 判斷一個文件是否是一個apk的文件(以apk結尾),或者是一個文件夾並且文件夾滿足isStageName的條件
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            try {
               // 會調用scanPackageLI進行接下來的解析
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
            } catch (PackageManagerException e) {
                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

                // Delete invalid userdata apps
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    // 只有非系統的apk掃描失敗的時候,纔會刪除該apk。
                    logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                    if (file.isDirectory()) {
                        mInstaller.rmPackageDir(file.getAbsolutePath());
                    } else {
                        file.delete();
                    }
                }
            }
        }
    }
    public static boolean isStageName(String name) {
         final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
         final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
         final boolean isLegacyContainer = name.startsWith("smdl2tmp");
         return isFile || isContainer || isLegacyContainer;
     }

找到apk文件後,會調用scanPackageLI來解析該文件,並將該apk轉換爲PackageParser.Package對象。

源碼位置:

1
Android-6.0/framworks/base/core/java/android/content/pm/PackageParser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public final static class Package {

        public String packageName;

        /** Names of any split APKs, ordered by parsed splitName */
        public String[] splitNames;

        // TODO: work towards making these paths invariant

        public String volumeUuid;

        /**
         * Path where this package was found on disk. For monolithic packages
         * this is path to single base APK file; for cluster packages this is
         * path to the cluster directory.
         */
        public String codePath;

        /** Path of base APK */
        public String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
        public String[] splitCodePaths;

        /** Revision code of base APK */
        public int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        public int[] splitRevisionCodes;

        /** Flags of any split APKs; ordered by parsed splitName */
        public int[] splitFlags;
        ...............................

PackageParser.Package是一個apk文件在PMS中的代表,利用pm命令獲得apk的信息,就是通過該類表示的。裏面記錄了apk的包名等信息。

scanPackageLI的代碼量也是很大,所以也分段解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);

        if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
        }

        final PackageParser.Package pkg;
        try {
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        }
.................

scanPackageLI創建了一個文件解析器PackageParser,然後調用的解析器的parsePackage方法解析。

繼續scanPackageLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
synchronized (mPackages) {
    // Look to see if we already know about this package.
    String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
    if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
        // This package has been renamed to its original name.  Let's
        // use that.
        ps = mSettings.peekPackageLPr(oldName);
    }
    // If there was no original package, see one for the real package name.
    if (ps == null) {
      //這裏當設備不是第一次開機時,會解析packages.xml,將裏面記錄的pacakge都存儲到mSettings的mPackages中,它是一個ArrayMap<String, PackageSetting>變量。
      //以掃描到的包名檢測是否已經存在ps了,PackageSetting:Settings data for a particular package we know about.
        ps = mSettings.peekPackageLPr(pkg.packageName);
    }

這裏主要是處理應用升級後包名不一致的情況,當設備第一次開機時,不存在這樣的情況。其他情況下,開機會解析packages.xml,當前後有apk的包名發生變化時,該app在packages.xml中會以標籤標記。
而且還會把這些包名更改了的信息計入 PMS的mSettings變量的ArrayMap 類型的變量mRenamedPackages中,key是newname.

繼續scanPackageLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
    // Check to see if this package could be hiding/updating a system
    // package.  Must look for it either under the original or real
    // package name depending on our state.
    updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
    if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
}
boolean updatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
    // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
    // it needs to drop FLAG_PRIVILEGED.
    if (locationIsPrivileged(scanFile)) {
        updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
    } else {
        updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
    }

    if (ps != null && !ps.codePath.equals(scanFile)) {
        // The path has changed from what was last scanned...  check the
        // version of the new path against what we have stored to determine
        // what to do.
        if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
        if (pkg.mVersionCode <= ps.versionCode) {
            // The system package has been updated and the code path does not match
            // Ignore entry. Skip it.
            if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
                    + " ignored: updated version " + ps.versionCode
                    + " better than this " + pkg.mVersionCode);
            if (!updatedPkg.codePath.equals(scanFile)) {
                Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : "
                        + ps.name + " changing from " + updatedPkg.codePathString
                        + " to " + scanFile);
                updatedPkg.codePath = scanFile;
                updatedPkg.codePathString = scanFile.toString();
                updatedPkg.resourcePath = scanFile;
                updatedPkg.resourcePathString = scanFile.toString();
            }
            updatedPkg.pkg = pkg;
            throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                    "Package " + ps.name + " at " + scanFile
                            + " ignored: updated version " + ps.versionCode
                            + " better than this " + pkg.mVersionCode);
        } else {
            // The current app on the system partition is better than
            // what we have updated to on the data partition; switch
            // back to the system partition version.
            // At this point, its safely assumed that package installation for
            // apps in system partition will go through. If not there won't be a working
            // version of the app
            // writer
            synchronized (mPackages) {
                // Just remove the loaded entries from package lists.
                mPackages.remove(ps.name);
            }

            logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                    + " reverting from " + ps.codePathString
                    + ": new version " + pkg.mVersionCode
                    + " better than installed " + ps.versionCode);

            InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
            synchronized (mInstallLock) {
                args.cleanUpResourcesLI();
            }
            synchronized (mPackages) {
                mSettings.enableSystemPackageLPw(ps.name);
            }
            updatedPkgBetter = true;
        }
    }
}

if (updatedPkg != null) {
    // An updated system app will not have the PARSE_IS_SYSTEM flag set
    // initially
    parseFlags |= PackageParser.PARSE_IS_SYSTEM;

    // An updated privileged app will not have the PARSE_IS_PRIVILEGED
    // flag set initially
    if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
        parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
    }
}

這裏是處理安裝了系統更新後的,檢查是否對系統app有影響。即是否將系統app更新爲更高的新版本了。是的話,要處理。

接下來是掃描apk的簽名。

1
2
// Verify certificates against what was last scanned
collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);

繼續scanPackageLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * A new system app appeared, but we already had a non-system one of the
 * same name installed earlier.
 */
boolean shouldHideSystemApp = false;
if (updatedPkg == null && ps != null
        && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
    /*
     * Check to make sure the signatures match first. If they don't,
     * wipe the installed application and its data.
     */
    if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
            != PackageManager.SIGNATURE_MATCH) {
        logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
                + " signatures don't match existing userdata copy; removing");
        deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
        ps = null;
    } else {
        /*
         * If the newly-added system app is an older version than the
         * already installed version, hide it. It will be scanned later
         * and re-added like an update.
         */
        if (pkg.mVersionCode <= ps.versionCode) {
            shouldHideSystemApp = true;
            logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
                    + " but new version " + pkg.mVersionCode + " better than installed "
                    + ps.versionCode + "; hiding system");
        } else {
            /*
             * The newly found system app is a newer version that the
             * one previously installed. Simply remove the
             * already-installed application and replace it with our own
             * while keeping the application data.
             */
            logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                    + " reverting from " + ps.codePathString + ": new version "
                    + pkg.mVersionCode + " better than installed " + ps.versionCode);
            InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
            synchronized (mInstallLock) {
                args.cleanUpResourcesLI();
            }
        }
    }
}

這裏的代碼是處理這樣的情景:當系統更新後,可能更新包會多出一些system app出來,那麼如果此時用戶恰好安裝了一個同包名的app.兩者的簽名還不一致,那麼就刪除掃描到的系統應用的信息。

當兩者簽名一致時,如果掃描到的app版本更高,那麼就刪除安裝的應用;如果掃描的app版本低,那麼隱藏掃描到的系統應用。

繼續scanPackageLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// The apk is forward locked (not public) if its code and resources
// are kept in different files. (except for app in either system or
// vendor path).
// TODO grab this value from PackageSettings
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
    if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
        parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
    }
}

// TODO: extend to support forward-locked splits
String resourcePath = null;
String baseResourcePath = null;
if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
    if (ps != null && ps.resourcePathString != null) {
        resourcePath = ps.resourcePathString;
        baseResourcePath = ps.resourcePathString;
    } else {
        // Should not happen at all. Just log an error.
        Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
    }
} else {
    resourcePath = pkg.codePath;
    baseResourcePath = pkg.baseCodePath;
}

// 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);

處理應用的代碼路徑和資源路徑。

繼續scanPackageLI:

1
2
3
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
        | SCAN_UPDATE_SIGNATURE, currentTime, user);

調用PackageParser的另一個scanPackageLI繼續處理,後續會對其分析。

繼續scanPackageLI:

1
2
3
4
5
6
7
8
9
10
11
12
13
    /*
     * If the system app should be overridden by a previously installed
     * data, hide the system app now and let the /data/app scan pick it up
     * again.
     */
    if (shouldHideSystemApp) {
        synchronized (mPackages) {
            mSettings.disableSystemPackageLPw(pkg.packageName);
        }
    }

    return scannedPkg;
}

如果掃描的系統app需要被隱藏,那麼通過mSettings.disableSystemPackageLPw方法將其信息記錄在mSettings的mDisabledSysPackages中。

發佈了14 篇原創文章 · 獲贊 26 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章