[view compilation] Add --compile-layouts flag to pm compile

This allows us to generate precompiled layouts for installed applications.

If the system property view.precompiled_layout_enabled is set, then
PackageMannager will also automatically generate precompiled layouts for apps at
install or upgrade time.

Bug: 111895153
Test: manual
Change-Id: If6455e1b9b0542a36882af9f3e29d0185a53393c
This commit is contained in:
Eric Holk
2019-01-07 13:58:25 -08:00
parent d906d809fb
commit a1485f61e9
7 changed files with 180 additions and 12 deletions

View File

@@ -539,6 +539,11 @@ interface IPackageManager {
boolean performDexOptSecondary(String packageName,
String targetCompilerFilter, boolean force);
/**
* Ask the package manager to compile layouts in the given package.
*/
boolean compileLayouts(String packageName);
/**
* Ask the package manager to dump profiles associated with a package.
*/

View File

@@ -822,4 +822,9 @@ public abstract class PackageManagerInternal {
* PACKAGE_ROLLBACK_AGENT permission.
*/
public abstract void setEnableRollbackCode(int token, int enableRollbackCode);
/**
* Ask the package manager to compile layouts in the given package.
*/
public abstract boolean compileLayouts(String packageName);
}

View File

@@ -621,6 +621,14 @@ public class Installer extends SystemService {
throw new InstallerException("Invalid instruction set: " + instructionSet);
}
public boolean compileLayouts(String apkPath, String packageName, String outDexFile, int uid) {
try {
return mInstalld.compileLayouts(apkPath, packageName, outDexFile, uid);
} catch (RemoteException e) {
return false;
}
}
public static class InstallerException extends Exception {
public InstallerException(String detailMessage) {
super(detailMessage);

View File

@@ -311,6 +311,7 @@ import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
@@ -446,6 +447,9 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
private static final boolean PRECOMPILED_LAYOUT_ENABLED =
SystemProperties.getBoolean("view.precompiled_layout_enabled", false);
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
@@ -888,6 +892,8 @@ public class PackageManagerService extends IPackageManager.Stub
// is used by other apps).
private final DexManager mDexManager;
private final ViewCompiler mViewCompiler;
private AtomicInteger mNextMoveId = new AtomicInteger();
private final MoveCallbacks mMoveCallbacks;
@@ -2262,6 +2268,8 @@ public class PackageManagerService extends IPackageManager.Stub
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = new ViewCompiler(mInstallLock, mInstaller);
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
@@ -9104,6 +9112,10 @@ public class PackageManagerService extends IPackageManager.Stub
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
if (PRECOMPILED_LAYOUT_ENABLED) {
mArtManagerService.compileLayouts(pkg);
}
// checkProfiles is false to avoid merging profiles during boot which
// might interfere with background compilation (b/28612421).
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
@@ -9248,6 +9260,21 @@ public class PackageManagerService extends IPackageManager.Stub
return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
}
/**
* Ask the package manager to compile layouts in the given package.
*/
@Override
public boolean compileLayouts(String packageName) {
PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
if (pkg == null) {
return false;
}
}
return mViewCompiler.compileLayouts(pkg);
}
/*package*/ boolean performDexOpt(DexoptOptions options) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
@@ -16143,6 +16170,13 @@ public class PackageManagerService extends IPackageManager.Stub
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
if (performDexopt) {
// Compile the layout resources.
if (PRECOMPILED_LAYOUT_ENABLED) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
@@ -23737,6 +23771,21 @@ public class PackageManagerService extends IPackageManager.Stub
public void setEnableRollbackCode(int token, int enableRollbackCode) {
PackageManagerService.this.setEnableRollbackCode(token, enableRollbackCode);
}
/**
* Ask the package manager to compile layouts in the given package.
*/
@Override
public boolean compileLayouts(String packageName) {
PackageParser.Package pkg;
synchronized (mPackages) {
pkg = mPackages.get(packageName);
if (pkg == null) {
return false;
}
}
return mArtManagerService.compileLayouts(pkg);
}
}
@GuardedBy("mPackages")

View File

@@ -40,6 +40,7 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -1170,6 +1171,7 @@ class PackageManagerShellCommand extends ShellCommand {
String checkProfilesRaw = null;
boolean secondaryDex = false;
String split = null;
boolean compileLayouts = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -1189,6 +1191,9 @@ class PackageManagerShellCommand extends ShellCommand {
case "-r":
compilationReason = getNextArgRequired();
break;
case "--compile-layouts":
compileLayouts = true;
break;
case "--check-prof":
checkProfilesRaw = getNextArgRequired();
break;
@@ -1220,14 +1225,16 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
if (compilerFilter != null && compilationReason != null) {
pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
"at the same time");
return 1;
}
if (compilerFilter == null && compilationReason == null) {
pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " +
"reason (\"-r\") at the same time");
final boolean compilerFilterGiven = compilerFilter != null;
final boolean compilationReasonGiven = compilationReason != null;
// Make sure exactly one of -m, -r, or --compile-layouts is given.
if ((!compilerFilterGiven && !compilationReasonGiven && !compileLayouts)
|| (!compilerFilterGiven && compilationReasonGiven && compileLayouts)
|| (compilerFilterGiven && !compilationReasonGiven && compileLayouts)
|| (compilerFilterGiven && compilationReasonGiven && !compileLayouts)
|| (compilerFilterGiven && compilationReasonGiven && compileLayouts)) {
pw.println("Must specify exactly one of compilation filter (\"-m\"), compilation " +
"reason (\"-r\"), or compile layouts (\"--compile-layouts\")");
return 1;
}
@@ -1241,15 +1248,16 @@ class PackageManagerShellCommand extends ShellCommand {
return 1;
}
String targetCompilerFilter;
if (compilerFilter != null) {
String targetCompilerFilter = null;
if (compilerFilterGiven) {
if (!DexFile.isValidCompilerFilter(compilerFilter)) {
pw.println("Error: \"" + compilerFilter +
"\" is not a valid compilation filter.");
return 1;
}
targetCompilerFilter = compilerFilter;
} else {
}
if (compilationReasonGiven) {
int reason = -1;
for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
@@ -1291,12 +1299,19 @@ class PackageManagerShellCommand extends ShellCommand {
pw.flush();
}
boolean result = secondaryDex
boolean result = true;
if (compileLayouts) {
PackageManagerInternal internal = LocalServices.getService(
PackageManagerInternal.class);
result = internal.compileLayouts(packageName);
} else {
result = secondaryDex
? mInterface.performDexOptSecondary(packageName,
targetCompilerFilter, forceCompilation)
: mInterface.performDexOptMode(packageName,
checkProfiles, targetCompilerFilter, forceCompilation,
true /* bootComplete */, split);
}
if (!result) {
failedPackages.add(packageName);
}
@@ -3004,6 +3019,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
pw.println(" --secondary-dex: compile app secondary dex files");
pw.println(" --split SPLIT: compile only the given split name");
pw.println(" --compile-layouts: compile layout resources for faster inflation");
pw.println("");
pw.println(" force-dex-opt PACKAGE");
pw.println(" Force immediate execution of dex opt for the given PACKAGE.");

View File

@@ -471,6 +471,33 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
}
}
/**
* Compile layout resources in a given package.
*/
public boolean compileLayouts(PackageParser.Package pkg) {
try {
final String packageName = pkg.packageName;
final String apkPath = pkg.baseCodePath;
final ApplicationInfo appInfo = pkg.applicationInfo;
final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
appInfo.uid);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
catch (Throwable e) {
Log.e("PackageManager", "Failed to compile layouts", e);
return false;
}
}
/**
* Build the profiles names for all the package code paths (excluding resource only paths).
* Return the map [code path -> profile name].

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.os.Binder;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.server.pm.Installer;
public class ViewCompiler {
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
public ViewCompiler(Object installLock, Installer installer) {
mInstallLock = installLock;
mInstaller = installer;
}
public boolean compileLayouts(PackageParser.Package pkg) {
try {
final String packageName = pkg.packageName;
final String apkPath = pkg.baseCodePath;
final ApplicationInfo appInfo = pkg.applicationInfo;
final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
appInfo.uid);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
} catch (Throwable e) {
Log.e("PackageManager", "Failed to compile layouts", e);
return false;
}
}
}