Merge "Handle WindowProviderService#onConfigurationChanged" into sc-v2-dev
This commit is contained in:
@@ -18,7 +18,6 @@ package android.app;
|
|||||||
|
|
||||||
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
|
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
|
||||||
import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
|
import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
|
||||||
import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded;
|
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
|
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
|
||||||
@@ -31,6 +30,10 @@ import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
|
|||||||
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
|
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
|
||||||
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
|
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
|
||||||
import static android.view.Display.INVALID_DISPLAY;
|
import static android.view.Display.INVALID_DISPLAY;
|
||||||
|
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
|
||||||
|
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
|
||||||
|
import static android.window.ConfigurationHelper.isDifferentDisplay;
|
||||||
|
import static android.window.ConfigurationHelper.shouldUpdateResources;
|
||||||
|
|
||||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||||
|
|
||||||
@@ -91,7 +94,6 @@ import android.database.sqlite.SQLiteDebug.DbStats;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.HardwareRenderer;
|
import android.graphics.HardwareRenderer;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.hardware.display.DisplayManagerGlobal;
|
import android.hardware.display.DisplayManagerGlobal;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
@@ -186,6 +188,7 @@ import android.webkit.WebView;
|
|||||||
import android.window.SizeConfigurationBuckets;
|
import android.window.SizeConfigurationBuckets;
|
||||||
import android.window.SplashScreen;
|
import android.window.SplashScreen;
|
||||||
import android.window.SplashScreenView;
|
import android.window.SplashScreenView;
|
||||||
|
import android.window.WindowProviderService;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
@@ -5756,7 +5759,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
|
public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
|
||||||
ArrayList<ComponentCallbacks2> callbacks
|
ArrayList<ComponentCallbacks2> callbacks
|
||||||
= new ArrayList<ComponentCallbacks2>();
|
= new ArrayList<ComponentCallbacks2>();
|
||||||
|
|
||||||
@@ -5765,7 +5768,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
for (int i=0; i<NAPP; i++) {
|
for (int i=0; i<NAPP; i++) {
|
||||||
callbacks.add(mAllApplications.get(i));
|
callbacks.add(mAllApplications.get(i));
|
||||||
}
|
}
|
||||||
if (includeActivities) {
|
if (includeUiContexts) {
|
||||||
for (int i = mActivities.size() - 1; i >= 0; i--) {
|
for (int i = mActivities.size() - 1; i >= 0; i--) {
|
||||||
final Activity a = mActivities.valueAt(i).activity;
|
final Activity a = mActivities.valueAt(i).activity;
|
||||||
if (a != null && !a.mFinished) {
|
if (a != null && !a.mFinished) {
|
||||||
@@ -5775,11 +5778,15 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
}
|
}
|
||||||
final int NSVC = mServices.size();
|
final int NSVC = mServices.size();
|
||||||
for (int i=0; i<NSVC; i++) {
|
for (int i=0; i<NSVC; i++) {
|
||||||
final ComponentCallbacks2 serviceComp = mServices.valueAt(i);
|
final Service service = mServices.valueAt(i);
|
||||||
if (serviceComp instanceof InputMethodService) {
|
if (service instanceof InputMethodService) {
|
||||||
mHasImeComponent = true;
|
mHasImeComponent = true;
|
||||||
}
|
}
|
||||||
callbacks.add(serviceComp);
|
// If {@code includeUiContext} is set to false, WindowProviderService should not be
|
||||||
|
// collected because WindowProviderService is a UI Context.
|
||||||
|
if (includeUiContexts || !(service instanceof WindowProviderService)) {
|
||||||
|
callbacks.add(service);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized (mProviderMap) {
|
synchronized (mProviderMap) {
|
||||||
@@ -5834,35 +5841,26 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
// change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
|
// change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
|
||||||
handleWindowingModeChangeIfNeeded(activity, newConfig);
|
handleWindowingModeChangeIfNeeded(activity, newConfig);
|
||||||
|
|
||||||
final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
|
final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
|
||||||
boolean shouldReportChange = false;
|
displayId);
|
||||||
if (activity.mCurrentConfig == null) {
|
final SizeConfigurationBuckets buckets = getActivityClient(activityToken)
|
||||||
shouldReportChange = true;
|
.mSizeConfigurations;
|
||||||
} else {
|
final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig,
|
||||||
// If the new config is the same as the config this Activity is already running with and
|
newConfig, buckets);
|
||||||
// the override config also didn't change, then don't bother calling
|
final boolean hasPublicConfigChange = diff != 0;
|
||||||
// onConfigurationChanged.
|
|
||||||
// TODO(b/173090263): Use diff instead after the improvement of AssetManager and
|
// TODO(b/173090263): Use diff instead after the improvement of AssetManager and
|
||||||
// ResourcesImpl constructions.
|
// ResourcesImpl constructions.
|
||||||
int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
|
final boolean shouldUpdateResources = hasPublicConfigChange
|
||||||
final ActivityClientRecord cr = getActivityClient(activityToken);
|
|| shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig,
|
||||||
diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig,
|
amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange);
|
||||||
cr != null ? cr.mSizeConfigurations : null);
|
final boolean shouldReportChange = hasPublicConfigChange
|
||||||
|
|
||||||
if (diff == 0) {
|
|
||||||
if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
|
|
||||||
&& !movedToDifferentDisplay
|
|
||||||
&& mResourcesManager.isSameResourcesOverrideConfig(
|
|
||||||
activityToken, amOverrideConfig)) {
|
|
||||||
// Nothing significant, don't proceed with updating and reporting.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
|
|
||||||
// If this activity doesn't handle any of the config changes, then don't bother
|
// If this activity doesn't handle any of the config changes, then don't bother
|
||||||
// calling onConfigurationChanged. Otherwise, report to the activity for the
|
// calling onConfigurationChanged. Otherwise, report to the activity for the
|
||||||
// changes.
|
// changes.
|
||||||
shouldReportChange = true;
|
&& (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0;
|
||||||
}
|
// Nothing significant, don't proceed with updating and reporting.
|
||||||
|
if (!shouldUpdateResources) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propagate the configuration change to ResourcesManager and Activity.
|
// Propagate the configuration change to ResourcesManager and Activity.
|
||||||
@@ -5913,26 +5911,6 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
return configToReport;
|
return configToReport;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
|
|
||||||
// constructions.
|
|
||||||
/**
|
|
||||||
* Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
|
|
||||||
* should be updated.
|
|
||||||
*
|
|
||||||
* @see WindowManager#getCurrentWindowMetrics()
|
|
||||||
* @see WindowManager#getMaximumWindowMetrics()
|
|
||||||
*/
|
|
||||||
private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
|
|
||||||
@NonNull Configuration newConfig) {
|
|
||||||
final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
|
|
||||||
final Rect newBounds = newConfig.windowConfiguration.getBounds();
|
|
||||||
|
|
||||||
final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
|
|
||||||
final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
|
|
||||||
|
|
||||||
return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void applyConfigurationToResources(Configuration config) {
|
public final void applyConfigurationToResources(Configuration config) {
|
||||||
synchronized (mResourcesManager) {
|
synchronized (mResourcesManager) {
|
||||||
mResourcesManager.applyConfigurationToResources(config, null);
|
mResourcesManager.applyConfigurationToResources(config, null);
|
||||||
@@ -6080,7 +6058,8 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
// display.
|
// display.
|
||||||
displayId = r.activity.getDisplayId();
|
displayId = r.activity.getDisplayId();
|
||||||
}
|
}
|
||||||
final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
|
final boolean movedToDifferentDisplay = isDifferentDisplay(
|
||||||
|
r.activity.getDisplayId(), displayId);
|
||||||
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
|
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
|
||||||
&& !movedToDifferentDisplay) {
|
&& !movedToDifferentDisplay) {
|
||||||
if (DEBUG_CONFIGURATION) {
|
if (DEBUG_CONFIGURATION) {
|
||||||
@@ -6116,14 +6095,6 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
mSomeActivitiesChanged = true;
|
mSomeActivitiesChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the display id of activity is different from the given one. Note that
|
|
||||||
* {@link Display#INVALID_DISPLAY} means no difference.
|
|
||||||
*/
|
|
||||||
private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
|
|
||||||
return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
|
|
||||||
}
|
|
||||||
|
|
||||||
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
|
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
|
||||||
if (start) {
|
if (start) {
|
||||||
try {
|
try {
|
||||||
@@ -6301,7 +6272,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
|
|
||||||
final void handleLowMemory() {
|
final void handleLowMemory() {
|
||||||
final ArrayList<ComponentCallbacks2> callbacks =
|
final ArrayList<ComponentCallbacks2> callbacks =
|
||||||
collectComponentCallbacks(true /* includeActivities */);
|
collectComponentCallbacks(true /* includeUiContexts */);
|
||||||
|
|
||||||
final int N = callbacks.size();
|
final int N = callbacks.size();
|
||||||
for (int i=0; i<N; i++) {
|
for (int i=0; i<N; i++) {
|
||||||
@@ -6334,7 +6305,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
final ArrayList<ComponentCallbacks2> callbacks =
|
final ArrayList<ComponentCallbacks2> callbacks =
|
||||||
collectComponentCallbacks(true /* includeActivities */);
|
collectComponentCallbacks(true /* includeUiContexts */);
|
||||||
|
|
||||||
final int N = callbacks.size();
|
final int N = callbacks.size();
|
||||||
for (int i = 0; i < N; i++) {
|
for (int i = 0; i < N; i++) {
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ interface ActivityThreadInternal {
|
|||||||
|
|
||||||
Application getApplication();
|
Application getApplication();
|
||||||
|
|
||||||
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities);
|
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,17 +17,16 @@
|
|||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
|
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
|
||||||
|
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.ComponentCallbacks2;
|
import android.content.ComponentCallbacks2;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.res.CompatibilityInfo;
|
import android.content.res.CompatibilityInfo;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.HardwareRenderer;
|
import android.graphics.HardwareRenderer;
|
||||||
import android.inputmethodservice.InputMethodService;
|
import android.inputmethodservice.InputMethodService;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -228,7 +227,7 @@ class ConfigurationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final ArrayList<ComponentCallbacks2> callbacks =
|
final ArrayList<ComponentCallbacks2> callbacks =
|
||||||
mActivityThread.collectComponentCallbacks(false /* includeActivities */);
|
mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
|
||||||
|
|
||||||
freeTextLayoutCachesIfNeeded(configDiff);
|
freeTextLayoutCachesIfNeeded(configDiff);
|
||||||
|
|
||||||
@@ -326,16 +325,4 @@ class ConfigurationController {
|
|||||||
return newConfig;
|
return newConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Ask test layout engine to free its caches if there is a locale change. */
|
|
||||||
static void freeTextLayoutCachesIfNeeded(int configDiff) {
|
|
||||||
if (configDiff != 0) {
|
|
||||||
boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
|
|
||||||
if (hasLocaleConfigChange) {
|
|
||||||
Canvas.freeTextLayoutCaches();
|
|
||||||
if (DEBUG_CONFIGURATION) {
|
|
||||||
Slog.v(TAG, "Cleared TextLayout Caches");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -730,7 +730,7 @@ public class ResourcesManager {
|
|||||||
* @return true if activity resources override config matches the provided one or they are both
|
* @return true if activity resources override config matches the provided one or they are both
|
||||||
* null, false otherwise.
|
* null, false otherwise.
|
||||||
*/
|
*/
|
||||||
boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
|
public boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
|
||||||
@Nullable Configuration overrideConfig) {
|
@Nullable Configuration overrideConfig) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
final ActivityResources activityResources
|
final ActivityResources activityResources
|
||||||
|
|||||||
132
core/java/android/window/ConfigurationHelper.java
Normal file
132
core/java/android/window/ConfigurationHelper.java
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import static android.view.Display.INVALID_DISPLAY;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.app.ResourcesManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class to maintain {@link android.content.res.Configuration} related methods used both
|
||||||
|
* in {@link android.app.Activity} and {@link WindowContext}.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class ConfigurationHelper {
|
||||||
|
private ConfigurationHelper() {}
|
||||||
|
|
||||||
|
/** Ask text layout engine to free its caches if there is a locale change. */
|
||||||
|
public static void freeTextLayoutCachesIfNeeded(int configDiff) {
|
||||||
|
if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
|
||||||
|
Canvas.freeTextLayoutCaches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper method to filter out {@link ActivityInfo#CONFIG_SCREEN_SIZE} if the
|
||||||
|
* {@link Configuration#diffPublicOnly(Configuration) diff} of two {@link Configuration}
|
||||||
|
* doesn't cross the boundary.
|
||||||
|
*
|
||||||
|
* @see SizeConfigurationBuckets#filterDiff(int, Configuration, Configuration,
|
||||||
|
* SizeConfigurationBuckets)
|
||||||
|
*/
|
||||||
|
public static int diffPublicWithSizeBuckets(@Nullable Configuration currentConfig,
|
||||||
|
@NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
|
||||||
|
// If current configuration is null, it is definitely different from updated Configuration.
|
||||||
|
if (currentConfig == null) {
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
int publicDiff = currentConfig.diffPublicOnly(newConfig);
|
||||||
|
return SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, newConfig, buckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the {@link android.content.res.Resources} associated with
|
||||||
|
* a {@code token} needs to be updated.
|
||||||
|
*
|
||||||
|
* @param token A {@link Context#getActivityToken() activity token} or
|
||||||
|
* {@link Context#getWindowContextToken() window context token}
|
||||||
|
* @param config The original {@link Configuration}
|
||||||
|
* @param newConfig The updated Configuration
|
||||||
|
* @param displayChanged a flag to indicate there's a display change
|
||||||
|
* @param configChanged a flag to indicate there's a Configuration change.
|
||||||
|
*
|
||||||
|
* @see ResourcesManager#updateResourcesForActivity(IBinder, Configuration, int)
|
||||||
|
*/
|
||||||
|
public static boolean shouldUpdateResources(IBinder token, @Nullable Configuration config,
|
||||||
|
@NonNull Configuration newConfig, @NonNull Configuration overrideConfig,
|
||||||
|
boolean displayChanged, @Nullable Boolean configChanged) {
|
||||||
|
// The configuration has not yet been initialized. We should update it.
|
||||||
|
if (config == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If the token associated context is moved to another display, we should update the
|
||||||
|
// ResourcesKey.
|
||||||
|
if (displayChanged) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If the new config is the same as the config this Activity is already running with and
|
||||||
|
// the override config also didn't change, then don't update the Resources
|
||||||
|
if (!ResourcesManager.getInstance().isSameResourcesOverrideConfig(token, overrideConfig)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If there's a update on WindowConfiguration#mBounds or maxBounds, we should update the
|
||||||
|
// Resources to make WindowMetrics API report the updated result.
|
||||||
|
if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if {@code displayId} is different from {@code newDisplayId}.
|
||||||
|
* Note that {@link Display#INVALID_DISPLAY} means no difference.
|
||||||
|
*/
|
||||||
|
public static boolean isDifferentDisplay(int displayId, int newDisplayId) {
|
||||||
|
return newDisplayId != INVALID_DISPLAY && displayId != newDisplayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
|
||||||
|
// constructions.
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
|
||||||
|
* should be updated.
|
||||||
|
*
|
||||||
|
* @see WindowManager#getCurrentWindowMetrics()
|
||||||
|
* @see WindowManager#getMaximumWindowMetrics()
|
||||||
|
*/
|
||||||
|
private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
|
||||||
|
@NonNull Configuration newConfig) {
|
||||||
|
final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
|
||||||
|
final Rect newBounds = newConfig.windowConfiguration.getBounds();
|
||||||
|
|
||||||
|
final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
|
||||||
|
final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
|
||||||
|
|
||||||
|
return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package android.window;
|
package android.window;
|
||||||
|
|
||||||
|
import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
|
||||||
|
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
|
||||||
|
import static android.window.ConfigurationHelper.isDifferentDisplay;
|
||||||
|
import static android.window.ConfigurationHelper.shouldUpdateResources;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.app.ActivityThread;
|
import android.app.ActivityThread;
|
||||||
import android.app.IWindowToken;
|
import android.app.IWindowToken;
|
||||||
@@ -33,7 +38,7 @@ import java.lang.ref.WeakReference;
|
|||||||
* {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
|
* {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
|
||||||
*
|
*
|
||||||
* @see WindowContext
|
* @see WindowContext
|
||||||
* @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle)
|
* @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
|
||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -46,11 +51,13 @@ public class WindowTokenClient extends IWindowToken.Stub {
|
|||||||
|
|
||||||
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
|
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
|
||||||
|
|
||||||
|
private final Configuration mConfiguration = new Configuration();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
|
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
|
||||||
* can only attach one {@link Context}.
|
* can only attach one {@link Context}.
|
||||||
* <p>This method must be called before invoking
|
* <p>This method must be called before invoking
|
||||||
* {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
|
* {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
|
||||||
* Bundle, boolean)}.<p/>
|
* Bundle, boolean)}.<p/>
|
||||||
*
|
*
|
||||||
* @param context context to be attached
|
* @param context context to be attached
|
||||||
@@ -61,6 +68,7 @@ public class WindowTokenClient extends IWindowToken.Stub {
|
|||||||
throw new IllegalStateException("Context is already attached.");
|
throw new IllegalStateException("Context is already attached.");
|
||||||
}
|
}
|
||||||
mContextRef = new WeakReference<>(context);
|
mContextRef = new WeakReference<>(context);
|
||||||
|
mConfiguration.setTo(context.getResources().getConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,17 +77,34 @@ public class WindowTokenClient extends IWindowToken.Stub {
|
|||||||
if (context == null) {
|
if (context == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int currentDisplayId = context.getDisplayId();
|
final boolean displayChanged = isDifferentDisplay(context.getDisplayId(), newDisplayId);
|
||||||
final boolean displayChanged = newDisplayId != currentDisplayId;
|
final boolean shouldUpdateResources = shouldUpdateResources(this, mConfiguration,
|
||||||
final Configuration config = context.getResources().getConfiguration();
|
newConfig, newConfig /* overrideConfig */, displayChanged,
|
||||||
final boolean configChanged = config.diff(newConfig) != 0;
|
null /* configChanged */);
|
||||||
if (displayChanged || configChanged) {
|
|
||||||
|
if (shouldUpdateResources) {
|
||||||
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
|
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
|
||||||
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
|
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
|
||||||
|
|
||||||
if (context instanceof WindowContext) {
|
if (context instanceof WindowContext) {
|
||||||
ActivityThread.currentActivityThread().getHandler().post(
|
ActivityThread.currentActivityThread().getHandler().post(
|
||||||
() -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
|
() -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch onConfigurationChanged only if there's a significant public change to
|
||||||
|
// make it compatible with the original behavior.
|
||||||
|
final Configuration[] sizeConfigurations = context.getResources()
|
||||||
|
.getSizeConfigurations();
|
||||||
|
final SizeConfigurationBuckets buckets = sizeConfigurations != null
|
||||||
|
? new SizeConfigurationBuckets(sizeConfigurations) : null;
|
||||||
|
final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
|
||||||
|
|
||||||
|
if (context instanceof WindowProviderService && diff != 0) {
|
||||||
|
ActivityThread.currentActivityThread().getHandler().post(() ->
|
||||||
|
((WindowProviderService) context).onConfigurationChanged(newConfig));
|
||||||
|
}
|
||||||
|
freeTextLayoutCachesIfNeeded(diff);
|
||||||
|
mConfiguration.setTo(newConfig);
|
||||||
}
|
}
|
||||||
if (displayChanged) {
|
if (displayChanged) {
|
||||||
context.updateDisplay(newDisplayId);
|
context.updateDisplay(newDisplayId);
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||||
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.app.ResourcesManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.platform.test.annotations.Presubmit;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoSession;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ConfigurationHelper}
|
||||||
|
*
|
||||||
|
* <p>Build/Install/Run:
|
||||||
|
* atest FrameworksMockingCoreTests:ConfigurationHelperTest
|
||||||
|
*
|
||||||
|
* <p>This test class is a part of Window Manager Service tests and specified in
|
||||||
|
* {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
@Presubmit
|
||||||
|
public class ConfigurationHelperTest {
|
||||||
|
MockitoSession mMockitoSession;
|
||||||
|
ResourcesManager mResourcesManager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mMockitoSession = mockitoSession()
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.spyStatic(ResourcesManager.class)
|
||||||
|
.startMocking();
|
||||||
|
doReturn(mock(ResourcesManager.class)).when(ResourcesManager::getInstance);
|
||||||
|
mResourcesManager = ResourcesManager.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
mMockitoSession.finishMocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_NullConfig_ReturnsTrue() {
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), null /* config */,
|
||||||
|
new Configuration(), new Configuration(), false /* displayChanged */,
|
||||||
|
null /* configChanged */)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_DisplayChanged_ReturnsTrue() {
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
|
||||||
|
new Configuration(), new Configuration(), true /* displayChanged */,
|
||||||
|
null /* configChanged */)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_DifferentResources_ReturnsTrue() {
|
||||||
|
doReturn(false).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
|
||||||
|
new Configuration(), new Configuration(), false /* displayChanged */,
|
||||||
|
null /* configChanged */)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_DifferentBounds_ReturnsTrue() {
|
||||||
|
doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
final Configuration config = new Configuration();
|
||||||
|
config.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
|
||||||
|
config.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
|
||||||
|
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.windowConfiguration.setBounds(new Rect(0, 0, 20, 20));
|
||||||
|
newConfig.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
|
||||||
|
new Configuration(), false /* displayChanged */, null /* configChanged */))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_SameConfig_ReturnsFalse() {
|
||||||
|
doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
final Configuration config = new Configuration();
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
|
||||||
|
new Configuration(), false /* displayChanged */, null /* configChanged */))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_DifferentConfig_ReturnsTrue() {
|
||||||
|
doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
final Configuration config = new Configuration();
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.setToDefaults();
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
|
||||||
|
new Configuration(), false /* displayChanged */, null /* configChanged */))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_DifferentNonPublicConfig_ReturnsTrue() {
|
||||||
|
doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
final Configuration config = new Configuration();
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
newConfig.windowConfiguration.setAppBounds(new Rect(0, 0, 10, 10));
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
|
||||||
|
new Configuration(), false /* displayChanged */, null /* configChanged */))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldUpdateResources_OverrideConfigChanged_ReturnsFalse() {
|
||||||
|
doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
|
||||||
|
|
||||||
|
final Configuration config = new Configuration();
|
||||||
|
final Configuration newConfig = new Configuration();
|
||||||
|
final boolean configChanged = true;
|
||||||
|
|
||||||
|
assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
|
||||||
|
new Configuration(), false /* displayChanged */, configChanged))
|
||||||
|
.isEqualTo(configChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ public final class FrameworksTestsFilter extends SelectTest {
|
|||||||
// Test specifications for FrameworksMockingCoreTests.
|
// Test specifications for FrameworksMockingCoreTests.
|
||||||
"android.app.activity.ActivityThreadClientTest",
|
"android.app.activity.ActivityThreadClientTest",
|
||||||
"android.view.DisplayTest",
|
"android.view.DisplayTest",
|
||||||
|
"android.window.ConfigurationHelperTest",
|
||||||
// Test specifications for FrameworksCoreTests.
|
// Test specifications for FrameworksCoreTests.
|
||||||
"android.app.servertransaction.", // all tests under the package.
|
"android.app.servertransaction.", // all tests under the package.
|
||||||
"android.view.CutoutSpecificationTest",
|
"android.view.CutoutSpecificationTest",
|
||||||
@@ -59,10 +60,8 @@ public final class FrameworksTestsFilter extends SelectTest {
|
|||||||
"android.view.RoundedCornersTest",
|
"android.view.RoundedCornersTest",
|
||||||
"android.view.WindowMetricsTest",
|
"android.view.WindowMetricsTest",
|
||||||
"android.view.PendingInsetsControllerTest",
|
"android.view.PendingInsetsControllerTest",
|
||||||
"android.window.WindowContextTest",
|
"android.window.", // all tests under the package.
|
||||||
"android.window.WindowMetricsHelperTest",
|
|
||||||
"android.app.activity.ActivityThreadTest",
|
"android.app.activity.ActivityThreadTest",
|
||||||
"android.window.WindowContextControllerTest"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public FrameworksTestsFilter(Bundle testArgs) {
|
public FrameworksTestsFilter(Bundle testArgs) {
|
||||||
|
|||||||
Reference in New Issue
Block a user