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.assertTrue; 20 import static org.junit.Assume.assumeTrue; 21 22 import android.app.Instrumentation; 23 import android.app.UiAutomation; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.PackageManager; 29 import android.os.Build; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.SystemProperties; 33 import android.support.test.uiautomator.UiDevice; 34 import android.telephony.ServiceState; 35 import android.telephony.SubscriptionInfo; 36 import android.telephony.SubscriptionManager; 37 import android.telephony.TelephonyCallback; 38 import android.telephony.TelephonyManager; 39 import android.telephony.mockmodem.IRadioMessagingImpl; 40 import android.telephony.mockmodem.MockModemConfigBase.SimInfoChangedResult; 41 import android.telephony.mockmodem.MockModemManager; 42 import android.telephony.mockmodem.MockSimService; 43 import android.util.Log; 44 45 import androidx.test.platform.app.InstrumentationRegistry; 46 47 import com.android.compatibility.common.util.ShellIdentityUtils; 48 import com.android.internal.telephony.CellBroadcastUtils; 49 import com.android.modules.utils.build.SdkLevel; 50 51 import org.json.JSONArray; 52 import org.json.JSONObject; 53 import org.junit.AfterClass; 54 import org.junit.Before; 55 import org.junit.BeforeClass; 56 import org.junit.Rule; 57 import org.junit.rules.TestName; 58 59 import java.io.IOException; 60 import java.io.InputStream; 61 import java.util.ArrayList; 62 import java.util.Iterator; 63 import java.util.concurrent.CountDownLatch; 64 import java.util.concurrent.TimeUnit; 65 66 67 public class CellBroadcastBaseTest { 68 private static final String TAG = "CellBroadcastBaseTest"; 69 protected static MockModemManager sMockModemManager; 70 protected static int sSlotId = 0; 71 protected static JSONObject sCarriersObject; 72 protected static JSONObject sChannelsObject; 73 protected static JSONObject sSettingsObject; 74 protected static int sPreconditionError = 0; 75 protected static final int ERROR_SDK_VERSION = 1; 76 protected static final int ERROR_NO_TELEPHONY = 2; 77 protected static final int ERROR_MOCK_MODEM_DISABLE = 3; 78 79 protected static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; 80 protected static final boolean DEBUG = !"user".equals(Build.TYPE); 81 82 protected static final String EXPECTED_RESULT_CHANNELS_JSON = "emergency_alert_channels.json"; 83 protected static final String CARRIER_LISTS_JSON = "region_plmn_list.json"; 84 protected static final String EXPECTED_RESULT_SETTINGS_JSON = "emergency_alert_settings.json"; 85 protected static final String CARRIER_MCCMNC_FIELD = "mccmnc"; 86 protected static final String CHANNEL_DEFAULT_VALUE_FIELD = "default_value"; 87 88 protected static final String ACTION_SET_CHANNELS_DONE = 89 "android.cellbroadcast.compliancetest.SET_CHANNELS_DONE"; 90 protected static CountDownLatch sSetChannelIsDone = new CountDownLatch(1); 91 protected static String sInputMccMnc = null; 92 protected static BroadcastReceiver sReceiver = null; 93 94 protected static final int MAX_WAIT_TIME = 15 * 1000; 95 96 protected static Instrumentation sInstrumentation = null; 97 protected static UiDevice sDevice = null; 98 protected static String sPackageName = null; 99 protected static IRadioMessagingImpl.CallBackWithExecutor sCallBackWithExecutor = null; 100 private static ServiceStateListener sServiceStateCallback; 101 private static int sServiceState = ServiceState.STATE_OUT_OF_SERVICE; 102 private static final Object OBJECT = new Object(); 103 private static final int SERVICE_STATE_MAX_WAIT = 20 * 1000; 104 protected static CountDownLatch sServiceStateLatch = new CountDownLatch(1); 105 106 private static class ServiceStateListener extends TelephonyCallback 107 implements TelephonyCallback.ServiceStateListener { 108 @Override onServiceStateChanged(ServiceState serviceState)109 public void onServiceStateChanged(ServiceState serviceState) { 110 Log.d(TAG, "Callback: service state = " + serviceState.getVoiceRegState()); 111 synchronized (OBJECT) { 112 sServiceState = serviceState.getVoiceRegState(); 113 if (sServiceState == ServiceState.STATE_IN_SERVICE) { 114 sServiceStateLatch.countDown(); 115 logd("countdown sServiceStateLatch"); 116 } 117 } 118 } 119 } 120 getContext()121 protected static Context getContext() { 122 return InstrumentationRegistry.getInstrumentation().getContext(); 123 } 124 125 private static class BroadcastChannelListener 126 implements IRadioMessagingImpl.BroadcastCallback { 127 @Override onGsmBroadcastActivated()128 public void onGsmBroadcastActivated() { 129 TelephonyManager tm = getContext().getSystemService(TelephonyManager.class); 130 String mccmnc = tm.getSimOperator(SubscriptionManager.getDefaultSubscriptionId()); 131 logd("onGsmBroadcastActivated, mccmnc = " + mccmnc); 132 if (sInputMccMnc != null && sInputMccMnc.equals(mccmnc)) { 133 sSetChannelIsDone.countDown(); 134 logd("wait is released"); 135 addSubIdToBeRemoved(SubscriptionManager.getDefaultSubscriptionId()); 136 } 137 } 138 139 @Override onCdmaBroadcastActivated()140 public void onCdmaBroadcastActivated() { 141 } 142 } 143 144 @BeforeClass beforeAllTests()145 public static void beforeAllTests() throws Exception { 146 logd("CellBroadcastBaseTest#beforeAllTests()"); 147 if (!SdkLevel.isAtLeastT()) { 148 Log.i(TAG, "sdk level is below the latest platform"); 149 sPreconditionError = ERROR_SDK_VERSION; 150 return; 151 } 152 153 final PackageManager pm = getContext().getPackageManager(); 154 boolean hasTelephonyFeature = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 155 if (!hasTelephonyFeature) { 156 Log.i(TAG, "Not have Telephony Feature"); 157 sPreconditionError = ERROR_NO_TELEPHONY; 158 return; 159 } 160 161 if (!isMockModemAllowed()) { 162 Log.i(TAG, "Mock Modem is not allowed"); 163 sPreconditionError = ERROR_MOCK_MODEM_DISABLE; 164 return; 165 } 166 167 if (!SdkLevel.isAtLeastU()) { 168 sReceiver = new BroadcastReceiver() { 169 @Override 170 public void onReceive(Context context, Intent intent) { 171 String action = intent.getAction(); 172 if (ACTION_SET_CHANNELS_DONE.equals(action)) { 173 int subId = intent.getIntExtra("sub_id", -1); 174 logd("INTENT_SET_CHANNELS_DONE is received, subId=" + subId); 175 TelephonyManager tm = getContext().getSystemService(TelephonyManager.class) 176 .createForSubscriptionId(subId); 177 if (tm != null) { 178 String mccMncOfIntent = tm.getSimOperator(); 179 logd("mccMncOfIntent = " + mccMncOfIntent); 180 if (sInputMccMnc != null && sInputMccMnc.equals(mccMncOfIntent)) { 181 sSetChannelIsDone.countDown(); 182 logd("wait is released"); 183 addSubIdToBeRemoved(SubscriptionManager.getDefaultSubscriptionId()); 184 } 185 } 186 } 187 } 188 }; 189 IntentFilter filter = new IntentFilter(); 190 filter.addAction(ACTION_SET_CHANNELS_DONE); 191 getContext().registerReceiver(sReceiver, filter, Context.RECEIVER_EXPORTED); 192 } 193 194 sInstrumentation = InstrumentationRegistry.getInstrumentation(); 195 sDevice = UiDevice.getInstance(sInstrumentation); 196 197 sMockModemManager = new MockModemManager(); 198 assertTrue(sMockModemManager.connectMockModemService( 199 MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT)); 200 201 if (SdkLevel.isAtLeastU()) { 202 BroadcastChannelListener broadcastCallback = new BroadcastChannelListener(); 203 sCallBackWithExecutor = new IRadioMessagingImpl.CallBackWithExecutor( 204 Runnable::run, broadcastCallback); 205 sMockModemManager.registerBroadcastCallback(sSlotId, sCallBackWithExecutor); 206 } 207 waitForNotify(); 208 209 enterService(); 210 211 String jsonCarrier = loadJsonFile(CARRIER_LISTS_JSON); 212 sCarriersObject = new JSONObject(jsonCarrier); 213 String jsonChannels = loadJsonFile(EXPECTED_RESULT_CHANNELS_JSON); 214 sChannelsObject = new JSONObject(jsonChannels); 215 String jsonSettings = loadJsonFile(EXPECTED_RESULT_SETTINGS_JSON); 216 sSettingsObject = new JSONObject(jsonSettings); 217 sPackageName = CellBroadcastUtils 218 .getDefaultCellBroadcastReceiverPackageName(getContext()); 219 } 220 waitForNotify()221 private static void waitForNotify() { 222 try { 223 sSetChannelIsDone.await(MAX_WAIT_TIME, TimeUnit.MILLISECONDS); 224 } catch (InterruptedException e) { 225 // do nothing 226 } 227 } 228 229 @AfterClass afterAllTests()230 public static void afterAllTests() throws Exception { 231 logd("CellBroadcastBaseTest#afterAllTests()"); 232 233 if (sIccIdForDummySub != null) { 234 deleteDummySubscriptionIds(); 235 } 236 237 if (sReceiver != null) { 238 getContext().unregisterReceiver(sReceiver); 239 } 240 if (sCallBackWithExecutor != null && sMockModemManager != null) { 241 sMockModemManager.unregisterBroadcastCallback(sSlotId, sCallBackWithExecutor); 242 } 243 if (sMockModemManager != null) { 244 // Rebind all interfaces which is binding to MockModemService to default. 245 assertTrue(sMockModemManager.disconnectMockModemService()); 246 sMockModemManager = null; 247 } 248 sInputMccMnc = null; 249 } 250 251 @Rule 252 public final TestName mTestNameRule = new TestName(); 253 @Before beforeTest()254 public void beforeTest() throws Exception { 255 assumeTrue(getErrorMessage(sPreconditionError), sPreconditionError == 0); 256 } 257 loadJsonFile(String jsonFile)258 protected static String loadJsonFile(String jsonFile) { 259 String json = null; 260 try { 261 InputStream inputStream = getContext().getAssets().open(jsonFile); 262 int size = inputStream.available(); 263 byte[] byteArray = new byte[size]; 264 inputStream.read(byteArray); 265 inputStream.close(); 266 json = new String(byteArray, "UTF-8"); 267 } catch (IOException e) { 268 e.printStackTrace(); 269 return null; 270 } 271 return json; 272 } 273 paramsForTest()274 protected String[] paramsForTest() throws Throwable { 275 logd("paramsForTest"); 276 String jsonCarrier = loadJsonFile(CARRIER_LISTS_JSON); 277 JSONObject carriersObject = new JSONObject(jsonCarrier); 278 Iterator<String> carrierList = carriersObject.keys(); 279 280 ArrayList<String> carrierLists = new ArrayList<>(); 281 for (Iterator<String> it = carrierList; it.hasNext();) { 282 carrierLists.add(it.next()); 283 } 284 return carrierLists.toArray(new String[]{}); 285 } 286 paramsCarrierAndMccMncForTest()287 protected Object[] paramsCarrierAndMccMncForTest() throws Throwable { 288 logd("paramsCarrierAndMccMncForTest"); 289 String jsonCarrier = loadJsonFile(CARRIER_LISTS_JSON); 290 JSONObject carriersObject = new JSONObject(jsonCarrier); 291 Iterator<String> carrierList = carriersObject.keys(); 292 293 ArrayList<Object> result = new ArrayList<Object>(); 294 for (Iterator<String> it = carrierList; it.hasNext();) { 295 String carrierName = it.next(); 296 JSONObject carrierObject = carriersObject.getJSONObject(carrierName); 297 JSONArray mccMncList = carrierObject.getJSONArray(CARRIER_MCCMNC_FIELD); 298 for (int i = 0; i < mccMncList.length(); i++) { 299 String mccMnc = mccMncList.getString(i); 300 result.add(new String[]{carrierName, mccMnc}); 301 } 302 } 303 return result.toArray(new Object[]{}); 304 } 305 paramsCarrierAndChannelForTest()306 protected Object[] paramsCarrierAndChannelForTest() throws Throwable { 307 logd("paramsCarrierAndChannelForTest"); 308 String jsonCarrier = loadJsonFile(CARRIER_LISTS_JSON); 309 JSONObject carriersObject = new JSONObject(jsonCarrier); 310 Iterator<String> carrierList = carriersObject.keys(); 311 312 ArrayList<Object> result = new ArrayList<Object>(); 313 for (Iterator<String> it = carrierList; it.hasNext();) { 314 String carrierName = it.next(); 315 String jsonChannels = loadJsonFile(EXPECTED_RESULT_CHANNELS_JSON); 316 JSONObject channelsObject = new JSONObject(jsonChannels); 317 JSONObject channelsForCarrier = channelsObject.getJSONObject(carrierName); 318 for (Iterator<String> iterator = channelsForCarrier.keys(); iterator.hasNext();) { 319 String channelId = iterator.next(); 320 result.add(new String[]{carrierName, channelId}); 321 } 322 } 323 return result.toArray(new Object[]{}); 324 } 325 setSimInfo(String carrierName, String inputMccMnc)326 protected void setSimInfo(String carrierName, String inputMccMnc) throws Throwable { 327 String mcc = inputMccMnc.substring(0, 3); 328 String mnc = inputMccMnc.substring(3); 329 sInputMccMnc = inputMccMnc; 330 sSetChannelIsDone = new CountDownLatch(1); 331 332 String[] mccMnc = new String[] {mcc, mnc}; 333 logd("carrierName = " + carrierName 334 + ", mcc = " + mccMnc[0] + ", mnc = " + mccMnc[1]); 335 336 int slotId = 0; 337 338 boolean isSuccessful = sMockModemManager.setSimInfo(slotId, 339 SimInfoChangedResult.SIM_INFO_TYPE_MCC_MNC, mccMnc); 340 assertTrue(isSuccessful); 341 waitForNotify(); 342 } 343 isMockModemAllowed()344 private static boolean isMockModemAllowed() { 345 boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false); 346 // Check for developer settings for user build. Always allow for debug builds 347 return isAllowed || DEBUG; 348 } 349 getErrorMessage(int error)350 protected String getErrorMessage(int error) { 351 String errorMessage = "Precondition Error"; 352 switch (error) { 353 case ERROR_SDK_VERSION: 354 errorMessage = "SDK level is below T"; 355 break; 356 case ERROR_NO_TELEPHONY: 357 errorMessage = "Not have Telephony Feature"; 358 break; 359 case ERROR_MOCK_MODEM_DISABLE: 360 errorMessage = "Please enable mock modem to run the test! The option can be " 361 + "updated in Settings -> System -> Developer options -> Allow Mock Modem"; 362 break; 363 } 364 return errorMessage; 365 } 366 logd(String msg)367 protected static void logd(String msg) { 368 if (DEBUG) Log.d(TAG, msg); 369 } 370 enterService()371 protected static void enterService() throws Exception { 372 logd("enterService"); 373 HandlerThread serviceStateChangeCallbackHandlerThread = 374 new HandlerThread("ServiceStateChangeCallback"); 375 serviceStateChangeCallbackHandlerThread.start(); 376 Handler serviceStateChangeCallbackHandler = 377 new Handler(serviceStateChangeCallbackHandlerThread.getLooper()); 378 TelephonyManager telephonyManager = 379 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); 380 sSetChannelIsDone = new CountDownLatch(1); 381 // Register service state change callback 382 synchronized (OBJECT) { 383 sServiceState = ServiceState.STATE_OUT_OF_SERVICE; 384 } 385 386 serviceStateChangeCallbackHandler.post( 387 () -> { 388 sServiceStateCallback = new ServiceStateListener(); 389 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 390 telephonyManager, 391 (tm) -> tm.registerTelephonyCallback( 392 Runnable::run, sServiceStateCallback)); 393 }); 394 395 // Enter Service 396 logd("Enter Service"); 397 sMockModemManager.changeNetworkService(sSlotId, MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT, 398 true); 399 400 // Expect: Home State 401 logd("Wait for service state change to in service"); 402 waitForNotifyForServiceState(); 403 404 // Unregister service state change callback 405 telephonyManager.unregisterTelephonyCallback(sServiceStateCallback); 406 sServiceStateCallback = null; 407 } 408 waitForNotifyForServiceState()409 private static void waitForNotifyForServiceState() { 410 try { 411 sServiceStateLatch.await(SERVICE_STATE_MAX_WAIT, TimeUnit.MILLISECONDS); 412 } catch (InterruptedException e) { 413 // do nothing 414 } 415 } 416 417 private static int sSubIdForDummySub; 418 private static String sIccIdForDummySub; 419 private static int sSubTypeForDummySub; 420 addSubIdToBeRemoved(int subId)421 private static void addSubIdToBeRemoved(int subId) { 422 logd("addSubIdToBeRemoved, subId = " + subId 423 + " subIdToBeRemoved = " + sSubIdForDummySub); 424 deleteDummySubscriptionIds(); 425 UiAutomation uiAutomation = sInstrumentation.getUiAutomation(); 426 uiAutomation.adoptShellPermissionIdentity(); 427 try { 428 SubscriptionManager subManager = 429 getContext().getSystemService(SubscriptionManager.class); 430 SubscriptionInfo subInfo = subManager.getActiveSubscriptionInfo(subId); 431 sSubIdForDummySub = subId; 432 sIccIdForDummySub = subInfo.getIccId(); 433 sSubTypeForDummySub = subInfo.getSubscriptionType(); 434 logd("addSubIdToBeRemoved, subId = " + sSubIdForDummySub 435 + " iccId=" + sIccIdForDummySub + " subType=" + sSubTypeForDummySub); 436 } catch (SecurityException e) { 437 logd("runWithShellPermissionIdentity exception = " + e); 438 } finally { 439 uiAutomation.dropShellPermissionIdentity(); 440 } 441 } 442 deleteDummySubscriptionIds()443 private static void deleteDummySubscriptionIds() { 444 if (sIccIdForDummySub != null) { 445 UiAutomation uiAutomation = sInstrumentation.getUiAutomation(); 446 uiAutomation.adoptShellPermissionIdentity(); 447 try { 448 SubscriptionManager subManager = 449 getContext().getSystemService(SubscriptionManager.class); 450 logd("deleteDummySubscriptionIds " 451 + " subId =" + sSubIdForDummySub 452 + " iccId=" + sIccIdForDummySub 453 + " subType=" + sSubTypeForDummySub); 454 subManager.removeSubscriptionInfoRecord(sIccIdForDummySub, sSubTypeForDummySub); 455 } catch (SecurityException e) { 456 logd("runWithShellPermissionIdentity exception = " + e); 457 } catch (IllegalArgumentException e) { 458 logd("catch IllegalArgumentException during removing subscriptionId = " + e); 459 } catch (NullPointerException e) { 460 logd("catch NullPointerException during removing subscriptionId = " + e); 461 } finally { 462 uiAutomation.dropShellPermissionIdentity(); 463 } 464 } 465 } 466 } 467