[framework] Prepare profile for app code paths

This CL is an intermediate step to enable the use of profiles shipped in
the dex metadata files.

The preparation consist of:
- creating the current profile
- merging the profile from the dex metadata file (if present) into the
reference profile

and happens:
- after the application data directory is created, to capture system
apps.
- post-install (right before we dexopt), to capture any new code paths

Test: manual (install apps and splits, take OTA)
Bug: 30934496

Change-Id: Id36474ab629ad3ffafd24381d30e3d88ac02d576
This commit is contained in:
Calin Juravle
2018-01-18 15:17:29 -08:00
parent f74e56af8c
commit 3621be71d0
4 changed files with 85 additions and 1 deletions

View File

@@ -153,4 +153,14 @@ public class ArtManager {
return true;
}
}
/**
* Return the profile name for the given split. If {@code splitName} is null the
* method returns the profile name for the base apk.
*
* @hide
*/
public static String getProfileName(String splitName) {
return splitName == null ? "primary.prof" : splitName + ".split.prof";
}
}

View File

@@ -16,7 +16,9 @@
package com.android.server.pm;
import android.annotation.AppIdInt;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
@@ -535,6 +537,17 @@ public class Installer extends SystemService {
}
}
public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
String profileName, String codePath, String dexMetadataPath) throws InstallerException {
if (!checkBeforeRemote()) return false;
try {
return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath,
dexMetadataPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {

View File

@@ -2425,6 +2425,7 @@ public class PackageManagerService extends IPackageManager.Stub
installer, mInstallLock);
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
dexManagerListener);
mArtManagerService = new ArtManagerService(this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -3081,7 +3082,6 @@ Slog.e("TODD",
}
mInstallerService = new PackageInstallerService(context, this);
mArtManagerService = new ArtManagerService(this, mInstaller, mInstallLock);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
@@ -16901,6 +16901,11 @@ Slog.e("TODD",
}
}
// Prepare the application profiles for the new code paths.
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(pkg, args.user.getIdentifier());
// Check whether we need to dexopt the app.
//
// NOTE: it is IMPORTANT to call dexopt:
@@ -21915,6 +21920,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
}
}
// Prepare the application profiles.
mArtManagerService.prepareAppProfiles(pkg, userId);
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
// TODO: mark this structure as dirty so we persist it!

View File

@@ -17,9 +17,13 @@
package com.android.server.pm.dex;
import android.Manifest;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -29,10 +33,12 @@ import android.content.pm.IPackageManager;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
@@ -230,4 +236,52 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
// Should not happen.
}
}
/**
* Prepare the application profiles.
* For all code paths:
* - create the current primary profile to save time at app startup time.
* - copy the profiles from the associated dex metadata file to the reference profile.
*/
public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
try {
ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
String codePath = codePathsProfileNames.keyAt(i);
String profileName = codePathsProfileNames.valueAt(i);
File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
synchronized (mInstaller) {
boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
profileName, codePath, dexMetadataPath);
if (!result) {
Slog.e(TAG, "Failed to prepare profile for " +
pkg.packageName + ":" + codePath);
}
}
}
} catch (InstallerException e) {
Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
}
}
/**
* Build the profiles names for all the package code paths (excluding resource only paths).
* Return the map [code path -> profile name].
*/
private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
ArrayMap<String, String> result = new ArrayMap<>();
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
}
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
}
}
}
return result;
}
}