Merge "Handle WindowProviderService#onConfigurationChanged" into sc-v2-dev am: 41fc06529b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14550549

Change-Id: I9d6656b2f4bd55a1d45bb1c3fba7eca29d44865c
This commit is contained in:
Charles Chen
2021-06-18 06:47:48 +00:00
committed by Automerger Merge Worker
8 changed files with 369 additions and 91 deletions

View File

@@ -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. final boolean shouldUpdateResources = hasPublicConfigChange
int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); || shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig,
final ActivityClientRecord cr = getActivityClient(activityToken); amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange);
diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig, final boolean shouldReportChange = hasPublicConfigChange
cr != null ? cr.mSizeConfigurations : null);
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++) {

View File

@@ -38,5 +38,5 @@ interface ActivityThreadInternal {
Application getApplication(); Application getApplication();
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities); ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
} }

View File

@@ -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");
}
}
}
}
} }

View File

@@ -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

View 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);
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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) {