Android PackageManagerService 源碼分析 lib庫相關處理流程

一 . lib 庫的安裝

       安裝lib庫代碼,主要代碼都在scanPackageDirtyLI函數中,先是調用了derivePackageAbi,這個函數中就把lib庫的路徑創建好了,也把lib庫copy到這個目錄下,然後後面會調用Installd的方法,在data/data/apk相關目錄 下的lib 創建軟鏈接到真正放lib的地方。
 

derivePackageAbi :

derivePackageAbi這個函數一開始就調用了setNativeLibraryPaths :

    private void setNativeLibraryPaths(PackageParser.Package pkg) {
        final ApplicationInfo info = pkg.applicationInfo;
        final String codePath = pkg.codePath;
        final File codeFile = new File(codePath);
        final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
        info.nativeLibraryRootDir = null;
        info.nativeLibraryRootRequiresIsa = false;
        info.nativeLibraryDir = null;
        info.secondaryNativeLibraryDir = null;
 
        if (isApkFile(codeFile)) {//是apk還是目錄
            // Monolithic install
            if (bundledApp) {//系統應用相關
                // If "/system/lib64/apkname" exists, assume that is the per-package
                // native library directory to use; otherwise use "/system/lib/apkname".
                final String apkRoot = calculateBundledApkRoot(info.sourceDir);
                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
                        getPrimaryInstructionSet(info));
 
                // This is a bundled system app so choose the path based on the ABI.
                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
                // is just the default path.
                final String apkName = deriveCodePathName(codePath);
                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
                info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
                        apkName).getAbsolutePath();
 
                if (info.secondaryCpuAbi != null) {
                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
                    info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
                            secondaryLibDir, apkName).getAbsolutePath();
                }
            } else if (asecApp) {
                info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
                        .getAbsolutePath();
            } else {
                final String apkName = deriveCodePathName(codePath);
                info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)//在data/app-lib下建一個apk的目錄
                        .getAbsolutePath();
            }
 
            info.nativeLibraryRootRequiresIsa = false;
            info.nativeLibraryDir = info.nativeLibraryRootDir;
        } else {//如果是目錄
            // Cluster install
            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();//目錄下直接建一個lib目錄
            info.nativeLibraryRootRequiresIsa = true;
 
            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
                    getPrimaryInstructionSet(info)).getAbsolutePath();
 
            if (info.secondaryCpuAbi != null) {
                info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
                        VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
            }
        }
    }

上面這個函數就是看lib庫最終的目錄:

 1.如果是apk文件:

     1.如果是普通應用,在/data/app-lib 下建立一個apk目錄;

      2.如果是系統應用,調用calculateBundledApkRoot 函數確定目錄,最終會在/system/lib , /vendor/lib , /oem/lib 下建立相關目錄;

  2.如果不是apk文件,直接在當前目錄建一個lib目錄。

    calculateBundledApkRoot函數就是看apk是在system目錄最終返回system目錄,oem或者vendor目錄最後返回oem或者vendor目錄。

     

    private static String calculateBundledApkRoot(final String codePathString) {
        final File codePath = new File(codePathString);
        final File codeRoot;
        if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
            codeRoot = Environment.getRootDirectory();
        } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
            codeRoot = Environment.getOemDirectory();
        } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
            codeRoot = Environment.getVendorDirectory();
        } else {
            // Unrecognized code path; take its top real segment as the apk root:
            // e.g. /something/app/blah.apk => /something
            try {
                File f = codePath.getCanonicalFile();
                File parent = f.getParentFile();    // non-null because codePath is a file
                File tmp;
                while ((tmp = parent.getParentFile()) != null) {
                    f = parent;
                    parent = tmp;
                }
                codeRoot = f;
                Slog.w(TAG, "Unrecognized code path "
                        + codePath + " - using " + codeRoot);
            } catch (IOException e) {
                // Can't canonicalize the code path -- shenanigans?
                Slog.w(TAG, "Can't canonicalize code path " + codePath);
                return Environment.getRootDirectory().getPath();
            }
        }
        return codeRoot.getPath();
    }

 

二 . 從APK 中copy lib 庫到特定路徑:

繼續看derivePackageAbi函數:

1.調用了setNativeLibraryPaths來確定了lib庫的目錄 ; 

2.調用NativeLibraryHelper.Handle.create來打開apk中文件 ;

3.調用NativeLibraryHelper.copyNativeBinariesForSupportedAbi 把apk的lib庫文件copy到創建的lib目錄;

    public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
                                 String cpuAbiOverride, boolean extractLibs)
            throws PackageManagerException {
        setNativeLibraryPaths(pkg);
 
        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
            extractLibs = false;
        }
 
        final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
        final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
 
        NativeLibraryHelper.Handle handle = null;
        try {
            handle = NativeLibraryHelper.Handle.create(scanFile);
            final File nativeLibraryRoot = new File(nativeLibraryRootStr);
 
            // Null out the abis so that they can be recalculated.
            pkg.applicationInfo.primaryCpuAbi = null;
            pkg.applicationInfo.secondaryCpuAbi = null;
            if (isMultiArch(pkg.applicationInfo)) {
                // Warn if we've set an abiOverride for multi-lib packages..
                // By definition, we need to copy both 32 and 64 bit libraries for
                // such packages.
                if (pkg.cpuAbiOverride != null
                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
                    Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
                }
 
                int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
                int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                    if (extractLibs) {
                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
                                useIsaSpecificSubdirs);
                    } else {
                        abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
                    }
                }
 
                maybeThrowExceptionForMultiArchCopy(
                        "Error unpackaging 32 bit native libs for multiarch app.", abi32);
 
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                    if (extractLibs) {
                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
                                useIsaSpecificSubdirs);
                    } else {
                        abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
                    }
                }
 
                maybeThrowExceptionForMultiArchCopy(
                        "Error unpackaging 64 bit native libs for multiarch app.", abi64);
 
                if (abi64 >= 0) {
                    pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
                }
 
                if (abi32 >= 0) {
                    final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                    if (abi64 >= 0) {
                        pkg.applicationInfo.secondaryCpuAbi = abi;
                    } else {
                        pkg.applicationInfo.primaryCpuAbi = abi;
                    }
                }
            } else {
                String[] abiList = (cpuAbiOverride != null) ?
                        new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
 
                // Enable gross and lame hacks for apps that are built with old
                // SDK tools. We must scan their APKs for renderscript bitcode and
                // not launch them if it's present. Don't bother checking on devices
                // that don't have 64 bit support.
                boolean needsRenderScriptOverride = false;
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                    abiList = Build.SUPPORTED_32_BIT_ABIS;
                    needsRenderScriptOverride = true;
                }
                final int copyRet;
                if (extractLibs) {
                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
                } else {
                    copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
                }
 
                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Error unpackaging native libs for app, errorCode=" + copyRet);
                }
 
                if (copyRet >= 0) {
                    pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
                } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
                    pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
                } else if (needsRenderScriptOverride) {
                    pkg.applicationInfo.primaryCpuAbi = abiList[0];
                }
            }
        } catch (IOException ioe) {
            Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
        } finally {
            IoUtils.closeQuietly(handle);
        }
 
        // Now that we've calculated the ABIs and determined if it's an internal app,
        // we will go ahead and populate the nativeLibraryPath.
        setNativeLibraryPaths(pkg);
    }

再看scanPackageDirtyLI函數 , 會調用Installd的linkNativeLibraryDirectory函數來創建lib庫的軟鏈接:

            if (pkg.applicationInfo.primaryCpuAbi != null &&
                    !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
                final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
                for (int userId : userIds) {
                    if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
                            nativeLibPath, userId) < 0) {
                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                                "Failed linking native library dir (user=" + userId + ")");
                    }
                }
            }

三. Installd創建lib 軟連接

最終調研installd 的函數進行軟連接:

int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
{
    struct stat s, libStat;
    int rc = 0;
 
    std::string _pkgdir(create_data_user_package_path(uuid, userId, pkgname));
    std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
 
    const char* pkgdir = _pkgdir.c_str();
    const char* libsymlink = _libsymlink.c_str();
 
    if (stat(pkgdir, &s) < 0) return -1;
 
    if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
        ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
        return -1;
    }
 
    if (chmod(pkgdir, 0700) < 0) {
        ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
        rc = -1;
        goto out;
    }
 
    if (lstat(libsymlink, &libStat) < 0) {
        if (errno != ENOENT) {
            ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
            rc = -1;
            goto out;
        }
    } else {
        if (S_ISDIR(libStat.st_mode)) {
            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
                rc = -1;
                goto out;
            }
        } else if (S_ISLNK(libStat.st_mode)) {
            if (unlink(libsymlink) < 0) {
                ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));
                rc = -1;
                goto out;
            }
        }
    }
 
    if (symlink(asecLibDir, libsymlink) < 0) {
        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
                strerror(errno));
        rc = -errno;
        goto out;
    }
 
out:
    if (chmod(pkgdir, s.st_mode) < 0) {
        ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
        rc = -errno;
    }
 
    if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
        ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
        return -errno;
    }
 
    return rc;
}

  

  1.非system 且 沒有目錄的apk:

     沒有目錄且是普通的apk的lib庫安裝位置:

     當直接把apk放在/system/app  下時 , apk將會安裝在 “/data/data/com.IflytekInput ” 下

drwxrwx--x u0_a61   u0_a61            2016-09-21 22:16 cache
drwxrwx--x u0_a61   u0_a61            2016-09-21 22:16 code_cache
drwxrwx--x u0_a61   u0_a61            2016-09-21 22:16 databases
drwxrwx--x u0_a61   u0_a61            2016-09-21 22:16 files
lrwxrwxrwx root     root              2016-09-21 22:47 lib -> /data/app-lib/IflytekInput
drwx------ u0_a61   u0_a61            2016-09-21 22:47 shared_prefs

  此時APK 的lib庫是直接軟連接到 "/data/app-lib/XXXX" 下的

 

2.非system 且有安裝目錄的apk:

lib文件夾會直接放在安裝目錄下:

root@lte26007:/data/app/IflytekInput # ls
IflytekInput.apk
lib

  lib 庫軟連接會連接到"/data/app/XXXX/lin/arm" 下

root@lte26007:/data/data/com.iflytek.inputmethod # ls -l
drwxrwx--x u0_a62   u0_a62            1980-01-08 07:12 cache
drwxrwx--x u0_a62   u0_a62            1980-01-08 07:12 code_cache
drwxrwx--x u0_a62   u0_a62            1980-01-08 07:12 databases
drwxrwx--x u0_a62   u0_a62            1980-01-08 07:12 files
lrwxrwxrwx root     root              1980-01-08 07:11 lib -> /data/app/IflytekInput/lib/arm
drwx------ u0_a62   u0_a62            1980-01-08 07:12 shared_prefs

總結:

 

 1.  有安裝目錄 和 沒有安裝目錄的apk ,都是在/data/data/XXXX/lib 這個路徑代表此apk 的lib資源,不同的是比lib資源軟連接的路徑不同;

 2 . 有安裝目錄的 軟連接到/system/app/XXX/lib/ 下 , 沒有安裝目錄的軟連接到/data/app-lib/XXXX/下;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章