Merge "Support for storing OAT files in app directory"

This commit is contained in:
Fyodor Kupolov
2015-04-02 23:11:10 +00:00
committed by Gerrit Code Review
8 changed files with 155 additions and 72 deletions

View File

@@ -897,6 +897,20 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
/**
* @hide
*/
public boolean isSystemApp() {
return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
/**
* @hide
*/
public boolean isUpdatedSystemApp() {
return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
/**
* @hide
*/

View File

@@ -4444,6 +4444,20 @@ public class PackageParser {
return applicationInfo.isForwardLocked();
}
/**
* @hide
*/
public boolean isSystemApp() {
return applicationInfo.isSystemApp();
}
/**
* @hide
*/
public boolean isUpdatedSystemApp() {
return applicationInfo.isUpdatedSystemApp();
}
public String toString() {
return "Package{"
+ Integer.toHexString(System.identityHashCode(this))

View File

@@ -91,11 +91,11 @@ public class InstallerConnection {
}
public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false);
return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false, null);
}
public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet, boolean vmSafeMode, boolean debuggable) {
String instructionSet, boolean vmSafeMode, boolean debuggable, String outputPath) {
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
@@ -108,6 +108,8 @@ public class InstallerConnection {
builder.append(instructionSet);
builder.append(vmSafeMode ? " 1" : " 0");
builder.append(debuggable ? " 1" : " 0");
builder.append(' ');
builder.append(outputPath != null ? outputPath : "!");
return execute(builder.toString());
}

View File

@@ -16,6 +16,7 @@
package com.android.server.pm;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
@@ -83,14 +84,15 @@ public final class Installer extends SystemService {
}
public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet, boolean vmSafeMode, boolean debuggable) {
String instructionSet, boolean vmSafeMode, boolean debuggable,
@Nullable String outputPath) {
if (!isValidInstructionSet(instructionSet)) {
Slog.e(TAG, "Invalid instruction set: " + instructionSet);
return -1;
}
return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, instructionSet, vmSafeMode,
debuggable);
debuggable, outputPath);
}
public int idmap(String targetApkPath, String overlayApkPath, int uid) {
@@ -134,6 +136,16 @@ public final class Installer extends SystemService {
return mInstaller.execute(builder.toString());
}
/**
* Removes packageDir or its subdirectory
*/
public int rmPackageDir(String packageDir) {
StringBuilder builder = new StringBuilder("rmpackagedir");
builder.append(' ');
builder.append(packageDir);
return mInstaller.execute(builder.toString());
}
public int remove(String name, int userId) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
@@ -331,6 +343,15 @@ public final class Installer extends SystemService {
return (mInstaller.execute(builder.toString()) == 0);
}
public int createOatDir(String oatDir, String dexInstructionSet) {
StringBuilder builder = new StringBuilder("createoatdir");
builder.append(' ');
builder.append(oatDir);
builder.append(' ');
builder.append(dexInstructionSet);
return mInstaller.execute(builder.toString());
}
/**
* Returns true iff. {@code instructionSet} is a valid instruction set.
*/

View File

@@ -74,6 +74,7 @@ public class InstructionSets {
* a native bridge this might be different than the one shared libraries use.
*/
public static String getDexCodeInstructionSet(String sharedLibraryIsa) {
// TODO b/19550105 Build mapping once instead of querying each time
String dexCodeIsa = SystemProperties.get("ro.dalvik.vm.isa." + sharedLibraryIsa);
return TextUtils.isEmpty(dexCodeIsa) ? sharedLibraryIsa : dexCodeIsa;
}
@@ -111,4 +112,13 @@ public class InstructionSets {
return allInstructionSets;
}
public static String getPrimaryInstructionSet(ApplicationInfo info) {
if (info.primaryCpuAbi == null) {
return getPreferredInstructionSet();
}
return VMRuntime.getInstructionSet(info.primaryCpuAbi);
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.pm;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.os.UserHandle;
@@ -23,6 +24,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
@@ -38,7 +40,9 @@ import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
* Helper class for running dexopt command on packages.
*/
final class PackageDexOptimizer {
static final String TAG = "PackageManager.DexOptimizer";
private static final String TAG = "PackageManager.DexOptimizer";
static final String OAT_DIR_NAME = "oat";
// TODO b/19550105 Remove error codes and use exceptions
static final int DEX_OPT_SKIPPED = 0;
static final int DEX_OPT_PERFORMED = 1;
static final int DEX_OPT_DEFERRED = 2;
@@ -117,19 +121,30 @@ final class PackageDexOptimizer {
final byte isDexOptNeeded = DexFile.isDexOptNeededInternal(path,
pkg.packageName, dexCodeInstructionSet, defer);
if (forceDex || (!defer && isDexOptNeeded == DexFile.DEXOPT_NEEDED)) {
File oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
Log.i(TAG, "Running dexopt on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable);
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
+ " oatDir = " + oatDir);
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
!pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
vmSafeMode, debuggable);
if (ret < 0) {
// Don't bother running dexopt again if we failed, it will probably
// just result in an error again. Also, don't bother dexopting for other
// paths & ISAs.
return DEX_OPT_FAILED;
if (oatDir != null) {
int ret = mPackageManagerService.mInstaller.dexopt(
path, sharedGid, !pkg.isForwardLocked(), pkg.packageName,
dexCodeInstructionSet, vmSafeMode, debuggable,
oatDir.getAbsolutePath());
if (ret < 0) {
return DEX_OPT_FAILED;
}
} else {
final int ret = mPackageManagerService.mInstaller
.dexopt(path, sharedGid,
!pkg.isForwardLocked(), pkg.packageName,
dexCodeInstructionSet,
vmSafeMode, debuggable, null);
if (ret < 0) {
return DEX_OPT_FAILED;
}
}
performedDexOpt = true;
@@ -186,6 +201,36 @@ final class PackageDexOptimizer {
return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
}
/**
* Creates oat dir for the specified package. In certain cases oat directory
* <strong>cannot</strong> be created:
* <ul>
* <li>{@code pkg} is a system app, which is not updated.</li>
* <li>Package location is not a directory, i.e. monolithic install.</li>
* </ul>
*
* @return oat directory or null, if oat directory cannot be created.
*/
@Nullable
private File createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
throws IOException {
if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) {
return null;
}
File codePath = new File(pkg.codePath);
if (codePath.isDirectory()) {
File oatDir = getOatDir(codePath);
mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
dexInstructionSet);
return oatDir;
}
return null;
}
static File getOatDir(File codePath) {
return new File(codePath, OAT_DIR_NAME);
}
private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
boolean forceDex, boolean defer, ArraySet<String> done) {
for (String libName : libs) {
@@ -209,7 +254,7 @@ final class PackageDexOptimizer {
public void addPackageForDeferredDexopt(PackageParser.Package pkg) {
if (mDeferredDexOpt == null) {
mDeferredDexOpt = new ArraySet<PackageParser.Package>();
mDeferredDexOpt = new ArraySet<>();
}
mDeferredDexOpt.add(pkg);
}

View File

@@ -53,7 +53,6 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -225,9 +224,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
for (File stage : unclaimedStages) {
Slog.w(TAG, "Deleting orphan stage " + stage);
if (stage.isDirectory()) {
FileUtils.deleteContents(stage);
mPm.mInstaller.rmPackageDir(stage.getAbsolutePath());
} else {
stage.delete();
}
stage.delete();
}
// Clean up orphaned icons

View File

@@ -59,6 +59,7 @@ import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import android.util.ArrayMap;
@@ -1872,9 +1873,10 @@ public class PackageManagerService extends IPackageManager.Stub {
removeDataDirsLI(ps.name);
if (ps.codePath != null) {
if (ps.codePath.isDirectory()) {
FileUtils.deleteContents(ps.codePath);
mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
} else {
ps.codePath.delete();
}
ps.codePath.delete();
}
if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
if (ps.resourcePath.isDirectory()) {
@@ -4190,9 +4192,10 @@ public class PackageManagerService extends IPackageManager.Stub {
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
FileUtils.deleteContents(file);
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
file.delete();
}
}
}
@@ -4640,7 +4643,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Give priority to system apps.
for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
PackageParser.Package pkg = it.next();
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName);
}
@@ -4651,7 +4654,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Give priority to updated system apps.
for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) {
PackageParser.Package pkg = it.next();
if (isUpdatedSystemApp(pkg)) {
if (pkg.isUpdatedSystemApp()) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName);
}
@@ -4772,14 +4775,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return performDexOpt(packageName, instructionSet, false);
}
private static String getPrimaryInstructionSet(ApplicationInfo info) {
if (info.primaryCpuAbi == null) {
return getPreferredInstructionSet();
}
return VMRuntime.getInstructionSet(info.primaryCpuAbi);
}
public boolean performDexOpt(String packageName, String instructionSet, boolean backgroundDexopt) {
boolean dexopt = mLazyDexOpt || backgroundDexopt;
boolean updateUsage = !backgroundDexopt; // Don't update usage if this is just a backgroundDexopt
@@ -5513,7 +5508,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String path = scanFile.getPath();
final String codePath = pkg.applicationInfo.getCodePath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
// If we haven't found any native libraries for the app, check if it has
@@ -5728,7 +5723,6 @@ public class PackageManagerService extends IPackageManager.Stub {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
}
}
if (mFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
@@ -5744,7 +5738,7 @@ public class PackageManagerService extends IPackageManager.Stub {
for (int i=0; i<pkg.libraryNames.size(); i++) {
String name = pkg.libraryNames.get(i);
boolean allowed = false;
if (isUpdatedSystemApp(pkg)) {
if (pkg.isUpdatedSystemApp()) {
// New library entries can only be added through the
// system image. This is important to get rid of a lot
// of nasty edge cases: for example if we allowed a non-
@@ -6350,7 +6344,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final ApplicationInfo info = pkg.applicationInfo;
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);
final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
final boolean asecApp = info.isForwardLocked() || isExternal(info);
info.nativeLibraryRootDir = null;
@@ -7002,7 +6996,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isSystemApp(pkg)) {
// For updated system applications, a system permission
// is granted only if it had been defined by the original application.
if (isUpdatedSystemApp(pkg)) {
if (pkg.isUpdatedSystemApp()) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
final GrantedPermissions origGp = sysPs.sharedUser != null
@@ -7090,7 +7084,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = isSystemApp(a.info.applicationInfo);
final boolean systemApp = a.info.applicationInfo.isSystemApp();
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO)
Log.v(
@@ -7217,7 +7211,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
res.icon = info.icon;
}
res.system = isSystemApp(res.activityInfo.applicationInfo);
res.system = res.activityInfo.applicationInfo.isSystemApp();
return res;
}
@@ -7433,7 +7427,7 @@ public class PackageManagerService extends IPackageManager.Stub {
res.labelRes = info.labelRes;
res.nonLocalizedLabel = info.nonLocalizedLabel;
res.icon = info.icon;
res.system = isSystemApp(res.serviceInfo.applicationInfo);
res.system = res.serviceInfo.applicationInfo.isSystemApp();
return res;
}
@@ -7656,7 +7650,7 @@ public class PackageManagerService extends IPackageManager.Stub {
res.labelRes = info.labelRes;
res.nonLocalizedLabel = info.nonLocalizedLabel;
res.icon = info.icon;
res.system = isSystemApp(res.providerInfo.applicationInfo);
res.system = res.providerInfo.applicationInfo.isSystemApp();
return res;
}
@@ -9386,9 +9380,10 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (codeFile.isDirectory()) {
FileUtils.deleteContents(codeFile);
mInstaller.rmPackageDir(codeFile.getAbsolutePath());
} else {
codeFile.delete();
}
codeFile.delete();
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
@@ -9830,22 +9825,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return result;
}
// Utility method used to ignore ADD/REMOVE events
// by directory observer.
private static boolean ignoreCodePath(String fullPathStr) {
String apkName = deriveCodePathName(fullPathStr);
int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
if (idx != -1 && ((idx+1) < apkName.length())) {
// Make sure the package ends with a numeral
String version = apkName.substring(idx+1);
try {
Integer.parseInt(version);
return true;
} catch (NumberFormatException e) {}
}
return false;
}
// Utility method that returns the relative package path with respect
// to the installation directory. Like say for /data/data/com.test-1.apk
// string com.test-1 is returned.
@@ -10454,13 +10433,23 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
// Run dexopt before old package gets removed, to minimize time when app is not available
int result = mPackageDexOptimizer
.performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
false /* defer */, false /* inclDependencies */);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
return;
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
// Call replacePackageLI with SCAN_NO_DEX, since we already made dexopt
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
installerPackageName, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
@@ -10502,10 +10491,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
private static boolean isSystemApp(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
private static boolean isSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@@ -10514,14 +10499,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private static boolean isUpdatedSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private static boolean isUpdatedSystemApp(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
private int packageFlagsToInstallFlags(PackageSetting ps) {
int installFlags = 0;
if (isExternal(ps)) {