[framework] Extend profile operations to take the profile name

Extend the installd profile interface to take the profile name as
argument. This shifts the responsibility for choosing the names of
profiles for primary apks completely to PackageManager. Each of the
application code paths will get an unique profile name based on their
split name.

All the profile operations will now work on a specific profile name rather
than assuming a default global name.

Also, move dumpProfiles and clearProfiles functionality to the
ArtManagerService so that we can re-use profileName computations easier.

Test: manual (dexopt apps, merge profiles, clear profiles)
      gts GtsAndroidRuntimeManagerHostTestCases
Bug: 30934496

Change-Id: Ie65d45eed7de0844edf4b7af918d7eaa74ec1f2c
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 26f4980..d24d4f3 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -28,6 +28,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.dex.ArtManager;
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
@@ -35,7 +36,6 @@
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -49,13 +49,15 @@
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.LogPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
+
 import com.android.internal.util.ArrayUtils;
+
 import dalvik.system.VMRuntime;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -749,13 +751,6 @@
         }
     }
 
-    // Keep in sync with installd (frameworks/native/cmds/installd/commands.cpp).
-    private static File getPrimaryProfileFile(String packageName) {
-        File profileDir = Environment.getDataProfilesDePackageDirectory(
-                UserHandle.myUserId(), packageName);
-        return new File(profileDir, "primary.prof");
-    }
-
     private void setupJitProfileSupport() {
         if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
             return;
@@ -783,10 +778,12 @@
             return;
         }
 
-        final File profileFile = getPrimaryProfileFile(mPackageName);
-
-        VMRuntime.registerAppInfo(profileFile.getPath(),
-                codePaths.toArray(new String[codePaths.size()]));
+        for (int i = codePaths.size() - 1; i >= 0; i--) {
+            String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1];
+            String profileFile = ArtManager.getCurrentProfilePath(
+                    mPackageName, UserHandle.myUserId(), splitName);
+            VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)});
+        }
 
         // Register the app data directory with the reporter. It will
         // help deciding whether or not a dex file is the primary apk or a
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index aa9c46e6..f979576 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -26,6 +27,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.io.File;
+
 /**
  * Class for retrieving various kinds of information related to the runtime artifacts of
  * packages that are currently installed on the device.
@@ -163,4 +166,29 @@
     public static String getProfileName(String splitName) {
         return splitName == null ? "primary.prof" : splitName + ".split.prof";
     }
+
+    /**
+     * Return the path to the current profile corresponding to given package and split.
+     *
+     * @hide
+     */
+    public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
+        File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
+        return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+    }
+
+    /**
+     * Return the snapshot profile file for the given package and split.
+     *
+     * KEEP in sync with installd dexopt.cpp.
+     * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
+     *
+     * @hide
+     */
+    public static File getProfileSnapshotFile(String packageName, String splitName) {
+        File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+        String snapshotFile = getProfileName(splitName) + ".snapshot";
+        return new File(profileDir, snapshotFile);
+
+    }
 }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 62731e8..158041d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -302,9 +302,8 @@
     }
 
     /** {@hide} */
-    public static File getProfileSnapshotPath(String packageName, String codePath) {
-        return buildPath(buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName,
-                "primary.prof.snapshot"));
+    public static File getDataRefProfilesDePackageDirectory(String packageName) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
     }
 
     /** {@hide} */
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5659470..39279b5 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -583,7 +583,7 @@
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
                             uuid, classLoaderContext, seInfo, false /* downgrade */,
-                            targetSdkVersion);
+                            targetSdkVersion, /*profileName*/ null);
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index cdc79c7..2e9ab0b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -286,43 +286,44 @@
     public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
-            @Nullable String seInfo, boolean downgrade, int targetSdkVersion)
-            throws InstallerException {
+            @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
+            @Nullable String profileName) throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
                     dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
-                    targetSdkVersion);
+                    targetSdkVersion, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
     }
 
-    public boolean mergeProfiles(int uid, String packageName) throws InstallerException {
-        if (!checkBeforeRemote()) return false;
-        try {
-            return mInstalld.mergeProfiles(uid, packageName);
-        } catch (Exception e) {
-            throw InstallerException.from(e);
-        }
-    }
-
-    public boolean dumpProfiles(int uid, String packageName, String codePaths)
+    public boolean mergeProfiles(int uid, String packageName, String profileName)
             throws InstallerException {
         if (!checkBeforeRemote()) return false;
         try {
-            return mInstalld.dumpProfiles(uid, packageName, codePaths);
+            return mInstalld.mergeProfiles(uid, packageName, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
     }
 
-    public boolean copySystemProfile(String systemProfile, int uid, String packageName)
+    public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath)
             throws InstallerException {
         if (!checkBeforeRemote()) return false;
         try {
-            return mInstalld.copySystemProfile(systemProfile, uid, packageName);
+            return mInstalld.dumpProfiles(uid, packageName, profileName, codePath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public boolean copySystemProfile(String systemProfile, int uid, String packageName,
+                String profileName) throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
@@ -366,10 +367,10 @@
         }
     }
 
-    public void clearAppProfiles(String packageName) throws InstallerException {
+    public void clearAppProfiles(String packageName, String profileName) throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.clearAppProfiles(packageName);
+            mInstalld.clearAppProfiles(packageName, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
@@ -502,21 +503,21 @@
         }
     }
 
-    public boolean createProfileSnapshot(int appId, String packageName, String codePath)
+    public boolean createProfileSnapshot(int appId, String packageName, String profileName)
             throws InstallerException {
         if (!checkBeforeRemote()) return false;
         try {
-            return mInstalld.createProfileSnapshot(appId, packageName, codePath);
+            return mInstalld.createProfileSnapshot(appId, packageName, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
     }
 
-    public void destroyProfileSnapshot(String packageName, String codePath)
+    public void destroyProfileSnapshot(String packageName, String profileName)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.destroyProfileSnapshot(packageName, codePath);
+            mInstalld.destroyProfileSnapshot(packageName, profileName);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 0395011..5bf38dc 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -261,12 +261,12 @@
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
                     @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
-                    int targetSdkVersion)
+                    int targetSdkVersion, @Nullable String profileName)
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 4.
-                builder.append("4 ");
+                // The version. Right now it's 5.
+                builder.append("5 ");
 
                 builder.append("dexopt");
 
@@ -283,6 +283,7 @@
                 encodeParameter(builder, seInfo);
                 encodeParameter(builder, downgrade);
                 encodeParameter(builder, targetSdkVersion);
+                encodeParameter(builder, profileName);
 
                 commands.add(builder.toString());
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 6a08e1b..8c18511 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.dex.ArtManager;
 import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -211,12 +212,14 @@
                 }
             }
 
+            String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+
             final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                     || packageUseInfo.isUsedByOtherApps(path);
             final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
                 options.getCompilerFilter(), isUsedByOtherApps);
             final boolean profileUpdated = options.isCheckForProfileUpdates() &&
-                isProfileUpdated(pkg, sharedGid, compilerFilter);
+                isProfileUpdated(pkg, sharedGid, profileName, compilerFilter);
 
             // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
             // flags.
@@ -225,7 +228,7 @@
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
-                        packageStats, options.isDowngrade());
+                        packageStats, options.isDowngrade(), profileName);
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
@@ -249,7 +252,8 @@
     @GuardedBy("mInstallLock")
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String classLoaderContext,
-            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
+            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
+            String profileName) {
         int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
                 profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -275,7 +279,8 @@
             // primary dex files.
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                     compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
-                    false /* downgrade*/, pkg.applicationInfo.targetSdkVersion);
+                    false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
+                    profileName);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -396,7 +401,7 @@
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
                         compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
-                        options.isDowngrade(), info.targetSdkVersion);
+                        options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null);
             }
 
             return DEX_OPT_PERFORMED;
@@ -550,14 +555,15 @@
      * current profile and the reference profile will be merged and subsequent calls
      * may return a different result.
      */
-    private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String compilerFilter) {
+    private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String profileName,
+            String compilerFilter) {
         // Check if we are allowed to merge and if the compiler filter is profile guided.
         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
             return false;
         }
         // Merge profiles. It returns whether or not there was an updated in the profile info.
         try {
-            return mInstaller.mergeProfiles(uid, pkg.packageName);
+            return mInstaller.mergeProfiles(uid, pkg.packageName, profileName);
         } catch (InstallerException e) {
             Slog.w(TAG, "Failed to merge profiles", e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 299c59a..9fdbdbe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -195,6 +195,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.IArtManager;
 import android.content.res.Resources;
@@ -8873,7 +8874,8 @@
                         // PackageDexOptimizer to prevent this happening on first boot. The issue
                         // is that we don't have a good way to say "do this only once".
                         if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                pkg.applicationInfo.uid, pkg.packageName)) {
+                                pkg.applicationInfo.uid, pkg.packageName,
+                                ArtManager.getProfileName(null))) {
                             Log.e(TAG, "Installer failed to copy system profile!");
                         } else {
                             // Disabled as this causes speed-profile compilation during first boot
@@ -8908,7 +8910,8 @@
                                 // issue is that we don't have a good way to say "do this only
                                 // once".
                                 if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                        pkg.applicationInfo.uid, pkg.packageName)) {
+                                        pkg.applicationInfo.uid, pkg.packageName,
+                                        ArtManager.getProfileName(null))) {
                                     Log.e(TAG, "Failed to copy system profile for stub package!");
                                 } else {
                                     useProfileForDexopt = true;
@@ -9333,14 +9336,7 @@
 
         synchronized (mInstallLock) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
-            final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-            try {
-                List<String> allCodePaths = pkg.getAllCodePathsExcludingResourceOnly();
-                String codePaths = TextUtils.join(";", allCodePaths);
-                mInstaller.dumpProfiles(sharedGid, packageName, codePaths);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to dump profiles", e);
-            }
+            mArtManagerService.dumpProfiles(pkg);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
@@ -9416,6 +9412,8 @@
         for (int i = 0; i < childCount; i++) {
             clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
         }
+
+        clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
     }
 
     private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
@@ -9488,18 +9486,10 @@
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
-        clearAppProfilesLeafLIF(pkg);
+        mArtManagerService.clearAppProfiles(pkg);
         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
         for (int i = 0; i < childCount; i++) {
-            clearAppProfilesLeafLIF(pkg.childPackages.get(i));
-        }
-    }
-
-    private void clearAppProfilesLeafLIF(PackageParser.Package pkg) {
-        try {
-            mInstaller.clearAppProfiles(pkg.packageName);
-        } catch (InstallerException e) {
-            Slog.w(TAG, String.valueOf(e));
+            mArtManagerService.clearAppProfiles(pkg.childPackages.get(i));
         }
     }
 
@@ -16108,7 +16098,6 @@
 
             clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
                     | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-            clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
 
             try {
                 final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
@@ -16247,7 +16236,6 @@
         // Successfully disabled the old package. Now proceed with re-installation
         clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
                 | StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-        clearAppProfilesLIF(deletedPackage, UserHandle.USER_ALL);
 
         res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
         pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -20283,7 +20271,6 @@
                     }
                     clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
                             | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-                    clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
                     mDexManager.notifyPackageUpdated(pkg.packageName,
                             pkg.baseCodePath, pkg.splitCodePaths);
                 }
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 8178689..f15dc2d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -25,7 +25,6 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -111,11 +110,13 @@
         }
 
         boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
+        String splitName = null;
         String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
         if (!pathFound && (splitCodePaths != null)) {
-            for (String path : splitCodePaths) {
-                if (path.equals(codePath)) {
+            for (int i = splitCodePaths.length - 1; i >= 0; i--) {
+                if (splitCodePaths[i].equals(codePath)) {
                     pathFound = true;
+                    splitName = info.applicationInfo.splitNames[i];
                     break;
                 }
             }
@@ -126,18 +127,18 @@
         }
 
         // All good, create the profile snapshot.
-        createProfileSnapshot(packageName, codePath, callback, info);
+        createProfileSnapshot(packageName, splitName, callback, info);
         // Destroy the snapshot, we no longer need it.
-        destroyProfileSnapshot(packageName, codePath);
+        destroyProfileSnapshot(packageName, splitName);
     }
 
-    private void createProfileSnapshot(String packageName, String codePath,
+    private void createProfileSnapshot(String packageName, String splitName,
             ISnapshotRuntimeProfileCallback callback, PackageInfo info) {
         // Ask the installer to snapshot the profile.
         synchronized (mInstallLock) {
             try {
                 if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid),
-                        packageName, codePath)) {
+                        packageName, ArtManager.getProfileName(splitName))) {
                     postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
                     return;
                 }
@@ -148,28 +149,30 @@
         }
 
         // Open the snapshot and invoke the callback.
-        File snapshotProfile = Environment.getProfileSnapshotPath(packageName, codePath);
+        File snapshotProfile = ArtManager.getProfileSnapshotFile(packageName, splitName);
         ParcelFileDescriptor fd;
         try {
             fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
             postSuccess(packageName, fd, callback);
         } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + codePath, e);
+            Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
+                    + snapshotProfile, e);
             postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
         }
     }
 
-    private void destroyProfileSnapshot(String packageName, String codePath) {
+    private void destroyProfileSnapshot(String packageName, String splitName) {
         if (DEBUG) {
-            Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + codePath);
+            Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + splitName);
         }
 
         synchronized (mInstallLock) {
             try {
-                mInstaller.destroyProfileSnapshot(packageName, codePath);
+                mInstaller.destroyProfileSnapshot(packageName,
+                        ArtManager.getProfileName(splitName));
             } catch (InstallerException e) {
                 Slog.e(TAG, "Failed to destroy profile snapshot for " +
-                    packageName + ":" + codePath, e);
+                    packageName + ":" + splitName, e);
             }
         }
     }
@@ -284,6 +287,40 @@
     }
 
     /**
+     * Clear the profiles for the given package.
+     */
+    public void clearAppProfiles(PackageParser.Package pkg) {
+        try {
+            ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
+            for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
+                String profileName = packageProfileNames.valueAt(i);
+                mInstaller.clearAppProfiles(pkg.packageName, profileName);
+            }
+        } catch (InstallerException e) {
+            Slog.w(TAG, String.valueOf(e));
+        }
+    }
+
+    /**
+     * Dumps the profiles for the given package.
+     */
+    public void dumpProfiles(PackageParser.Package pkg) {
+        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+        try {
+            ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
+            for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
+                String codePath = packageProfileNames.keyAt(i);
+                String profileName = packageProfileNames.valueAt(i);
+                synchronized (mInstallLock) {
+                    mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
+                }
+            }
+        } catch (InstallerException e) {
+            Slog.w(TAG, "Failed to dump profiles", e);
+        }
+    }
+
+    /**
      * Build the profiles names for all the package code paths (excluding resource only paths).
      * Return the map [code path -> profile name].
      */