summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityThread.java12
-rw-r--r--core/java/android/app/ApplicationPackageManager.java3
-rw-r--r--core/java/android/app/ContextImpl.java58
-rw-r--r--core/java/android/app/LoadedApk.java5
-rw-r--r--core/java/android/app/ResourcesManager.java434
-rw-r--r--core/java/android/app/WindowTokenClient.java3
-rw-r--r--core/java/android/content/pm/PackageParser.java3
-rw-r--r--core/java/android/content/res/ResourcesKey.java21
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java49
-rw-r--r--core/tests/coretests/src/android/content/res/ResourcesManagerTest.java111
10 files changed, 509 insertions, 190 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d67b98620f37..f6b045349219 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2242,9 +2242,9 @@ public final class ActivityThread extends ClientTransactionHandler {
* Resources if one has already been created.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, int displayId, LoadedApk pkgInfo) {
+ String[] libDirs, LoadedApk pkgInfo) {
return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
- displayId, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
+ null, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
}
@UnsupportedAppUsage
@@ -5692,8 +5692,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// many places.
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
- mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig,
- displayId, movedToDifferentDisplay);
+ mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);
@@ -6014,6 +6013,11 @@ public final class ActivityThread extends ClientTransactionHandler {
r.mPendingOverrideConfig = null;
}
+ if (displayId == INVALID_DISPLAY) {
+ // If INVALID_DISPLAY is passed assume that the activity should keep its current
+ // display.
+ displayId = r.activity.getDisplayId();
+ }
final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
&& !movedToDifferentDisplay) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 676c6c01d349..340d5a12f92e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -96,7 +96,6 @@ import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LauncherIcons;
import android.util.Log;
-import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -1748,7 +1747,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, Display.DEFAULT_DISPLAY,
+ app.resourceDirs, app.sharedLibraryFiles,
mContext.mPackageInfo);
if (r != null) {
return r;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f6b533434ac2..96d0d40f8027 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -227,6 +227,15 @@ class ContextImpl extends Context {
private @NonNull Resources mResources;
private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet.
+ /**
+ * If set to {@code true} the resources for this context will be configured for mDisplay which
+ * will override the display configuration inherited from {@link #mToken} (or the global
+ * configuration if mToken is null). Typically set for display contexts and contexts derived
+ * from display contexts where changes to the activity display and the global configuration
+ * display should not impact their resources.
+ */
+ private boolean mForceDisplayOverrideInResources;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mFlags;
@@ -2246,8 +2255,8 @@ class ContextImpl extends Context {
}
private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
- int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
- List<ResourcesLoader> resourcesLoader) {
+ @Nullable Integer overrideDisplayId, Configuration overrideConfig,
+ CompatibilityInfo compatInfo, List<ResourcesLoader> resourcesLoader) {
final String[] splitResDirs;
final ClassLoader classLoader;
try {
@@ -2261,7 +2270,7 @@ class ContextImpl extends Context {
splitResDirs,
pi.getOverlayDirs(),
pi.getApplicationInfo().sharedLibraryFiles,
- displayId,
+ overrideDisplayId,
overrideConfig,
compatInfo,
classLoader,
@@ -2278,8 +2287,10 @@ class ContextImpl extends Context {
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
final int displayId = getDisplayId();
+ final Integer overrideDisplayId = mForceDisplayOverrideInResources
+ ? displayId : null;
- c.setResources(createResources(mToken, pi, null, displayId, null,
+ c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
if (c.mResources != null) {
return c;
@@ -2313,8 +2324,10 @@ class ContextImpl extends Context {
mToken, user, flags, null, null);
final int displayId = getDisplayId();
+ final Integer overrideDisplayId = mForceDisplayOverrideInResources
+ ? displayId : null;
- c.setResources(createResources(mToken, pi, null, displayId, null,
+ c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
if (c.mResources != null) {
return c;
@@ -2348,15 +2361,13 @@ class ContextImpl extends Context {
final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null);
- final int displayId = getDisplayId();
-
context.setResources(ResourcesManager.getInstance().getResources(
mToken,
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
mPackageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId,
+ mForceDisplayOverrideInResources ? getDisplayId() : null,
null,
mPackageInfo.getCompatibilityInfo(),
classLoader,
@@ -2370,12 +2381,23 @@ class ContextImpl extends Context {
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
+ if (mForceDisplayOverrideInResources) {
+ // Ensure the resources display metrics are adjusted to match the display this context
+ // is based on.
+ Configuration displayAdjustedConfig = new Configuration();
+ displayAdjustedConfig.setTo(mDisplay.getDisplayAdjustments().getConfiguration(),
+ ActivityInfo.CONFIG_WINDOW_CONFIGURATION, 1);
+ displayAdjustedConfig.updateFrom(overrideConfiguration);
+ overrideConfiguration = displayAdjustedConfig;
+ }
+
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
-
- context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
+ final Integer overrideDisplayId = mForceDisplayOverrideInResources
+ ? displayId : null;
+ context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
context.mIsUiContext = isUiContext() || isOuterUiContext();
@@ -2393,11 +2415,20 @@ class ContextImpl extends Context {
final int displayId = display.getDisplayId();
+ // Ensure the resources display metrics are adjusted to match the provided display.
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.setTo(display.getDisplayAdjustments().getConfiguration(),
+ ActivityInfo.CONFIG_WINDOW_CONFIGURATION, 1);
+
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
- null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+ overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
context.mIsAssociatedWithDisplay = true;
+ // Display contexts and any context derived from a display context should always override
+ // the display that would otherwise be inherited from mToken (or the global configuration if
+ // mToken is null).
+ context.mForceDisplayOverrideInResources = true;
return context;
}
@@ -2415,8 +2446,10 @@ class ContextImpl extends Context {
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
mSplitName, token, mUser, mFlags, mClassLoader, null);
context.mIsUiContext = true;
-
context.mIsAssociatedWithDisplay = true;
+ // Window contexts receive configurations directly from the server and as such do not
+ // need to override their display in ResourcesManager.
+ context.mForceDisplayOverrideInResources = false;
return context;
}
@@ -2759,6 +2792,7 @@ class ContextImpl extends Context {
mDisplay = container.mDisplay;
mIsAssociatedWithDisplay = container.mIsAssociatedWithDisplay;
mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
+ mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources;
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index aa6a08b6d2e4..202b6152d2ea 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -56,7 +56,6 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.Display;
import android.view.DisplayAdjustments;
import com.android.internal.util.ArrayUtils;
@@ -367,7 +366,7 @@ public final class LoadedApk {
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+ null, null, getCompatibilityInfo(),
getClassLoader(), mApplication == null ? null
: mApplication.getResources().getLoaders());
}
@@ -1231,7 +1230,7 @@ public final class LoadedApk {
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+ null, null, getCompatibilityInfo(),
getClassLoader(), null);
}
return mResources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fb2120e5a35b..887802d2660d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -17,6 +17,8 @@
package android.app;
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -61,6 +63,7 @@ import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Consumer;
+import java.util.function.Function;
/** @hide */
public class ResourcesManager {
@@ -82,6 +85,12 @@ public class ResourcesManager {
private final Configuration mResConfiguration = new Configuration();
/**
+ * The display upon which all Resources are based. Activity, window token, and display context
+ * resources apply their overrides to this display id.
+ */
+ private int mResDisplayId = DEFAULT_DISPLAY;
+
+ /**
* A mapping of ResourceImpls and their configurations. These are heavy weight objects
* which should be reused as much as possible.
*/
@@ -155,20 +164,79 @@ public class ResourcesManager {
private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
/**
- * Resources and base configuration override associated with an Activity.
+ * Class containing the base configuration override and set of resources associated with an
+ * Activity or {@link WindowContext}.
*/
private static class ActivityResources {
+ /**
+ * Override config to apply to all resources associated with the token this instance is
+ * based on.
+ *
+ * @see #activityResources
+ * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
+ * CompatibilityInfo, ClassLoader, List)
+ */
+ public final Configuration overrideConfig = new Configuration();
+
+ /**
+ * The display to apply to all resources associated with the token this instance is based
+ * on.
+ */
+ public int overrideDisplayId;
+
+ /** List of {@link ActivityResource} associated with the token this instance is based on. */
+ public final ArrayList<ActivityResource> activityResources = new ArrayList<>();
+
+ public final ReferenceQueue<Resources> activityResourcesQueue = new ReferenceQueue<>();
+
@UnsupportedAppUsage
- private ActivityResources() {
+ private ActivityResources() {}
+
+ /** Returns the number of live resource references within {@code activityResources}. */
+ public int countLiveReferences() {
+ int count = 0;
+ for (int i = 0; i < activityResources.size(); i++) {
+ WeakReference<Resources> resources = activityResources.get(i).resources;
+ if (resources != null && resources.get() != null) {
+ count++;
+ }
+ }
+ return count;
}
+ }
+
+ /**
+ * Contains a resource derived from an {@link Activity} or {@link WindowContext} and information
+ * about how this resource expects its configuration to differ from the token's.
+ *
+ * @see ActivityResources
+ */
+ // TODO: Ideally this class should be called something token related, like TokenBasedResource.
+ private static class ActivityResource {
+ /**
+ * The override configuration applied on top of the token's override config for this
+ * resource.
+ */
public final Configuration overrideConfig = new Configuration();
- public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
- final ReferenceQueue<Resources> activityResourcesQueue = new ReferenceQueue<>();
+
+ /**
+ * If non-null this resource expects its configuration to override the display from the
+ * token's configuration.
+ *
+ * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
+ */
+ @Nullable
+ public Integer overrideDisplayId;
+
+ @Nullable
+ public WeakReference<Resources> resources;
+
+ private ActivityResource() {}
}
/**
- * Each Activity may has a base override configuration that is applied to each Resources object,
- * which in turn may have their own override configuration specified.
+ * Each Activity or WindowToken may has a base override configuration that is applied to each
+ * Resources object, which in turn may have their own override configuration specified.
*/
@UnsupportedAppUsage
private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
@@ -241,8 +309,7 @@ public class ResourcesManager {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public DisplayMetrics getDisplayMetrics() {
- return getDisplayMetrics(Display.DEFAULT_DISPLAY,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ return getDisplayMetrics(mResDisplayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
}
/**
@@ -260,8 +327,8 @@ public class ResourcesManager {
return dm;
}
- private static void applyNonDefaultDisplayMetricsToConfiguration(
- @NonNull DisplayMetrics dm, @NonNull Configuration config) {
+ private static void applyDisplayMetricsToConfiguration(@NonNull DisplayMetrics dm,
+ @NonNull Configuration config) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.densityDpi = dm.densityDpi;
config.screenWidthDp = (int) (dm.widthPixels / dm.density);
@@ -502,7 +569,7 @@ public class ResourcesManager {
int references = countLiveReferences(mResourceReferences);
for (ActivityResources activityResources : mActivityResourceReferences.values()) {
- references += countLiveReferences(activityResources.activityResources);
+ references += activityResources.countLiveReferences();
}
pw.println(references);
@@ -511,38 +578,36 @@ public class ResourcesManager {
}
}
- private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
+ private Configuration generateConfig(@NonNull ResourcesKey key) {
Configuration config;
- final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfig) {
+ if (hasOverrideConfig) {
config = new Configuration(getConfiguration());
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, config);
- }
- if (hasOverrideConfig) {
- config.updateFrom(key.mOverrideConfiguration);
- if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
- }
+ config.updateFrom(key.mOverrideConfiguration);
+ if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
} else {
config = getConfiguration();
}
return config;
}
+ private int generateDisplayId(@NonNull ResourcesKey key) {
+ return key.mDisplayId != INVALID_DISPLAY ? key.mDisplayId : mResDisplayId;
+ }
+
private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
- final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
- daj.setCompatibilityInfo(key.mCompatInfo);
-
final AssetManager assets = createAssetManager(key, apkSupplier);
if (assets == null) {
return null;
}
- final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
- final Configuration config = generateConfig(key, dm);
- final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
+ final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
+ daj.setCompatibilityInfo(key.mCompatInfo);
+
+ final Configuration config = generateConfig(key);
+ final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
+ final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -652,8 +717,8 @@ public class ResourcesManager {
final int size = activityResources.activityResources.size();
for (int index = 0; index < size; index++) {
- WeakReference<Resources> ref = activityResources.activityResources.get(index);
- Resources resources = ref.get();
+ ActivityResource activityResource = activityResources.activityResources.get(index);
+ Resources resources = activityResource.resources.get();
ResourcesKey key = resources == null ? null : findKeyForResourceImplLocked(
resources.getImpl());
@@ -667,20 +732,28 @@ public class ResourcesManager {
return null;
}
- private @NonNull Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+ @NonNull
+ private Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+ @NonNull Configuration initialOverrideConfig, @Nullable Integer overrideDisplayId,
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
@NonNull CompatibilityInfo compatInfo) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
cleanupReferences(activityResources.activityResources,
- activityResources.activityResourcesQueue);
+ activityResources.activityResourcesQueue,
+ (r) -> r.resources);
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
resources.setCallbacks(mUpdateCallbacks);
- activityResources.activityResources.add(
- new WeakReference<>(resources, activityResources.activityResourcesQueue));
+
+ ActivityResource activityResource = new ActivityResource();
+ activityResource.resources = new WeakReference<>(resources,
+ activityResources.activityResourcesQueue);
+ activityResource.overrideConfig.setTo(initialOverrideConfig);
+ activityResource.overrideDisplayId = overrideDisplayId;
+ activityResources.activityResources.add(activityResource);
if (DEBUG) {
Slog.d(TAG, "- creating new ref=" + resources);
Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
@@ -706,7 +779,7 @@ public class ResourcesManager {
/**
* Creates base resources for a binder token. Calls to
- * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
+ * {@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.
*
@@ -743,7 +816,7 @@ public class ResourcesManager {
overlayDirs,
libDirs,
displayId,
- overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ overrideConfig,
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
@@ -759,10 +832,7 @@ public class ResourcesManager {
}
// Update any existing Activity Resources references.
- updateResourcesForActivity(token, overrideConfig, displayId,
- false /* movedToDifferentDisplay */);
-
- rebaseKeyForActivity(token, key);
+ updateResourcesForActivity(token, overrideConfig, displayId);
synchronized (this) {
Resources resources = findResourcesForActivityLocked(token, key,
@@ -773,7 +843,9 @@ public class ResourcesManager {
}
// Now request an actual Resources object.
- return createResources(token, key, classLoader, /* apkSupplier */ null);
+ return createResourcesForActivity(token, key,
+ /* initialOverrideConfig */ Configuration.EMPTY, /* overrideDisplayId */ null,
+ classLoader, /* apkSupplier */ null);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -781,20 +853,65 @@ public class ResourcesManager {
/**
* Rebases a key's override config on top of the Activity's base override.
+ *
+ * @param activityToken the token the supplied {@code key} is derived from.
+ * @param key the key to rebase
+ * @param overridesActivityDisplay whether this key is overriding the display from the token
*/
- private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key) {
+ private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
+ boolean overridesActivityDisplay) {
synchronized (this) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
- // Rebase the key's override config on top of the Activity's base override.
- if (key.hasOverrideConfiguration()
- && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
- final Configuration temp = new Configuration(activityResources.overrideConfig);
- temp.updateFrom(key.mOverrideConfiguration);
- key.mOverrideConfiguration.setTo(temp);
+ if (key.mDisplayId == INVALID_DISPLAY) {
+ key.mDisplayId = activityResources.overrideDisplayId;
+ }
+
+ Configuration config;
+ if (key.hasOverrideConfiguration()) {
+ config = new Configuration(activityResources.overrideConfig);
+ config.updateFrom(key.mOverrideConfiguration);
+ } else {
+ config = activityResources.overrideConfig;
+ }
+
+ if (overridesActivityDisplay
+ && key.mOverrideConfiguration.windowConfiguration.getAppBounds() == null) {
+ if (!key.hasOverrideConfiguration()) {
+ // Make a copy to handle the case where the override config is set to defaults.
+ config = new Configuration(config);
+ }
+
+ // If this key is overriding the display from the token and the key's
+ // window config app bounds is null we need to explicitly override this to
+ // ensure the display adjustments are as expected.
+ config.windowConfiguration.setAppBounds(null);
}
+
+ key.mOverrideConfiguration.setTo(config);
+ }
+ }
+
+ /**
+ * Rebases a key's override config with display metrics of the {@code overrideDisplay} paired
+ * with the {code displayAdjustments}.
+ *
+ * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
+ */
+ private void rebaseKeyForDisplay(ResourcesKey key, int overrideDisplay) {
+ final Configuration temp = new Configuration();
+
+ DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
+ daj.setCompatibilityInfo(key.mCompatInfo);
+
+ final DisplayMetrics dm = getDisplayMetrics(overrideDisplay, daj);
+ applyDisplayMetricsToConfiguration(dm, temp);
+
+ if (key.hasOverrideConfiguration()) {
+ temp.updateFrom(key.mOverrideConfiguration);
}
+ key.mOverrideConfiguration.setTo(temp);
}
/**
@@ -802,18 +919,28 @@ public class ResourcesManager {
*/
private static <T> void cleanupReferences(ArrayList<WeakReference<T>> references,
ReferenceQueue<T> referenceQueue) {
- Reference<? extends T> enduedRef = referenceQueue.poll();
- if (enduedRef == null) {
+ cleanupReferences(references, referenceQueue, Function.identity());
+ }
+
+ /**
+ * Check WeakReferences and remove any dead references so they don't pile up.
+ */
+ private static <C, T> void cleanupReferences(ArrayList<C> referenceContainers,
+ ReferenceQueue<T> referenceQueue, Function<C, WeakReference<T>> unwrappingFunction) {
+ Reference<? extends T> enqueuedRef = referenceQueue.poll();
+ if (enqueuedRef == null) {
return;
}
final HashSet<Reference<? extends T>> deadReferences = new HashSet<>();
- for (; enduedRef != null; enduedRef = referenceQueue.poll()) {
- deadReferences.add(enduedRef);
+ for (; enqueuedRef != null; enqueuedRef = referenceQueue.poll()) {
+ deadReferences.add(enqueuedRef);
}
- ArrayUtils.unstableRemoveIf(references,
- (ref) -> ref == null || deadReferences.contains(ref));
+ ArrayUtils.unstableRemoveIf(referenceContainers, (refContainer) -> {
+ WeakReference<T> ref = unwrappingFunction.apply(refContainer);
+ return ref == null || deadReferences.contains(ref);
+ });
}
/**
@@ -849,23 +976,21 @@ public class ResourcesManager {
/**
* Creates a Resources object set with a ResourcesImpl object matching the given key.
*
- * @param activityToken The Activity this Resources object should be associated with.
* @param key The key describing the parameters of the ResourcesImpl object.
* @param classLoader The classloader to use for the Resources object.
* If null, {@link ClassLoader#getSystemClassLoader()} is used.
- * @param apkSupplier The apk assets supplier to use when creating a new ResourcesImpl object.
* @return A Resources object that gets updated when
* {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
* is called.
*/
- private @Nullable Resources createResources(@Nullable IBinder activityToken,
- @NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
+ @Nullable
+ private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
@Nullable ApkAssetsSupplier apkSupplier) {
synchronized (this) {
if (DEBUG) {
Throwable here = new Throwable();
here.fillInStackTrace();
- Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
+ Slog.w(TAG, "!! Create resources for key=" + key, here);
}
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
@@ -873,12 +998,29 @@ public class ResourcesManager {
return null;
}
- if (activityToken != null) {
- return createResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl, key.mCompatInfo);
- } else {
- return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
+ return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
+ }
+ }
+
+ @Nullable
+ private Resources createResourcesForActivity(@NonNull IBinder activityToken,
+ @NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
+ @Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
+ @Nullable ApkAssetsSupplier apkSupplier) {
+ synchronized (this) {
+ if (DEBUG) {
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
+ }
+
+ ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
+ if (resourcesImpl == null) {
+ return null;
}
+
+ return createResourcesForActivityLocked(activityToken, initialOverrideConfig,
+ overrideDisplayId, classLoader, resourcesImpl, key.mCompatInfo);
}
}
@@ -899,7 +1041,10 @@ public class ResourcesManager {
* @param splitResDirs An array of split resource paths. Can be null.
* @param overlayDirs An array of overlay 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 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
+ * Resources for the {@code activityToken}, or the global configuration if {@code activityToken}
+ * is null.
* @param overrideConfig The configuration to apply on top of the base configuration. Can be
* null. Mostly used with Activities that are in multi-window which may override width and
* height properties from the base config.
@@ -909,13 +1054,14 @@ public class ResourcesManager {
* {@link ClassLoader#getSystemClassLoader()} is used.
* @return a Resources object from which to access resources.
*/
- public @Nullable Resources getResources(
+ @Nullable
+ public Resources getResources(
@Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@Nullable String[] libDirs,
- int displayId,
+ @Nullable Integer overrideDisplayId,
@Nullable Configuration overrideConfig,
@NonNull CompatibilityInfo compatInfo,
@Nullable ClassLoader classLoader,
@@ -927,20 +1073,30 @@ public class ResourcesManager {
splitResDirs,
overlayDirs,
libDirs,
- displayId,
- overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
+ overrideConfig,
compatInfo,
loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- if (activityToken != null) {
- rebaseKeyForActivity(activityToken, key);
- }
-
// Preload the ApkAssets required by the key to prevent performing heavy I/O while the
// ResourcesManager lock is held.
final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
- return createResources(activityToken, key, classLoader, assetsSupplier);
+
+ if (overrideDisplayId != null) {
+ rebaseKeyForDisplay(key, overrideDisplayId);
+ }
+
+ Resources resources;
+ if (activityToken != null) {
+ Configuration initialOverrideConfig = new Configuration(key.mOverrideConfiguration);
+ rebaseKeyForActivity(activityToken, key, overrideDisplayId != null);
+ resources = createResourcesForActivity(activityToken, key, initialOverrideConfig,
+ overrideDisplayId, classLoader, assetsSupplier);
+ } else {
+ resources = createResources(key, classLoader, assetsSupplier);
+ }
+ return resources;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -949,24 +1105,26 @@ public class ResourcesManager {
/**
* Updates an Activity's Resources object with overrideConfig. The Resources object
* that was previously returned by {@link #getResources(IBinder, String, String[], String[],
- * String[], int, Configuration, CompatibilityInfo, ClassLoader, List)} is still valid and will
- * have the updated configuration.
+ * String[], Integer, Configuration, CompatibilityInfo, ClassLoader, List)} is still valid and
+ * will have the updated configuration.
*
* @param activityToken The Activity token.
* @param overrideConfig The configuration override to update.
* @param displayId Id of the display where activity currently resides.
- * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
*/
public void updateResourcesForActivity(@NonNull IBinder activityToken,
- @Nullable Configuration overrideConfig, int displayId,
- boolean movedToDifferentDisplay) {
+ @Nullable Configuration overrideConfig, int displayId) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#updateResourcesForActivity");
+ if (displayId == INVALID_DISPLAY) {
+ throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
+ }
synchronized (this) {
final ActivityResources activityResources =
getOrCreateActivityResourcesStructLocked(activityToken);
+ boolean movedToDifferentDisplay = activityResources.overrideDisplayId != displayId;
if (Objects.equals(activityResources.overrideConfig, overrideConfig)
&& !movedToDifferentDisplay) {
// They are the same and no change of display id, no work to do.
@@ -983,6 +1141,8 @@ public class ResourcesManager {
} else {
activityResources.overrideConfig.unset();
}
+ // Update the Activity's override display id.
+ activityResources.overrideDisplayId = displayId;
if (DEBUG) {
Throwable here = new Throwable();
@@ -1000,24 +1160,26 @@ public class ResourcesManager {
// Rebase each Resources associated with this Activity.
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
- final WeakReference<Resources> weakResRef =
+ final ActivityResource activityResource =
activityResources.activityResources.get(i);
- final Resources resources = weakResRef.get();
+ final Resources resources = activityResource.resources.get();
if (resources == null) {
continue;
}
- final ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+ final ResourcesKey newKey = rebaseActivityOverrideConfig(activityResource,
overrideConfig, displayId);
- if (newKey != null) {
- final ResourcesImpl resourcesImpl =
- findOrCreateResourcesImplForKeyLocked(newKey);
- if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
- // Set the ResourcesImpl, updating it for all users of this Resources
- // object.
- resources.setImpl(resourcesImpl);
- }
+ if (newKey == null) {
+ continue;
+ }
+
+ final ResourcesImpl resourcesImpl =
+ findOrCreateResourcesImplForKeyLocked(newKey);
+ if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+ // Set the ResourcesImpl, updating it for all users of this Resources
+ // object.
+ resources.setImpl(resourcesImpl);
}
}
}
@@ -1031,9 +1193,13 @@ public class ResourcesManager {
* that an Activity's Resources should be set to.
*/
@Nullable
- private ResourcesKey rebaseActivityOverrideConfig(@NonNull Resources resources,
- @NonNull Configuration oldOverrideConfig, @Nullable Configuration newOverrideConfig,
- int displayId) {
+ private ResourcesKey rebaseActivityOverrideConfig(@NonNull ActivityResource activityResource,
+ @Nullable Configuration newOverrideConfig, int displayId) {
+ final Resources resources = activityResource.resources.get();
+ if (resources == null) {
+ return null;
+ }
+
// Extract the ResourcesKey that was last used to create the Resources for this
// activity.
final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
@@ -1049,16 +1215,33 @@ public class ResourcesManager {
rebasedOverrideConfig.setTo(newOverrideConfig);
}
- final boolean hadOverrideConfig = !oldOverrideConfig.equals(Configuration.EMPTY);
- if (hadOverrideConfig && oldKey.hasOverrideConfiguration()) {
- // Generate a delta between the old base Activity override configuration and
- // the actual final override configuration that was used to figure out the
- // real delta this Resources object wanted.
- Configuration overrideOverrideConfig = Configuration.generateDelta(
- oldOverrideConfig, oldKey.mOverrideConfiguration);
- rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ final Integer overrideDisplayId = activityResource.overrideDisplayId;
+ if (overrideDisplayId != null) {
+ DisplayAdjustments displayAdjustments = new DisplayAdjustments(rebasedOverrideConfig);
+ displayAdjustments.getConfiguration().setTo(activityResource.overrideConfig);
+ displayAdjustments.setCompatibilityInfo(oldKey.mCompatInfo);
+
+ DisplayMetrics dm = getDisplayMetrics(overrideDisplayId, displayAdjustments);
+ applyDisplayMetricsToConfiguration(dm, rebasedOverrideConfig);
+ }
+
+ final boolean hasOverrideConfig =
+ !activityResource.overrideConfig.equals(Configuration.EMPTY);
+ if (hasOverrideConfig) {
+ rebasedOverrideConfig.updateFrom(activityResource.overrideConfig);
}
+ if (activityResource.overrideDisplayId != null
+ && activityResource.overrideConfig.windowConfiguration.getAppBounds() == null) {
+ // If this activity resource is overriding the display from the token and the key's
+ // window config app bounds is null we need to explicitly override this to
+ // ensure the display adjustments are as expected.
+ rebasedOverrideConfig.windowConfiguration.setAppBounds(null);
+ }
+
+ // Ensure the new key keeps the expected override display instead of the new token display.
+ displayId = overrideDisplayId != null ? overrideDisplayId : displayId;
+
// Create the new ResourcesKey with the rebased override config.
final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs,
@@ -1090,12 +1273,11 @@ public class ResourcesManager {
+ mResConfiguration.seq + ", newSeq=" + config.seq);
return false;
}
- int changes = mResConfiguration.updateFrom(config);
+
// Things might have changed in display manager, so clear the cached displays.
mAdjustedDisplays.clear();
- DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
-
+ int changes = mResConfiguration.updateFrom(config);
if (compat != null && (mResCompatibilityInfo == null ||
!mResCompatibilityInfo.equals(compat))) {
mResCompatibilityInfo = compat;
@@ -1104,10 +1286,10 @@ public class ResourcesManager {
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
}
- Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
+ DisplayMetrics displayMetrics = getDisplayMetrics();
+ Resources.updateSystemConfiguration(config, displayMetrics, compat);
ApplicationPackageManager.configurationChanged();
- //Slog.i(TAG, "Configuration changed in " + currentPackageName());
Configuration tmpConfig = new Configuration();
@@ -1137,11 +1319,7 @@ public class ResourcesManager {
}
tmpConfig.setTo(config);
-
- // Apply the override configuration before setting the display adjustments to ensure that
- // the process config does not override activity display adjustments.
- final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (hasOverrideConfiguration) {
+ if (key.hasOverrideConfiguration()) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
}
@@ -1153,22 +1331,8 @@ public class ResourcesManager {
daj = new DisplayAdjustments(daj);
daj.setCompatibilityInfo(compat);
}
-
- final int displayId = key.mDisplayId;
- if (displayId == Display.DEFAULT_DISPLAY) {
- daj.setConfiguration(tmpConfig);
- }
- DisplayMetrics dm = getDisplayMetrics(displayId, daj);
- if (displayId != Display.DEFAULT_DISPLAY) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-
- // Re-apply the override configuration to ensure that configuration contexts based on
- // a display context (ex: createDisplayContext().createConfigurationContext()) have the
- // correct override.
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- }
+ daj.setConfiguration(tmpConfig);
+ DisplayMetrics dm = getDisplayMetrics(generateDisplayId(key), daj);
resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
}
@@ -1305,8 +1469,10 @@ public class ResourcesManager {
for (ActivityResources activityResources : mActivityResourceReferences.values()) {
final int resCount = activityResources.activityResources.size();
for (int i = 0; i < resCount; i++) {
- final WeakReference<Resources> ref = activityResources.activityResources.get(i);
- final Resources r = ref != null ? ref.get() : null;
+ final ActivityResource activityResource =
+ activityResources.activityResources.get(i);
+ final Resources r = activityResource != null
+ ? activityResource.resources.get() : null;
if (r != null) {
final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
if (key != null) {
@@ -1338,10 +1504,16 @@ public class ResourcesManager {
if (tokenResources == null) {
return false;
}
- final ArrayList<WeakReference<Resources>> resourcesRefs =
- tokenResources.activityResources;
+ final ArrayList<ActivityResource> resourcesRefs = tokenResources.activityResources;
for (int i = resourcesRefs.size() - 1; i >= 0; i--) {
- final Resources res = resourcesRefs.get(i).get();
+ final ActivityResource activityResource = resourcesRefs.get(i);
+ if (activityResource.overrideDisplayId != null) {
+ // This resource overrides the display of the token so we should not be
+ // modifying its display adjustments here.
+ continue;
+ }
+
+ final Resources res = activityResource.resources.get();
if (res != null) {
res.overrideDisplayAdjustments(override);
handled = true;
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index b5d103959818..9092ef36deb6 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -71,8 +71,7 @@ public class WindowTokenClient extends IWindowToken.Stub {
final boolean configChanged = config.diff(newConfig) != 0;
if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
- mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId,
- displayChanged);
+ mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
}
if (displayChanged) {
context.updateDisplay(newDisplayId);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 200604801b97..0f1f276ce0f9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -88,7 +88,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureVerifier;
-import android.view.Display;
import android.view.Gravity;
import com.android.internal.R;
@@ -8536,7 +8535,7 @@ public class PackageParser {
null,
androidAppInfo.resourceDirs,
androidAppInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY,
+ null,
null,
systemResources.getCompatibilityInfo(),
systemResources.getClassLoader(),
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 9da0f20d1006..fcb80aa3e58d 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -16,6 +16,8 @@
package android.content.res;
+import static android.os.Build.VERSION_CODES.O;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -41,8 +43,17 @@ public final class ResourcesKey {
@Nullable
public final String[] mLibDirs;
- public final int mDisplayId;
-
+ /**
+ * The display ID that overrides the global resources display to produce the Resources display.
+ * If set to something other than {@link android.view.Display#INVALID_DISPLAY} this will
+ * override the global resources display for this key.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = O)
+ public int mDisplayId;
+
+ /**
+ * The configuration applied to the global configuration to produce the Resources configuration.
+ */
@NonNull
public final Configuration mOverrideConfiguration;
@@ -58,7 +69,7 @@ public final class ResourcesKey {
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@Nullable String[] libDirs,
- int displayId,
+ int overrideDisplayId,
@Nullable Configuration overrideConfig,
@Nullable CompatibilityInfo compatInfo,
@Nullable ResourcesLoader[] loader) {
@@ -67,7 +78,7 @@ public final class ResourcesKey {
mOverlayDirs = overlayDirs;
mLibDirs = libDirs;
mLoaders = (loader != null && loader.length == 0) ? null : loader;
- mDisplayId = displayId;
+ mDisplayId = overrideDisplayId;
mOverrideConfiguration = new Configuration(overrideConfig != null
? overrideConfig : Configuration.EMPTY);
mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
@@ -77,7 +88,7 @@ public final class ResourcesKey {
hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
hash = 31 * hash + Arrays.hashCode(mLibDirs);
- hash = 31 * hash + mDisplayId;
+ hash = 31 * hash + Objects.hashCode(mDisplayId);
hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
hash = 31 * hash + Objects.hashCode(mCompatInfo);
hash = 31 * hash + Arrays.hashCode(mLoaders);
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 000e870369db..0a751dd7c66b 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -408,6 +408,9 @@ public class ActivityThreadTest {
int originalVirtualDisplayOrientation = virtualDisplayContext.getResources()
.getConfiguration().orientation;
+
+ // Perform global config change and verify there is no config change in derived display
+ // context.
Configuration newAppConfig = new Configuration(originalAppConfig);
newAppConfig.seq++;
newAppConfig.orientation = newAppConfig.orientation == ORIENTATION_PORTRAIT
@@ -417,7 +420,7 @@ public class ActivityThreadTest {
activityThread.handleConfigurationChanged(newAppConfig);
try {
- assertEquals("Virtual display orientation should not change when process"
+ assertEquals("Virtual display orientation must not change when process"
+ " configuration orientation changes.",
originalVirtualDisplayOrientation,
virtualDisplayContext.getResources().getConfiguration().orientation);
@@ -438,6 +441,50 @@ public class ActivityThreadTest {
}
@Test
+ public void testActivityOrientationChanged_DoesntOverrideVirtualDisplayOrientation() {
+ final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ Configuration originalActivityConfig =
+ new Configuration(activity.getResources().getConfiguration());
+ DisplayManager dm = activity.getSystemService(DisplayManager.class);
+
+ int virtualDisplayWidth;
+ int virtualDisplayHeight;
+ if (originalActivityConfig.orientation == ORIENTATION_PORTRAIT) {
+ virtualDisplayWidth = 100;
+ virtualDisplayHeight = 200;
+ } else {
+ virtualDisplayWidth = 200;
+ virtualDisplayHeight = 100;
+ }
+ Display virtualDisplay = dm.createVirtualDisplay("virtual-display",
+ virtualDisplayWidth, virtualDisplayHeight, 200, null, 0).getDisplay();
+ Context virtualDisplayContext = activity.createDisplayContext(virtualDisplay);
+ int originalVirtualDisplayOrientation = virtualDisplayContext.getResources()
+ .getConfiguration().orientation;
+
+ // Perform activity config change and verify there is no config change in derived
+ // display context.
+ Configuration newActivityConfig = new Configuration(originalActivityConfig);
+ newActivityConfig.seq++;
+ newActivityConfig.orientation = newActivityConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+
+ activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+ newActivityConfig);
+ activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+ newActivityConfig, INVALID_DISPLAY);
+
+ assertEquals("Virtual display orientation must not change when activity"
+ + " configuration orientation changes.",
+ originalVirtualDisplayOrientation,
+ virtualDisplayContext.getResources().getConfiguration().orientation);
+ });
+ }
+
+ @Test
public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index efcd458e19cc..45adf833de97 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -29,31 +29,50 @@ import androidx.test.filters.SmallTest;
import junit.framework.TestCase;
+import java.util.HashMap;
+import java.util.Map;
+
public class ResourcesManagerTest extends TestCase {
+ private static final int SECONDARY_DISPLAY_ID = 1;
private static final String APP_ONE_RES_DIR = "app_one.apk";
private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk";
private static final String APP_TWO_RES_DIR = "app_two.apk";
private static final String LIB_RES_DIR = "lib.apk";
private ResourcesManager mResourcesManager;
- private DisplayMetrics mDisplayMetrics;
+ private Map<Integer, DisplayMetrics> mDisplayMetricsMap;
@Override
protected void setUp() throws Exception {
super.setUp();
- mDisplayMetrics = new DisplayMetrics();
- mDisplayMetrics.setToDefaults();
+ mDisplayMetricsMap = new HashMap<>();
+
+ DisplayMetrics defaultDisplayMetrics = new DisplayMetrics();
+ defaultDisplayMetrics.setToDefaults();
// Override defaults (which take device specific properties).
- mDisplayMetrics.density = 1.0f;
- mDisplayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
- mDisplayMetrics.xdpi = DisplayMetrics.DENSITY_DEFAULT;
- mDisplayMetrics.ydpi = DisplayMetrics.DENSITY_DEFAULT;
- mDisplayMetrics.noncompatDensity = mDisplayMetrics.density;
- mDisplayMetrics.noncompatDensityDpi = mDisplayMetrics.densityDpi;
- mDisplayMetrics.noncompatXdpi = DisplayMetrics.DENSITY_DEFAULT;
- mDisplayMetrics.noncompatYdpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.density = 1.0f;
+ defaultDisplayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.xdpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.ydpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.widthPixels = 1440;
+ defaultDisplayMetrics.heightPixels = 2960;
+ defaultDisplayMetrics.noncompatDensity = defaultDisplayMetrics.density;
+ defaultDisplayMetrics.noncompatDensityDpi = defaultDisplayMetrics.densityDpi;
+ defaultDisplayMetrics.noncompatXdpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.noncompatYdpi = DisplayMetrics.DENSITY_DEFAULT;
+ defaultDisplayMetrics.noncompatWidthPixels = defaultDisplayMetrics.widthPixels;
+ defaultDisplayMetrics.noncompatHeightPixels = defaultDisplayMetrics.heightPixels;
+ mDisplayMetricsMap.put(Display.DEFAULT_DISPLAY, defaultDisplayMetrics);
+
+ DisplayMetrics secondaryDisplayMetrics = new DisplayMetrics();
+ secondaryDisplayMetrics.setTo(defaultDisplayMetrics);
+ secondaryDisplayMetrics.widthPixels = 50;
+ secondaryDisplayMetrics.heightPixels = 100;
+ secondaryDisplayMetrics.noncompatWidthPixels = secondaryDisplayMetrics.widthPixels;
+ secondaryDisplayMetrics.noncompatHeightPixels = secondaryDisplayMetrics.heightPixels;
+ mDisplayMetricsMap.put(SECONDARY_DISPLAY_ID, secondaryDisplayMetrics);
mResourcesManager = new ResourcesManager() {
@Override
@@ -63,7 +82,7 @@ public class ResourcesManagerTest extends TestCase {
@Override
protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
- return mDisplayMetrics;
+ return mDisplayMetricsMap.get(displayId);
}
};
}
@@ -71,12 +90,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithIdenticalParametersCacheReference() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ null, APP_ONE_RES_DIR, 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, Display.DEFAULT_DISPLAY, null,
+ null, APP_ONE_RES_DIR, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertSame(resources, newResources);
@@ -85,14 +104,14 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
Resources resources = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ null, APP_ONE_RES_DIR, 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, Display.DEFAULT_DISPLAY, overrideConfig,
+ null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(newResources);
assertNotSame(resources, newResources);
@@ -101,13 +120,13 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testAddingASplitCreatesANewImpl() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ null, APP_ONE_RES_DIR, 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,
- Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,null,
+ null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null,
null);
assertNotNull(resources2);
@@ -118,12 +137,12 @@ public class ResourcesManagerTest extends TestCase {
@SmallTest
public void testUpdateConfigurationUpdatesAllAssetManagers() {
Resources resources1 = mResourcesManager.getResources(
- null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ null, APP_ONE_RES_DIR, 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, Display.DEFAULT_DISPLAY, null,
+ null, APP_TWO_RES_DIR, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -131,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, Display.DEFAULT_DISPLAY,
+ activity, APP_ONE_RES_DIR, null, null, null, null,
overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources3);
@@ -152,7 +171,7 @@ public class ResourcesManagerTest extends TestCase {
final Configuration expectedConfig = new Configuration();
expectedConfig.setToDefaults();
expectedConfig.setLocales(LocaleList.getAdjustedDefault());
- expectedConfig.densityDpi = mDisplayMetrics.densityDpi;
+ expectedConfig.densityDpi = mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).densityDpi;
expectedConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
assertEquals(expectedConfig, resources1.getConfiguration());
@@ -164,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, Display.DEFAULT_DISPLAY, null,
+ activity1, APP_ONE_RES_DIR, 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, Display.DEFAULT_DISPLAY, null,
+ activity2, APP_ONE_RES_DIR, null, null, null, null, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources1);
@@ -201,7 +220,7 @@ public class ResourcesManagerTest extends TestCase {
final Configuration overrideConfig = new Configuration();
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
mResourcesManager.updateResourcesForActivity(activity1, overrideConfig,
- Display.DEFAULT_DISPLAY, false /* movedToDifferentDisplay */);
+ Display.DEFAULT_DISPLAY);
assertSame(resources1, theme.getResources());
// Make sure we can still access the data.
@@ -226,7 +245,7 @@ public class ResourcesManagerTest extends TestCase {
Configuration config2 = new Configuration();
config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
Resources resources2 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config2,
+ activity1, APP_ONE_RES_DIR, null, null, null, null, config2,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
assertNotNull(resources2);
@@ -250,8 +269,7 @@ public class ResourcesManagerTest extends TestCase {
// Now update the Activity base override, and both resources should update.
config1.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mResourcesManager.updateResourcesForActivity(activity1, config1, Display.DEFAULT_DISPLAY,
- false /* movedToDifferentDisplay */);
+ mResourcesManager.updateResourcesForActivity(activity1, config1, Display.DEFAULT_DISPLAY);
expectedConfig1.orientation = Configuration.ORIENTATION_LANDSCAPE;
assertEquals(expectedConfig1, resources1.getConfiguration());
@@ -290,4 +308,41 @@ public class ResourcesManagerTest extends TestCase {
assertEquals(originalOverrideDensity,
resources.getDisplayAdjustments().getConfiguration().densityDpi);
}
+
+ @SmallTest
+ public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() {
+ Binder activity = new Binder();
+
+ // 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,
+ 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,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+ activityResources.getDisplayMetrics().widthPixels);
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).heightPixels,
+ activityResources.getDisplayMetrics().heightPixels);
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+ defaultDisplayResources.getDisplayMetrics().widthPixels);
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+ defaultDisplayResources.getDisplayMetrics().widthPixels);
+
+ // Now change the display of the activity and ensure the activity's display metrics match
+ // the new display, but the other resources remain based on the default display.
+ mResourcesManager.updateResourcesForActivity(activity, null, SECONDARY_DISPLAY_ID);
+
+ assertEquals(mDisplayMetricsMap.get(SECONDARY_DISPLAY_ID).widthPixels,
+ activityResources.getDisplayMetrics().widthPixels);
+ assertEquals(mDisplayMetricsMap.get(SECONDARY_DISPLAY_ID).heightPixels,
+ activityResources.getDisplayMetrics().heightPixels);
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+ defaultDisplayResources.getDisplayMetrics().widthPixels);
+ assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+ defaultDisplayResources.getDisplayMetrics().widthPixels);
+ }
}