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