Android6.0 PackageManagerServiceインストールlib
19605 ワード
以前はPKMSで三者アプリケーションをプリインストールしていた時、system/third_に置いた.appの下のスキャンでは、libとoatファイルのインストールに慣れていないため、多くのエラーが発生しました.このブログでは、この2つを分析します.
一、libライブラリのインストール
libライブラリコードをインストールします.主なコードはscanPackageDirtyLI関数で、まずderivePackageAbiを呼び出します.この関数ではlibライブラリのパスを作成し、libライブラリのcopyをこのディレクトリの下にします.その後、Installdのメソッドを呼び出し、data/data/apk関連ディレクトリのlibに実際にlibを置く場所にソフトリンクを作成します.
二、libディレクトリの確定
では、まず最もコアなderivePackageAbi関数から分析を始めます.derivePackageAbiという関数は最初からsetNativeLibraryPathsを呼び出しています.そのため、まずこの関数を見てみましょう.
上の関数はlibライブラリの最終ディレクトリを見て、私たちはまず論理を見て、まずファイルがapkかどうかを見て、apkがディレクトリでなければ、直接現在のディレクトリにlibディレクトリを建てます.通常のアプリケーションであればdata/app-libの下にapkディレクトリを作成します.システムアプリケーションがcalculateBundledApkRoot関数を呼び出してディレクトリを決定する場合、最終的にはsystem/lib vendor/lib oem/libの下に関連ディレクトリが作成されます.
calculateBundledApkRoot関数は、apkがシステムディレクトリで最終的にシステムディレクトリに戻り、oemまたはvendorディレクトリが最終的にoemまたはvendorディレクトリに戻るのを見ることです.
三、apkからcopy libライブラリ
次にderivePackageAbi関数を見て、setNativeLibraryPathsを呼び出してlibライブラリのディレクトリを決定し、NativeLibraryHelper.Handle.createはlapkのファイルを開き、NativeLibraryHelperを呼び出します.copy NativeBinariesForSupportedAbiはapkのlibライブラリファイルを作成したlibディレクトリにcopyします.
次にscanPackageDirtyLI関数に戻ります.次に、InstalldのlinkNativeLibraryDirectory関数を呼び出してlibライブラリのソフトリンクを作成します.
四、libソフトリンクの作成
最後にinstalldで次のコードを呼び出し、symlinkを呼び出してソフトリンクを作成します.
五、実例
この節では主に具体的なapkのインストールディレクトリを見てみましょう
5.1一般応用
5.1.1ディレクトリなし
ディレクトリがなく、通常のapkのlibインストール場所です.
さらにdata/dataの下にあるアイフライディレクトリを見ると、libディレクトリは直接/data/app_にソフトリンクされています.lib/IflytekInput
5.1.2ディレクトリあり
メッセージ入力方式をdata/app/IflytekInputディレクトリの下に置くとlibファイルがそのディレクトリの下に置かれます
もちろんソフトリンクは/data/app/IflytekInput/lib/armディレクトリにも
5.2システム応用
システムアプリケーションは不思議で、スキャン時にディレクトリがあってもなくても、最後にlibディレクトリが生成されず、copylibライブラリもありません(携帯電話の上でbusybox find関連ライブラリも見つかりませんでした)が、data/data関連appの下にlibのソフトリンクがありますが、開け間違えました.
この後も研究を続けなければならない.
一、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がシステムディレクトリで最終的にシステムディレクトリに戻り、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を呼び出します.copy NativeBinariesForSupportedAbiはapkのlibライブラリファイルを作成したlibディレクトリにcopyします.
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
", pkgdir, strerror(errno));
return -1;
}
if (chmod(pkgdir, 0700) < 0) {
ALOGE("linklib() 1: failed to chmod '%s': %s
", pkgdir, strerror(errno));
rc = -1;
goto out;
}
if (lstat(libsymlink, &libStat) < 0) {
if (errno != ENOENT) {
ALOGE("couldn't stat lib dir: %s
", 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
", strerror(errno));
rc = -1;
goto out;
}
}
}
if (symlink(asecLibDir, libsymlink) < 0) {
ALOGE("couldn't symlink directory '%s' -> '%s': %s
", libsymlink, asecLibDir,
strerror(errno));
rc = -errno;
goto out;
}
out:
if (chmod(pkgdir, s.st_mode) < 0) {
ALOGE("linklib() 2: failed to chmod '%s': %s
", pkgdir, strerror(errno));
rc = -errno;
}
if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
ALOGE("failed to chown '%s' : %s
", 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のソフトリンクがありますが、開け間違えました.
この後も研究を続けなければならない.