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

Commit f2984242 authored by Jiakai Zhang's avatar Jiakai Zhang Committed by Android (Google) Code Review
Browse files

Merge "Use speed-profile with the cloud profile for apks loaded by other apps." into tm-dev

parents 7c7f54ec 28a4816d
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -89,6 +89,14 @@ public class Installer extends SystemService {
     */
    public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3;

    /**
     * The results of {@code getOdexVisibility}. See
     * {@link #getOdexVisibility(String, String, String)} for details.
     */
    public static final int ODEX_NOT_FOUND = 0;
    public static final int ODEX_IS_PUBLIC = 1;
    public static final int ODEX_IS_PRIVATE = 2;


    public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
    public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
@@ -866,6 +874,15 @@ public class Installer extends SystemService {
        }
    }

    /**
     * Prepares the app profile for the package at the given path:
     * <ul>
     *   <li>Creates the current profile for the given user ID, unless the user ID is
     *     {@code UserHandle.USER_NULL}.</li>
     *   <li>Merges the profile from the dex metadata file (if present) into the reference
     *     profile.</li>
     * </ul>
     */
    public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
            String profileName, String codePath, String dexMetadataPath) throws InstallerException {
        if (!checkBeforeRemote()) return false;
@@ -1016,6 +1033,33 @@ public class Installer extends SystemService {
        }
    }

    /**
     * Returns the visibility of the optimized artifacts.
     *
     * @param packageName name of the package.
     * @param apkPath path to the APK.
     * @param instructionSet instruction set of the optimized artifacts.
     * @param outputPath path to the directory that contains the optimized artifacts (i.e., the
     *   directory that {@link #dexopt} outputs to).
     *
     * @return {@link #ODEX_NOT_FOUND} if the optimized artifacts are not found, or
     *   {@link #ODEX_IS_PUBLIC} if the optimized artifacts are accessible by all apps, or
     *   {@link #ODEX_IS_PRIVATE} if the optimized artifacts are only accessible by this app.
     *
     * @throws InstallerException if failed to get the visibility of the optimized artifacts.
     */
    public int getOdexVisibility(String packageName, String apkPath, String instructionSet,
            String outputPath) throws InstallerException {
        if (!checkBeforeRemote()) return -1;
        BlockGuard.getVmPolicy().onPathAccess(apkPath);
        BlockGuard.getVmPolicy().onPathAccess(outputPath);
        try {
            return mInstalld.getOdexVisibility(packageName, apkPath, instructionSet, outputPath);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

    public static class InstallerException extends Exception {
        public InstallerException(String detailMessage) {
            super(detailMessage);
+97 −34
Original line number Diff line number Diff line
@@ -319,28 +319,42 @@ public class PackageDexOptimizer {

            String profileName = ArtManager.getProfileName(
                    i == 0 ? null : pkg.getSplitNames()[i - 1]);
            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                    || packageUseInfo.isUsedByOtherApps(path);
            String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
            // If the app is used by other apps, we must not use the existing profile because it
            // may contain user data, unless the profile is newly created on install.
            final boolean resetProfile = isProfileGuidedCompilerFilter(compilerFilter)
                    && isUsedByOtherApps
                    && options.getCompilationReason() != PackageManagerService.REASON_INSTALL;

            String dexMetadataPath = null;
            if (options.isDexoptInstallWithDexMetadata()) {
            if (options.isDexoptInstallWithDexMetadata() || resetProfile) {
                File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
                dexMetadataPath = dexMetadataFile == null
                        ? null : dexMetadataFile.getAbsolutePath();
            }

            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                    || packageUseInfo.isUsedByOtherApps(path);
            final String compilerFilter = getRealCompilerFilter(pkg,
                options.getCompilerFilter(), isUsedByOtherApps);
            // If we don't have to check for profiles updates assume
            // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
            // profiles.
            final int profileAnalysisResult = options.isCheckForProfileUpdates()
                    ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter)
                    : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
            int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
            if (resetProfile) {
                if (!resetProfile(pkg, profileName, path, dexMetadataPath)) {
                    // Fall back to use the shared filter.
                    compilerFilter =
                            PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                                    PackageManagerService.REASON_SHARED);
                }
            } else if (options.isCheckForProfileUpdates()) {
                profileAnalysisResult =
                        analyseProfiles(pkg, sharedGid, profileName, compilerFilter);
            }

            // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
            // flags.
            final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, options);
            final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, resetProfile,
                    options);

            for (String dexCodeIsa : dexCodeInstructionSets) {
                int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
@@ -390,6 +404,30 @@ public class PackageDexOptimizer {
        return result;
    }

    /**
     * Resets the profiles of the dex file at {@code path} belonging to the package {@code pkg} to
     * the initial state as if the package is newly installed. Returns true on success, or false
     * otherwise.
     */
    @GuardedBy("mInstallLock")
    private boolean resetProfile(AndroidPackage pkg, String profileName, String path,
            @Nullable String dexMetadataPath) {
        if (dexMetadataPath != null) {
            try {
                mInstaller.clearAppProfiles(pkg.getPackageName(), profileName);
                final int appId = UserHandle.getAppId(pkg.getUid());
                mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL,
                        appId, profileName, path, dexMetadataPath);
                return true;
            } catch (InstallerException e) {
                Slog.w(TAG, "Failed to reset profile", e);
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * Performs dexopt on the {@code path} belonging to the package {@code pkg}.
     *
@@ -405,15 +443,15 @@ public class PackageDexOptimizer {
            String classLoaderContext, int dexoptFlags, int uid,
            CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
            String dexMetadataPath, int compilationReason) {
        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
                profileAnalysisResult, downgrade);
        String oatDir = getPackageOatDirIfSupported(pkg,
                pkgSetting.getTransientState().isUpdatedSystemApp());

        int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
                classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir);
        if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
            return DEX_OPT_SKIPPED;
        }

        String oatDir = getPackageOatDirIfSupported(pkg,
                pkgSetting.getTransientState().isUpdatedSystemApp());

        Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
                + " pkg=" + pkg.getPackageName() + " isa=" + isa
                + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
@@ -456,6 +494,7 @@ public class PackageDexOptimizer {
    /**
     * Perform dexopt (if needed) on a system server code path).
     */
    @GuardedBy("mInstallLock")
    @DexOptResult
    public int dexoptSystemServerPath(
            String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
@@ -466,12 +505,15 @@ public class PackageDexOptimizer {
        int result = DEX_OPT_SKIPPED;
        for (String isa : dexUseInfo.getLoaderIsas()) {
            int dexoptNeeded = getDexoptNeeded(
                    PackageManagerService.PLATFORM_PACKAGE_NAME,
                    dexPath,
                    isa,
                    options.getCompilerFilter(),
                    dexUseInfo.getClassLoaderContext(),
                    PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES,
                    /* downgrade= */ false);
                    /* downgrade= */ false,
                    dexoptFlags,
                    /* oatDir= */ null);

            if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
                continue;
@@ -714,7 +756,7 @@ public class PackageDexOptimizer {
    }

    /**
     * Returns the compiler filter that should be used to optimize the package code.
     * Returns the compiler filter that should be used to optimize the secondary dex.
     * The target filter will be updated if the package code is used by other apps
     * or if it has the safe mode flag set.
     */
@@ -754,12 +796,12 @@ public class PackageDexOptimizer {
    }

    /**
     * Returns the compiler filter that should be used to optimize the package code.
     * The target filter will be updated if the package code is used by other apps
     * or if it has the safe mode flag set.
     * Returns the compiler filter that should be used to optimize the primary dex.
     * The target filter will be updated if the package has the safe mode flag set. Note that this
     * method does NOT take other app use into account. The caller should be responsible for
     * handling the case where the package code is used by other apps.
     */
    private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter,
            boolean isUsedByOtherApps) {
    private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) {
        // When an app or priv app is configured to run out of box, only verify it.
        if (pkg.isUseEmbeddedDex()
                || (pkg.isPrivileged()
@@ -783,12 +825,6 @@ public class PackageDexOptimizer {
            return getSafeModeCompilerFilter(targetCompilerFilter);
        }

        if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
            // If the dex files is used by other apps, apply the shared filter.
            return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                    PackageManagerService.REASON_SHARED);
        }

        return targetCompilerFilter;
    }

@@ -799,14 +835,16 @@ public class PackageDexOptimizer {
    private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
        return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
                info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
                info.requestsIsolatedSplitLoading(), compilerFilter, options);
                info.requestsIsolatedSplitLoading(), compilerFilter, false /* resetProfile */,
                options);
    }

    private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
            String compilerFilter, DexoptOptions options) {
            String compilerFilter, boolean resetProfile, DexoptOptions options) {
        return getDexFlags(pkg.isDebuggable(),
                AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
                pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
                options);
                resetProfile, options);
    }

    /**
@@ -815,13 +853,15 @@ public class PackageDexOptimizer {
     */
    private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
            SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
            String compilerFilter, DexoptOptions options) {
            String compilerFilter, boolean resetProfile, DexoptOptions options) {
        // Profile guide compiled oat files should not be public unles they are based
        // on profiles from dex metadata archives.
        // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
        // the user does not have an existing profile.
        // The flag resetProfile applies only when the existing profile is already reset.
        boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
        boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
        boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()
                || resetProfile;
        int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
        // Some apps are executed with restrictions on hidden API usage. If this app is one
        // of them, pass a flag to dexopt to enable the same restrictions during compilation.
@@ -866,8 +906,19 @@ public class PackageDexOptimizer {
     * Assesses if there's a need to perform dexopt on {@code path} for the given
     * configuration (isa, compiler filter, profile).
     */
    private int getDexoptNeeded(String path, String isa, String compilerFilter,
            String classLoaderContext, int profileAnalysisResult, boolean downgrade) {
    @GuardedBy("mInstallLock")
    private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
            String classLoaderContext, int profileAnalysisResult, boolean downgrade,
            int dexoptFlags, String oatDir) {
        final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
        // If the artifacts should be public while the current artifacts are not, we should
        // re-compile anyway.
        if (shouldBePublic && isOdexPrivate(packageName, path, isa, oatDir)) {
            // Ensure compilation by pretending a compiler filter change on the apk/odex location
            // (the reason for the '-'. A positive value means the 'oat' location).
            return adjustDexoptNeeded(-DexFile.DEX2OAT_FOR_FILTER);
        }

        int dexoptNeeded;
        try {
            // A profile guided optimizations with an empty profile is essentially 'verify' and
@@ -901,6 +952,18 @@ public class PackageDexOptimizer {
        return compilerFilter.endsWith("-profile");
    }

    /** Returns true if the current artifacts of the app are private to the app itself. */
    @GuardedBy("mInstallLock")
    private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) {
        try {
            return mInstaller.getOdexVisibility(packageName, path, isa, oatDir)
                    == Installer.ODEX_IS_PRIVATE;
        } catch (Exception e) {
            Slog.w(TAG, "Failed to get odex visibility for " + path, e);
            return false;
        }
    }

    /**
     * Checks if there is an update on the profile information of the {@code pkg}.
     * If the compiler filter is not profile guided the method returns a safe default: