Add overlayPaths to ApplicationInfo
RROs have historically been APK packages. We now have the ability to
generate RROs on-the-fly. These "fabricated" RROs are not APKs.
ApplicationInfo#resourceDirs documentation states that it only contains
paths to packages. To prevent changing the behavior of resourceDirs
until we can deprecate and remove it, a new overlayPaths field has been
added to ApplicationInfo. This new field contains APK overlay paths as
well as non-APK overlay paths.
Bug: 172471315
Test: boot enable/disable overlays and examine overlays working as well
as package manager dumpsys
Change-Id: I78c5eeef73b7d8bada61edc0f64a12a3cdc1ce16
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index d3938f4..afd8e29 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 @@
}
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 f4c0a17..45c723b 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 @@
? 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 e5a04c98..0cccbf4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2289,11 +2289,12 @@
* 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 @@
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 8ac9139..062cab4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1728,7 +1728,7 @@
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 4ddeb8f..9a20e0f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2345,6 +2345,7 @@
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
overrideDisplayId,
overrideConfig,
@@ -2442,6 +2443,7 @@
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
+ mPackageInfo.getOverlayPaths(),
mPackageInfo.getApplicationInfo().sharedLibraryFiles,
mForceDisplayOverrideInResources ? getDisplayId() : null,
null,
@@ -2558,7 +2560,8 @@
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 @@
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 @@
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 c01b5a3..be426aa 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -113,7 +113,8 @@
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 @@
mSplitAppDirs = null;
mSplitResDirs = null;
mSplitClassLoaderNames = null;
- mOverlayDirs = null;
+ mLegacyOverlayDirs = null;
+ mOverlayPaths = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -364,8 +366,8 @@
}
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 @@
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 @@
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 @@
}
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 772833cc..ac8d3a2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@
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.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 @@
* 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 @@
}
}
- 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 @@
/**
* 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 @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 @@
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
displayId,
overrideConfig,
@@ -1043,7 +1048,8 @@
* @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 @@
@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 @@
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
@@ -1250,7 +1257,7 @@
// 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 @@
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
- key.mOverlayDirs,
+ key.mOverlayPaths,
newLibAssets,
key.mDisplayId,
key.mOverrideConfiguration,
@@ -1423,7 +1430,8 @@
// 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 @@
}
}
+ /**
+ * 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 @@
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 6ec1169..01ff432 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -923,6 +923,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* @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 e6c0f6a..0819d17 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
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 @@
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 @@
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 5cc74c0..e115597 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@
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 java.io.IOException;
import java.util.Arrays;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -85,9 +85,10 @@
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 @@
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 String[] getOverlayPaths() {
+ @Nullable
+ public OverlayPaths getOverlayPaths() {
return overlayPaths;
}
- public void setOverlayPaths(String[] paths) {
- overlayPaths = paths;
- cachedOverlayPaths = null;
- }
-
- public Map<String, String[]> getSharedLibraryOverlayPaths() {
+ @Nullable
+ public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
return sharedLibraryOverlayPaths;
}
- public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 @@
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 0000000..a4db733
--- /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 b7365b3..fb0d904 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.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 @@
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 05769dd..99b56a8 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -38,7 +38,7 @@
public final String[] mSplitResDirs;
@Nullable
- public final String[] mOverlayDirs;
+ public final String[] mOverlayPaths;
@Nullable
public final String[] mLibDirs;
@@ -67,7 +67,7 @@
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 @@
@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 @@
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 @@
@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 @@
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 @@
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 @@
}
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 bb39ea8..5c6116a 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,4 +114,5 @@
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 45adf83..46dbe0f 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 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 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 @@
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 @@
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 @@
// 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 6886cde..737a9e4 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.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 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 @@
* 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 50fb176..fd2fb1f 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.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 @@
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 @@
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 e60411bb..c547c36 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.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 @@
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 5d4fa1e..42723f4 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.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.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 @@
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 @@
@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 @@
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 3a14283..5364cbf 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.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 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 @@
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 fb033e6..311d6622 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.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.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 @@
}
}
- 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 61fe023..5460e36 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 @@
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 e6a238a..ba60111 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 @@
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 938e4cc..7297622 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 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 @@
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 61252c4..ff0aec7 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 @@
.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")}