From e0dac90a9aebee765ace40982ef5cadbf27a9a84 Mon Sep 17 00:00:00 2001 From: Pratyush More Date: Mon, 6 Sep 2021 18:44:06 +0000 Subject: [PATCH] Add APIs for supporting app-specific locale configurations. Bug: 194094301 Bug: 193787310 Test: atest CtsLocaleManagerTestCases Change-Id: Ia17cd99a27d593da3b4de1979457ae8ed870a4c6 --- core/api/current.txt | 6 + core/api/system-current.txt | 5 + core/api/test-current.txt | 5 + core/java/android/app/LocaleManager.java | 152 ++++++++++++++++++ core/java/android/app/OWNERS | 3 + .../android/app/SystemServiceRegistry.java | 8 + core/java/android/content/Context.java | 1 - 7 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 core/java/android/app/LocaleManager.java diff --git a/core/api/current.txt b/core/api/current.txt index 0e948db82cee9..7799b697c1ea5 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5604,6 +5604,11 @@ package android.app { method @Deprecated public android.view.Window startActivity(String, android.content.Intent); } + public class LocaleManager { + method @NonNull public android.os.LocaleList getApplicationLocales(); + method public void setApplicationLocales(@NonNull android.os.LocaleList); + } + public class MediaRouteActionProvider extends android.view.ActionProvider { ctor public MediaRouteActionProvider(android.content.Context); method public android.view.View onCreateActionView(); @@ -10708,6 +10713,7 @@ package android.content { field public static final String KEYGUARD_SERVICE = "keyguard"; field public static final String LAUNCHER_APPS_SERVICE = "launcherapps"; field @UiContext public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; + field public static final String LOCALE_SERVICE = "locale"; field public static final String LOCATION_SERVICE = "location"; field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication"; field public static final String MEDIA_METRICS_SERVICE = "media_metrics"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 95d06b0351e49..7a69b6dad9643 100755 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -740,6 +740,11 @@ package android.app { field public static final int PIN = 1; // 0x1 } + public class LocaleManager { + method @NonNull @RequiresPermission(android.Manifest.permission.READ_APP_SPECIFIC_LOCALES) public android.os.LocaleList getApplicationLocales(@NonNull String); + method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void setApplicationLocales(@NonNull String, @NonNull android.os.LocaleList); + } + public class Notification implements android.os.Parcelable { field public static final String CATEGORY_CAR_EMERGENCY = "car_emergency"; field public static final String CATEGORY_CAR_INFORMATION = "car_information"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 71ed8348d3972..8c238cd4b2c31 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -276,6 +276,11 @@ package android.app { method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]); } + public class LocaleManager { + method @Nullable public android.os.LocaleList getSystemLocales(); + method public void setSystemLocales(@NonNull android.os.LocaleList); + } + public class Notification implements android.os.Parcelable { method public boolean shouldShowForegroundImmediately(); } diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java new file mode 100644 index 0000000000000..2aa7c8e60236a --- /dev/null +++ b/core/java/android/app/LocaleManager.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021 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.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UserHandleAware; +import android.content.Context; +import android.content.res.Configuration; +import android.os.LocaleList; +import android.os.RemoteException; + +/** + * This class gives access to system locale services. These services allow applications to control + * granular locale settings (such as per-app locales). + * + *

Third party applications should treat this as a write-side surface, and continue reading + * locales via their in-process {@link LocaleList}s. + */ +@SystemService(Context.LOCALE_SERVICE) +public class LocaleManager { + private static final String TAG = "LocaleManager"; + + /** Context required for getting the user for which API calls are made. */ + private Context mContext; + private ILocaleManager mService; + + /** @hide Instantiated by ContextImpl */ + public LocaleManager(Context context, ILocaleManager service) { + mContext = context; + mService = service; + } + + /** + * Sets the UI locales for the calling app. + * + *

Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale. + * + * @param locales the desired locales for the calling app. + */ + @UserHandleAware + public void setApplicationLocales(@NonNull LocaleList locales) { + setApplicationLocales(mContext.getPackageName(), locales); + } + + /** + * Sets the UI locales for a specified app (described by package name). + * + *

Pass a {@link LocaleList#getEmptyLocaleList()} to reset to the system locale. + * + * @param appPackageName the package name of the app for which to set the locales. + * @param locales the desired locales for the specified app. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CHANGE_CONFIGURATION) + @UserHandleAware + public void setApplicationLocales(@NonNull String appPackageName, @NonNull LocaleList locales) { + try { + mService.setApplicationLocales(appPackageName, mContext.getUser().getIdentifier(), + locales); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the UI locales for the calling app. + * + *

Returns a {@link LocaleList#getEmptyLocaleList()} if no app-specific locales are set. + */ + @UserHandleAware + @NonNull + public LocaleList getApplicationLocales() { + return getApplicationLocales(mContext.getPackageName()); + } + + /** + * Returns the current UI locales for a specific app (described by package name). + * + *

Returns a {@link LocaleList#getEmptyLocaleList()} if no app-specific locales are set. + * + * Note: Non-system apps should read Locale information via their in-process + * LocaleLists. + * + * @param appPackageName the package name of the app for which to retrieve the locales. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_APP_SPECIFIC_LOCALES) + @UserHandleAware + @NonNull + public LocaleList getApplicationLocales(@NonNull String appPackageName) { + try { + return mService.getApplicationLocales(appPackageName, mContext.getUser() + .getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the current system locales to the provided value. + * + * @hide + */ + @TestApi + public void setSystemLocales(@NonNull LocaleList locales) { + try { + Configuration conf = ActivityManager.getService().getConfiguration(); + conf.setLocales(locales); + ActivityManager.getService().updatePersistentConfiguration(conf); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the current system locales for the device. + * + * @hide + */ + @TestApi + @Nullable + public LocaleList getSystemLocales() { + try { + return ActivityManager.getService().getConfiguration().getLocales(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + +} diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 4da51c13045b2..1909f3c03da4d 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -40,6 +40,9 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS # AppOps per-file *AppOp* = file:/core/java/android/permission/OWNERS +# LocaleManager +per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS + # Multiuser per-file *User* = file:/MULTIUSER_OWNERS diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 890a8d6cbe62b..be557475a2205 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1382,6 +1382,14 @@ public final class SystemServiceRegistry { throws ServiceNotFoundException { return new SystemLightsManager(ctx); }}); + registerService(Context.LOCALE_SERVICE, LocaleManager.class, + new CachedServiceFetcher() { + @Override + public LocaleManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new LocaleManager(ctx, ILocaleManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.LOCALE_SERVICE))); + }}); registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bfc43334d7c19..d7d3fe7755e37 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5814,7 +5814,6 @@ public abstract class Context { * {@link android.app.LocaleManager}. * * @see #getSystemService(String) - * @hide */ public static final String LOCALE_SERVICE = "locale";