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