1 /* 2 * Copyright (C) 2022 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 com.android.cellbroadcastreceiver.compliancetests; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assume.assumeTrue; 22 23 import android.app.KeyguardManager; 24 import android.app.LocaleManager; 25 import android.app.UiAutomation; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.PackageManager; 30 import android.net.Uri; 31 import android.os.LocaleList; 32 import android.provider.Settings; 33 import android.provider.Telephony; 34 import android.support.test.uiautomator.By; 35 import android.support.test.uiautomator.BySelector; 36 import android.support.test.uiautomator.UiObject; 37 import android.support.test.uiautomator.UiObject2; 38 import android.support.test.uiautomator.UiObjectNotFoundException; 39 import android.support.test.uiautomator.UiScrollable; 40 import android.support.test.uiautomator.UiSelector; 41 import android.support.test.uiautomator.Until; 42 import android.text.TextUtils; 43 import android.widget.LinearLayout; 44 45 import com.android.internal.util.HexDump; 46 47 import junitparams.JUnitParamsRunner; 48 import junitparams.Parameters; 49 50 import org.json.JSONObject; 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.Iterator; 57 58 @RunWith(JUnitParamsRunner.class) 59 public class CellBroadcastUiTest extends CellBroadcastBaseTest { 60 private static final String TAG = "CellBroadcastUiTest"; 61 private static final int UI_TIMEOUT = 10000; 62 private static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts"); 63 private static final String SELECT_BY_SERIAL_NUMBER = 64 Telephony.CellBroadcasts.SERIAL_NUMBER + "=?"; 65 private static int sSerialId = 0; 66 /** Bitmask for messages of ETWS type (including future extensions). */ 67 private static final int MESSAGE_ID_ETWS_TYPE_MASK = 0xFFF8; 68 /** Value for messages of ETWS type after applying {@link #MESSAGE_ID_ETWS_TYPE_MASK}. */ 69 private static final int MESSAGE_ID_ETWS_TYPE = 0x1100; // 4352 70 private static final String CELL_BROADCAST_LIST_ACTIVITY = 71 "com.android.cellbroadcastreceiver.CellBroadcastSettings"; 72 private static final BySelector SYSUI_FULL_SCREEN_DIALOG = 73 By.res("com.android.systemui", "immersive_cling_title"); 74 private static final BySelector SYSUI_CLOSE_BUTTON = 75 By.res("com.android.systemui", "ok"); 76 private static final BySelector FULL_SCREEN_DIALOG = 77 By.res("android:id/immersive_cling_title"); 78 private static final BySelector CLOSE_BUTTON = 79 By.res("android:id/ok"); 80 81 private static final BySelector YES_BUTTON = 82 By.res("android:id/button1"); 83 84 private boolean mIsOptOutDialogHandled = false; 85 86 @Before beforeTest()87 public void beforeTest() throws Exception { 88 super.beforeTest(); 89 90 if ("testEmergencyAlertSettingsUi".equals(mTestNameRule.getMethodName()) 91 || "testAlertUiOnReceivedAlert".equals(mTestNameRule.getMethodName())) { 92 KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class); 93 assumeTrue("cannot test under secure keyguard", 94 keyguardManager != null && !keyguardManager.isKeyguardSecure()); 95 // dismiss keyguard and wait from idle 96 if (keyguardManager != null && keyguardManager.isKeyguardLocked()) { 97 dismissKeyGuard(); 98 } 99 } 100 if ("testAlertUiOnReceivedAlert".equals(mTestNameRule.getMethodName())) { 101 PackageManager pm = getContext().getPackageManager(); 102 assumeTrue("FULL_ACCESS_CELL_BROADCAST_HISTORY permission " 103 + "is necessary for this test", pm.checkPermission( 104 "com.android.cellbroadcastservice.FULL_ACCESS_CELL_BROADCAST_HISTORY", 105 "com.android.shell") != PackageManager.PERMISSION_DENIED); 106 } 107 if ("testEmergencyAlertSettingsUi".equals(mTestNameRule.getMethodName()) 108 || "testAlertUiOnReceivedAlert".equals(mTestNameRule.getMethodName())) { 109 // close disturbing dialog if exist 110 UiObject2 yesButton = sDevice.wait(Until.findObject(YES_BUTTON), 100); 111 if (yesButton != null) { 112 logd("dismiss disturbing dialog"); 113 yesButton.click(); 114 } 115 // if left alertdialog exist, close it 116 UiObject2 okItem = sDevice.wait(Until.findObject( 117 By.res(sPackageName, "dismissButton")), 100); 118 if (okItem != null) { 119 logd("dismiss left alertdialog"); 120 okItem.click(); 121 } 122 sDevice.pressHome(); 123 } 124 } 125 126 @After afterTest()127 public void afterTest() { 128 if (sPreconditionError != 0) { 129 return; 130 } 131 132 if ("testAlertUiOnReceivedAlert".equals(mTestNameRule.getMethodName()) 133 && (sSerialId > 0)) { 134 deleteMessageWithShellPermissionIdentity(); 135 136 if (!mIsOptOutDialogHandled) { 137 UiObject2 yesButton = sDevice.wait(Until.findObject(YES_BUTTON), 1000); 138 if (yesButton != null) { 139 logd("yesButton click"); 140 yesButton.click(); 141 mIsOptOutDialogHandled = true; 142 } 143 } 144 } 145 146 if ("testEmergencyAlertSettingsUi".equals(mTestNameRule.getMethodName())) { 147 sDevice.pressBack(); 148 sDevice.pressHome(); 149 } 150 151 if ("testEmergencyAlertSettingsUi".equals(mTestNameRule.getMethodName()) 152 || "testAlertUiOnReceivedAlert".equals(mTestNameRule.getMethodName())) { 153 LocaleManager localeManager = getContext().getSystemService(LocaleManager.class); 154 localeManager.setApplicationLocales(sPackageName, LocaleList.getEmptyLocaleList()); 155 } 156 } 157 158 @Test 159 @Parameters(method = "paramsCarrierAndChannelForTest") testAlertUiOnReceivedAlert(String carrierName, String channel)160 public void testAlertUiOnReceivedAlert(String carrierName, String channel) throws Throwable { 161 logd("CellBroadcastUiTest#testAlertUiOnReceivedAlert"); 162 CellBroadcastCarrierTestConfig carrierInfo = 163 new CellBroadcastCarrierTestConfig(sCarriersObject, carrierName); 164 CellBroadcastChannelTestConfig channelInfo = 165 new CellBroadcastChannelTestConfig(sChannelsObject, carrierName, channel); 166 // setup mccmnc 167 if (sInputMccMnc == null || (sInputMccMnc != null 168 && !sInputMccMnc.equals(carrierInfo.mMccMnc))) { 169 setSimInfo(carrierName, carrierInfo.mMccMnc); 170 } 171 172 // change language of CBR 173 changeLocale(carrierInfo, sPackageName, true); 174 175 if (!channelInfo.mChannelDefaultValue || TextUtils.isEmpty(channelInfo.mExpectedTitle) 176 || channelInfo.mFilteredLanguageBySecondLanguagePref 177 || channelInfo.mIsEnabledOnTestMode 178 || !channelInfo.mNeedDisplay) { 179 // let's skip for alerttitle 180 return; 181 } 182 boolean isMessageEnglish = !channelInfo.mIgnoreMessageByLanguageFilter 183 || (channelInfo.mIgnoreMessageByLanguageFilter && carrierInfo.mLanguageTag != null 184 && !carrierInfo.mLanguageTag.equals("en")); 185 186 // receive broadcast message 187 receiveBroadcastMessage(channel, channelInfo.mWarningType, isMessageEnglish); 188 189 logd("carrier " + carrierName + ", expectedTitle = " 190 + channelInfo.mExpectedTitle + " for channel " + channel 191 + ", alertTypeIsNotification = " + channelInfo.mAlertTypeIsNotification); 192 if (channelInfo.mAlertTypeIsNotification) { 193 verifyNotificationPosted(carrierName, channelInfo.mExpectedTitle, channel, 194 sPackageName, channelInfo.mIgnoreMessageByLanguageFilter); 195 } else { 196 verifyAlertDialogTitle(carrierName, channelInfo.mExpectedTitle, channel, 197 carrierInfo.mDisableNavigation, carrierInfo.mNeedFullScreen, sPackageName, 198 channelInfo.mIgnoreMessageByLanguageFilter); 199 } 200 } 201 receiveBroadcastMessage(String channelName, String warningType, boolean isMessageEnglish)202 public void receiveBroadcastMessage(String channelName, String warningType, 203 boolean isMessageEnglish) { 204 int channel = Integer.parseInt(channelName); 205 String hexChannel = String.format("%04X", channel); 206 207 sSerialId++; 208 String serialHexString = String.format("%04X", sSerialId); 209 logd("receiveBroadcastMessage, channel = " + hexChannel 210 + ", serialIdHexString = " + serialHexString); 211 212 boolean isEtws = (channel & MESSAGE_ID_ETWS_TYPE_MASK) == MESSAGE_ID_ETWS_TYPE; 213 String langCode = isMessageEnglish ? "01" : "00"; // 01 is english, 00 is german 214 String etwsWarningType = TextUtils.isEmpty(warningType) ? "00" : warningType; 215 String pduCode = isEtws ? etwsWarningType : langCode; 216 String hexString = serialHexString + hexChannel + pduCode + "11D4F29C0E0AB2CB727A08"; 217 byte[] data = HexDump.hexStringToByteArray(hexString); 218 219 sMockModemManager.newBroadcastSms(sSlotId, data); 220 } 221 verifyAlertDialogTitle(String carrier, String title, String channel, boolean disableNavigation, boolean needsFullScreen, String packageName, boolean ignoreMessageByLanguageFilter)222 private void verifyAlertDialogTitle(String carrier, String title, String channel, 223 boolean disableNavigation, boolean needsFullScreen, String packageName, 224 boolean ignoreMessageByLanguageFilter) { 225 boolean expectedResult = ignoreMessageByLanguageFilter ? false : true; 226 if (needsFullScreen && !isConfirmedForFullScreenGuide(getContext())) { 227 checkForFullScreenGuide(); 228 } 229 boolean result = false; 230 String outputTitle = null; 231 UiObject2 item = sDevice.wait(Until.findObject(By.res(packageName, "alertTitle")), 232 UI_TIMEOUT); 233 if (item != null) { 234 outputTitle = item.getText(); 235 if (outputTitle != null && outputTitle.startsWith(title)) { 236 result = true; 237 } 238 if (disableNavigation) { 239 // for certain country like chile, check if system navigation bar is disabled 240 sDevice.openNotification(); 241 UiSelector notificationStackScroller = new UiSelector() 242 .packageName("com.android.systemui") 243 .resourceId("com.android.systemui:id/notification_stack_scroller"); 244 UiObject widget = new UiObject(notificationStackScroller); 245 boolean canOpenNotification = widget.waitForExists(3000); 246 assertEquals("carrier=" + carrier + ", channel=" + channel 247 + ", system ui should be disabled. ", canOpenNotification, false); 248 } 249 // dismiss dialog 250 UiObject2 okItem = sDevice.wait(Until.findObject( 251 By.res(packageName, "dismissButton")), UI_TIMEOUT); 252 if (okItem != null) { 253 okItem.click(); 254 } 255 } 256 assertEquals("carrier=" + carrier + ", channel=" + channel 257 + ", output title=" + outputTitle 258 + ", expected title=" + title, expectedResult, result); 259 } 260 261 /** Pulls down notification shade and verifies that message text is found. */ verifyNotificationPosted(String carrier, String title, String channel, String packageName, boolean ignoreMessageByLanguageFilter)262 private void verifyNotificationPosted(String carrier, String title, String channel, 263 String packageName, boolean ignoreMessageByLanguageFilter) 264 throws UiObjectNotFoundException { 265 boolean expectedResult = ignoreMessageByLanguageFilter ? false : true; 266 267 // open notification shade 268 sDevice.openNotification(); 269 boolean hasObject = sDevice.wait(Until.hasObject(By.text(title)), UI_TIMEOUT); 270 if (hasObject) { 271 dismissNotificationAsNeeded(title, packageName); 272 } 273 assertEquals("carrier=" + carrier + ", channel=" + channel 274 + ", expected title=" + title, expectedResult, hasObject); 275 } 276 dismissNotificationAsNeeded(String title, String packageName)277 private void dismissNotificationAsNeeded(String title, String packageName) 278 throws UiObjectNotFoundException { 279 UiSelector notificationStackScroller = new UiSelector() 280 .packageName("com.android.systemui") 281 .resourceId("com.android.systemui:id/notification_stack_scroller"); 282 UiObject object = sDevice.findObject(notificationStackScroller); 283 UiObject object2 = object.getChild(new UiSelector().textContains(title)); 284 if (object2 != null) { 285 object2.click(); 286 UiObject2 okItem = sDevice.wait(Until.findObject( 287 By.res(packageName, "dismissButton")), UI_TIMEOUT); 288 if (okItem != null) { 289 okItem.click(); 290 } 291 } 292 } 293 294 @Test 295 @Parameters(method = "paramsForTest") testEmergencyAlertSettingsUi(String carrierName)296 public void testEmergencyAlertSettingsUi(String carrierName) throws Throwable { 297 logd("CellBroadcastUiTest#testEmergencyAlertSettingsUi"); 298 CellBroadcastCarrierTestConfig carrierInfo = 299 new CellBroadcastCarrierTestConfig(sCarriersObject, carrierName); 300 // setup mccmnc 301 if (sInputMccMnc == null || (sInputMccMnc != null 302 && !sInputMccMnc.equals(carrierInfo.mMccMnc))) { 303 setSimInfo(carrierName, carrierInfo.mMccMnc); 304 } 305 306 // change language of CBR 307 changeLocale(carrierInfo, sPackageName, false); 308 309 // launch setting activity of CBR 310 Intent intent = new Intent(Intent.ACTION_MAIN); 311 intent.setComponent(new ComponentName(sPackageName, CELL_BROADCAST_LIST_ACTIVITY)); 312 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 313 getContext().startActivity(intent); 314 315 UiScrollable listView = new UiScrollable(new UiSelector().resourceId("android:id" 316 + "/list_container")); 317 listView.waitForExists(2000); 318 319 // check if there is each setting menu 320 JSONObject settingsForCarrier = sSettingsObject.getJSONObject(carrierName); 321 for (Iterator<String> iterator = settingsForCarrier.keys(); iterator.hasNext(); ) { 322 String settingName = iterator.next(); 323 CellBroadcastSettingTestConfig settingInfo = 324 new CellBroadcastSettingTestConfig(settingsForCarrier, settingName); 325 if (!settingInfo.mIsToggleAvailability) { 326 continue; 327 } 328 UiObject item = null; 329 try { 330 item = listView.getChildByText(new UiSelector() 331 .className(LinearLayout.class.getName()), settingName, true); 332 } catch (UiObjectNotFoundException e) { 333 logd("let's scrollforward if it cannot find switch"); 334 listView.scrollForward(); 335 try { 336 item = listView.getChildByText(new UiSelector() 337 .className(LinearLayout.class.getName()), 338 settingName, true); 339 } catch (UiObjectNotFoundException e2) { 340 assertTrue("carrier=" + carrierName + ", settingName=" 341 + settingName, false); 342 } 343 } 344 UiObject itemSwitch = null; 345 try { 346 itemSwitch = item.getChild(new UiSelector() 347 .className(android.widget.Switch.class.getName())); 348 logd("itemSwitch = " + itemSwitch.isChecked()); 349 } catch (UiObjectNotFoundException e) { 350 logd("switch not found, let's find again"); 351 String searchText = settingName; 352 if (settingInfo.mSummary != null) { 353 searchText = settingInfo.mSummary; 354 } else { 355 // let's scrollforward if it cannot find switch 356 listView.scrollForward(); 357 } 358 item = listView.getChildByText(new UiSelector() 359 .className(LinearLayout.class.getName()), searchText, true); 360 itemSwitch = item.getChild(new UiSelector() 361 .className(android.widget.Switch.class.getName())); 362 } 363 assertEquals("carrierName=" + carrierName + ", settingName=" + settingName 364 + ", expectedSwitchValue=" + settingInfo.mIsToggleAvailability, 365 settingInfo.mExpectedSwitchValue, itemSwitch.isChecked()); 366 } 367 } 368 dismissKeyGuard()369 private void dismissKeyGuard() throws Exception { 370 logd("dismissKeyGuard"); 371 sDevice.wakeUp(); 372 sDevice.executeShellCommand("wm dismiss-keyguard"); 373 } 374 checkForFullScreenGuide()375 private boolean checkForFullScreenGuide() { 376 logd("checkForFullScreenGuide"); 377 UiObject2 viewObject = sDevice.wait(Until.findObject(FULL_SCREEN_DIALOG), 378 UI_TIMEOUT); 379 if (viewObject != null) { 380 return dismissFullScreenGuide(CLOSE_BUTTON); 381 } else { 382 logd("check systemui's fullscreen guide"); 383 viewObject = sDevice.wait(Until.findObject(SYSUI_FULL_SCREEN_DIALOG), UI_TIMEOUT); 384 if (viewObject != null) { 385 return dismissFullScreenGuide(SYSUI_CLOSE_BUTTON); 386 } else { 387 logd("failed to find fullscreen guide"); 388 return false; 389 } 390 } 391 } 392 dismissFullScreenGuide(BySelector closeButton)393 private boolean dismissFullScreenGuide(BySelector closeButton) { 394 logd("Found full screen dialog, dismissing."); 395 UiObject2 okButton = sDevice.wait(Until.findObject(closeButton), UI_TIMEOUT); 396 if (okButton != null) { 397 okButton.click(); 398 return true; 399 } else { 400 logd("Unable to dismiss full screen dialog"); 401 } 402 return false; 403 } 404 isConfirmedForFullScreenGuide(Context context)405 private boolean isConfirmedForFullScreenGuide(Context context) { 406 logd("isConfirmedForFullScreenGuide"); 407 String value = null; 408 boolean isConfirmed = false; 409 try { 410 value = Settings.Secure.getString(context.getContentResolver(), 411 Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS); 412 isConfirmed = "confirmed".equals(value); 413 logd("Loaded isConfirmed = " + isConfirmed); 414 } catch (Throwable t) { 415 logd("Error loading confirmations, value=" + value); 416 } 417 return isConfirmed; 418 } 419 changeLocale(CellBroadcastCarrierTestConfig info, String packageName, boolean checkAlertUi)420 private void changeLocale(CellBroadcastCarrierTestConfig info, 421 String packageName, boolean checkAlertUi) { 422 LocaleManager localeManager = getContext().getSystemService(LocaleManager.class); 423 if (info.mLanguageTag != null && (checkAlertUi || info.mCheckSettingWithMainLanguage)) { 424 logd("setApplicationLocales " + info.mLanguageTag); 425 localeManager.setApplicationLocales(packageName, 426 LocaleList.forLanguageTags(info.mLanguageTag)); 427 } else { 428 logd("setApplicationLocales to default"); 429 localeManager.setApplicationLocales(packageName, 430 LocaleList.forLanguageTags("en-US")); 431 } 432 } 433 deleteMessageWithShellPermissionIdentity()434 private void deleteMessageWithShellPermissionIdentity() { 435 UiAutomation uiAutomation = sInstrumentation.getUiAutomation(); 436 uiAutomation.adoptShellPermissionIdentity(); 437 try { 438 getContext().getContentResolver().delete(CONTENT_URI, 439 SELECT_BY_SERIAL_NUMBER, new String[]{String.valueOf(sSerialId)}); 440 } catch (SecurityException e) { 441 logd("runWithShellPermissionIdentity exception = " + e); 442 } finally { 443 uiAutomation.dropShellPermissionIdentity(); 444 } 445 } 446 } 447