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.telephony.mockmodem; 18 19 import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_DEFAULT; 20 21 import android.app.Service; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.hardware.radio.RadioError; 25 import android.hardware.radio.RadioResponseInfo; 26 import android.hardware.radio.RadioResponseType; 27 import android.os.Binder; 28 import android.os.IBinder; 29 import android.telephony.TelephonyManager; 30 import android.util.Log; 31 import android.util.Pair; 32 33 import androidx.test.platform.app.InstrumentationRegistry; 34 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 38 public class MockModemService extends Service { 39 private static final String TAG = "MockModemService"; 40 private static final String RESOURCE_PACKAGE_NAME = "android"; 41 42 public static final int TEST_TIMEOUT_MS = 30000; 43 public static final String IRADIOCONFIG_INTERFACE = "android.telephony.mockmodem.iradioconfig"; 44 public static final String IRADIOMODEM_INTERFACE = "android.telephony.mockmodem.iradiomodem"; 45 public static final String IRADIOSIM_INTERFACE = "android.telephony.mockmodem.iradiosim"; 46 public static final String IRADIONETWORK_INTERFACE = 47 "android.telephony.mockmodem.iradionetwork"; 48 public static final String IRADIODATA_INTERFACE = "android.telephony.mockmodem.iradiodata"; 49 public static final String IRADIOMESSAGING_INTERFACE = 50 "android.telephony.mockmodem.iradiomessaging"; 51 public static final String IRADIOVOICE_INTERFACE = "android.telephony.mockmodem.iradiovoice"; 52 public static final String IRADIOIMS_INTERFACE = "android.telephony.mockmodem.iradioims"; 53 public static final String PHONE_ID = "phone_id"; 54 55 private static MockModemConfigInterface sMockModemConfigInterface; 56 private static MockCentralizedNetworkAgent sMockCentralizedNetworkAgent; 57 private static IRadioConfigImpl sIRadioConfigImpl; 58 private static IRadioModemImpl[] sIRadioModemImpl; 59 private static IRadioSimImpl[] sIRadioSimImpl; 60 private static IRadioNetworkImpl[] sIRadioNetworkImpl; 61 private static IRadioDataImpl[] sIRadioDataImpl; 62 private static IRadioMessagingImpl[] sIRadioMessagingImpl; 63 private static IRadioVoiceImpl[] sIRadioVoiceImpl; 64 private static IRadioImsImpl[] sIRadioImsImpl; 65 66 public static final byte PHONE_ID_0 = 0x00; 67 public static final byte PHONE_ID_1 = 0x01; 68 public static final byte PHONE_ID_2 = 0x02; 69 public static final byte MAX_PHONE_NUM = 3; 70 71 public static final int LATCH_MOCK_MODEM_SERVICE_READY = 0; 72 public static final int LATCH_RADIO_INTERFACES_READY = 1; 73 public static final int LATCH_MAX = 2; 74 75 private static final int IRADIO_CONFIG_INTERFACE_NUMBER = 1; 76 private static final int IRADIO_INTERFACE_NUMBER = 6; 77 78 private Context mContext; 79 private TelephonyManager mTelephonyManager; 80 private int mNumOfSim; 81 private int mNumOfPhone; 82 private static final int DEFAULT_SUB_ID = 0; 83 84 private Object mLock; 85 protected static CountDownLatch[] sLatches; 86 private LocalBinder mBinder; 87 88 // For local access of this Service. 89 class LocalBinder extends Binder { getService()90 MockModemService getService() { 91 return MockModemService.this; 92 } 93 } 94 95 @Override onCreate()96 public void onCreate() { 97 Log.d(TAG, "Mock Modem Service Created"); 98 99 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 100 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 101 mNumOfSim = getNumPhysicalSlots(); 102 mNumOfPhone = mTelephonyManager.getActiveModemCount(); 103 Log.d(TAG, "Support number of phone = " + mNumOfPhone + ", number of SIM = " + mNumOfSim); 104 105 if (mNumOfPhone > MAX_PHONE_NUM) { 106 mNumOfPhone = MAX_PHONE_NUM; 107 } 108 109 // Number of physical Sim slot should be equals to or greater than number of phone. 110 if (mNumOfSim < mNumOfPhone) { 111 mNumOfSim = mNumOfPhone; 112 } 113 114 mLock = new Object(); 115 sLatches = new CountDownLatch[LATCH_MAX]; 116 117 for (int i = 0; i < LATCH_MAX; i++) { 118 sLatches[i] = new CountDownLatch(1); 119 if (i == LATCH_RADIO_INTERFACES_READY) { 120 int radioServiceSupportedNumber = 0; 121 int radioInterfaceNumber = 0; 122 123 try { 124 for (int j = TelephonyManager.HAL_SERVICE_DATA; 125 j <= TelephonyManager.HAL_SERVICE_IMS; 126 j++) { 127 Pair<Integer, Integer> halVersion = mTelephonyManager.getHalVersion(j); 128 if (halVersion.first == -2 && halVersion.second == -2) { 129 Log.d(TAG, "Service: " + j + " unsupported"); 130 } else { 131 radioServiceSupportedNumber++; 132 } 133 } 134 } catch (NoSuchMethodError | IllegalStateException e) { 135 Log.e(TAG, "Use the default number of interfaces - " + IRADIO_INTERFACE_NUMBER); 136 radioServiceSupportedNumber = IRADIO_INTERFACE_NUMBER; 137 } 138 139 radioInterfaceNumber = 140 IRADIO_CONFIG_INTERFACE_NUMBER + mNumOfPhone * radioServiceSupportedNumber; 141 sLatches[i] = new CountDownLatch(radioInterfaceNumber); 142 } else { 143 sLatches[i] = new CountDownLatch(1); 144 } 145 } 146 147 sMockModemConfigInterface = new MockModemConfigBase(mContext, mNumOfSim, mNumOfPhone); 148 sIRadioConfigImpl = new IRadioConfigImpl(this, 149 sMockModemConfigInterface, 150 sMockCentralizedNetworkAgent, 151 PHONE_ID_0); 152 sIRadioModemImpl = new IRadioModemImpl[mNumOfPhone]; 153 sIRadioSimImpl = new IRadioSimImpl[mNumOfPhone]; 154 sIRadioNetworkImpl = new IRadioNetworkImpl[mNumOfPhone]; 155 sIRadioDataImpl = new IRadioDataImpl[mNumOfPhone]; 156 sIRadioMessagingImpl = new IRadioMessagingImpl[mNumOfPhone]; 157 sIRadioVoiceImpl = new IRadioVoiceImpl[mNumOfPhone]; 158 sIRadioImsImpl = new IRadioImsImpl[mNumOfPhone]; 159 for (int i = 0; i < mNumOfPhone; i++) { 160 sIRadioModemImpl[i] = new IRadioModemImpl(this, sMockModemConfigInterface, i); 161 sIRadioSimImpl[i] = new IRadioSimImpl(this, sMockModemConfigInterface, i); 162 sIRadioNetworkImpl[i] = 163 new IRadioNetworkImpl(this, mContext, sMockModemConfigInterface, i); 164 sIRadioDataImpl[i] = new IRadioDataImpl(this, mContext, sMockModemConfigInterface, i); 165 sIRadioMessagingImpl[i] = new IRadioMessagingImpl(this, sMockModemConfigInterface, i); 166 sIRadioVoiceImpl[i] = new IRadioVoiceImpl(this, sMockModemConfigInterface, i); 167 sIRadioImsImpl[i] = new IRadioImsImpl(this, sMockModemConfigInterface, i); 168 } 169 try { 170 sMockCentralizedNetworkAgent.setNetworkAgentInfo(sIRadioDataImpl, mNumOfPhone); 171 } catch (Exception e) { 172 Log.e(TAG, "Exception error: " + e); 173 } 174 mBinder = new LocalBinder(); 175 } 176 177 @Override onDestroy()178 public void onDestroy() { 179 Log.d(TAG, "Mock Modem Service on distory"); 180 if (sMockModemConfigInterface != null) { 181 sMockModemConfigInterface.destroy(); 182 } 183 super.onDestroy(); 184 } 185 186 @Override onBind(Intent intent)187 public IBinder onBind(Intent intent) { 188 189 String action = intent.getAction(); 190 if (action == null) { 191 countDownLatch(LATCH_MOCK_MODEM_SERVICE_READY); 192 Log.i(TAG, "onBind-Local"); 193 return mBinder; 194 } 195 196 byte phoneId = intent.getByteExtra(PHONE_ID, PHONE_ID_0); 197 198 if (phoneId >= MAX_PHONE_NUM) { 199 Log.e(TAG, "Not suuport for phone " + phoneId); 200 return null; 201 } 202 203 if (action.startsWith(IRADIOCONFIG_INTERFACE)) { 204 Log.i(TAG, "onBind-IRadioConfig " + phoneId); 205 return sIRadioConfigImpl; 206 } else if (action.startsWith(IRADIOMODEM_INTERFACE)) { 207 Log.i(TAG, "onBind-IRadioModem " + phoneId); 208 return sIRadioModemImpl[phoneId]; 209 } else if (action.startsWith(IRADIOSIM_INTERFACE)) { 210 Log.i(TAG, "onBind-IRadioSim " + phoneId); 211 return sIRadioSimImpl[phoneId]; 212 } else if (action.startsWith(IRADIONETWORK_INTERFACE)) { 213 Log.i(TAG, "onBind-IRadioNetwork " + phoneId); 214 return sIRadioNetworkImpl[phoneId]; 215 } else if (action.startsWith(IRADIODATA_INTERFACE)) { 216 Log.i(TAG, "onBind-IRadioData " + phoneId); 217 return sIRadioDataImpl[phoneId]; 218 } else if (action.startsWith(IRADIOMESSAGING_INTERFACE)) { 219 Log.i(TAG, "onBind-IRadioMessaging " + phoneId); 220 return sIRadioMessagingImpl[phoneId]; 221 } else if (action.startsWith(IRADIOVOICE_INTERFACE)) { 222 Log.i(TAG, "onBind-IRadioVoice " + phoneId); 223 return sIRadioVoiceImpl[phoneId]; 224 } else if (action.startsWith(IRADIOIMS_INTERFACE)) { 225 Log.i(TAG, "onBind-IRadioIms " + phoneId); 226 return sIRadioImsImpl[phoneId]; 227 } 228 229 return null; 230 } 231 waitForLatchCountdown(int latchIndex)232 public boolean waitForLatchCountdown(int latchIndex) { 233 boolean complete = false; 234 try { 235 CountDownLatch latch; 236 synchronized (mLock) { 237 latch = sLatches[latchIndex]; 238 } 239 complete = latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); 240 } catch (InterruptedException e) { 241 } 242 synchronized (mLock) { 243 sLatches[latchIndex] = new CountDownLatch(1); 244 } 245 return complete; 246 } 247 waitForLatchCountdown(int latchIndex, long waitMs)248 public boolean waitForLatchCountdown(int latchIndex, long waitMs) { 249 boolean complete = false; 250 try { 251 CountDownLatch latch; 252 synchronized (mLock) { 253 latch = sLatches[latchIndex]; 254 } 255 complete = latch.await(waitMs, TimeUnit.MILLISECONDS); 256 } catch (InterruptedException e) { 257 } 258 synchronized (mLock) { 259 sLatches[latchIndex] = new CountDownLatch(1); 260 } 261 return complete; 262 } 263 countDownLatch(int latchIndex)264 public void countDownLatch(int latchIndex) { 265 synchronized (mLock) { 266 sLatches[latchIndex].countDown(); 267 } 268 } 269 getNumPhysicalSlots()270 public int getNumPhysicalSlots() { 271 int numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN; 272 int resourceId = 273 mContext.getResources() 274 .getIdentifier( 275 "config_num_physical_slots", "integer", RESOURCE_PACKAGE_NAME); 276 277 if (resourceId > 0) { 278 numPhysicalSlots = mContext.getResources().getInteger(resourceId); 279 } else { 280 Log.d(TAG, "Fail to get the resource Id, using default: " + numPhysicalSlots); 281 } 282 283 if (numPhysicalSlots > MockSimService.MOCK_SIM_SLOT_MAX) { 284 Log.d( 285 TAG, 286 "Number of physical Slot (" 287 + numPhysicalSlots 288 + ") > mock sim slot support. Reset to max number supported (" 289 + MockSimService.MOCK_SIM_SLOT_MAX 290 + ")."); 291 numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MAX; 292 } else if (numPhysicalSlots < MockSimService.MOCK_SIM_SLOT_MIN) { 293 Log.d( 294 TAG, 295 "Number of physical Slot (" 296 + numPhysicalSlots 297 + ") < mock sim slot support. Reset to min number supported (" 298 + MockSimService.MOCK_SIM_SLOT_MIN 299 + ")."); 300 numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN; 301 } 302 303 return numPhysicalSlots; 304 } 305 makeSolRsp(int serial)306 public RadioResponseInfo makeSolRsp(int serial) { 307 RadioResponseInfo rspInfo = new RadioResponseInfo(); 308 rspInfo.type = RadioResponseType.SOLICITED; 309 rspInfo.serial = serial; 310 rspInfo.error = RadioError.NONE; 311 312 return rspInfo; 313 } 314 makeSolRsp(int serial, int error)315 public RadioResponseInfo makeSolRsp(int serial, int error) { 316 RadioResponseInfo rspInfo = new RadioResponseInfo(); 317 rspInfo.type = RadioResponseType.SOLICITED; 318 rspInfo.serial = serial; 319 rspInfo.error = error; 320 321 return rspInfo; 322 } 323 initialize(int[] simprofiles)324 public boolean initialize(int[] simprofiles) { 325 Log.d(TAG, "initialize for num of Sim = " + simprofiles.length); 326 boolean result = true; 327 328 // Sync mock modem status between modules 329 for (int i = 0; i < mNumOfPhone; i++) { 330 // Set initial SIM profiles 331 if (simprofiles != null && i < simprofiles.length) { 332 result = sMockModemConfigInterface.changeSimProfile(i, simprofiles[i], TAG); 333 } else { 334 result = 335 sMockModemConfigInterface.changeSimProfile( 336 i, MOCK_SIM_PROFILE_ID_DEFAULT, TAG); 337 } 338 339 // Sync modem configurations to radio modules 340 sMockModemConfigInterface.notifyAllRegistrantNotifications(); 341 342 // Connect to telephony framework 343 sIRadioModemImpl[i].rilConnected(); 344 } 345 346 return result; 347 } 348 getMockModemConfigInterface()349 public MockModemConfigInterface getMockModemConfigInterface() { 350 return sMockModemConfigInterface; 351 } 352 getIRadioConfig()353 public IRadioConfigImpl getIRadioConfig() { 354 return sIRadioConfigImpl; 355 } 356 getIRadioModem()357 public IRadioModemImpl getIRadioModem() { 358 return getIRadioModem(PHONE_ID_0); 359 } 360 getIRadioModem(byte phoneId)361 public IRadioModemImpl getIRadioModem(byte phoneId) { 362 return sIRadioModemImpl[phoneId]; 363 } 364 getIRadioSim()365 public IRadioSimImpl getIRadioSim() { 366 return getIRadioSim(PHONE_ID_0); 367 } 368 getIRadioSim(byte phoneId)369 public IRadioSimImpl getIRadioSim(byte phoneId) { 370 return sIRadioSimImpl[phoneId]; 371 } 372 getIRadioNetwork()373 public IRadioNetworkImpl getIRadioNetwork() { 374 return getIRadioNetwork(PHONE_ID_0); 375 } 376 getIRadioNetwork(byte phoneId)377 public IRadioNetworkImpl getIRadioNetwork(byte phoneId) { 378 return sIRadioNetworkImpl[phoneId]; 379 } 380 getIRadioVoice()381 public IRadioVoiceImpl getIRadioVoice() { 382 return getIRadioVoice(PHONE_ID_0); 383 } 384 getIRadioVoice(byte phoneId)385 public IRadioVoiceImpl getIRadioVoice(byte phoneId) { 386 return sIRadioVoiceImpl[phoneId]; 387 } 388 getIRadioMessaging()389 public IRadioMessagingImpl getIRadioMessaging() { 390 return getIRadioMessaging(PHONE_ID_0); 391 } 392 getIRadioMessaging(byte phoneId)393 public IRadioMessagingImpl getIRadioMessaging(byte phoneId) { 394 return sIRadioMessagingImpl[phoneId]; 395 } 396 getIRadioData()397 public IRadioDataImpl getIRadioData() { 398 return getIRadioData(PHONE_ID_0); 399 } 400 getIRadioData(byte phoneId)401 public IRadioDataImpl getIRadioData(byte phoneId) { 402 return sIRadioDataImpl[phoneId]; 403 } 404 getIRadioIms()405 public IRadioImsImpl getIRadioIms() { 406 return getIRadioIms(PHONE_ID_0); 407 } 408 getIRadioIms(byte phoneId)409 public IRadioImsImpl getIRadioIms(byte phoneId) { 410 return sIRadioImsImpl[phoneId]; 411 } 412 getActiveMockModemCount()413 public int getActiveMockModemCount() { 414 return mNumOfPhone; 415 } 416 } 417