Merge "Preserve user's reference profile for apps used by other apps." into tm-dev

This commit is contained in:
Jiakai Zhang
2022-03-30 14:14:17 +00:00
committed by Android (Google) Code Review
2 changed files with 107 additions and 68 deletions

View File

@@ -692,6 +692,20 @@ public class Installer extends SystemService {
} }
} }
/**
* Deletes the reference profile with the given name of the given package.
* @throws InstallerException if the deletion fails.
*/
public void deleteReferenceProfile(String packageName, String profileName)
throws InstallerException {
if (!checkBeforeRemote()) return;
try {
mInstalld.deleteReferenceProfile(packageName, profileName);
} catch (Exception e) {
throw InstallerException.from(e);
}
}
public void createUserData(String uuid, int userId, int userSerial, int flags) public void createUserData(String uuid, int userId, int userSerial, int flags)
throws InstallerException { throws InstallerException {
if (!checkBeforeRemote()) return; if (!checkBeforeRemote()) return;

View File

@@ -324,12 +324,12 @@ public class PackageDexOptimizer {
String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter()); String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
// If the app is used by other apps, we must not use the existing profile because it // 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. // may contain user data, unless the profile is newly created on install.
final boolean resetProfile = isProfileGuidedCompilerFilter(compilerFilter) final boolean useCloudProfile = isProfileGuidedCompilerFilter(compilerFilter)
&& isUsedByOtherApps && isUsedByOtherApps
&& options.getCompilationReason() != PackageManagerService.REASON_INSTALL; && options.getCompilationReason() != PackageManagerService.REASON_INSTALL;
String dexMetadataPath = null; String dexMetadataPath = null;
if (options.isDexoptInstallWithDexMetadata() || resetProfile) { if (options.isDexoptInstallWithDexMetadata() || useCloudProfile) {
File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path)); File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path));
dexMetadataPath = dexMetadataFile == null dexMetadataPath = dexMetadataFile == null
? null : dexMetadataFile.getAbsolutePath(); ? null : dexMetadataFile.getAbsolutePath();
@@ -339,88 +339,113 @@ public class PackageDexOptimizer {
// PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
// profiles. // profiles.
int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
if (resetProfile) { if (options.isCheckForProfileUpdates()) {
if (!resetProfile(pkg, profileName, path, dexMetadataPath)) {
// Fall back to use the shared filter.
compilerFilter =
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
PackageManagerService.REASON_SHARED);
}
} else if (options.isCheckForProfileUpdates()) {
profileAnalysisResult = profileAnalysisResult =
analyseProfiles(pkg, sharedGid, profileName, compilerFilter); analyseProfiles(pkg, sharedGid, profileName, compilerFilter);
} }
String cloudProfileName = null;
try {
if (useCloudProfile) {
cloudProfileName = "cloud-" + profileName;
if (prepareCloudProfile(pkg, cloudProfileName, path, dexMetadataPath)) {
profileName = cloudProfileName;
} else {
// Fall back to use the shared filter.
compilerFilter =
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
PackageManagerService.REASON_SHARED);
profileName = null;
}
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct // We still run `analyseProfiles` even if `useCloudProfile` is true because it
// flags. // merges profiles into the reference profile, which a system API
final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, resetProfile, // `ArtManager.snapshotRuntimeProfile` takes snapshots from. However, we don't
options); // want the result to affect the decision of whether dexopt is needed.
profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
}
for (String dexCodeIsa : dexCodeInstructionSets) { // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter, // flags.
profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid, final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter,
packageStats, options.isDowngrade(), profileName, dexMetadataPath, useCloudProfile, options);
options.getCompilationReason());
// OTAPreopt doesn't have stats so don't report in that case. for (String dexCodeIsa : dexCodeInstructionSets) {
if (packageStats != null) { int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics"); profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
// OTAPreopt doesn't have stats so don't report in that case.
if (packageStats != null) {
Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics");
try {
long sessionId = sRandom.nextLong();
ArtStatsLogUtils.writeStatsLog(
mArtStatsLogger,
sessionId,
compilerFilter,
pkg.getUid(),
packageStats.getCompileTime(path),
dexMetadataPath,
options.getCompilationReason(),
newResult,
ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(),
pkg.getSplitCodePaths()),
dexCodeIsa,
path);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
}
}
// Should stop the operation immediately.
if (newResult == DEX_OPT_CANCELLED) {
// Even for the cancellation, return failed if has failed.
if (result == DEX_OPT_FAILED) {
return result;
}
return newResult;
}
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
// - SKIPPED when all paths are up to date
if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
result = newResult;
}
}
} finally {
if (cloudProfileName != null) {
try { try {
long sessionId = sRandom.nextLong(); mInstaller.deleteReferenceProfile(pkg.getPackageName(), cloudProfileName);
ArtStatsLogUtils.writeStatsLog( } catch (InstallerException e) {
mArtStatsLogger, Slog.w(TAG, "Failed to cleanup cloud profile", e);
sessionId,
compilerFilter,
pkg.getUid(),
packageStats.getCompileTime(path),
dexMetadataPath,
options.getCompilationReason(),
newResult,
ArtStatsLogUtils.getApkType(path, pkg.getBaseApkPath(),
pkg.getSplitCodePaths()),
dexCodeIsa,
path);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
} }
} }
// Should stop the operation immediately.
if (newResult == DEX_OPT_CANCELLED) {
// Even for the cancellation, return failed if has failed.
if (result == DEX_OPT_FAILED) {
return result;
}
return newResult;
}
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
// - SKIPPED when all paths are up to date
if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
result = newResult;
}
} }
} }
return result; return result;
} }
/** /**
* Resets the profiles of the dex file at {@code path} belonging to the package {@code pkg} to * Creates a profile with the name {@code profileName} from the dex metadata file at {@code
* the initial state as if the package is newly installed. Returns true on success, or false * dexMetadataPath} for the dex file at {@code path} belonging to the package {@code pkg}.
* otherwise. *
* @return true on success, or false otherwise.
*/ */
@GuardedBy("mInstallLock") @GuardedBy("mInstallLock")
private boolean resetProfile(AndroidPackage pkg, String profileName, String path, private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
@Nullable String dexMetadataPath) { @Nullable String dexMetadataPath) {
if (dexMetadataPath != null) { if (dexMetadataPath != null) {
try { try {
mInstaller.clearAppProfiles(pkg.getPackageName(), profileName); // Make sure we don't keep any existing contents.
mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName);
final int appId = UserHandle.getAppId(pkg.getUid()); final int appId = UserHandle.getAppId(pkg.getUid());
mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, appId,
appId, profileName, path, dexMetadataPath); profileName, path, dexMetadataPath);
return true; return true;
} catch (InstallerException e) { } catch (InstallerException e) {
Slog.w(TAG, "Failed to reset profile", e); Slog.w(TAG, "Failed to prepare cloud profile", e);
return false; return false;
} }
} else { } else {
@@ -835,16 +860,16 @@ public class PackageDexOptimizer {
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) { private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0, return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
info.getHiddenApiEnforcementPolicy(), info.splitDependencies, info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
info.requestsIsolatedSplitLoading(), compilerFilter, false /* resetProfile */, info.requestsIsolatedSplitLoading(), compilerFilter, false /* useCloudProfile */,
options); options);
} }
private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
String compilerFilter, boolean resetProfile, DexoptOptions options) { String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
return getDexFlags(pkg.isDebuggable(), return getDexFlags(pkg.isDebuggable(),
AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting), AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter, pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
resetProfile, options); useCloudProfile, options);
} }
/** /**
@@ -853,15 +878,15 @@ public class PackageDexOptimizer {
*/ */
private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
String compilerFilter, boolean resetProfile, DexoptOptions options) { String compilerFilter, boolean useCloudProfile, DexoptOptions options) {
// Profile guide compiled oat files should not be public unles they are based // Profile guide compiled oat files should not be public unles they are based
// on profiles from dex metadata archives. // on profiles from dex metadata archives.
// The flag isDexoptInstallWithDexMetadata applies only on installs when we know that // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
// the user does not have an existing profile. // the user does not have an existing profile.
// The flag resetProfile applies only when the existing profile is already reset. // The flag useCloudProfile applies only when the cloud profile should be used.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter); boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata() boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata()
|| resetProfile; || useCloudProfile;
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
// Some apps are executed with restrictions on hidden API usage. If this app is one // 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. // of them, pass a flag to dexopt to enable the same restrictions during compilation.