Merge changes from topic "configuration_gender"

* changes:
  Fix test case fail
  Add a new field in Configuration and persist the field
  Add a system service for grammatical gender
This commit is contained in:
Calvin Pan
2022-12-22 13:59:00 +00:00
committed by Android (Google) Code Review
19 changed files with 451 additions and 26 deletions

View File

@@ -5650,6 +5650,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);
@@ -10005,6 +10010,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";
@@ -11261,6 +11267,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
@@ -12875,6 +12882,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);
@@ -12904,6 +12912,10 @@ package android.content.res {
field @NonNull public static final android.os.Parcelable.Creator<android.content.res.Configuration> 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

View File

@@ -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<Integer> 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).
*
* <p><b>Note:</b> 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 <a
* href="https://developer.android.com/guide/topics/resources/runtime-changes">section on
* handling configuration changes</a>. 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();
}
}
}

View File

@@ -0,0 +1,19 @@
package android.app;
/**
* Internal interface used to control app-specific gender.
*
* <p>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);
}

View File

@@ -1547,6 +1547,7 @@ public final class SystemServiceRegistry {
IAmbientContextManager.Stub.asInterface(iBinder);
return new AmbientContextManager(ctx.getOuterContext(), manager);
}});
registerService(Context.WEARABLE_SENSING_SERVICE, WearableSensingManager.class,
new CachedServiceFetcher<WearableSensingManager>() {
@Override
@@ -1559,6 +1560,18 @@ public final class SystemServiceRegistry {
return new WearableSensingManager(ctx.getOuterContext(), manager);
}});
registerService(Context.GRAMMATICAL_INFLECTION_SERVICE, GrammaticalInflectionManager.class,
new CachedServiceFetcher<GrammaticalInflectionManager>() {
@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

View File

@@ -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;
@@ -3973,6 +3974,8 @@ public abstract class Context {
CREDENTIAL_SERVICE,
DEVICE_LOCK_SERVICE,
VIRTUALIZATION_SERVICE,
GRAMMATICAL_INFLECTION_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -6168,6 +6171,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.

View File

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

View File

@@ -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<Configuration
@UnsupportedAppUsage
public boolean userSetLocale;
/**
* Current user preference for the grammatical gender.
*/
@GrammaticalGender
private int mGrammaticalGender;
/** @hide */
@IntDef(prefix = { "GRAMMATICAL_GENDER_" }, value = {
GRAMMATICAL_GENDER_NOT_SPECIFIED,
GRAMMATICAL_GENDER_NEUTRAL,
GRAMMATICAL_GENDER_FEMININE,
GRAMMATICAL_GENDER_MASCULINE,
})
public @interface GrammaticalGender {}
/**
* Constant for grammatical gender: to indicate the user has not specified the terms
* of address for the application.
*/
public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0;
/**
* Constant for grammatical gender: to indicate the terms of address the user
* preferred in an application is neuter.
*/
public static final int GRAMMATICAL_GENDER_NEUTRAL = 2;
/**
* Constant for grammatical gender: to indicate the terms of address the user
* preferred in an application is feminine.
*/
public static final int GRAMMATICAL_GENDER_FEMININE = 3;
/**
* Constant for grammatical gender: to indicate the terms of address the user
* preferred in an application is masculine.
*/
public static final int GRAMMATICAL_GENDER_MASCULINE = 4;
/** Constant for {@link #colorMode}: bits that encode whether the screen is wide gamut. */
public static final int COLOR_MODE_WIDE_COLOR_GAMUT_MASK = 0x3;
@@ -1024,6 +1063,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
}
o.fixUpLocaleList();
mLocaleList = o.mLocaleList;
mGrammaticalGender = o.mGrammaticalGender;
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
keyboard = o.keyboard;
@@ -1510,6 +1550,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
seq = 0;
windowConfiguration.setToDefaults();
fontWeightAdjustment = FONT_WEIGHT_ADJUSTMENT_UNDEFINED;
mGrammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
}
/**
@@ -1712,6 +1753,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT;
fontWeightAdjustment = delta.fontWeightAdjustment;
}
if (delta.mGrammaticalGender != mGrammaticalGender) {
changed |= ActivityInfo.CONFIG_GRAMMATICAL_GENDER;
mGrammaticalGender = delta.mGrammaticalGender;
}
return changed;
}
@@ -1929,6 +1974,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
&& fontWeightAdjustment != delta.fontWeightAdjustment) {
changed |= ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT;
}
if (!publicOnly&& mGrammaticalGender != delta.mGrammaticalGender) {
changed |= ActivityInfo.CONFIG_GRAMMATICAL_GENDER;
}
return changed;
}
@@ -2023,6 +2072,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeInt(assetsSeq);
dest.writeInt(seq);
dest.writeInt(fontWeightAdjustment);
dest.writeInt(mGrammaticalGender);
}
public void readFromParcel(Parcel source) {
@@ -2055,6 +2105,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
assetsSeq = source.readInt();
seq = source.readInt();
fontWeightAdjustment = source.readInt();
mGrammaticalGender = source.readInt();
}
public static final @android.annotation.NonNull Parcelable.Creator<Configuration> CREATOR
@@ -2155,6 +2206,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (n != 0) return n;
n = this.fontWeightAdjustment - that.fontWeightAdjustment;
if (n != 0) return n;
n = this.mGrammaticalGender - that.mGrammaticalGender;
if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -2196,9 +2249,36 @@ public final class Configuration implements Parcelable, Comparable<Configuration
result = 31 * result + densityDpi;
result = 31 * result + assetsSeq;
result = 31 * result + fontWeightAdjustment;
result = 31 * result + mGrammaticalGender;
return result;
}
/**
* Returns the user preference for the grammatical gender. Will be
* {@link #GRAMMATICAL_GENDER_NOT_SPECIFIED} or
* {@link #GRAMMATICAL_GENDER_NEUTRAL} or
* {@link #GRAMMATICAL_GENDER_FEMININE} or
* {@link #GRAMMATICAL_GENDER_MASCULINE}.
*
* @return The preferred grammatical gender.
*/
@GrammaticalGender
public int getGrammaticalGender() {
return mGrammaticalGender;
}
/**
* Sets the user preference for the grammatical gender. This is only for frameworks to easily
* override the gender in the configuration. To update the grammatical gender for an application
* use {@link GrammaticalInflectionManager#setRequestedApplicationGrammaticalGender(int)}.
*
* @param grammaticalGender The preferred grammatical gender.
* @hide
*/
public void setGrammaticalGender(@GrammaticalGender int grammaticalGender) {
mGrammaticalGender = grammaticalGender;
}
/**
* Get the locale list. This is the preferred way for getting the locales (instead of using
* the direct accessor to {@link #locale}, which would only provide the primary locale).

View File

@@ -1027,6 +1027,9 @@
<flag name="layoutDirection" value="0x2000" />
<!-- The color mode of the screen has changed (color gamut or dynamic range). -->
<flag name="colorMode" value="0x4000" />
<!-- The grammatical gender has changed, for example the user set the grammatical gender
from the UI. -->
<flag name="grammaticalGender" value="0x8000" />
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />

View File

@@ -0,0 +1,76 @@
/*
* 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 com.android.server.grammaticalinflection;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import android.app.IGrammaticalInflectionManager;
import android.content.Context;
import android.os.IBinder;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
/**
* The implementation of IGrammaticalInflectionManager.aidl.
*
* <p>This service is API entry point for storing app-specific grammatical inflection.
*/
public class GrammaticalInflectionService extends SystemService {
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
/**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
* and passes it to super.
* </p>
*
* @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();
}
}

View File

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

View File

@@ -536,16 +536,19 @@ public abstract class ConfigurationContainer<E extends 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<E extends 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;
}

View File

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

View File

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

View File

@@ -868,13 +868,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// TODO(b/199277729): Consider whether we need to add special casing for edge cases like
// activity-embeddings etc.
void updateAppSpecificSettingsForAllActivitiesInPackage(String packageName, Integer nightMode,
LocaleList localesOverride) {
LocaleList localesOverride, @Configuration.GrammaticalGender int gender) {
for (int i = mActivities.size() - 1; i >= 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 */);
}

View File

@@ -132,6 +132,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;
@@ -1769,6 +1770,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");

View File

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

View File

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

View File

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

View File

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