• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.localemanager.cts;
18 
19 import static android.localemanager.cts.util.LocaleConstants.APP_RECEIVER_ACTION;
20 import static android.localemanager.cts.util.LocaleConstants.CALLING_PACKAGE;
21 import static android.localemanager.cts.util.LocaleConstants.DEFAULT_APP_LOCALES;
22 import static android.localemanager.cts.util.LocaleConstants.DEFAULT_SYSTEM_LOCALES;
23 import static android.localemanager.cts.util.LocaleConstants.EXTRA_QUERY_LOCALES;
24 import static android.localemanager.cts.util.LocaleConstants.EXTRA_SET_LOCALES;
25 import static android.localemanager.cts.util.LocaleConstants.IME_APP_CREATION_INFO_PROVIDER_ACTION;
26 import static android.localemanager.cts.util.LocaleConstants.IME_APP_MAIN_ACTIVITY;
27 import static android.localemanager.cts.util.LocaleConstants.IME_APP_PACKAGE;
28 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_BROADCAST_INFO_PROVIDER_ACTION;
29 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_BROADCAST_RECEIVER;
30 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_CREATION_INFO_PROVIDER_ACTION;
31 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_APP_MAIN_ACTIVITY;
32 import static android.localemanager.cts.util.LocaleConstants.INSTALLER_PACKAGE;
33 import static android.localemanager.cts.util.LocaleConstants.NON_EXISTENT_PACKAGE;
34 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_INFO_PROVIDER_ACTION;
35 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_BROADCAST_RECEIVER;
36 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_CONFIG_CHANGED_INFO_PROVIDER_ACTION;
37 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_CREATION_INFO_PROVIDER_ACTION;
38 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_MAIN_ACTIVITY;
39 import static android.localemanager.cts.util.LocaleConstants.TEST_APP_PACKAGE;
40 import static android.server.wm.CliIntentExtra.extraString;
41 
42 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
43 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
44 
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertNotEquals;
47 import static org.junit.Assert.assertTrue;
48 import static org.junit.Assert.fail;
49 
50 import android.Manifest;
51 import android.app.Activity;
52 import android.app.LocaleManager;
53 import android.content.BroadcastReceiver;
54 import android.content.ComponentName;
55 import android.content.Context;
56 import android.content.Intent;
57 import android.content.IntentFilter;
58 import android.os.LocaleList;
59 import android.server.wm.ActivityManagerTestBase;
60 
61 import androidx.test.InstrumentationRegistry;
62 import androidx.test.runner.AndroidJUnit4;
63 
64 import com.android.compatibility.common.util.AmUtils;
65 import com.android.compatibility.common.util.ShellIdentityUtils;
66 import com.android.compatibility.common.util.ShellUtils;
67 
68 import org.junit.After;
69 import org.junit.AfterClass;
70 import org.junit.Before;
71 import org.junit.BeforeClass;
72 import org.junit.Test;
73 import org.junit.runner.RunWith;
74 
75 import java.util.Locale;
76 import java.util.concurrent.CountDownLatch;
77 import java.util.concurrent.TimeUnit;
78 
79 /**
80  * Tests for {@link android.app.LocaleManager} API(s).
81  *
82  * Build/Install/Run: atest CtsLocaleManagerTestCases
83  */
84 @RunWith(AndroidJUnit4.class)
85 public class LocaleManagerTests extends ActivityManagerTestBase {
86     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10);
87     private static Context sContext;
88     private static LocaleManager sLocaleManager;
89 
90     /* System locales that were set on the device prior to running tests */
91     private static LocaleList sPreviousSystemLocales;
92 
93     /* Receiver to listen to the broadcast in the calling (instrumentation) app. */
94     private BlockingBroadcastReceiver mCallingAppBroadcastReceiver;
95 
96     /* Receiver to listen to the response from the test app's broadcast receiver. */
97     private BlockingBroadcastReceiver mTestAppBroadcastInfoProvider;
98 
99     /* Receiver to listen to the response from the test app's activity. */
100     private BlockingBroadcastReceiver mTestAppCreationInfoProvider;
101 
102     /* Receiver to listen to the response from the test app's onConfigChanged method. */
103     private BlockingBroadcastReceiver mTestAppConfigChangedInfoProvider;
104 
105     /* Receiver to listen to the response from the installer app. */
106     private BlockingBroadcastReceiver mInstallerBroadcastInfoProvider;
107 
108     /* Receiver to listen to the response from the installer app's activity. */
109     private BlockingBroadcastReceiver mInstallerAppCreationInfoProvider;
110 
111     /* Receiver to listen to the response from the ime app's activity. */
112     private BlockingBroadcastReceiver mImeAppCreationInfoProvider;
113 
114     /* Receiver to listen to the response of the active IME's change. */
115     private BlockingBroadcastReceiver mImeChangedBroadcastReceiver;
116 
117     private boolean mNeedsImeReset = false;
118     private String mTestIme = new ComponentName(IME_APP_PACKAGE,
119             IME_APP_PACKAGE + ".TestIme").flattenToShortString();
120 
121     @BeforeClass
setUpClass()122     public static void setUpClass() {
123         sContext = InstrumentationRegistry.getTargetContext();
124         sLocaleManager = sContext.getSystemService(LocaleManager.class);
125 
126         sPreviousSystemLocales = sLocaleManager.getSystemLocales();
127         runWithShellPermissionIdentity(() ->
128                         sLocaleManager.setSystemLocales(DEFAULT_SYSTEM_LOCALES),
129                 Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
130     }
131 
132     @AfterClass
tearDownClass()133     public static void tearDownClass() {
134         runWithShellPermissionIdentity(() ->
135                         sLocaleManager.setSystemLocales(sPreviousSystemLocales),
136                 Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
137     }
138 
139     @Before
setUp()140     public void setUp() throws Exception {
141         // Unlocks the device if locked, since we have tests where the app/activity needs
142         // to be in the foreground/background.
143         super.setUp();
144 
145         resetAppLocalesAsEmpty();
146         AmUtils.waitForBroadcastIdle();
147 
148         mCallingAppBroadcastReceiver = new BlockingBroadcastReceiver();
149         mTestAppBroadcastInfoProvider = new BlockingBroadcastReceiver();
150         mInstallerBroadcastInfoProvider = new BlockingBroadcastReceiver();
151         mTestAppCreationInfoProvider = new BlockingBroadcastReceiver();
152         mTestAppConfigChangedInfoProvider = new BlockingBroadcastReceiver();
153         mInstallerAppCreationInfoProvider = new BlockingBroadcastReceiver();
154         mImeAppCreationInfoProvider = new BlockingBroadcastReceiver();
155         mImeChangedBroadcastReceiver = new BlockingBroadcastReceiver();
156 
157 
158         sContext.registerReceiver(mCallingAppBroadcastReceiver,
159                 new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
160         sContext.registerReceiver(mTestAppBroadcastInfoProvider,
161                 new IntentFilter(TEST_APP_BROADCAST_INFO_PROVIDER_ACTION),
162                 Context.RECEIVER_EXPORTED_UNAUDITED);
163         sContext.registerReceiver(mInstallerBroadcastInfoProvider,
164                 new IntentFilter(INSTALLER_APP_BROADCAST_INFO_PROVIDER_ACTION),
165                 Context.RECEIVER_EXPORTED_UNAUDITED);
166         sContext.registerReceiver(mTestAppCreationInfoProvider,
167                 new IntentFilter(TEST_APP_CREATION_INFO_PROVIDER_ACTION),
168                 Context.RECEIVER_EXPORTED_UNAUDITED);
169         sContext.registerReceiver(mTestAppConfigChangedInfoProvider,
170                 new IntentFilter(TEST_APP_CONFIG_CHANGED_INFO_PROVIDER_ACTION),
171                 Context.RECEIVER_EXPORTED_UNAUDITED);
172         sContext.registerReceiver(mInstallerAppCreationInfoProvider,
173                 new IntentFilter(INSTALLER_APP_CREATION_INFO_PROVIDER_ACTION),
174                 Context.RECEIVER_EXPORTED_UNAUDITED);
175         sContext.registerReceiver(mImeAppCreationInfoProvider,
176                 new IntentFilter(IME_APP_CREATION_INFO_PROVIDER_ACTION),
177                 Context.RECEIVER_EXPORTED_UNAUDITED);
178         sContext.registerReceiver(mImeChangedBroadcastReceiver,
179                 new IntentFilter(Intent.ACTION_INPUT_METHOD_CHANGED));
180 
181         setInstallerForPackage(CALLING_PACKAGE);
182         setInstallerForPackage(TEST_APP_PACKAGE);
183 
184         bindToBroadcastReceiverOfApp(TEST_APP_PACKAGE, TEST_APP_BROADCAST_RECEIVER);
185         bindToBroadcastReceiverOfApp(INSTALLER_PACKAGE, INSTALLER_APP_BROADCAST_RECEIVER);
186 
187         resetReceivers();
188     }
189 
190     @After
tearDown()191     public void tearDown() throws Exception {
192         unRegisterReceiver(mCallingAppBroadcastReceiver);
193         unRegisterReceiver(mTestAppBroadcastInfoProvider);
194         unRegisterReceiver(mInstallerBroadcastInfoProvider);
195         unRegisterReceiver(mTestAppCreationInfoProvider);
196         unRegisterReceiver(mInstallerAppCreationInfoProvider);
197         unRegisterReceiver(mImeAppCreationInfoProvider);
198         unRegisterReceiver(mImeChangedBroadcastReceiver);
199         resetImes();
200     }
201 
unRegisterReceiver(BlockingBroadcastReceiver receiver)202     private void unRegisterReceiver(BlockingBroadcastReceiver receiver) {
203         if (receiver != null) {
204             sContext.unregisterReceiver(receiver);
205             receiver = null;
206         }
207     }
208 
209     /**
210      * Send broadcast to given app's receiver with flag {@link Intent#FLAG_INCLUDE_STOPPED_PACKAGES}
211      * and wait for it to be received.
212      *
213      * <p/> This is required since app is in stopped state after installation by tradefed,
214      * and the receivers in another apps don't receive the broadcast without
215      * FLAG_INCLUDE_STOPPED_PACKAGES because they have never been opened even once
216      * after installation.
217      * By doing this we make sure that app is un-stopped when the system sends the broadcasts
218      * in the tests.
219      */
bindToBroadcastReceiverOfApp(String packageName, String broadcastReceiver)220     private void bindToBroadcastReceiverOfApp(String packageName, String broadcastReceiver) {
221         final Intent intent = new Intent(APP_RECEIVER_ACTION)
222                 .setComponent(new ComponentName(packageName, broadcastReceiver))
223                 .setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
224         CountDownLatch latch = new CountDownLatch(1);
225         sContext.sendOrderedBroadcast(
226                 intent,
227                 null /* receiverPermission */,
228                 new BroadcastReceiver() { /* resultReceiver */
229                     @Override public void onReceive(Context context, Intent intent) {
230                         latch.countDown();
231                     }
232                 },
233                 null /* scheduler */,
234                 Activity.RESULT_OK, /* initialCode */
235                 null /* initialData */,
236                 null /* initialExtras */);
237         try {
238             assertTrue("Timed out waiting for test broadcast to be received",
239                     latch.await(5_000, TimeUnit.MILLISECONDS));
240         } catch (InterruptedException e) {
241             throw new IllegalStateException("Interrupted", e);
242         }
243     }
244 
245     /**
246      * Sets the installer as {@link #INSTALLER_PACKAGE} for the target package.
247      */
setInstallerForPackage(String targetPackageName)248     private void setInstallerForPackage(String targetPackageName) {
249         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sContext.getPackageManager(),
250                 (pm) -> pm.setInstallerPackageName(targetPackageName, INSTALLER_PACKAGE));
251     }
252 
253     /**
254      * Resets the countdown latch in all the receivers.
255      */
resetReceivers()256     private void resetReceivers() {
257         mCallingAppBroadcastReceiver.reset();
258         mInstallerBroadcastInfoProvider.reset();
259         mTestAppBroadcastInfoProvider.reset();
260         mTestAppCreationInfoProvider.reset();
261         mTestAppConfigChangedInfoProvider.reset();
262         mInstallerAppCreationInfoProvider.reset();
263         mImeAppCreationInfoProvider.reset();
264         mImeChangedBroadcastReceiver.reset();
265     }
266 
267     @Test
testSetApplicationLocales_persistsAndSendsBroadcast()268     public void testSetApplicationLocales_persistsAndSendsBroadcast() throws Exception {
269         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
270 
271         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
272         assertTrue(mCallingAppBroadcastReceiver.await());
273         assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
274                 CALLING_PACKAGE, DEFAULT_APP_LOCALES);
275 
276         assertTrue(mInstallerBroadcastInfoProvider.await());
277         assertReceivedBroadcastContains(mInstallerBroadcastInfoProvider,
278                 CALLING_PACKAGE, DEFAULT_APP_LOCALES);
279     }
280 
281     @Test
testSetApplicationLocales_resetToEmptyLocales_persistsAndSendsBroadcast()282     public void testSetApplicationLocales_resetToEmptyLocales_persistsAndSendsBroadcast()
283             throws Exception {
284         // First set the locales to non-empty
285         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
286         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
287         assertTrue(mCallingAppBroadcastReceiver.await());
288         assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
289                 CALLING_PACKAGE, DEFAULT_APP_LOCALES);
290         mCallingAppBroadcastReceiver.reset();
291 
292         sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
293 
294         assertLocalesCorrectlySetForCallingApp(LocaleList.getEmptyLocaleList());
295         mCallingAppBroadcastReceiver.await();
296         assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
297                 CALLING_PACKAGE, LocaleList.getEmptyLocaleList());
298     }
299 
300     @Test
testSetApplicationLocales_sameLocalesEmpty_noBroadcastSent()301     public void testSetApplicationLocales_sameLocalesEmpty_noBroadcastSent() throws Exception {
302         // At the start of the test, no app-specific locales are set, so just try to set it to
303         //   empty again
304         sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
305 
306         assertLocalesCorrectlySetForCallingApp(LocaleList.getEmptyLocaleList());
307         mCallingAppBroadcastReceiver.assertNoBroadcastReceived();
308     }
309 
310     @Test
testSetApplicationLocales_sameLocalesNonEmpty_noBroadcastSent()311     public void testSetApplicationLocales_sameLocalesNonEmpty_noBroadcastSent() throws Exception {
312         // First set the locales to non-empty
313         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
314         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
315         assertTrue(mCallingAppBroadcastReceiver.await());
316         assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
317                 CALLING_PACKAGE, DEFAULT_APP_LOCALES);
318         mCallingAppBroadcastReceiver.reset();
319 
320         // Then reset to the same app-specific locales, and verify no broadcasts are sent
321         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
322 
323         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
324         mCallingAppBroadcastReceiver.assertNoBroadcastReceived();
325     }
326 
327     @Test
testSetApplicationLocales_getDefaultLocaleList_returnsCorrectList()328     public void testSetApplicationLocales_getDefaultLocaleList_returnsCorrectList()
329             throws Exception {
330         // Fetch the current system locales when there are no app-locales set.
331         LocaleList systemLocales = LocaleList.getDefault();
332         assertEquals(DEFAULT_SYSTEM_LOCALES, systemLocales);
333 
334         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
335 
336         // Wait for a while since LocaleList::getDefault could take a while to
337         // reflect the new app locales.
338         assertTrue(mCallingAppBroadcastReceiver.await());
339         assertEquals(combineLocales(DEFAULT_APP_LOCALES, systemLocales), LocaleList.getDefault());
340     }
341 
342     @Test
testSetApplicationLocales_forAnotherAppInForeground_persistsAndSendsBroadcast()343     public void testSetApplicationLocales_forAnotherAppInForeground_persistsAndSendsBroadcast()
344             throws Exception {
345         // Bring the TestApp to the foreground by invoking an activity and verify its visibility.
346         launchActivity(TEST_APP_MAIN_ACTIVITY);
347         mWmState.assertVisibility(TEST_APP_MAIN_ACTIVITY, /* visible*/ true);
348 
349         runWithShellPermissionIdentity(() ->
350                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
351                 Manifest.permission.CHANGE_CONFIGURATION);
352         assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
353 
354         assertTrue(mTestAppBroadcastInfoProvider.await());
355         assertReceivedBroadcastContains(mTestAppBroadcastInfoProvider, TEST_APP_PACKAGE,
356                 DEFAULT_APP_LOCALES);
357 
358         mInstallerBroadcastInfoProvider.await();
359         assertReceivedBroadcastContains(mInstallerBroadcastInfoProvider, TEST_APP_PACKAGE,
360                 DEFAULT_APP_LOCALES);
361     }
362 
363     @Test
testSetApplicationLocales_forAnotherAppInBackground_persistsAndSendsBroadcast()364     public void testSetApplicationLocales_forAnotherAppInBackground_persistsAndSendsBroadcast()
365             throws Exception {
366         // Invoke the app by launching an activity.
367         launchActivity(TEST_APP_MAIN_ACTIVITY);
368         // Send the TestApp to the background.
369         launchHomeActivity();
370         mWmState.waitAndAssertVisibilityGone(TEST_APP_MAIN_ACTIVITY);
371 
372         runWithShellPermissionIdentity(() ->
373                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
374                 Manifest.permission.CHANGE_CONFIGURATION);
375 
376         assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
377 
378         assertTrue(mTestAppBroadcastInfoProvider.await());
379         assertReceivedBroadcastContains(mTestAppBroadcastInfoProvider, TEST_APP_PACKAGE,
380                 DEFAULT_APP_LOCALES);
381 
382         assertTrue(mInstallerBroadcastInfoProvider.await());
383         assertReceivedBroadcastContains(mInstallerBroadcastInfoProvider, TEST_APP_PACKAGE,
384                 DEFAULT_APP_LOCALES);
385     }
386 
387     @Test
testSetApplicationLocales_forAnotherApp_persistsOnAppRestart()388     public void testSetApplicationLocales_forAnotherApp_persistsOnAppRestart() throws Exception {
389         runWithShellPermissionIdentity(() ->
390                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
391                 Manifest.permission.CHANGE_CONFIGURATION);
392 
393         // Re-start the app by starting an activity and check if locales correctly
394         // received by the app and listen to the broadcast for result from the app.
395         launchActivity(TEST_APP_MAIN_ACTIVITY, extraString(EXTRA_QUERY_LOCALES, "true"));
396 
397         assertTrue(mTestAppCreationInfoProvider.await());
398         assertReceivedBroadcastContains(mTestAppCreationInfoProvider, TEST_APP_PACKAGE,
399                 DEFAULT_APP_LOCALES);
400     }
401 
402     @Test
403     public void
testSetApplicationLocales_wthoutPermissionforAnotherApp_throwsExceptionAndNoBroadcast()404             testSetApplicationLocales_wthoutPermissionforAnotherApp_throwsExceptionAndNoBroadcast()
405             throws Exception {
406         try {
407             sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
408         } catch (SecurityException e) {
409             // expected as not having appropriate permission to change locales for another app.
410         }
411 
412         // Since the locales weren't allowed to persist, no broadcasts should be sent by the system.
413         mTestAppBroadcastInfoProvider.assertNoBroadcastReceived();
414         mInstallerBroadcastInfoProvider.assertNoBroadcastReceived();
415     }
416 
417     @Test
testSetApplicationLocales_forAnotherAppInForeground_callsOnConfigChanged()418     public void testSetApplicationLocales_forAnotherAppInForeground_callsOnConfigChanged()
419             throws Exception {
420         // Bring the TestApp to the foreground by invoking an activity and verify its visibility.
421         launchActivity(TEST_APP_MAIN_ACTIVITY);
422         mWmState.assertVisibility(TEST_APP_MAIN_ACTIVITY, /* visible*/ true);
423 
424         runWithShellPermissionIdentity(() ->
425                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
426                 Manifest.permission.CHANGE_CONFIGURATION);
427         assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
428 
429         assertTrue(mTestAppConfigChangedInfoProvider.await());
430         assertReceivedBroadcastContains(mTestAppConfigChangedInfoProvider, TEST_APP_PACKAGE,
431                 combineLocales(DEFAULT_APP_LOCALES, DEFAULT_SYSTEM_LOCALES));
432     }
433 
434     @Test
testSetApplicationLocales_withReadAppSpecificLocalesPermission_receivesBroadcast()435     public void testSetApplicationLocales_withReadAppSpecificLocalesPermission_receivesBroadcast()
436             throws Exception {
437 
438         BlockingBroadcastReceiver appSpecificLocaleBroadcastReceiver =
439                 new BlockingBroadcastReceiver();
440         sContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
441                 new IntentFilter(Intent.ACTION_APPLICATION_LOCALE_CHANGED));
442 
443         // Hold Manifest.permission.READ_APP_SPECIFIC_LOCALES while the broadcast is sent,
444         //   so that we receive it
445         runWithShellPermissionIdentity(() -> {
446             // Tell the test app to change its app-specific locales
447             launchActivity(TEST_APP_MAIN_ACTIVITY, extraString(EXTRA_SET_LOCALES,
448                     DEFAULT_APP_LOCALES.toLanguageTags()));
449             assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
450 
451             appSpecificLocaleBroadcastReceiver.await();
452         }, Manifest.permission.READ_APP_SPECIFIC_LOCALES);
453 
454         assertReceivedBroadcastContains(appSpecificLocaleBroadcastReceiver,
455                 TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
456     }
457 
458 
459     @Test
testSetApplicationLocales_appWithoutPermission_noBroadcastsReceived()460     public void testSetApplicationLocales_appWithoutPermission_noBroadcastsReceived()
461             throws Exception {
462 
463         BlockingBroadcastReceiver appSpecificLocaleBroadcastReceiver =
464                 new BlockingBroadcastReceiver();
465         sContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
466                 new IntentFilter(Intent.ACTION_APPLICATION_LOCALE_CHANGED));
467 
468         // Tell the test app to change its app-specific locales
469         launchActivity(TEST_APP_MAIN_ACTIVITY, extraString(EXTRA_SET_LOCALES,
470                 DEFAULT_APP_LOCALES.toLanguageTags()));
471         assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
472 
473         // Ensure that no broadcasts were received since the change was for another app (the Test
474         //   App) and we are neither the Test App's installer, nor do we hold
475         //   Manifest.permission.READ_APP_SPECIFIC_LOCALES
476         appSpecificLocaleBroadcastReceiver.assertNoBroadcastReceived();
477         mCallingAppBroadcastReceiver.assertNoBroadcastReceived();
478     }
479 
480     @Test
testGetApplicationLocales_callerIsInstaller_returnsLocales()481     public void testGetApplicationLocales_callerIsInstaller_returnsLocales() throws Exception {
482         // Set locales for calling app
483         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
484 
485         // Make sure that locales were set for the app.
486         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
487         // Tell the installer app to fetch locales for calling package.
488         launchActivity(INSTALLER_APP_MAIN_ACTIVITY,
489                 extraString(EXTRA_QUERY_LOCALES, CALLING_PACKAGE));
490 
491         assertTrue(mInstallerAppCreationInfoProvider.await());
492         assertReceivedBroadcastContains(mInstallerAppCreationInfoProvider,
493                 CALLING_PACKAGE, DEFAULT_APP_LOCALES);
494     }
495 
496     @Test
testGetApplicationLocales_nonCurrentIme_noBroadcastReceived()497     public void testGetApplicationLocales_nonCurrentIme_noBroadcastReceived()
498             throws Exception {
499         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
500 
501         // Make sure that locales were set for the app.
502         assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
503 
504         // Tell the IME app to fetch locales for the test app.
505         launchActivity(IME_APP_MAIN_ACTIVITY,
506                 extraString(EXTRA_QUERY_LOCALES, CALLING_PACKAGE));
507 
508         // Since the locales weren't allowed to get by none current IME app, no broadcast was sent
509         // by test IME
510         mImeAppCreationInfoProvider.assertNoBroadcastReceived();
511     }
512 
513     @Test
testGetApplicationLocales_currentImeGetNonForegroundAppLocales_noBroadcastReceived()514     public void testGetApplicationLocales_currentImeGetNonForegroundAppLocales_noBroadcastReceived()
515             throws Exception {
516         //Set the IME app as the active IME.
517         setTestImeAsActive();
518 
519         // Invoke the app by launching an activity.
520         launchActivity(TEST_APP_MAIN_ACTIVITY);
521         // Send the TestApp to the background.
522         launchHomeActivity();
523         mWmState.waitAndAssertVisibilityGone(TEST_APP_MAIN_ACTIVITY);
524 
525         // Set test app's locales
526         runWithShellPermissionIdentity(() ->
527                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
528                 Manifest.permission.CHANGE_CONFIGURATION);
529         // Tell the IME app to fetch locales for the background app.
530         launchActivity(IME_APP_MAIN_ACTIVITY,
531                 extraString(EXTRA_QUERY_LOCALES, TEST_APP_PACKAGE));
532 
533         // Since it is not allowed for the current IME app to get locales of non-foreground app, no
534         // broadcast was sent by test IME
535         mImeAppCreationInfoProvider.assertNoBroadcastReceived();
536     }
537 
538     @Test
testGetApplicationLocales_currentImeGetForegroundAppLocales_returnsLocales()539     public void testGetApplicationLocales_currentImeGetForegroundAppLocales_returnsLocales()
540             throws Exception {
541         //Set the IME app as the active IME.
542         setTestImeAsActive();
543 
544         //Set app locales
545         sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
546         // Tell the IME app to fetch locales for the foreground app.
547         launchActivity(IME_APP_MAIN_ACTIVITY,
548                 extraString(EXTRA_QUERY_LOCALES, CALLING_PACKAGE));
549 
550         assertTrue(mImeAppCreationInfoProvider.await());
551         assertReceivedBroadcastContains(mImeAppCreationInfoProvider, CALLING_PACKAGE,
552                 DEFAULT_APP_LOCALES);
553     }
554     @Test(expected = SecurityException.class)
testGetApplicationLocales_withoutPermissionforAnotherApp_throwsException()555     public void testGetApplicationLocales_withoutPermissionforAnotherApp_throwsException()
556             throws Exception {
557         sLocaleManager.getApplicationLocales(TEST_APP_PACKAGE);
558         fail("Expected SecurityException due to not having appropriate permission "
559                 + "for querying locales of another app.");
560     }
561 
562     @Test
testGetApplicationLocales_noAppLocalesSet_returnsEmptyList()563     public void testGetApplicationLocales_noAppLocalesSet_returnsEmptyList() {
564         // When app-specific locales aren't set, we expect get api to return empty list
565         // and not throw any error.
566         assertEquals(LocaleList.getEmptyLocaleList(), sLocaleManager.getApplicationLocales());
567     }
568 
569     @Test(expected = NullPointerException.class)
testGetApplicationLocales_nullPackageName_throwsNPE()570     public void testGetApplicationLocales_nullPackageName_throwsNPE() {
571         sLocaleManager.getApplicationLocales(/* appPackageName= */ null);
572         fail("Expected NullPointerException due to null package name argument.");
573     }
574 
575     @Test(expected = IllegalArgumentException.class)
testGetApplicationLocales_invalidPackageName_throwsIllegalArgumentException()576     public void testGetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
577         sLocaleManager.getApplicationLocales(NON_EXISTENT_PACKAGE);
578         fail("Expected IllegalArgumentException due to invalid package.");
579     }
580 
581     @Test(expected = NullPointerException.class)
testSetApplicationLocales_nullPackageName_throwsNPE()582     public void testSetApplicationLocales_nullPackageName_throwsNPE() {
583         sLocaleManager.setApplicationLocales(/* appPackageName= */ null, DEFAULT_APP_LOCALES);
584         fail("Expected NullPointerException due to null package name argument.");
585     }
586 
587     @Test(expected = NullPointerException.class)
testSetApplicationLocales_nullLocales_throwsNPE()588     public void testSetApplicationLocales_nullLocales_throwsNPE() {
589         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, /* locales= */ null);
590         fail("Expected NullPointerException due to null locales argument.");
591     }
592 
593     @Test(expected = IllegalArgumentException.class)
testSetApplicationLocales_invalidPackageName_throwsIllegalArgumentException()594     public void testSetApplicationLocales_invalidPackageName_throwsIllegalArgumentException() {
595         sLocaleManager.setApplicationLocales(NON_EXISTENT_PACKAGE, DEFAULT_APP_LOCALES);
596         fail("Expected IllegalArgumentException due to invalid package.");
597     }
598 
599     /**
600      * Verifies that the locales are correctly set for calling(instrumentation) app
601      * by fetching locales of the app with a binder call.
602      */
assertLocalesCorrectlySetForCallingApp(LocaleList expectedLocales)603     private void assertLocalesCorrectlySetForCallingApp(LocaleList expectedLocales) {
604         assertEquals(expectedLocales, sLocaleManager.getApplicationLocales());
605     }
606 
607     /**
608      * Verifies that the locales are correctly set for another package
609      * by fetching locales of the app with a binder call.
610      */
assertLocalesCorrectlySetForAnotherApp(String packageName, LocaleList expectedLocales)611     private void assertLocalesCorrectlySetForAnotherApp(String packageName,
612             LocaleList expectedLocales) throws Exception {
613         assertEquals(expectedLocales, getApplicationLocales(packageName));
614     }
615 
getApplicationLocales(String packageName)616     private LocaleList getApplicationLocales(String packageName) throws Exception {
617         return callWithShellPermissionIdentity(() ->
618                 sLocaleManager.getApplicationLocales(packageName),
619                 Manifest.permission.READ_APP_SPECIFIC_LOCALES);
620     }
621 
622     /**
623      * Verifies that the broadcast received in the relevant apps have the correct information
624      * in the intent extras. It verifies the below extras:
625      * <ul>
626      * <li> {@link Intent#EXTRA_PACKAGE_NAME}
627      * <li> {@link Intent#EXTRA_LOCALE_LIST}
628      * </ul>
629      */
assertReceivedBroadcastContains(BlockingBroadcastReceiver receiver, String expectedPackageName, LocaleList expectedLocales)630     private void assertReceivedBroadcastContains(BlockingBroadcastReceiver receiver,
631             String expectedPackageName, LocaleList expectedLocales) {
632         assertEquals(expectedPackageName, receiver.getPackageName());
633         assertEquals(expectedLocales, receiver.getLocales());
634     }
635 
636     /**
637      * Creates a combined {@link LocaleList} by placing app locales before system locales and
638      * dropping any duplicates.
639      *
640      * We need to combine them since {@link LocaleList} does not have any direct way like
641      * a normal list to check if locale or subset of locales is present.
642      */
combineLocales(LocaleList appLocales, LocaleList systemLocales)643     private LocaleList combineLocales(LocaleList appLocales, LocaleList systemLocales) {
644         Locale[] combinedLocales = new Locale[appLocales.size() + systemLocales.size()];
645         for (int i = 0; i < appLocales.size(); i++) {
646             combinedLocales[i] = appLocales.get(i);
647         }
648         for (int i = 0; i < systemLocales.size(); i++) {
649             combinedLocales[i + appLocales.size()] = systemLocales.get(i);
650         }
651         // Constructor of {@link LocaleList} removes duplicates.
652         return new LocaleList(combinedLocales);
653     }
654 
655     /**
656      * Reset the locales for the apps to empty list as they could have been set previously
657      * by some other test.
658      */
resetAppLocalesAsEmpty()659     private void resetAppLocalesAsEmpty() throws Exception {
660         // Reset locales for the calling app.
661         sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
662 
663         // Reset locales for the TestApp.
664         runWithShellPermissionIdentity(() ->
665                         sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE,
666                                 LocaleList.getEmptyLocaleList()),
667                 Manifest.permission.CHANGE_CONFIGURATION);
668     }
669 
resetImes()670     private void resetImes() throws Exception {
671         if (mNeedsImeReset) {
672             ShellUtils.runShellCommand("ime reset");
673             mNeedsImeReset = false;
674         }
675 
676         mImeChangedBroadcastReceiver.await();
677         assertNotEquals("Should be reset to the default IME", mTestIme,
678                 mImeChangedBroadcastReceiver.getInputMethodId());
679         mImeChangedBroadcastReceiver.reset();
680     }
681 
setTestImeAsActive()682     private void setTestImeAsActive() throws Exception {
683         ShellUtils.runShellCommand("ime enable " + mTestIme);
684         ShellUtils.runShellCommand("ime set " + mTestIme);
685         mNeedsImeReset = true;
686 
687         assertTrue(mImeChangedBroadcastReceiver.await());
688         assertEquals(mTestIme, mImeChangedBroadcastReceiver.getInputMethodId());
689         mImeChangedBroadcastReceiver.reset();
690     }
691 }
692