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