diff options
25 files changed, 548 insertions, 179 deletions
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java index d3938f4c0926..afd8e2948c41 100644 --- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java +++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java @@ -77,7 +77,7 @@ public class ResourcesManagerPerfTest { } private void getResourcesForPath(String path) { - ResourcesManager.getInstance().getResources(null, path, null, null, null, + ResourcesManager.getInstance().getResources(null, path, null, null, null, null, Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(), null, null); } diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java index f4c0a172710b..45c723bea9db 100644 --- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java +++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java @@ -95,8 +95,9 @@ public class ResourcesThemePerfTest { ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; Resources destResources = resourcesManager.getResources(null, ai.sourceDir, - ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - c, mContext.getResources().getCompatibilityInfo(), null, null); + ai.splitSourceDirs, ai.resourceDirs, ai.overlayPaths, ai.sharedLibraryFiles, + Display.DEFAULT_DISPLAY, c, mContext.getResources().getCompatibilityInfo(), + null, null); Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets()); Resources.Theme destTheme = destResources.newTheme(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e5a04c98b9e7..0cccbf44ce5e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2289,11 +2289,12 @@ public final class ActivityThread extends ClientTransactionHandler { * Creates the top level resources for the given package. Will return an existing * Resources if one has already been created. */ - Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, - String[] libDirs, LoadedApk pkgInfo, Configuration overrideConfig) { - return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs, - null, overrideConfig, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), - null); + Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs, + String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo, + Configuration overrideConfig) { + return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs, + overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(), + pkgInfo.getClassLoader(), null); } @UnsupportedAppUsage @@ -2462,12 +2463,15 @@ public final class ActivityThread extends ClientTransactionHandler { private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, ApplicationInfo appInfo) { Resources packageResources = loadedApk.mResources; - String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()); - String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs); + boolean resourceDirsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.resourceDirs), + ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); + boolean overlayPathsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.overlayPaths), + ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); return (packageResources == null || packageResources.getAssets().isUpToDate()) - && overlayDirs.length == resourceDirs.length - && ArrayUtils.containsAll(overlayDirs, resourceDirs); + && resourceDirsUpToDate && overlayPathsUpToDate; } @UnsupportedAppUsage diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 8ac91396a6b0..062cab457ebe 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1728,7 +1728,7 @@ public class ApplicationPackageManager extends PackageManager { final Resources r = mContext.mMainThread.getTopLevelResources( sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, - app.resourceDirs, app.sharedLibraryFiles, + app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles, mContext.mPackageInfo, configuration); if (r != null) { return r; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4ddeb8fbfbef..9a20e0fefd33 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2345,6 +2345,7 @@ class ContextImpl extends Context { pi.getResDir(), splitResDirs, pi.getOverlayDirs(), + pi.getOverlayPaths(), pi.getApplicationInfo().sharedLibraryFiles, overrideDisplayId, overrideConfig, @@ -2442,6 +2443,7 @@ class ContextImpl extends Context { mPackageInfo.getResDir(), paths, mPackageInfo.getOverlayDirs(), + mPackageInfo.getOverlayPaths(), mPackageInfo.getApplicationInfo().sharedLibraryFiles, mForceDisplayOverrideInResources ? getDisplayId() : null, null, @@ -2558,7 +2560,8 @@ class ContextImpl extends Context { Resources createWindowContextResources() { final String resDir = mPackageInfo.getResDir(); final String[] splitResDirs = mPackageInfo.getSplitResDirs(); - final String[] overlayDirs = mPackageInfo.getOverlayDirs(); + final String[] legacyOverlayDirs = mPackageInfo.getOverlayDirs(); + final String[] overlayPaths = mPackageInfo.getOverlayPaths(); final String[] libDirs = mPackageInfo.getApplicationInfo().sharedLibraryFiles; final int displayId = getDisplayId(); final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY) @@ -2567,7 +2570,7 @@ class ContextImpl extends Context { final List<ResourcesLoader> loaders = mResources.getLoaders(); return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs, - overlayDirs, libDirs, displayId, null /* overrideConfig */, + legacyOverlayDirs, overlayPaths, libDirs, displayId, null /* overrideConfig */, compatInfo, mClassLoader, loaders); } @@ -2855,6 +2858,7 @@ class ContextImpl extends Context { packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), + packageInfo.getOverlayPaths(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfiguration, diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index c01b5a32b98b..be426aa7ed2b 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -113,7 +113,8 @@ public final class LoadedApk { private String mAppDir; @UnsupportedAppUsage private String mResDir; - private String[] mOverlayDirs; + private String[] mLegacyOverlayDirs; + private String[] mOverlayPaths; @UnsupportedAppUsage private String mDataDir; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -222,7 +223,8 @@ public final class LoadedApk { mSplitAppDirs = null; mSplitResDirs = null; mSplitClassLoaderNames = null; - mOverlayDirs = null; + mLegacyOverlayDirs = null; + mOverlayPaths = null; mDataDir = null; mDataDirFile = null; mDeviceProtectedDataDirFile = null; @@ -364,8 +366,8 @@ public final class LoadedApk { } mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - null, null, getCompatibilityInfo(), + splitPaths, mLegacyOverlayDirs, mOverlayPaths, + mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(), getClassLoader(), mApplication == null ? null : mApplication.getResources().getLoaders()); } @@ -379,7 +381,8 @@ public final class LoadedApk { mApplicationInfo = aInfo; mAppDir = aInfo.sourceDir; mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; - mOverlayDirs = aInfo.resourceDirs; + mLegacyOverlayDirs = aInfo.resourceDirs; + mOverlayPaths = aInfo.overlayPaths; mDataDir = aInfo.dataDir; mLibDir = aInfo.nativeLibraryDir; mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir); @@ -1213,9 +1216,19 @@ public final class LoadedApk { return mSplitResDirs; } + /** + * Corresponds to {@link ApplicationInfo#resourceDirs}. + */ @UnsupportedAppUsage public String[] getOverlayDirs() { - return mOverlayDirs; + return mLegacyOverlayDirs; + } + + /** + * Corresponds to {@link ApplicationInfo#overlayPaths}. + */ + public String[] getOverlayPaths() { + return mOverlayPaths; } public String getDataDir() { @@ -1252,8 +1265,8 @@ public final class LoadedApk { } mResources = ResourcesManager.getInstance().getResources(null, mResDir, - splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, - null, null, getCompatibilityInfo(), + splitPaths, mLegacyOverlayDirs, mOverlayPaths, + mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(), getClassLoader(), null); } return mResources; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 772833cc6d2d..ac8d3a261ac6 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.Process; import android.os.Trace; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; @@ -60,6 +61,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.WeakHashMap; @@ -174,8 +176,8 @@ public class ResourcesManager { * based on. * * @see #activityResources - * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration, - * CompatibilityInfo, ClassLoader, List) + * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer, + * Configuration, CompatibilityInfo, ClassLoader, List) */ public final Configuration overrideConfig = new Configuration(); @@ -482,8 +484,8 @@ public class ResourcesManager { } } - if (key.mOverlayDirs != null) { - for (final String idmapPath : key.mOverlayDirs) { + if (key.mOverlayPaths != null) { + for (final String idmapPath : key.mOverlayPaths) { apkKeys.add(new ApkKey(idmapPath, false /*sharedLib*/, true /*overlay*/)); } } @@ -783,14 +785,16 @@ public class ResourcesManager { /** * Creates base resources for a binder token. Calls to - * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration, - * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override - * configurations merged with the one specified here. + * + * {@link #getResources(IBinder, String, String[], String[], String[], String[], Integer, + * Configuration, CompatibilityInfo, ClassLoader, List)} with the same binder token will have + * their override configurations merged with the one specified here. * * @param token Represents an {@link Activity} or {@link WindowContext}. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. - * @param overlayDirs An array of overlay paths. Can be null. + * @param legacyOverlayDirs An array of overlay APK paths. Can be null. + * @param overlayPaths An array of overlay APK and non-APK paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param displayId The ID of the display for which to create the resources. * @param overrideConfig The configuration to apply on top of the base configuration. Can be @@ -804,7 +808,8 @@ public class ResourcesManager { public @Nullable Resources createBaseTokenResources(@NonNull IBinder token, @Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] legacyOverlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int displayId, @Nullable Configuration overrideConfig, @@ -817,7 +822,7 @@ public class ResourcesManager { final ResourcesKey key = new ResourcesKey( resDir, splitResDirs, - overlayDirs, + combinedOverlayPaths(legacyOverlayDirs, overlayPaths), libDirs, displayId, overrideConfig, @@ -1043,7 +1048,8 @@ public class ResourcesManager { * @param activityToken Represents an Activity. If null, global resources are assumed. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. - * @param overlayDirs An array of overlay paths. Can be null. + * @param legacyOverlayDirs An array of overlay APK paths. Can be null. + * @param overlayPaths An array of overlay APK and non-APK paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param overrideDisplayId The ID of the display for which the returned Resources should be * based. This will cause display-based configuration properties to override those of the base @@ -1063,7 +1069,8 @@ public class ResourcesManager { @Nullable IBinder activityToken, @Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] legacyOverlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, @Nullable Integer overrideDisplayId, @Nullable Configuration overrideConfig, @@ -1075,7 +1082,7 @@ public class ResourcesManager { final ResourcesKey key = new ResourcesKey( resDir, splitResDirs, - overlayDirs, + combinedOverlayPaths(legacyOverlayDirs, overlayPaths), libDirs, overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY, overrideConfig, @@ -1250,7 +1257,7 @@ public class ResourcesManager { // Create the new ResourcesKey with the rebased override config. final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, - oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs, + oldKey.mSplitResDirs, oldKey.mOverlayPaths, oldKey.mLibDirs, displayId, rebasedOverrideConfig, oldKey.mCompatInfo, oldKey.mLoaders); if (DEBUG) { @@ -1393,7 +1400,7 @@ public class ResourcesManager { updatedResourceKeys.put(impl, new ResourcesKey( key.mResDir, key.mSplitResDirs, - key.mOverlayDirs, + key.mOverlayPaths, newLibAssets, key.mDisplayId, key.mOverrideConfiguration, @@ -1423,7 +1430,8 @@ public class ResourcesManager { // ApplicationInfo is mutable, so clone the arrays to prevent outside modification String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs); - String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs); + String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs, + appInfo.overlayPaths); final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); @@ -1458,6 +1466,39 @@ public class ResourcesManager { } } + /** + * Creates an array with the contents of {@param overlayPaths} and the unique elements of + * {@param resourceDirs}. + * + * {@link ApplicationInfo#resourceDirs} only contains paths of overlays APKs. + * {@link ApplicationInfo#overlayPaths} was created to contain paths of overlay of varying file + * formats. It also contains the contents of {@code resourceDirs} because the order of loaded + * overlays matter. In case {@code resourceDirs} contains overlay APK paths that are not present + * in overlayPaths (perhaps an app inserted an additional overlay path into a + * {@code resourceDirs}), this method is used to combine the contents of {@code resourceDirs} + * that do not exist in {@code overlayPaths}} and {@code overlayPaths}}. + */ + @Nullable + private static String[] combinedOverlayPaths(@Nullable String[] resourceDirs, + @Nullable String[] overlayPaths) { + if (resourceDirs == null) { + return ArrayUtils.cloneOrNull(overlayPaths); + } else if(overlayPaths == null) { + return ArrayUtils.cloneOrNull(resourceDirs); + } else { + final ArrayList<String> paths = new ArrayList<>(); + for (final String path : overlayPaths) { + paths.add(path); + } + for (final String path : resourceDirs) { + if (!paths.contains(path)) { + paths.add(path); + } + } + return paths.toArray(new String[0]); + } + } + private void redirectResourcesToNewImplLocked( @NonNull final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys) { // Bail early if there is no work to do. @@ -1559,7 +1600,7 @@ public class ResourcesManager { final ResourcesKey newKey = new ResourcesKey( oldKey.mResDir, oldKey.mSplitResDirs, - oldKey.mOverlayDirs, + oldKey.mOverlayPaths, oldKey.mLibDirs, oldKey.mDisplayId, oldKey.mOverrideConfiguration, diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6ec11693d69b..01ff4326a800 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -923,6 +923,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String[] resourceDirs; /** + * Contains the contents of {@link #resourceDirs} and along with paths for overlays that may or + * may not be APK packages. + * + * {@hide} + */ + public String[] overlayPaths; + + /** * String retrieved from the seinfo tag found in selinux policy. This value can be set through * the mac_permissions.xml policy construct. This value is used for setting an SELinux security * context on the process as well as its data directory. @@ -1472,6 +1480,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { if (resourceDirs != null) { pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs)); } + if (overlayPaths != null) { + pw.println(prefix + "overlayPaths=" + Arrays.toString(overlayPaths)); + } if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) { pw.println(prefix + "seinfo=" + seInfo); pw.println(prefix + "seinfoUser=" + seInfoUser); @@ -1568,6 +1579,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { proto.write(ApplicationInfoProto.RESOURCE_DIRS, dir); } } + if (overlayPaths != null) { + for (String dir : overlayPaths) { + proto.write(ApplicationInfoProto.OVERLAY_PATHS, dir); + } + } proto.write(ApplicationInfoProto.DATA_DIR, dataDir); proto.write(ApplicationInfoProto.CLASS_LOADER_NAME, classLoaderName); if (!ArrayUtils.isEmpty(splitClassLoaderNames)) { @@ -1717,6 +1733,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { primaryCpuAbi = orig.primaryCpuAbi; secondaryCpuAbi = orig.secondaryCpuAbi; resourceDirs = orig.resourceDirs; + overlayPaths = orig.overlayPaths; seInfo = orig.seInfo; seInfoUser = orig.seInfoUser; sharedLibraryFiles = orig.sharedLibraryFiles; @@ -1803,6 +1820,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString8(primaryCpuAbi); dest.writeString8(secondaryCpuAbi); dest.writeString8Array(resourceDirs); + dest.writeString8Array(overlayPaths); dest.writeString8(seInfo); dest.writeString8(seInfoUser); dest.writeString8Array(sharedLibraryFiles); @@ -1886,6 +1904,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { primaryCpuAbi = source.readString8(); secondaryCpuAbi = source.readString8(); resourceDirs = source.createString8Array(); + overlayPaths = source.createString8Array(); seInfo = source.readString8(); seInfoUser = source.readString8(); sharedLibraryFiles = source.createString8Array(); @@ -2282,7 +2301,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public String[] getAllApkPaths() { - final String[][] inputLists = { splitSourceDirs, sharedLibraryFiles, resourceDirs }; + final String[][] inputLists = { + splitSourceDirs, sharedLibraryFiles, resourceDirs, overlayPaths + }; final List<String> output = new ArrayList<>(10); if (sourceDir != null) { output.add(sourceDir); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e6c0f6a4c2fa..0819d1743ad6 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.split.SplitAssetLoader; import android.content.res.ApkAssets; import android.content.res.AssetManager; @@ -7969,7 +7970,11 @@ public class PackageParser { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.getAllOverlayPaths(); + final OverlayPaths overlayPaths = state.getAllOverlayPaths(); + if (overlayPaths != null) { + ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]); + ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]); + } ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes; } @@ -8600,6 +8605,7 @@ public class PackageParser { null, null, androidAppInfo.resourceDirs, + androidAppInfo.overlayPaths, androidAppInfo.sharedLibraryFiles, null, null, diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 5cc74c0a1c8e..e115597865b3 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -31,6 +31,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ParsedMainComponent; import android.os.BaseBundle; import android.os.Debug; @@ -53,7 +54,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; @@ -85,9 +85,10 @@ public class PackageUserState { public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; - private String[] overlayPaths; - private ArrayMap<String, String[]> sharedLibraryOverlayPaths; // Lib name to overlay paths - private String[] cachedOverlayPaths; + private OverlayPaths overlayPaths; + // Maps library name to overlay paths. + private ArrayMap<String, OverlayPaths> sharedLibraryOverlayPaths; + private OverlayPaths cachedOverlayPaths; @Nullable private ArrayMap<ComponentName, Pair<String, Integer>> componentLabelIconOverrideMap; @@ -121,8 +122,7 @@ public class PackageUserState { uninstallReason = o.uninstallReason; disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); - overlayPaths = - o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length); + overlayPaths = o.overlayPaths; if (o.sharedLibraryOverlayPaths != null) { sharedLibraryOverlayPaths = new ArrayMap<>(o.sharedLibraryOverlayPaths); } @@ -132,25 +132,55 @@ public class PackageUserState { } } - public String[] getOverlayPaths() { + @Nullable + public OverlayPaths getOverlayPaths() { return overlayPaths; } - public void setOverlayPaths(String[] paths) { - overlayPaths = paths; - cachedOverlayPaths = null; + @Nullable + public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() { + return sharedLibraryOverlayPaths; } - public Map<String, String[]> getSharedLibraryOverlayPaths() { - return sharedLibraryOverlayPaths; + /** + * Sets the path of overlays currently enabled for this package and user combination. + * @return true if the path contents differ than what they were previously + */ + @Nullable + public boolean setOverlayPaths(@Nullable OverlayPaths paths) { + if (Objects.equals(paths, overlayPaths)) { + return false; + } + if ((overlayPaths == null && paths.isEmpty()) + || (paths == null && overlayPaths.isEmpty())) { + return false; + } + overlayPaths = paths; + cachedOverlayPaths = null; + return true; } - public void setSharedLibraryOverlayPaths(String library, String[] paths) { + /** + * Sets the path of overlays currently enabled for a library that this package uses. + * + * @return true if the path contents for the library differ than what they were previously + */ + public boolean setSharedLibraryOverlayPaths(@NonNull String library, + @Nullable OverlayPaths paths) { if (sharedLibraryOverlayPaths == null) { sharedLibraryOverlayPaths = new ArrayMap<>(); } - sharedLibraryOverlayPaths.put(library, paths); + final OverlayPaths currentPaths = sharedLibraryOverlayPaths.get(library); + if (Objects.equals(paths, currentPaths)) { + return false; + } cachedOverlayPaths = null; + if (paths == null || paths.isEmpty()) { + return sharedLibraryOverlayPaths.remove(library) != null; + } else { + sharedLibraryOverlayPaths.put(library, paths); + return true; + } } /** @@ -332,35 +362,21 @@ public class PackageUserState { return isComponentEnabled; } - public String[] getAllOverlayPaths() { + public OverlayPaths getAllOverlayPaths() { if (overlayPaths == null && sharedLibraryOverlayPaths == null) { return null; } - if (cachedOverlayPaths != null) { return cachedOverlayPaths; } - - final LinkedHashSet<String> paths = new LinkedHashSet<>(); - if (overlayPaths != null) { - final int N = overlayPaths.length; - for (int i = 0; i < N; i++) { - paths.add(overlayPaths[i]); - } - } - + final OverlayPaths.Builder newPaths = new OverlayPaths.Builder(); + newPaths.addAll(overlayPaths); if (sharedLibraryOverlayPaths != null) { - for (String[] libOverlayPaths : sharedLibraryOverlayPaths.values()) { - if (libOverlayPaths != null) { - final int N = libOverlayPaths.length; - for (int i = 0; i < N; i++) { - paths.add(libOverlayPaths[i]); - } - } + for (final OverlayPaths libOverlayPaths : sharedLibraryOverlayPaths.values()) { + newPaths.addAll(libOverlayPaths); } } - - cachedOverlayPaths = paths.toArray(new String[0]); + cachedOverlayPaths = newPaths.build(); return cachedOverlayPaths; } diff --git a/core/java/android/content/pm/overlay/OverlayPaths.java b/core/java/android/content/pm/overlay/OverlayPaths.java new file mode 100644 index 000000000000..a4db733af013 --- /dev/null +++ b/core/java/android/content/pm/overlay/OverlayPaths.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm.overlay; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** @hide */ +@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false, + genEqualsHashCode = true, genToString = true) +public class OverlayPaths { + /** + * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}. + * Only contains paths to APKs of overlays that can have their idmap resolved from their base + * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap + * path. + */ + @NonNull + private final List<String> mResourceDirs = new ArrayList<>(); + + /** + * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}. + * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays + * that are not APKs. + */ + @NonNull + private final List<String> mOverlayPaths = new ArrayList<>(); + + public static class Builder { + final OverlayPaths mPaths = new OverlayPaths(); + + /** + * Adds a non-APK path to the contents of {@link OverlayPaths#getOverlayPaths()}. + */ + public Builder addNonApkPath(@NonNull String idmapPath) { + mPaths.mOverlayPaths.add(idmapPath); + return this; + } + + /** + * Adds a overlay APK path to the contents of {@link OverlayPaths#getResourceDirs()} and + * {@link OverlayPaths#getOverlayPaths()}. + */ + public Builder addApkPath(@NonNull String overlayPath) { + addUniquePath(mPaths.mResourceDirs, overlayPath); + addUniquePath(mPaths.mOverlayPaths, overlayPath); + return this; + } + + public Builder addAll(@Nullable OverlayPaths other) { + if (other != null) { + for (final String path : other.getResourceDirs()) { + addUniquePath(mPaths.mResourceDirs, path); + } + for (final String path : other.getOverlayPaths()) { + addUniquePath(mPaths.mOverlayPaths, path); + } + } + return this; + } + + public OverlayPaths build() { + return mPaths; + } + + private static void addUniquePath(@NonNull List<String> paths, @NonNull String path) { + if (!paths.contains(path)) { + paths.add(path); + } + } + } + + /** + * Returns whether {@link #getOverlayPaths()} and {@link #getOverlayPaths} are empty. + */ + public boolean isEmpty() { + return mResourceDirs.isEmpty() && mOverlayPaths.isEmpty(); + } + + private OverlayPaths() { + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Represents {@link android.content.pm.ApplicationInfo#resourceDirs}. + * Only contains paths to APKs of overlays that can have their idmap resolved from their base + * APK path. Currently all overlay APKs can have their idmap path resolved from their idmap + * path. + */ + @DataClass.Generated.Member + public @NonNull List<String> getResourceDirs() { + return mResourceDirs; + } + + /** + * Represents {@link android.content.pm.ApplicationInfo#overlayPaths}. + * Contains the contents of {@link #getResourceDirs()} and along with paths for overlays + * that are not APKs. + */ + @DataClass.Generated.Member + public @NonNull List<String> getOverlayPaths() { + return mOverlayPaths; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "OverlayPaths { " + + "resourceDirs = " + mResourceDirs + ", " + + "overlayPaths = " + mOverlayPaths + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(OverlayPaths other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + OverlayPaths that = (OverlayPaths) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mResourceDirs, that.mResourceDirs) + && Objects.equals(mOverlayPaths, that.mOverlayPaths); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mResourceDirs); + _hash = 31 * _hash + Objects.hashCode(mOverlayPaths); + return _hash; + } + + @DataClass.Generated( + time = 1612307813586L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/overlay/OverlayPaths.java", + inputSignatures = "private final @android.annotation.NonNull java.util.List<java.lang.String> mResourceDirs\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mOverlayPaths\npublic boolean isEmpty()\nclass OverlayPaths extends java.lang.Object implements []\nfinal android.content.pm.overlay.OverlayPaths mPaths\npublic android.content.pm.overlay.OverlayPaths.Builder addNonApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addApkPath(java.lang.String)\npublic android.content.pm.overlay.OverlayPaths.Builder addAll(android.content.pm.overlay.OverlayPaths)\npublic android.content.pm.overlay.OverlayPaths build()\nprivate static void addUniquePath(java.util.List<java.lang.String>,java.lang.String)\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index b7365b3eaf61..fb0d90490567 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -41,6 +41,7 @@ import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.SigningInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ComponentParseUtils; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedAttribution; @@ -412,7 +413,11 @@ public class PackageInfoWithoutStateUtils { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); - ai.resourceDirs = state.getAllOverlayPaths(); + final OverlayPaths overlayPaths = state.getAllOverlayPaths(); + if (overlayPaths != null) { + ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]); + ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]); + } return ai; } diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index 05769ddc5397..99b56a82173e 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -38,7 +38,7 @@ public final class ResourcesKey { public final String[] mSplitResDirs; @Nullable - public final String[] mOverlayDirs; + public final String[] mOverlayPaths; @Nullable public final String[] mLibDirs; @@ -67,7 +67,7 @@ public final class ResourcesKey { public ResourcesKey(@Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int overrideDisplayId, @Nullable Configuration overrideConfig, @@ -75,7 +75,7 @@ public final class ResourcesKey { @Nullable ResourcesLoader[] loader) { mResDir = resDir; mSplitResDirs = splitResDirs; - mOverlayDirs = overlayDirs; + mOverlayPaths = overlayPaths; mLibDirs = libDirs; mLoaders = (loader != null && loader.length == 0) ? null : loader; mDisplayId = overrideDisplayId; @@ -86,7 +86,7 @@ public final class ResourcesKey { int hash = 17; hash = 31 * hash + Objects.hashCode(mResDir); hash = 31 * hash + Arrays.hashCode(mSplitResDirs); - hash = 31 * hash + Arrays.hashCode(mOverlayDirs); + hash = 31 * hash + Arrays.hashCode(mOverlayPaths); hash = 31 * hash + Arrays.hashCode(mLibDirs); hash = 31 * hash + Objects.hashCode(mDisplayId); hash = 31 * hash + Objects.hashCode(mOverrideConfiguration); @@ -98,12 +98,12 @@ public final class ResourcesKey { @UnsupportedAppUsage public ResourcesKey(@Nullable String resDir, @Nullable String[] splitResDirs, - @Nullable String[] overlayDirs, + @Nullable String[] overlayPaths, @Nullable String[] libDirs, int displayId, @Nullable Configuration overrideConfig, @Nullable CompatibilityInfo compatInfo) { - this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo, + this(resDir, splitResDirs, overlayPaths, libDirs, displayId, overrideConfig, compatInfo, null); } @@ -115,7 +115,7 @@ public final class ResourcesKey { if (mResDir != null && mResDir.startsWith(path)) { return true; } else { - return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayDirs, path) + return anyStartsWith(mSplitResDirs, path) || anyStartsWith(mOverlayPaths, path) || anyStartsWith(mLibDirs, path); } } @@ -154,7 +154,7 @@ public final class ResourcesKey { if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) { return false; } - if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) { + if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) { return false; } if (!Arrays.equals(mLibDirs, peer.mLibDirs)) { @@ -186,8 +186,8 @@ public final class ResourcesKey { } builder.append("]"); builder.append(" mOverlayDirs=["); - if (mOverlayDirs != null) { - builder.append(TextUtils.join(",", mOverlayDirs)); + if (mOverlayPaths != null) { + builder.append(TextUtils.join(",", mOverlayPaths)); } builder.append("]"); builder.append(" mLibDirs=["); diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index bb39ea810add..5c6116a09b77 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -114,4 +114,5 @@ message ApplicationInfoProto { optional bool native_heap_zero_init = 21; } optional Detail detail = 17; + repeated string overlay_paths = 18; } diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index 45adf833de97..46dbe0fd658a 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -90,12 +90,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testMultipleCallsWithIdenticalParametersCacheReference() { Resources resources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources); Resources newResources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(newResources); assertSame(resources, newResources); @@ -104,14 +104,14 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() { Resources resources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources); Configuration overrideConfig = new Configuration(); overrideConfig.smallestScreenWidthDp = 200; Resources newResources = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig, + null, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(newResources); assertNotSame(resources, newResources); @@ -120,12 +120,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testAddingASplitCreatesANewImpl() { Resources resources1 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Resources resources2 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, + null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -137,12 +137,12 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testUpdateConfigurationUpdatesAllAssetManagers() { Resources resources1 = mResourcesManager.getResources( - null, APP_ONE_RES_DIR, null, null, null, null, null, + null, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Resources resources2 = mResourcesManager.getResources( - null, APP_TWO_RES_DIR, null, null, null, null, null, + null, APP_TWO_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -150,7 +150,7 @@ public class ResourcesManagerTest extends TestCase { final Configuration overrideConfig = new Configuration(); overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE; Resources resources3 = mResourcesManager.getResources( - activity, APP_ONE_RES_DIR, null, null, null, null, + activity, APP_ONE_RES_DIR, null, null, null, null, null, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources3); @@ -183,13 +183,13 @@ public class ResourcesManagerTest extends TestCase { public void testTwoActivitiesWithIdenticalParametersShareImpl() { Binder activity1 = new Binder(); Resources resources1 = mResourcesManager.getResources( - activity1, APP_ONE_RES_DIR, null, null, null, null, null, + activity1, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); Binder activity2 = new Binder(); Resources resources2 = mResourcesManager.getResources( - activity2, APP_ONE_RES_DIR, null, null, null, null, null, + activity2, APP_ONE_RES_DIR, null, null, null, null, null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -204,7 +204,7 @@ public class ResourcesManagerTest extends TestCase { public void testThemesGetUpdatedWithNewImpl() { Binder activity1 = new Binder(); Resources resources1 = mResourcesManager.createBaseTokenResources( - activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -237,15 +237,15 @@ public class ResourcesManagerTest extends TestCase { Configuration config1 = new Configuration(); config1.densityDpi = 280; Resources resources1 = mResourcesManager.createBaseTokenResources( - activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); + activity1, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, + config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); // Create a Resources based on the Activity. Configuration config2 = new Configuration(); config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES; Resources resources2 = mResourcesManager.getResources( - activity1, APP_ONE_RES_DIR, null, null, null, null, config2, + activity1, APP_ONE_RES_DIR, null, null, null, null, null, config2, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources2); @@ -286,8 +286,8 @@ public class ResourcesManagerTest extends TestCase { final Configuration overrideConfig = new Configuration(); overrideConfig.densityDpi = originalOverrideDensity; final Resources resources = mResourcesManager.createBaseTokenResources( - token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* overlayDirs */, - null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig, + token, APP_ONE_RES_DIR, null /* splitResDirs */, null /* legacyOverlayDirs */, + null /* overlayDirs */,null /* libDirs */, Display.DEFAULT_DISPLAY, overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* classLoader */, null /* loaders */); @@ -315,12 +315,12 @@ public class ResourcesManagerTest extends TestCase { // Create a base token resources that are based on the default display. Resources activityResources = mResourcesManager.createBaseTokenResources( - activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity, APP_ONE_RES_DIR, null, null, null,null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); // Create another resources that explicitly override the display of the base token above // and set it to DEFAULT_DISPLAY. Resources defaultDisplayResources = mResourcesManager.getResources( - activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, + activity, APP_ONE_RES_DIR, null, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels, diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 6886cdefc28a..737a9e4ff46c 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.ComponentInfoFlags; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.component.ParsedMainComponent; import android.os.Bundle; import android.os.Handler; @@ -49,8 +50,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -535,17 +536,17 @@ public abstract class PackageManagerInternal { * Set which overlay to use for a package. * @param userId The user for which to update the overlays. * @param targetPackageName The package name of the package for which to update the overlays. - * @param overlayPackageNames The complete list of overlay packages that should be enabled for - * the target. Previously enabled overlays not specified in the list - * will be disabled. Pass in null or an empty list to disable - * all overlays. The order of the items is significant if several - * overlays modify the same resource. + * @param overlayPaths The complete list of overlay paths that should be enabled for + * the target. Previously enabled overlays not specified in the list + * will be disabled. Pass in null or empty paths to disable all overlays. + * The order of the items is significant if several overlays modify the + * same resource. * @param outUpdatedPackageNames An output list that contains the package names of packages * affected by the update of enabled overlays. * @return true if all packages names were known by the package manager, false otherwise */ public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName, - List<String> overlayPackageNames, Collection<String> outUpdatedPackageNames); + @Nullable OverlayPaths overlayPaths, Set<String> outUpdatedPackageNames); /** * Resolves an activity intent, allowing instant apps to be resolved. diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 50fb176e3a5a..fd2fb1fcab39 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -50,6 +50,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.res.ApkAssets; import android.net.Uri; import android.os.Binder; @@ -1376,18 +1377,18 @@ public final class OverlayManagerService extends SystemService { targetPackageNames = pm.getTargetPackageNames(userId); } - final Map<String, List<String>> pendingChanges = + final Map<String, OverlayPaths> pendingChanges = new ArrayMap<>(targetPackageNames.size()); synchronized (mLock) { - final List<String> frameworkOverlays = - mImpl.getEnabledOverlayPackageNames("android", userId); + final OverlayPaths frameworkOverlays = + mImpl.getEnabledOverlayPaths("android", userId); for (final String targetPackageName : targetPackageNames) { - List<String> list = new ArrayList<>(); + final OverlayPaths.Builder list = new OverlayPaths.Builder(); if (!"android".equals(targetPackageName)) { list.addAll(frameworkOverlays); } - list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); - pendingChanges.put(targetPackageName, list); + list.addAll(mImpl.getEnabledOverlayPaths(targetPackageName, userId)); + pendingChanges.put(targetPackageName, list.build()); } } @@ -1395,7 +1396,7 @@ public final class OverlayManagerService extends SystemService { for (final String targetPackageName : targetPackageNames) { if (DEBUG) { Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" - + TextUtils.join(",", pendingChanges.get(targetPackageName)) + + pendingChanges.get(targetPackageName) + "] userId=" + userId); } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index e60411bb78c5..c547c36a8033 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.content.om.OverlayInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.overlay.OverlayPaths; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -697,19 +698,20 @@ final class OverlayManagerServiceImpl { removeIdmapIfPossible(oi); } - List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, + OverlayPaths getEnabledOverlayPaths(@NonNull final String targetPackageName, final int userId) { final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId); - final List<String> paths = new ArrayList<>(overlays.size()); + final OverlayPaths.Builder paths = new OverlayPaths.Builder(); final int n = overlays.size(); for (int i = 0; i < n; i++) { final OverlayInfo oi = overlays.get(i); - if (oi.isEnabled()) { - paths.add(oi.packageName); + if (!oi.isEnabled()) { + continue; } + paths.addApkPath(oi.baseCodePath); } - return paths; + return paths.build(); } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5d4fa1e1c123..42723f468680 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -193,6 +193,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.KeySet; import android.content.pm.ModuleInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageChangeEvent; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -234,6 +235,7 @@ 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.pm.overlay.OverlayPaths; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.ParsingPackageUtils; @@ -18122,11 +18124,8 @@ public class PackageManagerService extends IPackageManager.Stub if (libPs == null) { continue; } - final String[] overlayPaths = libPs.getOverlayPaths(currentUserId); - if (overlayPaths != null) { - ps.setOverlayPathsForLibrary(sharedLib.getName(), - Arrays.asList(overlayPaths), currentUserId); - } + ps.setOverlayPathsForLibrary(sharedLib.getName(), + libPs.getOverlayPaths(currentUserId), currentUserId); } } } @@ -26616,34 +26615,19 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, - @Nullable List<String> overlayPackageNames, - @NonNull Collection<String> outUpdatedPackageNames) { + @Nullable OverlayPaths overlayPaths, + @NonNull Set<String> outUpdatedPackageNames) { + boolean modified = false; synchronized (mLock) { final AndroidPackage targetPkg = mPackages.get(targetPackageName); if (targetPackageName == null || targetPkg == null) { Slog.e(TAG, "failed to find package " + targetPackageName); return false; } - ArrayList<String> overlayPaths = null; - if (overlayPackageNames != null && overlayPackageNames.size() > 0) { - final int N = overlayPackageNames.size(); - overlayPaths = new ArrayList<>(N); - for (int i = 0; i < N; i++) { - final String packageName = overlayPackageNames.get(i); - final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { - Slog.e(TAG, "failed to find package " + packageName); - return false; - } - overlayPaths.add(pkg.getBaseApkPath()); - } - } - ArraySet<String> updatedPackageNames = null; if (targetPkg.getLibraryNames() != null) { // Set the overlay paths for dependencies of the shared library. - updatedPackageNames = new ArraySet<>(); - for (String libName : targetPkg.getLibraryNames()) { + for (final String libName : targetPkg.getLibraryNames()) { final SharedLibraryInfo info = getSharedLibraryInfoLPr(libName, SharedLibraryInfo.VERSION_UNDEFINED); if (info == null) { @@ -26654,28 +26638,30 @@ public class PackageManagerService extends IPackageManager.Stub if (dependents == null) { continue; } - for (VersionedPackage dependent : dependents) { + for (final VersionedPackage dependent : dependents) { final PackageSetting ps = mSettings.getPackageLPr( dependent.getPackageName()); if (ps == null) { continue; } - ps.setOverlayPathsForLibrary(libName, overlayPaths, userId); - updatedPackageNames.add(dependent.getPackageName()); + if (ps.setOverlayPathsForLibrary(libName, overlayPaths, userId)) { + outUpdatedPackageNames.add(dependent.getPackageName()); + modified = true; + } } } } final PackageSetting ps = mSettings.getPackageLPr(targetPackageName); - ps.setOverlayPaths(overlayPaths, userId); - - outUpdatedPackageNames.add(targetPackageName); - if (updatedPackageNames != null) { - outUpdatedPackageNames.addAll(updatedPackageNames); + if (ps.setOverlayPaths(overlayPaths, userId)) { + outUpdatedPackageNames.add(targetPackageName); + modified = true; } } - invalidatePackageInfoCache(); + if (modified) { + invalidatePackageInfoCache(); + } return true; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 3a142837e063..5364cbfede86 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -31,6 +31,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; +import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.os.incremental.IncrementalManager; import android.service.pm.PackageProto; @@ -44,7 +45,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -327,21 +327,20 @@ public abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).uninstallReason = uninstallReason; } - void setOverlayPaths(List<String> overlayPaths, int userId) { - modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null : - overlayPaths.toArray(new String[overlayPaths.size()])); + boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) { + return modifyUserState(userId).setOverlayPaths(overlayPaths); } - String[] getOverlayPaths(int userId) { + OverlayPaths getOverlayPaths(int userId) { return readUserState(userId).getOverlayPaths(); } - void setOverlayPathsForLibrary(String libName, List<String> overlayPaths, int userId) { - modifyUserState(userId).setSharedLibraryOverlayPaths(libName, - overlayPaths == null ? null : overlayPaths.toArray(new String[0])); + boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths, + int userId) { + return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths); } - Map<String, String[]> getOverlayPathsForLibrary(int userId) { + Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) { return readUserState(userId).getSharedLibraryOverlayPaths(); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fb033e6594b8..311d662254b4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -40,6 +40,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageUserState; @@ -49,6 +50,7 @@ import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.content.pm.UserInfo; import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.overlay.OverlayPaths; import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.content.pm.parsing.component.ParsedComponent; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -4686,26 +4688,58 @@ public final class Settings implements Watchable, Snappable { } } - String[] overlayPaths = ps.getOverlayPaths(user.id); - if (overlayPaths != null && overlayPaths.length > 0) { - pw.print(prefix); pw.println(" overlay paths:"); - for (String path : overlayPaths) { - pw.print(prefix); pw.print(" "); pw.println(path); + final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id); + if (overlayPaths != null) { + if (!overlayPaths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" overlay paths:"); + for (String path : overlayPaths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!overlayPaths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" legacy overlay paths:"); + for (String path : overlayPaths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } } } - Map<String, String[]> sharedLibraryOverlayPaths = + final Map<String, OverlayPaths> sharedLibraryOverlayPaths = ps.getOverlayPathsForLibrary(user.id); if (sharedLibraryOverlayPaths != null) { - for (Map.Entry<String, String[]> libOverlayPaths : + for (Map.Entry<String, OverlayPaths> libOverlayPaths : sharedLibraryOverlayPaths.entrySet()) { - if (libOverlayPaths.getValue() == null) { + final OverlayPaths paths = libOverlayPaths.getValue(); + if (paths == null) { continue; } - pw.print(prefix); pw.print(" "); - pw.print(libOverlayPaths.getKey()); pw.println(" overlay paths:"); - for (String path : libOverlayPaths.getValue()) { - pw.print(prefix); pw.print(" "); pw.println(path); + if (!paths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" overlay paths:"); + for (String path : paths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!paths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" legacy overlay paths:"); + for (String path : paths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } } } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 61fe023ae1b9..5460e36a5f1b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2086,6 +2086,7 @@ public class DisplayPolicy { pi.getResDir(), null /* splitResDirs */, pi.getOverlayDirs(), + pi.getOverlayPaths(), pi.getApplicationInfo().sharedLibraryFiles, mDisplayContent.getDisplayId(), null /* overrideConfig */, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index e6a238a775c6..ba6011140cf1 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -814,6 +814,7 @@ public class PackageParserTest { assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs); assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs); assertArrayEquals(a.resourceDirs, that.resourceDirs); + assertArrayEquals(a.overlayPaths, that.overlayPaths); assertEquals(a.seInfo, that.seInfo); assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles); assertEquals(a.dataDir, that.dataDir); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 938e4cc84e62..7297622b523f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -21,11 +21,14 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import android.content.pm.PackageManager; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; +import android.content.pm.overlay.OverlayPaths; import android.os.PersistableBundle; import android.util.ArrayMap; import android.util.ArraySet; @@ -346,4 +349,40 @@ public class PackageUserStateTest { assertLastPackageUsageSet( testState6, PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT - 1, 60L); } + + @Test + public void testOverlayPaths() { + final PackageUserState testState = new PackageUserState(); + assertFalse(testState.setOverlayPaths(null)); + assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder().build())); + + assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertFalse(testState.setOverlayPaths(new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + + assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build())); + assertFalse(testState.setOverlayPaths(null)); + } + @Test + public void testSharedLibOverlayPaths() { + final PackageUserState testState = new PackageUserState(); + final String LIB_ONE = "lib1"; + final String LIB_TW0 = "lib2"; + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null)); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, + new OverlayPaths.Builder().build())); + + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_TW0, new OverlayPaths.Builder() + .addApkPath("/path/to/some.apk").build())); + + assertTrue(testState.setSharedLibraryOverlayPaths(LIB_ONE, + new OverlayPaths.Builder().build())); + assertFalse(testState.setSharedLibraryOverlayPaths(LIB_ONE, null)); + } + } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt index 61252c473530..ff0aec71a0df 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt @@ -248,6 +248,7 @@ open class AndroidPackageParsingTestBase { .ignored("Deferred pre-R, but assigned immediately in R")} requiresSmallestWidthDp=${this.requiresSmallestWidthDp} resourceDirs=${this.resourceDirs?.contentToString()} + overlayPaths=${this.overlayPaths?.contentToString()} roundIconRes=${this.roundIconRes} scanPublicSourceDir=${this.scanPublicSourceDir .ignored("Deferred pre-R, but assigned immediately in R")} |