Android6.0 PackageManagerService 安装lib
2016-11-28 15:27
417 查看
之前在PKMS预装三方应用时,放在system/third_app下面扫描出现了很多错,原因就是对安装lib和oat文件不熟悉。这篇博客就分析下这两项。
上面这个函数就是看lib库最终的目录,我们先看逻辑,先来看文件是否是apk,如果不是apk是目录的话,直接在当前目录建一个lib目录。如果是普通应用就在data/app-lib下建一个apk目录。如果是系统应用会调用calculateBundledApkRoot函数来确定目录,最终会在system/lib vendor/lib oem/lib下建立相关目录。
calculateBundledApkRoot函数就是看apk是在system目录最终返回system目录,oem或者vendor目录最后返回oem或者vendor目录。
然后我们再回到scanPackageDirtyLI函数,下面会调用Installd的linkNativeLibraryDirectory函数来创建lib库的软链接
再来看data/data下面的讯飞目录,lib目录直接是软链接到/data/app_lib/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
而如果我们把讯飞输入法放在data/app/IflytekInput目录下,就会把lib文件放在该目录下
root@lte26007:/data/app/IflytekInput # ls
IflytekInput.apk
lib
当然软链接也会到/data/app/IflytekInput/lib/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
这个后续还要继续研究下。
一、安装lib库
安装lib库代码,主要代码都在scanPackageDirtyLI函数中,先是调用了derivePackageAbi,这个函数中就把lib库的路径创建好了,也把lib库copy到这个目录下,然后后面会调用Installd的方法,在data/data/apk相关目录 下的lib 创建软链接到真正放lib的地方。if ((scanFlags & SCAN_NEW_INSTALL) == 0) { derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */); // Some system apps still use directory structure for native libraries // in which case we might end up not detecting abi solely based on apk // structure. Try to detect abi based on directory structure. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { setBundledAppAbisAndRoots(pkg, pkgSetting); setNativeLibraryPaths(pkg); } } else { if ((scanFlags & SCAN_MOVE) != 0) { // We haven't run dex-opt for this move (since we've moved the compiled output too) // but we already have this packages package info in the PackageSetting. We just // use that and derive the native library path based on the new codepath. pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString; pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString; } // Set native library paths again. For moves, the path will be updated based on the // ABIs we've determined above. For non-moves, the path will be updated based on the // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). setNativeLibraryPaths(pkg); } if (true) Slog.i(TAG, "Linking native library dir for " + path); final int[] userIds = sUserManager.getUserIds(); synchronized (mInstallLock) { // Make sure all user data directories are ready to roll; we're okay // if they already exist if (!TextUtils.isEmpty(pkg.volumeUuid)) { for (int userId : userIds) { if (userId != 0) { mInstaller.createUserData(pkg.volumeUuid, pkg.packageName, UserHandle.getUid(userId, pkg.applicationInfo.uid), userId, pkg.applicationInfo.seinfo); } } } // Create a native library symlink only if we have native libraries // and if the native libraries are 32 bit libraries. We do not provide // this symlink for 64 bit libraries. 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 + ")"); } } } }
二、 确定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库最终的目录,我们先看逻辑,先来看文件是否是apk,如果不是apk是目录的话,直接在当前目录建一个lib目录。如果是普通应用就在data/app-lib下建一个apk目录。如果是系统应用会调用calculateBundledApkRoot函数来确定目录,最终会在system/lib vendor/lib oem/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函数,我们先调用了setNativeLibraryPaths来确定了lib库的目录,然后NativeLibraryHelper.Handle.create来打开lapk中文件,接着又调用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 + ")"); } } }
四、创建lib软链接
最后在installd中调用如下代码,最后调用symlink来创建软链接。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; }
五、实例
这节主要看下具体apk的安装目录5.1 普通应用
5.1.1 没有目录
没有目录的且是普通apk的lib安装位置root@lc1861evb_arm64:/data/app-lib # ls IflytekInput MOffice NotePadPlus
再来看data/data下面的讯飞目录,lib目录直接是软链接到/data/app_lib/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
5.1.2 有目录
而如果我们把讯飞输入法放在data/app/IflytekInput目录下,就会把lib文件放在该目录下root@lte26007:/data/app/IflytekInput # ls
IflytekInput.apk
lib
当然软链接也会到/data/app/IflytekInput/lib/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
5.2 系统应用
系统应用很奇怪,在扫描时无论是有目录还是没有目录,最后都没有生成lib目录,也没有copylib库(在手机上面busybox find相关库也没有找到),但是data/data相关app下面有lib的软链接,但是打开错误。这个后续还要继续研究下。
相关文章推荐
- Android6.0 PackageManagerService(PMS)-安装
- 通过 PackageManagerService 接口queryIntentActivities 获取 全部 安装的apk信息
- PackageManagerService(Android5.1)深入分析(四)安装应用
- PackageManagerService安装APK流程
- Android应用管理四 -- APK包的安装、卸载和优化(PackageManagerService)
- Android5.1--APK包的安装、卸载和优化(PackageManagerService)(一)
- Android6.0 PackageManagerService卸载应用
- Android -- PackageManagerService APK安装流程简要分析
- Android framework 应用安装流程 分析 PackageManagerService(Android5.1)
- PackageManagerService安装流程
- Android5.1--APK包的安装、卸载和优化(PackageManagerService)(三)
- Android PackageManagerService分析二:安装APK
- Android6.0 PackageManagerService(PMS)-简介
- Android PackageManagerService分析二:安装APK
- PackageManagerService中运用inotify去监控目录中的APK更新、安装和删除
- Android7.0 PackageManagerService (3) APK安装
- PackageManagerService源码分析之安装应用(四)
- Android6.0 PackageManagerService(PMS)-卸载