From e702359658a6657f87f6f5f8a1edc3253614f51e Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Tue, 13 Dec 2022 19:24:33 +0800 Subject: [PATCH] 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 --- core/java/android/app/Activity.java | 19 +++++++++++ core/java/android/app/ActivityClient.java | 8 +++++ .../app/IActivityClientController.aidl | 6 ++++ .../server/wm/ActivityClientController.java | 15 +++++++++ .../com/android/server/wm/ActivityRecord.java | 33 +++++++++++++++++++ .../core/java/com/android/server/wm/Task.java | 2 ++ 6 files changed, 83 insertions(+) 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,