Enable Window Manager to store app-specific locales and apply them. Test: atest WmTests:ActivityTaskManagerServiceTests Bug: 194095029 Bug: 194369257 Bug: 194369420 Bug: 194369426 Bug: 194370141 Change-Id: I743a9cfb84f4b054caa03413f39671d3f43a81c4
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 5174a38..0ba77d8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; +import android.os.LocaleList; import android.os.RemoteException; import android.service.voice.IVoiceInteractionSession; import android.util.IntArray; @@ -611,6 +612,14 @@ PackageConfigurationUpdater setNightMode(int nightMode); /** + * Sets the app-specific locales for the application referenced by this updater. + * This setting is persisted and will overlay on top of the system locales for + * the said application. + * @return the current {@link PackageConfigurationUpdater} updated with the provided locale. + */ + PackageConfigurationUpdater setLocales(LocaleList locales); + + /** * Commit changes. */ void commit();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 081c618..ddd184e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -947,7 +947,7 @@ setRecentTasks(new RecentTasks(this, mTaskSupervisor)); mVrController = new VrController(mGlobalLock); mKeyguardController = mTaskSupervisor.getKeyguardController(); - mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue); + mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue, this); } public void onActivityManagerInternalAdded() { @@ -6519,7 +6519,8 @@ final class PackageConfigurationUpdaterImpl implements ActivityTaskManagerInternal.PackageConfigurationUpdater { private final int mPid; - private int mNightMode; + private Integer mNightMode; + private LocaleList mLocales; PackageConfigurationUpdaterImpl(int pid) { mPid = pid; @@ -6532,6 +6533,13 @@ } @Override + public ActivityTaskManagerInternal.PackageConfigurationUpdater + setLocales(LocaleList locales) { + mLocales = locales; + return this; + } + + @Override public void commit() { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); @@ -6541,8 +6549,10 @@ Slog.w(TAG, "Override application configuration: cannot find pid " + mPid); return; } - wpc.setOverrideNightMode(mNightMode); - wpc.updateNightModeForAllActivities(mNightMode); + LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists( + mLocales, getGlobalConfiguration().getLocales()); + wpc.applyAppSpecificConfig(mNightMode, localesOverride); + wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride); mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this); } finally { Binder.restoreCallingIdentity(ident); @@ -6550,8 +6560,12 @@ } } - int getNightMode() { + Integer getNightMode() { return mNightMode; } + + LocaleList getLocales() { + return mLocales; + } } }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 6fafc02..eeb85c5 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -39,6 +39,7 @@ import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.os.LocaleList; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -512,7 +513,7 @@ return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM; } - /** Returns the activity type associated with the the configuration container. */ + /** Returns the activity type associated with the configuration container. */ /*@WindowConfiguration.ActivityType*/ public int getActivityType() { return mFullConfiguration.windowConfiguration.getActivityType(); @@ -546,20 +547,48 @@ } /** + * Applies app-specific nightMode and {@link LocaleList} on requested configuration. + * @return true if any of the requested configuration has been updated. + */ + public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales) { + mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); + boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig, + nightMode); + boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig, + locales); + if (newNightModeSet || newLocalesSet) { + onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); + } + return newNightModeSet || newLocalesSet; + } + + /** * Overrides the night mode applied to this ConfigurationContainer. * @return true if the nightMode has been changed. */ - public boolean setOverrideNightMode(int nightMode) { + private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) { final int currentUiMode = mRequestedOverrideConfiguration.uiMode; final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK; final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK; if (currentNightMode == validNightMode) { return false; } - mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); - mRequestsTmpConfig.uiMode = validNightMode + requestsTmpConfig.uiMode = validNightMode | (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK); - onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); + return true; + } + + /** + * Overrides the locales applied to this ConfigurationContainer. + * @return true if the LocaleList has been changed. + */ + private boolean setOverrideLocales(Configuration requestsTmpConfig, + @NonNull LocaleList overrideLocales) { + if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) { + return false; + } + requestsTmpConfig.setLocales(overrideLocales); + requestsTmpConfig.userSetLocale = true; return true; }
diff --git a/services/core/java/com/android/server/wm/LocaleOverlayHelper.java b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java new file mode 100644 index 0000000..a1a01db --- /dev/null +++ b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java
@@ -0,0 +1,62 @@ +/* + * 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 com.android.server.wm; + +import android.os.LocaleList; + +import java.util.Locale; + +/** + * Static utilities to overlay locales on top of another localeList. + * + * <p>This is used to overlay application-specific locales in + * {@link com.android.server.wm.ActivityTaskManagerInternal.PackageConfigurationUpdater} on top of + * system locales. + */ +final class LocaleOverlayHelper { + + /** + * Combines the overlay locales and base locales. + * @return the combined {@link LocaleList} if the overlay locales is not empty/null else + * returns the empty/null LocaleList. + */ + static LocaleList combineLocalesIfOverlayExists(LocaleList overlayLocales, + LocaleList baseLocales) { + if (overlayLocales == null || overlayLocales.isEmpty()) { + return overlayLocales; + } + return combineLocales(overlayLocales, baseLocales); + } + + /** + * Creates a combined {@link LocaleList} by placing overlay locales before base locales and + * dropping duplicates from the base locales. + */ + private static LocaleList combineLocales(LocaleList overlayLocales, LocaleList baseLocales) { + Locale[] combinedLocales = new Locale[overlayLocales.size() + baseLocales.size()]; + for (int i = 0; i < overlayLocales.size(); i++) { + combinedLocales[i] = overlayLocales.get(i); + } + for (int i = 0; i < baseLocales.size(); i++) { + combinedLocales[i + overlayLocales.size()] = baseLocales.get(i); + } + // Constructor of {@link LocaleList} removes duplicates + return new LocaleList(combinedLocales); + } + + +}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java index 1552a96..505c4beb 100644 --- a/services/core/java/com/android/server/wm/PackageConfigPersister.java +++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.os.Environment; +import android.os.LocaleList; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; @@ -54,12 +55,14 @@ private static final String TAG_CONFIG = "config"; private static final String ATTR_PACKAGE_NAME = "package_name"; private static final String ATTR_NIGHT_MODE = "night_mode"; + private static final String ATTR_LOCALES = "locale_list"; private static final String PACKAGE_DIRNAME = "package_configs"; private static final String SUFFIX_FILE_NAME = "_config.xml"; private final PersisterQueue mPersisterQueue; private final Object mLock = new Object(); + private final ActivityTaskManagerService mAtm; @GuardedBy("mLock") private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite = @@ -72,8 +75,9 @@ return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME); } - PackageConfigPersister(PersisterQueue queue) { + PackageConfigPersister(PersisterQueue queue, ActivityTaskManagerService atm) { mPersisterQueue = queue; + mAtm = atm; } @GuardedBy("mLock") @@ -100,7 +104,8 @@ final TypedXmlPullParser in = Xml.resolvePullParser(is); int event; String packageName = null; - int nightMode = MODE_NIGHT_AUTO; + Integer nightMode = null; + LocaleList locales = null; while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && event != XmlPullParser.END_TAG) { final String name = in.getName(); @@ -120,6 +125,9 @@ case ATTR_NIGHT_MODE: nightMode = Integer.parseInt(attrValue); break; + case ATTR_LOCALES: + locales = LocaleList.forLanguageTags(attrValue); + break; } } } @@ -130,6 +138,7 @@ final PackageConfigRecord initRecord = findRecordOrCreate(mModified, packageName, userId); initRecord.mNightMode = nightMode; + initRecord.mLocales = locales; if (DEBUG) { Slog.d(TAG, "loadPackages: load one package " + initRecord); } @@ -155,7 +164,9 @@ "updateConfigIfNeeded record " + container + " find? " + modifiedRecord); } if (modifiedRecord != null) { - container.setOverrideNightMode(modifiedRecord.mNightMode); + container.applyAppSpecificConfig(modifiedRecord.mNightMode, + LocaleOverlayHelper.combineLocalesIfOverlayExists( + modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales())); } } } @@ -165,10 +176,16 @@ ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) { synchronized (mLock) { PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId); - record.mNightMode = impl.getNightMode(); - - if (record.isResetNightMode()) { - removePackage(record.mName, record.mUserId); + if (impl.getNightMode() != null) { + record.mNightMode = impl.getNightMode(); + } + if (impl.getLocales() != null) { + record.mLocales = impl.getLocales(); + } + if ((record.mNightMode == null || record.isResetNightMode()) + && (record.mLocales == null || record.mLocales.isEmpty())) { + // if all values default to system settings, we can remove the package. + removePackage(packageName, userId); } else { final PackageConfigRecord pendingRecord = findRecord(mPendingWrite, record.mName, record.mUserId); @@ -179,10 +196,11 @@ } else { writeRecord = pendingRecord; } - if (writeRecord.mNightMode == record.mNightMode) { + + if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) { return; } - writeRecord.mNightMode = record.mNightMode; + if (DEBUG) { Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord); } @@ -191,6 +209,22 @@ } } + private boolean updateNightMode(PackageConfigRecord record, PackageConfigRecord writeRecord) { + if (record.mNightMode == null || record.mNightMode.equals(writeRecord.mNightMode)) { + return false; + } + writeRecord.mNightMode = record.mNightMode; + return true; + } + + private boolean updateLocales(PackageConfigRecord record, PackageConfigRecord writeRecord) { + if (record.mLocales == null || record.mLocales.equals(writeRecord.mLocales)) { + return false; + } + writeRecord.mLocales = record.mLocales; + return true; + } + @GuardedBy("mLock") void removeUser(int userId) { synchronized (mLock) { @@ -210,7 +244,7 @@ @GuardedBy("mLock") void onPackageUninstall(String packageName) { synchronized (mLock) { - for (int i = mModified.size() - 1; i > 0; i--) { + for (int i = mModified.size() - 1; i >= 0; i--) { final int userId = mModified.keyAt(i); removePackage(packageName, userId); } @@ -242,7 +276,8 @@ static class PackageConfigRecord { final String mName; final int mUserId; - int mNightMode; + Integer mNightMode; + LocaleList mLocales; PackageConfigRecord(String name, int userId) { mName = name; @@ -256,7 +291,7 @@ @Override public String toString() { return "PackageConfigRecord package name: " + mName + " userId " + mUserId - + " nightMode " + mNightMode; + + " nightMode " + mNightMode + " locales " + mLocales; } } @@ -369,7 +404,13 @@ } xmlSerializer.startTag(null, TAG_CONFIG); xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName); - xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode); + if (mRecord.mNightMode != null) { + xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode); + } + if (mRecord.mLocales != null) { + xmlSerializer.attribute(null, ATTR_LOCALES, mRecord.mLocales + .toLanguageTags()); + } xmlSerializer.endTag(null, TAG_CONFIG); xmlSerializer.endDocument(); xmlSerializer.flush();
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index cd29f0e..6eb2e8a 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -57,6 +57,7 @@ import android.os.Binder; import android.os.Build; import android.os.IBinder; +import android.os.LocaleList; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -817,10 +818,13 @@ return false; } - void updateNightModeForAllActivities(int nightMode) { + // TODO(b/199277065): Re-assess how app-specific locales are applied based on UXR + // TODO(b/199277729): Consider whether we need to add special casing for edge cases like + // activity-embeddings etc. + void updateAppSpecificSettingsForAllActivities(Integer nightMode, LocaleList localesOverride) { for (int i = mActivities.size() - 1; i >= 0; --i) { final ActivityRecord r = mActivities.get(i); - if (r.setOverrideNightMode(nightMode) && r.mVisibleRequested) { + if (r.applyAppSpecificConfig(nightMode, localesOverride) && r.mVisibleRequested) { r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */); } }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index b95d56b..509b460 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -43,13 +43,17 @@ import android.app.Activity; import android.app.ActivityManager; +import android.app.IApplicationThread; import android.app.PictureInPictureParams; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.EnterPipRequestedItem; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Binder; import android.os.IBinder; +import android.os.LocaleList; import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -61,6 +65,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.mockito.MockitoSession; import java.util.ArrayList; @@ -80,6 +85,9 @@ private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor = ArgumentCaptor.forClass(ClientTransaction.class); + private static final String DEFAULT_PACKAGE_NAME = "my.application.package"; + private static final int DEFAULT_USER_ID = 100; + @Before public void setUp() throws Exception { setBooted(mAtm); @@ -480,5 +488,269 @@ assertTrue(activity.supportsMultiWindow()); assertTrue(task.supportsMultiWindow()); } + + @Test + public void testPackageConfigUpdate_locales_successfullyApplied() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")).commit(); + + WindowProcessController wpcAfterConfigChange = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange.getConfiguration().isNightModeActive()); + } + + @Test + public void testPackageConfigUpdate_nightMode_successfullyApplied() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertTrue(wpcAfterConfigChange.getConfiguration().isNightModeActive()); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpcAfterConfigChange.getConfiguration().getLocales()); + } + + @Test + public void testPackageConfigUpdate_multipleLocaleUpdates_successfullyApplied() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + WindowProcessController wpc = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), wpc); + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpc.getConfiguration().getLocales()); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("ja-XC,en-XC")).commit(); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + + assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"), + wpc.getConfiguration().getLocales()); + } + + @Test + public void testPackageConfigUpdate_multipleNightModeUpdates_successfullyApplied() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + + packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_NO).commit(); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + } + + @Test + public void testPackageConfigUpdate_onPackageUninstall_configShouldNotApply() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + + mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + } + + @Test + public void testPackageConfigUpdate_LocalesEmptyAndNightModeUndefined_configShouldNotApply() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + WindowProcessController wpc = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), wpc); + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpc.getConfiguration().getLocales()); + + packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()) + .setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit(); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpc.getConfiguration().getLocales()); + } + + @Test + public void testPackageConfigUpdate_WhenUserRemoved_configShouldNotApply() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + + mAtm.mInternal.removeUser(DEFAULT_USER_ID); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + } + + @Test + public void testPackageConfigUpdate_setLocaleListToEmpty_doesNotOverlayLocaleListInWpc() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + + packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()).commit(); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + } + + @Test + public void testPackageConfigUpdate_resetNightMode_doesNotOverrideNightModeInWpc() { + Configuration config = mAtm.getGlobalConfiguration(); + config.setLocales(LocaleList.forLanguageTags("en-XC")); + mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID); + mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID)); + + ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater = + mAtm.mInternal.createPackageConfigurationUpdater(); + + packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")) + .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit(); + + WindowProcessController wpcAfterConfigChange1 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange1.getConfiguration().getLocales()); + assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive()); + + packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit(); + + WindowProcessController wpcAfterConfigChange2 = createWindowProcessController( + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID); + assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"), + wpcAfterConfigChange2.getConfiguration().getLocales()); + assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive()); + } + + private WindowProcessController createWindowProcessController(String packageName, + int userId) { + WindowProcessListener mMockListener = Mockito.mock(WindowProcessListener.class); + ApplicationInfo info = mock(ApplicationInfo.class); + info.packageName = packageName; + WindowProcessController wpc = new WindowProcessController( + mAtm, info, packageName, 0, userId, null, mMockListener); + wpc.setThread(mock(IApplicationThread.class)); + return wpc; + } + } + +
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index d3f2d14..c56b614 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -49,6 +50,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.LocaleList; import android.platform.test.annotations.Presubmit; import org.junit.Before; @@ -371,8 +373,9 @@ public void testTopActivityUiModeChangeScheduleConfigChange() { final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; - doReturn(true).when(activity).setOverrideNightMode(anyInt()); - mWpc.updateNightModeForAllActivities(Configuration.UI_MODE_NIGHT_YES); + doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any()); + mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES, + LocaleList.forLanguageTags("en-XA")); verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean()); }