From 63aa98db6de16abc6a8ef827161ad84e8045f39e Mon Sep 17 00:00:00 2001 From: Jackal Guo Date: Tue, 14 Apr 2020 16:47:32 +0800 Subject: [PATCH] Invalidate the cache for APK-in-APEX PackageCacher uses file name and modified time to determine if the parsed cache is still valid. However, all APK-in-APEX would have the same name and modified time. This results in the out-of-date cache would be treated as valid. We need to invlidate the cache for the APK-in-APEX when that APEX is going to be installed. Bug: 152352677 Test: atest -p frameworks/base/core/java/android/content/pm Test: atest -p frameworks/base/services/core/java/android/content/pm Test: Update/rollback an APK-in-APEX, and check its version code Change-Id: I2a722036d8b1e9c5121d385f8d0667fb908cc7a1 --- .../content/pm/PackageManagerInternal.java | 6 ++++++ .../server/pm/PackageManagerService.java | 20 +++++++++++++++++++ .../com/android/server/pm/StagingManager.java | 7 ++++++- .../server/pm/parsing/PackageCacher.java | 15 ++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index c27ec66b5db3e..63b56e0f92d4d 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -479,6 +479,12 @@ public abstract class PackageManagerInternal { */ public abstract void pruneInstantApps(); + /** + * Prunes the cache of the APKs in the given APEXes. + * @param apexPackages The list of APEX packages that may contain APK-in-APEX. + */ + public abstract void pruneCachedApksInApex(@NonNull List apexPackages); + /** * @return The SetupWizard package name. */ diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 59ac603875e2b..c965d42bdbb6e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -348,6 +348,7 @@ import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; +import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.library.PackageBackwardCompatibility; @@ -24111,6 +24112,25 @@ public class PackageManagerService extends IPackageManager.Stub mInstantAppRegistry.pruneInstantApps(); } + @Override + public void pruneCachedApksInApex(@NonNull List apexPackages) { + if (mCacheDir == null) { + return; + } + + final PackageCacher cacher = new PackageCacher(mCacheDir); + synchronized (mLock) { + for (int i = 0, size = apexPackages.size(); i < size; i++) { + final List apkNames = + mApexManager.getApksInApex(apexPackages.get(i).packageName); + for (int j = 0, apksInApex = apkNames.size(); j < apksInApex; j++) { + final AndroidPackage pkg = getPackage(apkNames.get(j)); + cacher.cleanCachedResult(new File(pkg.getCodePath())); + } + } + } + } + @Override public String getSetupWizardPackageName() { return mSetupWizardPackage; diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 8f6bd026a9bda..50ff833f23acd 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -1218,8 +1218,9 @@ public class StagingManager { // APEX checks. For single-package sessions, check if they contain an APEX. For // multi-package sessions, find all the child sessions that contain an APEX. if (hasApex) { + final List apexPackages; try { - final List apexPackages = submitSessionToApexService(session); + apexPackages = submitSessionToApexService(session); for (int i = 0, size = apexPackages.size(); i < size; i++) { validateApexSignature(apexPackages.get(i)); } @@ -1227,6 +1228,10 @@ public class StagingManager { session.setStagedSessionFailed(e.error, e.getMessage()); return; } + + final PackageManagerInternal packageManagerInternal = + LocalServices.getService(PackageManagerInternal.class); + packageManagerInternal.pruneCachedApksInApex(apexPackages); } notifyPreRebootVerification_Apex_Complete(session.sessionId); diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java index e5e1b0b209556..99c6dd1f03128 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java +++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java @@ -18,6 +18,7 @@ package com.android.server.pm.parsing; import android.annotation.NonNull; import android.content.pm.PackageParserCacheHelper; +import android.os.FileUtils; import android.os.Parcel; import android.system.ErrnoException; import android.system.Os; @@ -197,4 +198,18 @@ public class PackageCacher { Slog.w(TAG, "Error saving package cache.", e); } } + + /** + * Delete the cache files for the given {@code packageFile}. + */ + public void cleanCachedResult(@NonNull File packageFile) { + final String packageName = packageFile.getName(); + final File[] files = FileUtils.listFilesOrEmpty(mCacheDir, + (dir, name) -> name.startsWith(packageName)); + for (File file : files) { + if (!file.delete()) { + Slog.e(TAG, "Unable to clean cache file: " + file); + } + } + } }