Merge "Enable Window Manager to store app-specific locales and apply them." into sc-v2-dev

This commit is contained in:
Riya Ghai
2021-09-14 09:34:00 +00:00
committed by Android (Google) Code Review
8 changed files with 461 additions and 27 deletions

View File

@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo; import android.content.res.CompatibilityInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException; import android.os.RemoteException;
import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSession;
import android.util.IntArray; import android.util.IntArray;
@@ -610,6 +611,14 @@ public abstract class ActivityTaskManagerInternal {
*/ */
PackageConfigurationUpdater setNightMode(int nightMode); 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. * Commit changes.
*/ */

View File

@@ -956,7 +956,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
setRecentTasks(new RecentTasks(this, mTaskSupervisor)); setRecentTasks(new RecentTasks(this, mTaskSupervisor));
mVrController = new VrController(mGlobalLock); mVrController = new VrController(mGlobalLock);
mKeyguardController = mTaskSupervisor.getKeyguardController(); mKeyguardController = mTaskSupervisor.getKeyguardController();
mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue); mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue, this);
} }
public void onActivityManagerInternalAdded() { public void onActivityManagerInternalAdded() {
@@ -6575,7 +6575,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final class PackageConfigurationUpdaterImpl implements final class PackageConfigurationUpdaterImpl implements
ActivityTaskManagerInternal.PackageConfigurationUpdater { ActivityTaskManagerInternal.PackageConfigurationUpdater {
private final int mPid; private final int mPid;
private int mNightMode; private Integer mNightMode;
private LocaleList mLocales;
PackageConfigurationUpdaterImpl(int pid) { PackageConfigurationUpdaterImpl(int pid) {
mPid = pid; mPid = pid;
@@ -6587,6 +6588,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return this; return this;
} }
@Override
public ActivityTaskManagerInternal.PackageConfigurationUpdater
setLocales(LocaleList locales) {
mLocales = locales;
return this;
}
@Override @Override
public void commit() { public void commit() {
synchronized (mGlobalLock) { synchronized (mGlobalLock) {
@@ -6597,8 +6605,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Slog.w(TAG, "Override application configuration: cannot find pid " + mPid); Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
return; return;
} }
wpc.setOverrideNightMode(mNightMode); LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
wpc.updateNightModeForAllActivities(mNightMode); mLocales, getGlobalConfiguration().getLocales());
wpc.applyAppSpecificConfig(mNightMode, localesOverride);
wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this); mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
@@ -6606,8 +6616,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
} }
} }
int getNightMode() { Integer getNightMode() {
return mNightMode; return mNightMode;
} }
LocaleList getLocales() {
return mLocales;
}
} }
} }

View File

@@ -39,6 +39,7 @@ import android.app.WindowConfiguration;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.LocaleList;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -512,7 +513,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM; 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*/ /*@WindowConfiguration.ActivityType*/
public int getActivityType() { public int getActivityType() {
return mFullConfiguration.windowConfiguration.getActivityType(); return mFullConfiguration.windowConfiguration.getActivityType();
@@ -545,21 +546,49 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return getActivityType() == ACTIVITY_TYPE_ASSISTANT; return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
} }
/**
* 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. * Overrides the night mode applied to this ConfigurationContainer.
* @return true if the nightMode has been changed. * @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 currentUiMode = mRequestedOverrideConfiguration.uiMode;
final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK; final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK; final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentNightMode == validNightMode) { if (currentNightMode == validNightMode) {
return false; return false;
} }
mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); requestsTmpConfig.uiMode = validNightMode
mRequestsTmpConfig.uiMode = validNightMode
| (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK); | (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; return true;
} }

View File

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

View File

@@ -21,6 +21,7 @@ import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.os.Environment; import android.os.Environment;
import android.os.LocaleList;
import android.util.AtomicFile; import android.util.AtomicFile;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
@@ -54,12 +55,14 @@ public class PackageConfigPersister {
private static final String TAG_CONFIG = "config"; private static final String TAG_CONFIG = "config";
private static final String ATTR_PACKAGE_NAME = "package_name"; private static final String ATTR_PACKAGE_NAME = "package_name";
private static final String ATTR_NIGHT_MODE = "night_mode"; 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 PACKAGE_DIRNAME = "package_configs";
private static final String SUFFIX_FILE_NAME = "_config.xml"; private static final String SUFFIX_FILE_NAME = "_config.xml";
private final PersisterQueue mPersisterQueue; private final PersisterQueue mPersisterQueue;
private final Object mLock = new Object(); private final Object mLock = new Object();
private final ActivityTaskManagerService mAtm;
@GuardedBy("mLock") @GuardedBy("mLock")
private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite = private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite =
@@ -72,8 +75,9 @@ public class PackageConfigPersister {
return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME); return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME);
} }
PackageConfigPersister(PersisterQueue queue) { PackageConfigPersister(PersisterQueue queue, ActivityTaskManagerService atm) {
mPersisterQueue = queue; mPersisterQueue = queue;
mAtm = atm;
} }
@GuardedBy("mLock") @GuardedBy("mLock")
@@ -100,7 +104,8 @@ public class PackageConfigPersister {
final TypedXmlPullParser in = Xml.resolvePullParser(is); final TypedXmlPullParser in = Xml.resolvePullParser(is);
int event; int event;
String packageName = null; String packageName = null;
int nightMode = MODE_NIGHT_AUTO; Integer nightMode = null;
LocaleList locales = null;
while (((event = in.next()) != XmlPullParser.END_DOCUMENT) while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
&& event != XmlPullParser.END_TAG) { && event != XmlPullParser.END_TAG) {
final String name = in.getName(); final String name = in.getName();
@@ -120,6 +125,9 @@ public class PackageConfigPersister {
case ATTR_NIGHT_MODE: case ATTR_NIGHT_MODE:
nightMode = Integer.parseInt(attrValue); nightMode = Integer.parseInt(attrValue);
break; break;
case ATTR_LOCALES:
locales = LocaleList.forLanguageTags(attrValue);
break;
} }
} }
} }
@@ -130,6 +138,7 @@ public class PackageConfigPersister {
final PackageConfigRecord initRecord = final PackageConfigRecord initRecord =
findRecordOrCreate(mModified, packageName, userId); findRecordOrCreate(mModified, packageName, userId);
initRecord.mNightMode = nightMode; initRecord.mNightMode = nightMode;
initRecord.mLocales = locales;
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "loadPackages: load one package " + initRecord); Slog.d(TAG, "loadPackages: load one package " + initRecord);
} }
@@ -155,7 +164,9 @@ public class PackageConfigPersister {
"updateConfigIfNeeded record " + container + " find? " + modifiedRecord); "updateConfigIfNeeded record " + container + " find? " + modifiedRecord);
} }
if (modifiedRecord != null) { if (modifiedRecord != null) {
container.setOverrideNightMode(modifiedRecord.mNightMode); container.applyAppSpecificConfig(modifiedRecord.mNightMode,
LocaleOverlayHelper.combineLocalesIfOverlayExists(
modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales()));
} }
} }
} }
@@ -165,10 +176,16 @@ public class PackageConfigPersister {
ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) { ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) {
synchronized (mLock) { synchronized (mLock) {
PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId); PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
if (impl.getNightMode() != null) {
record.mNightMode = impl.getNightMode(); record.mNightMode = impl.getNightMode();
}
if (record.isResetNightMode()) { if (impl.getLocales() != null) {
removePackage(record.mName, record.mUserId); 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 { } else {
final PackageConfigRecord pendingRecord = final PackageConfigRecord pendingRecord =
findRecord(mPendingWrite, record.mName, record.mUserId); findRecord(mPendingWrite, record.mName, record.mUserId);
@@ -179,10 +196,11 @@ public class PackageConfigPersister {
} else { } else {
writeRecord = pendingRecord; writeRecord = pendingRecord;
} }
if (writeRecord.mNightMode == record.mNightMode) {
if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
return; return;
} }
writeRecord.mNightMode = record.mNightMode;
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord); Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
} }
@@ -191,6 +209,22 @@ public class PackageConfigPersister {
} }
} }
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") @GuardedBy("mLock")
void removeUser(int userId) { void removeUser(int userId) {
synchronized (mLock) { synchronized (mLock) {
@@ -210,7 +244,7 @@ public class PackageConfigPersister {
@GuardedBy("mLock") @GuardedBy("mLock")
void onPackageUninstall(String packageName) { void onPackageUninstall(String packageName) {
synchronized (mLock) { 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); final int userId = mModified.keyAt(i);
removePackage(packageName, userId); removePackage(packageName, userId);
} }
@@ -242,7 +276,8 @@ public class PackageConfigPersister {
static class PackageConfigRecord { static class PackageConfigRecord {
final String mName; final String mName;
final int mUserId; final int mUserId;
int mNightMode; Integer mNightMode;
LocaleList mLocales;
PackageConfigRecord(String name, int userId) { PackageConfigRecord(String name, int userId) {
mName = name; mName = name;
@@ -256,7 +291,7 @@ public class PackageConfigPersister {
@Override @Override
public String toString() { public String toString() {
return "PackageConfigRecord package name: " + mName + " userId " + mUserId return "PackageConfigRecord package name: " + mName + " userId " + mUserId
+ " nightMode " + mNightMode; + " nightMode " + mNightMode + " locales " + mLocales;
} }
} }
@@ -369,7 +404,13 @@ public class PackageConfigPersister {
} }
xmlSerializer.startTag(null, TAG_CONFIG); xmlSerializer.startTag(null, TAG_CONFIG);
xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName); xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName);
if (mRecord.mNightMode != null) {
xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode); 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.endTag(null, TAG_CONFIG);
xmlSerializer.endDocument(); xmlSerializer.endDocument();
xmlSerializer.flush(); xmlSerializer.flush();

View File

@@ -57,6 +57,7 @@ import android.content.res.Configuration;
import android.os.Binder; import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.LocaleList;
import android.os.Message; import android.os.Message;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
@@ -817,10 +818,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return false; 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) { for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(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 */); r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
} }
} }

View File

@@ -43,13 +43,17 @@ import static org.mockito.Mockito.when;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams; import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem; import android.app.servertransaction.EnterPipRequestedItem;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.LocaleList;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
@@ -61,6 +65,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.MockitoSession; import org.mockito.MockitoSession;
import java.util.ArrayList; import java.util.ArrayList;
@@ -80,6 +85,9 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor = private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
ArgumentCaptor.forClass(ClientTransaction.class); ArgumentCaptor.forClass(ClientTransaction.class);
private static final String DEFAULT_PACKAGE_NAME = "my.application.package";
private static final int DEFAULT_USER_ID = 100;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
setBooted(mAtm); setBooted(mAtm);
@@ -489,5 +497,269 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
assertTrue(activity.supportsMultiWindow()); assertTrue(activity.supportsMultiWindow());
assertTrue(task.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;
}
} }

View File

@@ -34,6 +34,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@@ -49,6 +50,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import org.junit.Before; import org.junit.Before;
@@ -371,8 +373,9 @@ public class WindowProcessControllerTests extends WindowTestsBase {
public void testTopActivityUiModeChangeScheduleConfigChange() { public void testTopActivityUiModeChangeScheduleConfigChange() {
final ActivityRecord activity = createActivityRecord(mWpc); final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true; activity.mVisibleRequested = true;
doReturn(true).when(activity).setOverrideNightMode(anyInt()); doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any());
mWpc.updateNightModeForAllActivities(Configuration.UI_MODE_NIGHT_YES); mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES,
LocaleList.forLanguageTags("en-XA"));
verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean()); verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
} }