1 /** 2 * Copyright (C) 2017 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 package com.android.cellbroadcastreceiver.unit; 17 18 import static androidx.test.espresso.Espresso.onView; 19 import static androidx.test.espresso.action.ViewActions.click; 20 import static androidx.test.espresso.matcher.ViewMatchers.withText; 21 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.Matchers.any; 24 import static org.mockito.Matchers.anyInt; 25 import static org.mockito.Matchers.anyString; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.eq; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.SharedPreferences; 37 import android.content.res.Configuration; 38 import android.content.res.Resources; 39 import android.os.Looper; 40 import android.os.RemoteException; 41 import android.os.UserManager; 42 import android.telephony.SubscriptionInfo; 43 import android.telephony.SubscriptionManager; 44 45 import androidx.preference.Preference; 46 import androidx.preference.PreferenceManager; 47 import androidx.preference.TwoStatePreference; 48 import androidx.test.InstrumentationRegistry; 49 import androidx.test.filters.FlakyTest; 50 import androidx.test.uiautomator.UiDevice; 51 52 import com.android.cellbroadcastreceiver.CellBroadcastChannelManager; 53 import com.android.cellbroadcastreceiver.CellBroadcastConfigService; 54 import com.android.cellbroadcastreceiver.CellBroadcastSettings; 55 import com.android.cellbroadcastreceiver.R; 56 import com.android.modules.utils.build.SdkLevel; 57 58 import junit.framework.Assert; 59 60 import org.junit.After; 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.mockito.ArgumentCaptor; 64 import org.mockito.Captor; 65 import org.mockito.Mock; 66 import org.mockito.MockitoAnnotations; 67 68 import java.lang.reflect.Field; 69 import java.util.Locale; 70 71 72 public class CellBroadcastSettingsTest extends 73 CellBroadcastActivityTestCase<CellBroadcastSettings> { 74 75 private UiDevice mDevice; 76 private static final long DEVICE_WAIT_TIME = 1000L; 77 private static final String ROAMING_OPERATOR_SUPPORTED = "roaming_operator_supported"; 78 private static final String ACTION_TESTING_MODE_CHANGED = 79 "com.android.cellbroadcastreceiver.intent.ACTION_TESTING_MODE_CHANGED"; 80 private static final String TESTING_MODE = "testing_mode"; 81 private static final String MASTER_TOGGLE_ENABLED = "enable_alerts_master_toggle"; 82 private static final int PREFERENCE_PUT_TYPE_BOOL = 0; 83 private static final int PREFERENCE_PUT_TYPE_STRING = 1; 84 private static final long TEST_TIMEOUT_MILLIS = 1000L; 85 86 @Captor 87 private ArgumentCaptor<Intent> mIntent; 88 @Mock 89 private UserManager mUserManager; 90 @Mock 91 private SharedPreferences mMockedSharedPreference; 92 @Mock 93 private SharedPreferences.Editor mEditor; 94 95 FakeSharedPreferences mFakeSharedPreferences = new FakeSharedPreferences(); 96 CellBroadcastSettingsTest()97 public CellBroadcastSettingsTest() { 98 super(CellBroadcastSettings.class); 99 } 100 101 @Before setUp()102 public void setUp() throws Exception { 103 super.setUp(); 104 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 105 MockitoAnnotations.initMocks(this); 106 CellBroadcastSettings.resetResourcesCache(); 107 } 108 109 @After tearDown()110 public void tearDown() throws Exception { 111 CellBroadcastSettings.resetResourcesCache(); 112 CellBroadcastChannelManager.clearAllCellBroadcastChannelRanges(); 113 super.tearDown(); 114 } 115 116 @InstrumentationTest 117 // This test has a module dependency, so it is disabled for OEM testing because it is not a true 118 // unit test 119 @FlakyTest 120 @Test testRotateAlertReminderDialogOpen()121 public void testRotateAlertReminderDialogOpen() throws InterruptedException { 122 try { 123 mDevice.wakeUp(); 124 mDevice.pressMenu(); 125 } catch (RemoteException exception) { 126 Assert.fail("Exception " + exception); 127 } 128 129 InstrumentationRegistry.getInstrumentation().startActivitySync(createActivityIntent()); 130 int w = mDevice.getDisplayWidth(); 131 int h = mDevice.getDisplayHeight(); 132 133 waitUntilDialogOpens(()-> { 134 mDevice.swipe(w / 2 /* start X */, 135 h / 2 /* start Y */, 136 w / 2 /* end X */, 137 0 /* end Y */, 138 100 /* steps */); 139 140 openAlertReminderDialog(); 141 }, DEVICE_WAIT_TIME); 142 143 try { 144 mDevice.setOrientationLeft(); 145 mDevice.setOrientationNatural(); 146 mDevice.setOrientationRight(); 147 } catch (Exception e) { 148 Assert.fail("Exception " + e); 149 } 150 } 151 152 @Test testResetAllPreferences()153 public void testResetAllPreferences() throws Throwable { 154 Looper.prepare(); 155 mContext.injectSharedPreferences(mFakeSharedPreferences); 156 // set a few preferences so we can verify they are reset to the default 157 PreferenceManager.getDefaultSharedPreferences(mContext).edit() 158 .putBoolean(CellBroadcastSettings.KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE, true) 159 .putBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, false).apply(); 160 assertTrue("receive_cmas_in_second_language was not set to true", 161 PreferenceManager.getDefaultSharedPreferences(mContext) 162 .getBoolean(CellBroadcastSettings.KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE, 163 false)); 164 assertFalse("enable_alert_vibrate was not set to false", 165 PreferenceManager.getDefaultSharedPreferences(mContext) 166 .getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, true)); 167 168 // see preferences.xml for default values. 169 // receive_cmas_in_second_language is false by default 170 // enable_alert_vibrate is true by default 171 CellBroadcastSettings.resetAllPreferences(mContext); 172 173 assertFalse("receive_cmas_in_second_language was not reset to the default (false)", 174 PreferenceManager.getDefaultSharedPreferences(mContext) 175 .getBoolean(CellBroadcastSettings.KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE, true)); 176 assertTrue("enable_alert_vibrate was not reset to the default (true)", 177 PreferenceManager.getDefaultSharedPreferences(mContext) 178 .getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, false)); 179 } 180 181 @Test testHasAnyPreferenceChanged()182 public void testHasAnyPreferenceChanged() { 183 mContext.injectSharedPreferences(mFakeSharedPreferences); 184 assertFalse(CellBroadcastSettings.hasAnyPreferenceChanged(mContext)); 185 PreferenceManager.getDefaultSharedPreferences(mContext).edit() 186 .putBoolean("any_preference_changed_by_user", true).apply(); 187 assertTrue(CellBroadcastSettings.hasAnyPreferenceChanged(mContext)); 188 } 189 190 @Test testPreferenceChangeByUser()191 public void testPreferenceChangeByUser() { 192 Context mockContext = mock(Context.class); 193 Looper.prepare(); 194 CellBroadcastSettings.CellBroadcastSettingsFragment fragment = 195 new CellBroadcastSettings.CellBroadcastSettingsFragment(); 196 doReturn(mUserManager).when(mockContext).getSystemService(Context.USER_SERVICE); 197 doReturn(true).when(mUserManager).isSystemUser(); 198 doReturn(mMockedSharedPreference).when(mockContext).getSharedPreferences(anyString(), 199 anyInt()); 200 doReturn(mEditor).when(mMockedSharedPreference).edit(); 201 doReturn(mEditor).when(mEditor).putBoolean(anyString(), anyBoolean()); 202 203 fragment.onPreferenceChangedByUser(mockContext); 204 205 verify(mockContext, times(1)).startService(mIntent.capture()); 206 assertEquals(CellBroadcastConfigService.ACTION_ENABLE_CHANNELS, 207 (String) mIntent.getValue().getAction()); 208 } 209 210 @Test testGetResources()211 public void testGetResources() { 212 Context mockContext = mock(Context.class); 213 Resources mockResources = mock(Resources.class); 214 Configuration configuration = new Configuration(); 215 doReturn(mockResources).when(mockContext).getResources(); 216 doReturn(configuration).when(mockResources).getConfiguration(); 217 218 CellBroadcastSettings.getResources( 219 mockContext, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 220 verify(mockContext, never()).getSystemService(anyString()); 221 verify(mockContext, times(1)).getResources(); 222 223 CellBroadcastSettings.getResources( 224 mockContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 225 verify(mockContext, never()).getSystemService(anyString()); 226 verify(mockContext, times(2)).getResources(); 227 228 Context mockContext2 = mock(Context.class); 229 doReturn(mockResources).when(mockContext2).getResources(); 230 SubscriptionManager mockSubManager = mock(SubscriptionManager.class); 231 doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE).when(mockContext) 232 .getSystemServiceName(eq(SubscriptionManager.class)); 233 doReturn(mockSubManager).when(mockContext).getSystemService( 234 eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)); 235 SubscriptionInfo mockSubInfo = mock(SubscriptionInfo.class); 236 doReturn(mockSubInfo).when(mockSubManager).getActiveSubscriptionInfo(anyInt()); 237 doReturn(0).when(mockSubInfo).getMcc(); 238 doReturn(0).when(mockSubInfo).getMnc(); 239 doReturn(mockContext2).when(mockContext).createConfigurationContext(any()); 240 241 // The resource will not be cached for the sub 242 CellBroadcastSettings.getResources( 243 mockContext, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 1); 244 245 verify(mockContext, times(1)).createConfigurationContext(any()); 246 verify(mockContext2, times(1)).getResources(); 247 248 // The resources will be cached for ths sub 249 doReturn(123).when(mockSubInfo).getMcc(); 250 doReturn(456).when(mockSubInfo).getMnc(); 251 // The cache logic is updated on S 252 final int timesExpected = SdkLevel.isAtLeastS() ? 2 : 1; 253 254 CellBroadcastSettings.getResources( 255 mockContext, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 1); 256 257 verify(mockContext, times(timesExpected)).createConfigurationContext(any()); 258 verify(mockContext2, times(timesExpected)).getResources(); 259 260 // The resources should be read from the cached directly 261 CellBroadcastSettings.getResources( 262 mockContext, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 1); 263 264 verify(mockContext, times(timesExpected)).createConfigurationContext(any()); 265 verify(mockContext2, times(timesExpected)).getResources(); 266 267 Configuration configuration2 = new Configuration(); 268 configuration2.setLocale(Locale.ROOT); 269 doReturn(configuration2).when(mockResources).getConfiguration(); 270 CellBroadcastSettings.getResources( 271 mockContext, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID - 2); 272 273 verify(mockContext, times(timesExpected + 1)).createConfigurationContext(any()); 274 verify(mockContext2, times(timesExpected + 1)).getResources(); 275 } 276 277 @Test testGetResourcesByOperator()278 public void testGetResourcesByOperator() { 279 Context mockContext = mock(Context.class); 280 Resources mockResources = mock(Resources.class); 281 doReturn(mockResources).when(mockContext).getResources(); 282 283 CellBroadcastSettings.getResourcesByOperator(mockContext, 284 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, ""); 285 verify(mockContext, never()).createConfigurationContext(any()); 286 verify(mockContext, times(1)).getResources(); 287 288 int mcc = 123; 289 int mnc = 456; 290 Context mockContext2 = mock(Context.class); 291 ArgumentCaptor<Configuration> captorConfig = ArgumentCaptor.forClass(Configuration.class); 292 doReturn(mockResources).when(mockContext2).getResources(); 293 doReturn(mockContext2).when(mockContext).createConfigurationContext(any()); 294 295 CellBroadcastSettings.getResourcesByOperator(mockContext, 296 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 297 Integer.toString(mcc) + Integer.toString(mnc)); 298 verify(mockContext, times(1)).getResources(); 299 verify(mockContext2, times(1)).getResources(); 300 verify(mockContext, times(1)).createConfigurationContext(captorConfig.capture()); 301 assertEquals(mcc, captorConfig.getValue().mcc); 302 assertEquals(mnc, captorConfig.getValue().mnc); 303 } 304 waitUntilDialogOpens(Runnable r, long maxWaitMs)305 public void waitUntilDialogOpens(Runnable r, long maxWaitMs) { 306 long waitTime = 0; 307 while (waitTime < maxWaitMs) { 308 try { 309 r.run(); 310 // if the assert succeeds, return 311 return; 312 } catch (Exception e) { 313 waitTime += 100; 314 waitForMs(100); 315 } 316 } 317 // if timed out, run one last time without catching exception 318 r.run(); 319 } 320 321 @Override createActivityIntent()322 protected Intent createActivityIntent() { 323 Intent intent = new Intent(mContext, CellBroadcastSettings.class); 324 intent.setPackage("com.android.cellbroadcastreceiver"); 325 intent.setAction("android.intent.action.MAIN"); 326 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 327 return intent; 328 } 329 openAlertReminderDialog()330 private void openAlertReminderDialog() { 331 onView(withText(mContext.getString(com.android.cellbroadcastreceiver.R 332 .string.alert_reminder_interval_title))).perform(click()); 333 } 334 335 @Test testDisabledExtremeToggle()336 public void testDisabledExtremeToggle() throws Throwable { 337 SubscriptionManager mockSubManager = mock(SubscriptionManager.class); 338 injectSystemService(SubscriptionManager.class, mockSubManager); 339 SubscriptionInfo mockSubInfo = mock(SubscriptionInfo.class); 340 doReturn(mockSubInfo).when(mockSubManager).getActiveSubscriptionInfo(anyInt()); 341 342 setPreference(PREFERENCE_PUT_TYPE_BOOL, MASTER_TOGGLE_ENABLED, "true"); 343 doReturn(true).when(mContext.getResources()).getBoolean( 344 R.bool.extreme_threat_alerts_enabled_default); 345 doReturn(false).when(mContext.getResources()).getBoolean( 346 R.bool.disable_extreme_alert_settings); 347 348 CellBroadcastSettings cellBroadcastSettingActivity = startActivity(); 349 350 TwoStatePreference extremeCheckBox = 351 cellBroadcastSettingActivity.mCellBroadcastSettingsFragment.findPreference( 352 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS); 353 354 assertTrue(extremeCheckBox.isEnabled()); 355 356 stopActivity(); 357 waitForMs(100); 358 359 doReturn(true).when(mContext.getResources()).getBoolean( 360 R.bool.disable_extreme_alert_settings); 361 362 cellBroadcastSettingActivity.mCellBroadcastSettingsFragment 363 .initAlertsToggleDisabledAsNeeded(); 364 cellBroadcastSettingActivity.mCellBroadcastSettingsFragment.onResume(); 365 366 assertFalse(extremeCheckBox.isEnabled()); 367 } 368 369 @Test testTopIntroductionForRoamingSupport()370 public void testTopIntroductionForRoamingSupport() throws Throwable { 371 String topIntroRoamingText = "test"; 372 doReturn(topIntroRoamingText).when(mContext.getResources()).getString( 373 eq(R.string.top_intro_roaming_text)); 374 setSubscriptionManager(); 375 setPreference(PREFERENCE_PUT_TYPE_STRING, ROAMING_OPERATOR_SUPPORTED, "XXX"); 376 377 CellBroadcastSettings settings = startActivity(); 378 379 Preference topIntroPreference = settings.mCellBroadcastSettingsFragment.findPreference( 380 CellBroadcastSettings.KEY_PREFS_TOP_INTRO); 381 assertEquals(topIntroRoamingText, topIntroPreference.getTitle().toString()); 382 } 383 384 @Test testDoNotShowTestCheckBox()385 public void testDoNotShowTestCheckBox() throws Throwable { 386 setSubscriptionManager(); 387 setPreference(PREFERENCE_PUT_TYPE_BOOL, TESTING_MODE, "false"); 388 doReturn(false).when(mContext.getResources()).getBoolean( 389 eq(R.bool.show_separate_exercise_settings)); 390 doReturn(false).when(mContext.getResources()).getBoolean( 391 eq(R.bool.show_separate_operator_defined_settings)); 392 doReturn(new String[]{"0x111D:rat=gsm, emergency=true"}).when(mContext.getResources()) 393 .getStringArray(eq(R.array.exercise_alert_range_strings)); 394 doReturn(new String[]{"0x111E:rat=gsm, emergency=true"}).when(mContext.getResources()) 395 .getStringArray(eq(R.array.operator_defined_alert_range_strings)); 396 CellBroadcastSettings settings = startActivity(); 397 398 TwoStatePreference exerciseTestCheckBox = 399 settings.mCellBroadcastSettingsFragment.findPreference( 400 CellBroadcastSettings.KEY_ENABLE_EXERCISE_ALERTS); 401 TwoStatePreference operatorDefinedCheckBox = 402 settings.mCellBroadcastSettingsFragment.findPreference( 403 CellBroadcastSettings.KEY_OPERATOR_DEFINED_ALERTS); 404 405 // received the ACTION_TESTING_MODE_CHANGED, do not show exerciseTestCheckBox & 406 // operatorDefinedCheckBox 407 Field fieldTestingModeChangedReceiver = 408 CellBroadcastSettings.CellBroadcastSettingsFragment.class.getDeclaredField( 409 "mTestingModeChangedReceiver"); 410 fieldTestingModeChangedReceiver.setAccessible(true); 411 BroadcastReceiver broadcastReceiver = 412 (BroadcastReceiver) fieldTestingModeChangedReceiver.get( 413 settings.mCellBroadcastSettingsFragment); 414 broadcastReceiver.onReceive(mContext, new Intent().setAction(ACTION_TESTING_MODE_CHANGED)); 415 416 assertFalse(exerciseTestCheckBox.isVisible()); 417 assertFalse(operatorDefinedCheckBox.isVisible()); 418 } 419 420 @Test testShowTestCheckBox()421 public void testShowTestCheckBox() throws Throwable { 422 setSubscriptionManager(); 423 setPreference(PREFERENCE_PUT_TYPE_BOOL, TESTING_MODE, "true"); 424 doReturn(true).when(mContext.getResources()).getBoolean( 425 eq(R.bool.show_separate_exercise_settings)); 426 doReturn(true).when(mContext.getResources()).getBoolean( 427 eq(R.bool.show_separate_operator_defined_settings)); 428 doReturn(new String[]{"0x111D:rat=gsm, emergency=true"}).when(mContext.getResources()) 429 .getStringArray(eq(R.array.exercise_alert_range_strings)); 430 doReturn(new String[]{"0x111E:rat=gsm, emergency=true"}).when(mContext.getResources()) 431 .getStringArray(eq(R.array.operator_defined_alert_range_strings)); 432 CellBroadcastSettings settings = startActivity(); 433 434 TwoStatePreference exerciseTestCheckBox = 435 settings.mCellBroadcastSettingsFragment.findPreference( 436 CellBroadcastSettings.KEY_ENABLE_EXERCISE_ALERTS); 437 TwoStatePreference operatorDefinedCheckBox = 438 settings.mCellBroadcastSettingsFragment.findPreference( 439 CellBroadcastSettings.KEY_OPERATOR_DEFINED_ALERTS); 440 441 // received the ACTION_TESTING_MODE_CHANGED, show exerciseTestCheckBox & 442 // operatorDefinedCheckBox 443 Field fieldTestingModeChangedReceiver = 444 CellBroadcastSettings.CellBroadcastSettingsFragment.class.getDeclaredField( 445 "mTestingModeChangedReceiver"); 446 fieldTestingModeChangedReceiver.setAccessible(true); 447 BroadcastReceiver broadcastReceiver = 448 (BroadcastReceiver) fieldTestingModeChangedReceiver.get( 449 settings.mCellBroadcastSettingsFragment); 450 broadcastReceiver.onReceive(mContext, new Intent().setAction(ACTION_TESTING_MODE_CHANGED)); 451 452 waitForChange(() -> exerciseTestCheckBox.isVisible(), TEST_TIMEOUT_MILLIS); 453 assertTrue(exerciseTestCheckBox.isVisible()); 454 waitForChange(() -> operatorDefinedCheckBox.isVisible(), TEST_TIMEOUT_MILLIS); 455 assertTrue(operatorDefinedCheckBox.isVisible()); 456 } 457 setSubscriptionManager()458 private void setSubscriptionManager() { 459 SubscriptionManager mockSubManager = mock(SubscriptionManager.class); 460 injectSystemService(SubscriptionManager.class, mockSubManager); 461 SubscriptionInfo mockSubInfo = mock(SubscriptionInfo.class); 462 doReturn(mockSubInfo).when(mockSubManager).getActiveSubscriptionInfo(anyInt()); 463 } 464 setPreference(int putType, String key, String value)465 private void setPreference(int putType, String key, String value) { 466 mContext.injectSharedPreferences(mFakeSharedPreferences); 467 switch (putType) { 468 case PREFERENCE_PUT_TYPE_BOOL: 469 PreferenceManager.getDefaultSharedPreferences(mContext).edit() 470 .putBoolean(key, Boolean.valueOf(value)).apply(); 471 break; 472 case PREFERENCE_PUT_TYPE_STRING: 473 PreferenceManager.getDefaultSharedPreferences(mContext).edit() 474 .putString(key, value).apply(); 475 break; 476 } 477 } 478 } 479