一 . 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/下;