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