Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3621be71 authored by Calin Juravle's avatar Calin Juravle
Browse files

[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
parent f74e56af
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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";
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -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) {
+8 −1
Original line number Diff line number Diff line
@@ -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!
+54 −0
Original line number Diff line number Diff line
@@ -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;
    }
}