diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 32d88b2097048..c0239e8910fa8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9085,6 +9085,25 @@ public class Activity extends ContextThemeWrapper
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.
+ *
+ *
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.
+ *
+ * The
+ * activity embedding feature 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.
+ *
+ * @hide
+ */
+ public void enableTaskLocaleOverride() {
+ ActivityClient.getInstance().enableTaskLocaleOverride(mToken);
+ }
+
class HostCallbacks extends FragmentHostCallback {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 558dae5666886..aa868a768747f 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -563,6 +563,14 @@ public class ActivityClient {
}
}
+ 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 ecea46a01f92b..03646c693831e 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -171,4 +171,10 @@ interface IActivityClientController {
*/
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 a16e659610135..4428be7458f94 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 @@ class ActivityClientController extends IActivityClientController.Stub {
}
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 841094221d99b..9a7b16516bb48 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.Bundle;
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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ 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 3b5b5a903544b..b1e0798cda47a 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 @@ class Task extends TaskFragment {
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,