[ToA] Implement grammatical gender in ActivityTaskManagerService and WindowProcessController

Bug: 314886712
Bug: 314886661
Test: manual + Presubmit
Test: atest GrammaticalInflectionManagerTest
Test: atest WmTests:ActivityStarterTests
Test: atest WmTests:WindowProcessControllerTests
Change-Id: Iccd16c83f0d9d04db68d12355de749955c3d002b
Unresolved-Comment-Reason: will fix the comments in the next CL
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e99fcc9..84ef6e5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -914,6 +914,9 @@
     <!-- Permission required for Cts test ScreenRecordingCallbackTests -->
     <uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
 
+    <!-- Permissions required for CTS test - GrammaticalInflectionManagerTest -->
+    <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index c260f10..6a6e6ab 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -47,5 +47,19 @@
      * @see Configuration#getGrammaticalGender
      */
     public abstract @Configuration.GrammaticalGender int getSystemGrammaticalGender(int userId);
+
+    /**
+     * Retrieve the system grammatical gender.
+     *
+     * @return the value of grammatical gender
+     *
+     */
+    public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
+            Configuration configuration);
+
+    /**
+     * Whether the package can get the system grammatical gender or not.
+     */
+    public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName);
 }
 
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 6eb7e95..d01f54f 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -22,17 +22,21 @@
 import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
 
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.app.GrammaticalInflectionManager;
 import android.app.IGrammaticalInflectionManager;
 import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.permission.PermissionManager;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -43,6 +47,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -71,6 +76,7 @@
     private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
     private static final String GRAMMATICAL_INFLECTION_ENABLED =
             "i18n.grammatical_Inflection.enabled";
+    private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
 
     private final GrammaticalInflectionBackupHelper mBackupHelper;
     private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -121,16 +127,16 @@
         @Override
         public void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
             checkCallerIsSystem();
-            checkSystemTermsOfAddressIsEnabled();
             GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
                     userId);
         }
 
         @Override
         public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
-            checkSystemTermsOfAddressIsEnabled();
-            return GrammaticalInflectionService.this.getSystemGrammaticalGender(attributionSource,
-                    userId);
+            return canGetSystemGrammaticalGender(attributionSource)
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+                    attributionSource, userId)
+                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
         @Override
@@ -159,9 +165,33 @@
 
         @Override
         public int getSystemGrammaticalGender(int userId) {
-            checkCallerIsSystem();
-            return GrammaticalInflectionService.this.getSystemGrammaticalGender(
-                    mContext.getAttributionSource(), userId);
+            return checkSystemTermsOfAddressIsEnabled()
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+                    mContext.getAttributionSource(), userId)
+                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        }
+
+        @Override
+        public int retrieveSystemGrammaticalGender(Configuration configuration) {
+            int systemGrammaticalGender = getSystemGrammaticalGender(mContext.getUserId());
+            // Retrieve the grammatical gender from system property, set it into
+            // configuration which will get updated later if the grammatical gender raw value of
+            // current configuration is {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
+            if (configuration.getGrammaticalGenderRaw()
+                    == Configuration.GRAMMATICAL_GENDER_UNDEFINED
+                    || systemGrammaticalGender <= Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+                systemGrammaticalGender = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
+                        Configuration.GRAMMATICAL_GENDER_UNDEFINED);
+            }
+            return systemGrammaticalGender;
+        }
+
+        @Override
+        public boolean canGetSystemGrammaticalGender(int uid, String packageName) {
+            AttributionSource attributionSource = new AttributionSource.Builder(
+                    uid).setPackageName(packageName).build();
+            return GrammaticalInflectionService.this.canGetSystemGrammaticalGender(
+                    attributionSource);
         }
     }
 
@@ -202,11 +232,20 @@
     }
 
     protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+        Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender");
         if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
                 grammaticalGender)) {
             throw new IllegalArgumentException("Unknown grammatical gender");
         }
 
+        if (!checkSystemTermsOfAddressIsEnabled()) {
+            if (grammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+                return;
+            }
+            Log.d(TAG, "Clearing the system grammatical gender setting");
+            grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        }
+
         synchronized (mLock) {
             final File file = getGrammaticalGenderFile(userId);
             final AtomicFile atomicFile = new AtomicFile(file);
@@ -224,6 +263,15 @@
                 throw new RuntimeException(e);
             }
         }
+
+        try {
+            Configuration config = new Configuration();
+            config.setGrammaticalGender(grammaticalGender);
+            ActivityTaskManager.getService().updateConfiguration(config);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Can not update configuration", e);
+        }
+        Trace.endSection();
     }
 
     public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
@@ -233,34 +281,9 @@
             return GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
-        int callingUid = Binder.getCallingUid();
-        if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) != callingUid) {
-            Log.d(TAG,
-                    "Package " + packageName + " does not belong to the calling uid " + callingUid);
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
-
-        if (!checkSystemGrammaticalGenderPermission(mPermissionManager, attributionSource)) {
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
-
         synchronized (mLock) {
-            final File file = getGrammaticalGenderFile(userId);
-            if (!file.exists()) {
-                Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
-                return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-            }
-
-            if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
-                try {
-                    InputStream in = new FileInputStream(file);
-                    final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                    mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
-                } catch (IOException | XmlPullParserException e) {
-                    Log.e(TAG, "Failed to parse XML configuration from " + file, e);
-                }
-            }
-            return mGrammaticalGenderCache.get(userId);
+            int grammaticalGender = mGrammaticalGenderCache.get(userId);
+            return grammaticalGender < 0 ? GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender;
         }
     }
 
@@ -311,9 +334,39 @@
         }
     }
 
-    private void checkSystemTermsOfAddressIsEnabled() {
+    private boolean checkSystemTermsOfAddressIsEnabled() {
         if (!systemTermsOfAddressEnabled()) {
-            throw new RuntimeException("The flag must be enabled to allow calling the API.");
+            Log.d(TAG, "The flag must be enabled to allow calling the API.");
+            return false;
         }
+        return true;
+    }
+
+    private boolean canGetSystemGrammaticalGender(AttributionSource attributionSource) {
+        return checkSystemTermsOfAddressIsEnabled() && checkSystemGrammaticalGenderPermission(
+                mPermissionManager, attributionSource);
+    }
+
+    @Override
+    public void onUserUnlocked(TargetUser user) {
+        IoThread.getHandler().post(() -> {
+            int userId = user.getUserIdentifier();
+            final File file = getGrammaticalGenderFile(userId);
+            synchronized (mLock) {
+                if (!file.exists()) {
+                    Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+                    return;
+                }
+                if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+                    try {
+                        InputStream in = new FileInputStream(file);
+                        final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                        mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+                    } catch (IOException | XmlPullParserException e) {
+                        Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+                    }
+                }
+            }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0def5a1..8773366 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -276,6 +276,7 @@
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
 import com.android.server.firewall.IntentFirewall;
+import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
@@ -317,7 +318,6 @@
  * {@hide}
  */
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
-    private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
     static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
     static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -381,6 +381,7 @@
     private PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
+    GrammaticalInflectionManagerInternal mGrammaticalManagerInternal;
     PendingIntentController mPendingIntentController;
     IntentFirewall mIntentFirewall;
 
@@ -881,6 +882,8 @@
             mActivityClientController.onSystemReady();
             // TODO(b/258792202) Cleanup once ASM is ready to launch
             ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm);
+            mGrammaticalManagerInternal = LocalServices.getService(
+                    GrammaticalInflectionManagerInternal.class);
         }
     }
 
@@ -938,13 +941,8 @@
             configuration.setLayoutDirection(configuration.locale);
         }
 
-        // Retrieve the grammatical gender from system property, set it into configuration which
-        // will get updated later if the grammatical gender raw value of current configuration is
-        // {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
-        if (configuration.getGrammaticalGenderRaw() == Configuration.GRAMMATICAL_GENDER_UNDEFINED) {
-            configuration.setGrammaticalGender(SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
-                    Configuration.GRAMMATICAL_GENDER_UNDEFINED));
-        }
+        configuration.setGrammaticalGender(
+                mGrammaticalManagerInternal.retrieveSystemGrammaticalGender(configuration));
 
         synchronized (mGlobalLock) {
             mForceResizableActivities = forceResizable;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6d2e8cc..6acf1f3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -28,6 +28,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.am.ProcessList.INVALID_ADJ;
+import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
 import static com.android.server.wm.ActivityRecord.State.DESTROYING;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -298,6 +299,8 @@
      */
     private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
 
+    private boolean mCanUseSystemGrammaticalGender;
+
     public WindowProcessController(@NonNull ActivityTaskManagerService atm,
             @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
             @NonNull WindowProcessListener listener) {
@@ -319,6 +322,9 @@
             mIsActivityConfigOverrideAllowed = false;
         }
 
+        mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null
+                && mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid,
+                mInfo.packageName);
         onConfigurationChanged(atm.getGlobalConfiguration());
         mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
     }
@@ -1568,6 +1574,11 @@
             return;
         }
 
+        if (mCanUseSystemGrammaticalGender) {
+            config.setGrammaticalGender(
+                    mAtm.mGrammaticalManagerInternal.getSystemGrammaticalGender(mUserId));
+        }
+
         if (mPauseConfigurationDispatchCount > 0) {
             mHasPendingConfigurationChange = true;
             return;