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
This commit is contained in:
Calvin Pan
2022-12-22 01:49:09 +08:00
parent 4d625116b2
commit fdaffd24f7
9 changed files with 320 additions and 1 deletions

View File

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

@@ -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<WearableSensingManager>() {
@Override
@@ -1558,6 +1559,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;
@@ -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.

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

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

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