From fdaffd24f76937b83aece24ed4e8aa6d5e40d701 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Thu, 22 Dec 2022 01:49:09 +0800 Subject: [PATCH 1/3] Add a system service for grammatical gender Add getApplicationGender()/setApplicationGender() API to allow app set the application's grammatical gender Bug: 259175720 Test: atest and get/set the API Change-Id: I83842eadd8cdaa7c148acf6dfb37df32564e241d --- core/api/current.txt | 12 +++ .../app/GrammaticalInflectionManager.java | 93 +++++++++++++++++++ .../app/IGrammaticalInflectionManager.aidl | 19 ++++ .../android/app/SystemServiceRegistry.java | 13 +++ core/java/android/content/Context.java | 11 +++ .../java/android/content/pm/ActivityInfo.java | 8 +- .../android/content/res/Configuration.java | 80 ++++++++++++++++ .../GrammaticalInflectionService.java | 76 +++++++++++++++ .../java/com/android/server/SystemServer.java | 9 ++ 9 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 core/java/android/app/GrammaticalInflectionManager.java create mode 100644 core/java/android/app/IGrammaticalInflectionManager.aidl create mode 100644 services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java diff --git a/core/api/current.txt b/core/api/current.txt index 1ba99f995ec70..319551d835984 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5638,6 +5638,11 @@ package android.app { field public static final int MODE_UNKNOWN = 0; // 0x0 } + public class GrammaticalInflectionManager { + method public int getApplicationGrammaticalGender(); + method public void setRequestedApplicationGrammaticalGender(int); + } + public class Instrumentation { ctor public Instrumentation(); method public android.os.TestLooperManager acquireLooperManager(android.os.Looper); @@ -9955,6 +9960,7 @@ package android.content { field public static final String FILE_INTEGRITY_SERVICE = "file_integrity"; field public static final String FINGERPRINT_SERVICE = "fingerprint"; field public static final String GAME_SERVICE = "game"; + field public static final String GRAMMATICAL_INFLECTION_SERVICE = "grammatical_inflection"; field public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties"; field public static final String HEALTHCONNECT_SERVICE = "healthconnect"; field public static final String INPUT_METHOD_SERVICE = "input_method"; @@ -11166,6 +11172,7 @@ package android.content.pm { field public static final int CONFIG_DENSITY = 4096; // 0x1000 field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000 field public static final int CONFIG_FONT_WEIGHT_ADJUSTMENT = 268435456; // 0x10000000 + field public static final int CONFIG_GRAMMATICAL_GENDER = 32768; // 0x8000 field public static final int CONFIG_KEYBOARD = 16; // 0x10 field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20 field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000 @@ -12772,6 +12779,7 @@ package android.content.res { method public int diff(android.content.res.Configuration); method public boolean equals(android.content.res.Configuration); method @NonNull public static android.content.res.Configuration generateDelta(@NonNull android.content.res.Configuration, @NonNull android.content.res.Configuration); + method public int getGrammaticalGender(); method public int getLayoutDirection(); method @NonNull public android.os.LocaleList getLocales(); method public boolean isLayoutSizeAtLeast(int); @@ -12801,6 +12809,10 @@ package android.content.res { field @NonNull public static final android.os.Parcelable.Creator CREATOR; field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0 field public static final int FONT_WEIGHT_ADJUSTMENT_UNDEFINED = 2147483647; // 0x7fffffff + field public static final int GRAMMATICAL_GENDER_FEMININE = 3; // 0x3 + field public static final int GRAMMATICAL_GENDER_MASCULINE = 4; // 0x4 + field public static final int GRAMMATICAL_GENDER_NEUTRAL = 2; // 0x2 + field public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0; // 0x0 field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1 field public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0; // 0x0 field public static final int HARDKEYBOARDHIDDEN_YES = 2; // 0x2 diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java new file mode 100644 index 0000000000000..1905b6a46d7e8 --- /dev/null +++ b/core/java/android/app/GrammaticalInflectionManager.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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.app; + +import android.annotation.SystemService; +import android.content.Context; +import android.content.res.Configuration; +import android.os.RemoteException; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * This class allow applications to control granular grammatical inflection settings (such as + * per-app grammatical gender). + */ +@SystemService(Context.GRAMMATICAL_INFLECTION_SERVICE) +public class GrammaticalInflectionManager { + private static final Set VALID_GENDER_VALUES = new HashSet<>(Arrays.asList( + Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED, + Configuration.GRAMMATICAL_GENDER_NEUTRAL, + Configuration.GRAMMATICAL_GENDER_FEMININE, + Configuration.GRAMMATICAL_GENDER_MASCULINE)); + + private final Context mContext; + private final IGrammaticalInflectionManager mService; + + /** @hide Instantiated by ContextImpl */ + public GrammaticalInflectionManager(Context context, IGrammaticalInflectionManager service) { + mContext = context; + mService = service; + } + + /** + * Returns the current grammatical gender for the calling app. A new value can be requested via + * {@link #setRequestedApplicationGrammaticalGender(int)} and will be updated with a new + * configuration change. The method always returns the value received with the last received + * configuration change. + * + * @return the value of grammatical gender + * @see Configuration#getGrammaticalGender + */ + @Configuration.GrammaticalGender + public int getApplicationGrammaticalGender() { + return mContext.getApplicationContext() + .getResources() + .getConfiguration() + .getGrammaticalGender(); + } + + /** + * Sets the current grammatical gender for the calling app (keyed by package name and user ID + * retrieved from the calling pid). + * + *

Note: Changes to app grammatical gender will result in a configuration change (and + * potentially an Activity re-creation) being applied to the specified application. For more + * information, see the section on + * handling configuration changes. The set grammatical gender are persisted across + * application restarts; they are backed up if the user has enabled Backup & Restore.` + * + * @param grammaticalGender the terms of address the user preferred in an application. + * @see Configuration#getGrammaticalGender + */ + public void setRequestedApplicationGrammaticalGender( + @Configuration.GrammaticalGender int grammaticalGender) { + if (!VALID_GENDER_VALUES.contains(grammaticalGender)) { + throw new IllegalArgumentException("Unknown grammatical gender"); + } + + try { + mService.setRequestedApplicationGrammaticalGender( + mContext.getPackageName(), mContext.getUserId(), grammaticalGender); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/app/IGrammaticalInflectionManager.aidl b/core/java/android/app/IGrammaticalInflectionManager.aidl new file mode 100644 index 0000000000000..9366a45551da1 --- /dev/null +++ b/core/java/android/app/IGrammaticalInflectionManager.aidl @@ -0,0 +1,19 @@ +package android.app; + + +/** + * Internal interface used to control app-specific gender. + * + *

Use the {@link android.app.GrammarInflectionManager} class rather than going through + * this Binder interface directly. See {@link android.app.GrammarInflectionManager} for + * more complete documentation. + * + * @hide + */ + interface IGrammaticalInflectionManager { + + /** + * Sets a specified app’s app-specific grammatical gender. + */ + void setRequestedApplicationGrammaticalGender(String appPackageName, int userId, int gender); + } \ No newline at end of file diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3730a6f9b428e..9e22d465a41df 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1546,6 +1546,7 @@ public final class SystemServiceRegistry { IAmbientContextManager.Stub.asInterface(iBinder); return new AmbientContextManager(ctx.getOuterContext(), manager); }}); + registerService(Context.WEARABLE_SENSING_SERVICE, WearableSensingManager.class, new CachedServiceFetcher() { @Override @@ -1558,6 +1559,18 @@ public final class SystemServiceRegistry { return new WearableSensingManager(ctx.getOuterContext(), manager); }}); + registerService(Context.GRAMMATICAL_INFLECTION_SERVICE, GrammaticalInflectionManager.class, + new CachedServiceFetcher() { + @Override + public GrammaticalInflectionManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new GrammaticalInflectionManager(ctx, + IGrammaticalInflectionManager.Stub.asInterface( + ServiceManager.getServiceOrThrow( + Context.GRAMMATICAL_INFLECTION_SERVICE))); + }}); + + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 382e2bb6ee433..e6c5937f1d324 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -40,6 +40,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.BroadcastOptions; import android.app.GameManager; +import android.app.GrammaticalInflectionManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.VrManager; @@ -3940,6 +3941,8 @@ public abstract class Context { CREDENTIAL_SERVICE, DEVICE_LOCK_SERVICE, VIRTUALIZATION_SERVICE, + GRAMMATICAL_INFLECTION_SERVICE, + }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -6136,6 +6139,14 @@ public abstract class Context { @SystemApi public static final String VIRTUALIZATION_SERVICE = "virtualization"; + /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link GrammaticalInflectionManager}. + * + * @see #getSystemService(String) + */ + public static final String GRAMMATICAL_INFLECTION_SERVICE = "grammatical_inflection"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index dab57fda61066..68a84e8304920 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -807,6 +807,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { CONFIG_LAYOUT_DIRECTION, CONFIG_COLOR_MODE, CONFIG_FONT_SCALE, + CONFIG_GRAMMATICAL_GENDER, }) @Retention(RetentionPolicy.SOURCE) public @interface Config {} @@ -915,6 +916,12 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * range. Set from the {@link android.R.attr#configChanges} attribute. */ public static final int CONFIG_COLOR_MODE = 0x4000; + /** + * Bit in {@link #configChanges} that indicates that the activity + * can itself handle the change to gender. Set from the + * {@link android.R.attr#configChanges} attribute. + */ + public static final int CONFIG_GRAMMATICAL_GENDER = 0x8000; /** * Bit in {@link #configChanges} that indicates that the activity * can itself handle asset path changes. Set from the {@link android.R.attr#configChanges} @@ -946,7 +953,6 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * not a core resource configuration, but a higher-level value, so its * constant starts at the high bits. */ - public static final int CONFIG_FONT_WEIGHT_ADJUSTMENT = 0x10000000; /** @hide diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index f47c1e0723b51..96aa6249245c3 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -46,6 +46,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.app.GrammaticalInflectionManager; import android.app.WindowConfiguration; import android.compat.annotation.UnsupportedAppUsage; import android.content.LocaleProto; @@ -141,6 +142,44 @@ public final class Configuration implements Parcelable, Comparable CREATOR @@ -2155,6 +2206,8 @@ public final class Configuration implements Parcelable, ComparableThis service is API entry point for storing app-specific grammatical inflection. + */ +public class GrammaticalInflectionService extends SystemService { + + private final ActivityTaskManagerInternal mActivityTaskManagerInternal; + + /** + * Initializes the system service. + *

+ * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + *

+ * + * @param context The system server context. + * + * @hide + */ + public GrammaticalInflectionService(Context context) { + super(context); + mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + } + + @Override + public void onStart() { + publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mService); + } + + private final IBinder mService = new IGrammaticalInflectionManager.Stub() { + @Override + public void setRequestedApplicationGrammaticalGender( + String appPackageName, int userId, int gender) { + GrammaticalInflectionService.this.setRequestedApplicationGrammaticalGender( + appPackageName, userId, gender); + } + }; + + private void setRequestedApplicationGrammaticalGender( + String appPackageName, int userId, int gender) { + final ActivityTaskManagerInternal.PackageConfigurationUpdater updater = + mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName, + userId); + + updater.setGrammaticalGender(gender).commit(); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 509d75bf9b76c..8f6208c16d7b1 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -130,6 +130,7 @@ import com.android.server.display.DisplayManagerService; import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; +import com.android.server.grammaticalinflection.GrammaticalInflectionService; import com.android.server.gpu.GpuService; import com.android.server.graphics.fonts.FontManagerService; import com.android.server.hdmi.HdmiControlService; @@ -1753,6 +1754,14 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartGrammarInflectionService"); + try { + mSystemServiceManager.startService(GrammaticalInflectionService.class); + } catch (Throwable e) { + reportWtf("starting GrammarInflectionService service", e); + } + t.traceEnd(); + t.traceBegin("UpdatePackagesIfNeeded"); try { Watchdog.getInstance().pauseWatchingCurrentThread("dexopt"); From a50fde10604e1ffc40a234f22f23c8adffc86039 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Thu, 22 Dec 2022 01:50:01 +0800 Subject: [PATCH 2/3] Add a new field in Configuration and persist the field Bug: 259175720 Test: atest and get/set the API Change-Id: I938ededd94473d1e7c4c3b58982ca1ec04bc5977 --- core/res/res/values/attrs_manifest.xml | 3 ++ .../wm/ActivityTaskManagerInternal.java | 19 +++++++++++- .../server/wm/ConfigurationContainer.java | 24 +++++++++++++-- .../server/wm/PackageConfigPersister.java | 30 +++++++++++++++---- .../wm/PackageConfigurationUpdaterImpl.java | 21 +++++++++++-- .../server/wm/WindowProcessController.java | 4 +-- 6 files changed, 88 insertions(+), 13 deletions(-) diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d4644c59fa529..a4a39e9618900 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1027,6 +1027,9 @@ + + diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index ec486437734b5..dec403cf5c463 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -29,6 +29,7 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; @@ -622,10 +623,19 @@ public abstract class ActivityTaskManagerInternal { @Nullable public final LocaleList mLocales; + /** + * Gender for the application, null if app-specific grammatical gender is not set. + */ + @Nullable + public final @Configuration.GrammaticalGender + Integer mGrammaticalGender; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public PackageConfig(Integer nightMode, LocaleList locales) { + public PackageConfig(Integer nightMode, LocaleList locales, + @Configuration.GrammaticalGender Integer grammaticalGender) { mNightMode = nightMode; mLocales = locales; + mGrammaticalGender = grammaticalGender; } /** @@ -659,6 +669,13 @@ public abstract class ActivityTaskManagerInternal { */ PackageConfigurationUpdater setLocales(LocaleList locales); + /** + * Sets the gender for the current application. This setting is persisted and will + * override the system configuration for this application. + */ + PackageConfigurationUpdater setGrammaticalGender( + @Configuration.GrammaticalGender int gender); + /** * Commit changes. * @return true if the configuration changes were persisted, diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 0c6cea82e00cd..58d4e82961f68 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -536,16 +536,19 @@ public abstract class ConfigurationContainer { * 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) { + public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales, + @Configuration.GrammaticalGender Integer gender) { mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig, nightMode); boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig, locales); - if (newNightModeSet || newLocalesSet) { + boolean newGenderSet = (gender != null) && setOverrideGender(mRequestsTmpConfig, + gender); + if (newNightModeSet || newLocalesSet || newGenderSet) { onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); } - return newNightModeSet || newLocalesSet; + return newNightModeSet || newLocalesSet || newGenderSet; } /** @@ -578,6 +581,21 @@ public abstract class ConfigurationContainer { return true; } + /** + * Overrides the gender to this ConfigurationContainer. + * + * @return true if the grammatical gender has been changed. + */ + private boolean setOverrideGender(Configuration requestsTmpConfig, + @Configuration.GrammaticalGender int gender) { + if (mRequestedOverrideConfiguration.getGrammaticalGender() == gender) { + return false; + } else { + requestsTmpConfig.setGrammaticalGender(gender); + return true; + } + } + public boolean isActivityTypeDream() { return getActivityType() == ACTIVITY_TYPE_DREAM; } diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java index 18a7d2e760fc9..23127ac1ba098 100644 --- a/services/core/java/com/android/server/wm/PackageConfigPersister.java +++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED; + import android.annotation.NonNull; import android.content.res.Configuration; import android.os.Environment; @@ -165,7 +167,8 @@ public class PackageConfigPersister { if (modifiedRecord != null) { container.applyAppSpecificConfig(modifiedRecord.mNightMode, LocaleOverlayHelper.combineLocalesIfOverlayExists( - modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales())); + modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales()), + modifiedRecord.mGrammaticalGender); } } } @@ -188,16 +191,19 @@ public class PackageConfigPersister { } boolean isNightModeChanged = updateNightMode(impl.getNightMode(), record); boolean isLocalesChanged = updateLocales(impl.getLocales(), record); + boolean isGenderChanged = updateGender(impl.getGrammaticalGender(), record); if ((record.mNightMode == null || record.isResetNightMode()) - && (record.mLocales == null || record.mLocales.isEmpty())) { + && (record.mLocales == null || record.mLocales.isEmpty()) + && (record.mGrammaticalGender == null + || record.mGrammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED)) { // if all values default to system settings, we can remove the package. removePackage(packageName, userId); // if there was a pre-existing record for the package that was deleted, // we return true (since it was successfully deleted), else false (since there was // no change to the previous state). return isRecordPresent; - } else if (!isNightModeChanged && !isLocalesChanged) { + } else if (!isNightModeChanged && !isLocalesChanged && !isGenderChanged) { return false; } else { final PackageConfigRecord pendingRecord = @@ -211,7 +217,8 @@ public class PackageConfigPersister { } if (!updateNightMode(record.mNightMode, writeRecord) - && !updateLocales(record.mLocales, writeRecord)) { + && !updateLocales(record.mLocales, writeRecord) + && !updateGender(record.mGrammaticalGender, writeRecord)) { return false; } @@ -240,6 +247,15 @@ public class PackageConfigPersister { return true; } + private boolean updateGender(@Configuration.GrammaticalGender Integer requestedGender, + PackageConfigRecord record) { + if (requestedGender == null || requestedGender.equals(record.mGrammaticalGender)) { + return false; + } + record.mGrammaticalGender = requestedGender; + return true; + } + @GuardedBy("mLock") void removeUser(int userId) { synchronized (mLock) { @@ -305,7 +321,9 @@ public class PackageConfigPersister { return null; } return new ActivityTaskManagerInternal.PackageConfig( - packageConfigRecord.mNightMode, packageConfigRecord.mLocales); + packageConfigRecord.mNightMode, + packageConfigRecord.mLocales, + packageConfigRecord.mGrammaticalGender); } } @@ -336,6 +354,8 @@ public class PackageConfigPersister { final int mUserId; Integer mNightMode; LocaleList mLocales; + @Configuration.GrammaticalGender + Integer mGrammaticalGender; PackageConfigRecord(String name, int userId) { mName = name; diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java index f3be66c5e6da3..2cf8a4a7c1464 100644 --- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java +++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.os.Binder; import android.os.LocaleList; import android.util.ArraySet; @@ -33,6 +34,8 @@ final class PackageConfigurationUpdaterImpl implements private final Optional mPid; private Integer mNightMode; private LocaleList mLocales; + private @Configuration.GrammaticalGender + int mGrammaticalGender; private String mPackageName; private int mUserId; private ActivityTaskManagerService mAtm; @@ -67,6 +70,15 @@ final class PackageConfigurationUpdaterImpl implements return this; } + @Override + public ActivityTaskManagerInternal.PackageConfigurationUpdater setGrammaticalGender( + @Configuration.GrammaticalGender int gender) { + synchronized (this) { + mGrammaticalGender = gender; + } + return this; + } + @Override public boolean commit() { synchronized (this) { @@ -112,12 +124,12 @@ final class PackageConfigurationUpdaterImpl implements for (int i = processes.size() - 1; i >= 0; i--) { final WindowProcessController wpc = processes.valueAt(i); if (wpc.mInfo.packageName.equals(packageName)) { - wpc.applyAppSpecificConfig(mNightMode, localesOverride); + wpc.applyAppSpecificConfig(mNightMode, localesOverride, mGrammaticalGender); } // Always inform individual activities about the update, since activities from other // packages may be sharing this process wpc.updateAppSpecificSettingsForAllActivitiesInPackage(packageName, mNightMode, - localesOverride); + localesOverride, mGrammaticalGender); } } @@ -128,4 +140,9 @@ final class PackageConfigurationUpdaterImpl implements LocaleList getLocales() { return mLocales; } + + @Configuration.GrammaticalGender + Integer getGrammaticalGender() { + return mGrammaticalGender; + } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index e9b81ec030817..f9670ccbed0a0 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -867,13 +867,13 @@ public class WindowProcessController extends ConfigurationContainer= 0; --i) { final ActivityRecord r = mActivities.get(i); // Activities from other packages could be sharing this process. Only propagate updates // to those activities that are part of the package whose app-specific settings changed if (packageName.equals(r.packageName) - && r.applyAppSpecificConfig(nightMode, localesOverride) + && r.applyAppSpecificConfig(nightMode, localesOverride, gender) && r.isVisibleRequested()) { r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */); } From 5128bb95cf75785c44fb3b3445d853ccefe8b1d9 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Thu, 22 Dec 2022 01:50:23 +0800 Subject: [PATCH 3/3] Fix test case fail Bug: 259175720 Test: atest and get/set the API Change-Id: I52c316ddcfabd98d839bd7c4d1bac358b9f44a1e --- .../FakePackageConfigurationUpdater.java | 16 +++++++++++++++ .../locales/LocaleManagerServiceTest.java | 20 +++++++++++++------ .../locales/SystemAppUpdateTrackerTest.java | 8 ++++++-- .../wm/WindowProcessControllerTests.java | 11 ++++++---- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java b/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java index 6cdae53bc6da0..56df9d9cbf0d1 100644 --- a/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java +++ b/services/tests/servicestests/src/com/android/server/locales/FakePackageConfigurationUpdater.java @@ -16,6 +16,8 @@ package com.android.server.locales; +import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED; + import android.annotation.Nullable; import android.os.LocaleList; @@ -29,6 +31,8 @@ class FakePackageConfigurationUpdater implements PackageConfigurationUpdater { FakePackageConfigurationUpdater() {} + private int mGender = GRAMMATICAL_GENDER_NOT_SPECIFIED; + LocaleList mLocales = null; @Override @@ -42,6 +46,12 @@ class FakePackageConfigurationUpdater implements PackageConfigurationUpdater { return this; } + @Override + public PackageConfigurationUpdater setGrammaticalGender(int gender) { + mGender = gender; + return this; + } + @Override public boolean commit() { return mLocales != null; @@ -56,4 +66,10 @@ class FakePackageConfigurationUpdater implements PackageConfigurationUpdater { return mLocales; } + /** + * Returns the gender that were stored during the test run. + */ + int getGender() { + return mGender; + } } diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java index 79ed7d1ff783e..065aec5b2f640 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.locales; +import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -234,7 +236,8 @@ public class LocaleManagerServiceTest { throws Exception { doReturn(DEFAULT_UID).when(mMockPackageManager) .getPackageUidAsUser(anyString(), any(), anyInt()); - doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES)) + doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt()); String imPkgName = getCurrentInputMethodPackageName(); doReturn(Binder.getCallingUid()).when(mMockPackageManager) @@ -274,7 +277,8 @@ public class LocaleManagerServiceTest { doReturn(DEFAULT_UID).when(mMockPackageManager) .getPackageUidAsUser(anyString(), any(), anyInt()); setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES); - doReturn(new PackageConfig(/* nightMode = */ 0, /* locales = */ null)) + doReturn(new PackageConfig(/* nightMode = */ 0, /* locales = */ null, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(any(), anyInt()); LocaleList locales = mLocaleManagerService.getApplicationLocales( @@ -288,7 +292,8 @@ public class LocaleManagerServiceTest { throws Exception { doReturn(Binder.getCallingUid()).when(mMockPackageManager) .getPackageUidAsUser(anyString(), any(), anyInt()); - doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES)) + doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt()); LocaleList locales = @@ -303,7 +308,8 @@ public class LocaleManagerServiceTest { doReturn(DEFAULT_UID).when(mMockPackageManager) .getPackageUidAsUser(anyString(), any(), anyInt()); setUpPassingPermissionCheckFor(Manifest.permission.READ_APP_SPECIFIC_LOCALES); - doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES)) + doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt()); LocaleList locales = @@ -319,7 +325,8 @@ public class LocaleManagerServiceTest { .getPackageUidAsUser(eq(DEFAULT_PACKAGE_NAME), any(), anyInt()); doReturn(Binder.getCallingUid()).when(mMockPackageManager) .getPackageUidAsUser(eq(DEFAULT_INSTALLER_PACKAGE_NAME), any(), anyInt()); - doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES)) + doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt()); LocaleList locales = @@ -334,7 +341,8 @@ public class LocaleManagerServiceTest { throws Exception { doReturn(DEFAULT_UID).when(mMockPackageManager) .getPackageUidAsUser(anyString(), any(), anyInt()); - doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES)) + doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES, + GRAMMATICAL_GENDER_NOT_SPECIFIED)) .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt()); String imPkgName = getCurrentInputMethodPackageName(); doReturn(Binder.getCallingUid()).when(mMockPackageManager) diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java index dc0740ab61e0c..04871d6c1c0be 100644 --- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java @@ -16,6 +16,8 @@ package com.android.server.locales; +import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -168,7 +170,8 @@ public class SystemAppUpdateTrackerTest { /* isUpdatedSystemApp = */ true)) .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any()); doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0, - DEFAULT_LOCALES)).when(mMockActivityTaskManager) + DEFAULT_LOCALES, GRAMMATICAL_GENDER_NOT_SPECIFIED)) + .when(mMockActivityTaskManager) .getApplicationConfig(anyString(), anyInt()); mPackageMonitor.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1, @@ -186,7 +189,8 @@ public class SystemAppUpdateTrackerTest { /* isUpdatedSystemApp = */ true)) .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any()); doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0, - DEFAULT_LOCALES)).when(mMockActivityTaskManager) + DEFAULT_LOCALES, GRAMMATICAL_GENDER_NOT_SPECIFIED)) + .when(mMockActivityTaskManager) .getApplicationConfig(anyString(), anyInt()); // first update 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 8bd414856394a..d6cfd000abd04 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -414,9 +415,10 @@ public class WindowProcessControllerTests extends WindowTestsBase { public void testTopActivityUiModeChangeScheduleConfigChange() { final ActivityRecord activity = createActivityRecord(mWpc); activity.setVisibleRequested(true); - doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any()); + doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any(), anyInt()); mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME, - Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); + Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"), + GRAMMATICAL_GENDER_NOT_SPECIFIED); verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean()); } @@ -425,8 +427,9 @@ public class WindowProcessControllerTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(mWpc); activity.setVisibleRequested(true); mWpc.updateAppSpecificSettingsForAllActivitiesInPackage("com.different.package", - Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); - verify(activity, never()).applyAppSpecificConfig(anyInt(), any()); + Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"), + GRAMMATICAL_GENDER_NOT_SPECIFIED); + verify(activity, never()).applyAppSpecificConfig(anyInt(), any(), anyInt()); verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean()); }