Align locale with activity that start the task
System apps(Settings) are able to embed activities that don't belong
to them, which may cause users to set different languages in embedded
apps and display two different languages in one app.
- Align locale with the activity that start the task activity in
activity embedding feature.
- Since phone doesn't include the activity embedding feature, adding
a hidden API for system app to align the locale.
Bug: 199277729
Test: Verified on Cuttlefish
Change-Id: Ic23a93a492b6d0fecb5a9519396701273b751d39
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 32d88b2..c0239e8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9085,6 +9085,25 @@
state, sourceSpec, targetSpec, viewIds, uiTranslationSpec);
}
+ /**
+ * If set, any activity launch in the same task will be overridden to the locale of activity
+ * that started the task.
+ *
+ * <p>Currently, Android supports per app languages, and system apps are able to start
+ * activities of another package on the same task, which may cause users to set different
+ * languages in different apps and display two different languages in one app.</p>
+ *
+ * <p>The <a href="https://developer.android.com/guide/topics/large-screens/activity-embedding">
+ * activity embedding feature</a> will align the locale with root activity automatically, but
+ * it doesn't land on the phone yet. If activity embedding land on the phone in the future,
+ * please consider adapting activity embedding directly.</p>
+ *
+ * @hide
+ */
+ public void enableTaskLocaleOverride() {
+ ActivityClient.getInstance().enableTaskLocaleOverride(mToken);
+ }
+
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 558dae5..aa868a7 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -563,6 +563,14 @@
}
}
+ void enableTaskLocaleOverride(IBinder token) {
+ try {
+ getActivityClientController().enableTaskLocaleOverride(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Shows or hides a Camera app compat toggle for stretched issues with the requested state.
*
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index ecea46a..03646c6 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -171,4 +171,10 @@
*/
oneway void requestCompatCameraControl(in IBinder token, boolean showControl,
boolean transformationApplied, in ICompatCameraControlCallback callback);
+
+ /**
+ * If set, any activity launch in the same task will be overridden to the locale of activity
+ * that started the task.
+ */
+ void enableTaskLocaleOverride(in IBinder token);
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index a16e659..4428be7 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1644,4 +1644,19 @@
}
return false;
}
+
+ @Override
+ public void enableTaskLocaleOverride(IBinder token) {
+ if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) {
+ // Only allow system to align locale.
+ return;
+ }
+
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ r.getTask().mAlignActivityLocaleWithTask = true;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8410942..9a7b165 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -298,6 +298,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.LocaleList;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -7998,6 +7999,9 @@
}
super.resolveOverrideConfiguration(newParentConfiguration);
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+
+ applyLocaleOverrideIfNeeded(resolvedConfig);
+
if (isFixedRotationTransforming()) {
// The resolved configuration is applied with rotated display configuration. If this
// activity matches its parent (the following resolving procedures are no-op), then it
@@ -10160,6 +10164,35 @@
}
}
+ private void applyLocaleOverrideIfNeeded(Configuration resolvedConfig) {
+ // We always align the locale for ActivityEmbedding apps. System apps or some apps which
+ // has set known cert apps can embed across different uid activity.
+ boolean shouldAlignLocale = isEmbedded()
+ || (task != null && task.mAlignActivityLocaleWithTask);
+ if (!shouldAlignLocale) {
+ return;
+ }
+
+ boolean differentPackage = task != null
+ && task.realActivity != null
+ && !task.realActivity.getPackageName().equals(packageName);
+ if (!differentPackage) {
+ return;
+ }
+
+ LocaleList locale;
+ final ActivityTaskManagerInternal.PackageConfig appConfig =
+ mAtmService.mPackageConfigPersister.findPackageConfiguration(
+ task.realActivity.getPackageName(), mUserId);
+ // if there is no app locale for the package, clear the target activity's locale.
+ if (appConfig == null || appConfig.mLocales == null || appConfig.mLocales.isEmpty()) {
+ locale = LocaleList.getEmptyLocaleList();
+ } else {
+ locale = appConfig.mLocales;
+ }
+ resolvedConfig.setLocales(locale);
+ }
+
/**
* Whether we should send fake focus when the activity is resumed. This is done because some
* game engines wait to get focus before drawing the content of the app.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3b5b5a9..b1e0798 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -612,6 +612,8 @@
boolean mLastSurfaceShowing = true;
+ boolean mAlignActivityLocaleWithTask = false;
+
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,