From 3621be71d0fe8a349ca468aca99a53a17f6575b3 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Thu, 18 Jan 2018 15:17:29 -0800 Subject: [PATCH] [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 --- .../android/content/pm/dex/ArtManager.java | 10 ++++ .../java/com/android/server/pm/Installer.java | 13 +++++ .../server/pm/PackageManagerService.java | 9 +++- .../server/pm/dex/ArtManagerService.java | 54 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java index 201cd8d32cc1c..aa9c46e6a52d4 100644 --- a/core/java/android/content/pm/dex/ArtManager.java +++ b/core/java/android/content/pm/dex/ArtManager.java @@ -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"; + } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 4b3758d2e293e..7c448ed5fa776 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -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) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2585cf3d7fa5b..2a3bba5a0446c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -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 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! diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 2dbb34d888e5f..92d159b8ec049 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -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 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 getPackageProfileNames(PackageParser.Package pkg) { + ArrayMap 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; + } }