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