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 com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER; 20 21 import android.hardware.radio.RadioError; 22 import android.hardware.radio.RadioIndicationType; 23 import android.hardware.radio.RadioResponseInfo; 24 import android.hardware.radio.modem.IRadioModem; 25 import android.hardware.radio.modem.IRadioModemIndication; 26 import android.hardware.radio.modem.IRadioModemResponse; 27 import android.hardware.radio.modem.ImeiInfo; 28 import android.hardware.radio.modem.RadioState; 29 import android.os.AsyncResult; 30 import android.os.Handler; 31 import android.os.Message; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 38 public class IRadioModemImpl extends IRadioModem.Stub { 39 private static final String TAG = "MRMDM"; 40 41 private final MockModemService mService; 42 private IRadioModemResponse mRadioModemResponse; 43 private IRadioModemIndication mRadioModemIndication; 44 45 private int mForceRadioPowerError = -1; 46 47 private MockModemConfigInterface mMockModemConfigInterface; 48 private final Object mCacheUpdateMutex; 49 private final Handler mHandler; 50 private int mSubId; 51 private String mTag; 52 53 // ***** Events 54 static final int EVENT_BASEBAND_VERSION_CHANGED = 1; 55 static final int EVENT_DEVICE_IDENTITY_CHANGED = 2; 56 static final int EVENT_RADIO_STATE_CHANGED = 3; 57 static final int EVENT_DEVICE_IMEI_INFO_CHANGED = 4; 58 private static final int LATCH_MAX = 2; 59 private final CountDownLatch[] mLatches = new CountDownLatch[LATCH_MAX]; 60 // ***** Cache of modem attributes/status 61 private String mBasebandVer; 62 private String mImei; 63 private String mImeiSv; 64 private String mEsn; 65 private String mMeid; 66 private int mImeiType; 67 private int mRadioState; 68 IRadioModemImpl( MockModemService service, MockModemConfigInterface configInterface, int instanceId)69 public IRadioModemImpl( 70 MockModemService service, MockModemConfigInterface configInterface, int instanceId) { 71 mTag = TAG + "-" + instanceId; 72 Log.d(mTag, "Instantiated"); 73 74 this.mService = service; 75 mMockModemConfigInterface = configInterface; 76 mCacheUpdateMutex = new Object(); 77 mHandler = new IRadioModemHandler(); 78 mSubId = instanceId; 79 80 // Register events 81 mMockModemConfigInterface.registerForBasebandVersionChanged( 82 mSubId, mHandler, EVENT_BASEBAND_VERSION_CHANGED, null); 83 mMockModemConfigInterface.registerForDeviceIdentityChanged( 84 mSubId, mHandler, EVENT_DEVICE_IDENTITY_CHANGED, null); 85 mMockModemConfigInterface.registerForRadioStateChanged( 86 mSubId, mHandler, EVENT_RADIO_STATE_CHANGED, null); 87 mMockModemConfigInterface.registerForDeviceImeiInfoChanged( 88 mSubId, mHandler, EVENT_DEVICE_IMEI_INFO_CHANGED, null); 89 } 90 91 /** Handler class to handle callbacks */ 92 private final class IRadioModemHandler extends Handler { 93 @Override handleMessage(Message msg)94 public void handleMessage(Message msg) { 95 AsyncResult ar; 96 synchronized (mCacheUpdateMutex) { 97 switch (msg.what) { 98 case EVENT_BASEBAND_VERSION_CHANGED: 99 Log.d(mTag, "Received EVENT_BASEBAND_VERSION_CHANGED"); 100 ar = (AsyncResult) msg.obj; 101 if (ar != null && ar.exception == null) { 102 mBasebandVer = (String) ar.result; 103 Log.i(mTag, "Basedband version = " + mBasebandVer); 104 } else { 105 Log.e( 106 mTag, 107 msg.what 108 + " failure. Not update baseband version." 109 + ar.exception); 110 } 111 break; 112 case EVENT_DEVICE_IDENTITY_CHANGED: 113 Log.d(mTag, "Received EVENT_DEVICE_IDENTITY_CHANGED"); 114 ar = (AsyncResult) msg.obj; 115 if (ar != null && ar.exception == null) { 116 String[] deviceIdentity = (String[]) ar.result; 117 mImei = deviceIdentity[0]; 118 mImeiSv = deviceIdentity[1]; 119 mEsn = deviceIdentity[2]; 120 mMeid = deviceIdentity[3]; 121 Log.i( 122 mTag, 123 "Device identity: IMEI = " 124 + mImei 125 + " IMEISV = " 126 + mImeiSv 127 + " ESN = " 128 + mEsn 129 + " MEID =" 130 + mMeid); 131 } else { 132 Log.e( 133 mTag, 134 msg.what 135 + " failure. Not update device identity." 136 + ar.exception); 137 } 138 break; 139 case EVENT_RADIO_STATE_CHANGED: 140 Log.d(mTag, "Received EVENT_RADIO_STATE_CHANGED"); 141 ar = (AsyncResult) msg.obj; 142 if (ar != null && ar.exception == null) { 143 mRadioState = (int) ar.result; 144 Log.i(mTag, "Radio state: " + mRadioState); 145 } else { 146 Log.e(mTag, msg.what + " failure. Exception: " + ar.exception); 147 } 148 break; 149 case EVENT_DEVICE_IMEI_INFO_CHANGED: 150 Log.d(mTag, "Received EVENT_DEVICE_IMEIINFO_CHANGED"); 151 ar = (AsyncResult) msg.obj; 152 if (ar != null && ar.exception == null) { 153 ImeiInfo imeiInfo = (ImeiInfo) ar.result; 154 mImei = imeiInfo.imei; 155 mImeiSv = imeiInfo.svn; 156 mImeiType = imeiInfo.type; 157 Log.i(mTag, "IMEIInfo : ImeiType = " + mImeiType); 158 } else { 159 Log.e(mTag, msg.what + " failure. Not update device ImeiInfo." 160 + ar.exception); 161 } 162 break; 163 } 164 } 165 } 166 } 167 168 // Implementation of IRadioModem functions 169 @Override setResponseFunctions( IRadioModemResponse radioModemResponse, IRadioModemIndication radioModemIndication)170 public void setResponseFunctions( 171 IRadioModemResponse radioModemResponse, IRadioModemIndication radioModemIndication) { 172 Log.d(mTag, "setResponseFunctions"); 173 mRadioModemResponse = radioModemResponse; 174 mRadioModemIndication = radioModemIndication; 175 mService.countDownLatch(MockModemService.LATCH_RADIO_INTERFACES_READY); 176 } 177 178 @Override enableModem(int serial, boolean on)179 public void enableModem(int serial, boolean on) { 180 Log.d(mTag, "getNumOfLiveModems " + on); 181 182 // TODO: cache value 183 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 184 try { 185 mRadioModemResponse.enableModemResponse(rsp); 186 } catch (RemoteException ex) { 187 Log.e(mTag, "Failed to enableModem from AIDL. Exception" + ex); 188 } 189 } 190 191 @Override getBasebandVersion(int serial)192 public void getBasebandVersion(int serial) { 193 Log.d(mTag, "getBasebandVersion"); 194 195 String baseband; 196 197 synchronized (mCacheUpdateMutex) { 198 baseband = mBasebandVer; 199 } 200 201 RadioResponseInfo rsp = mService.makeSolRsp(serial); 202 try { 203 mRadioModemResponse.getBasebandVersionResponse(rsp, baseband); 204 } catch (RemoteException ex) { 205 Log.e(mTag, "Failed to getBasebandVersion from AIDL. Exception" + ex); 206 } 207 } 208 209 @Override getDeviceIdentity(int serial)210 public void getDeviceIdentity(int serial) { 211 Log.d(mTag, "getDeviceIdentity"); 212 213 String imei, imeisv, esn, meid; 214 215 synchronized (mCacheUpdateMutex) { 216 imei = mImei; 217 imeisv = mImeiSv; 218 esn = mEsn; 219 meid = mMeid; 220 } 221 222 RadioResponseInfo rsp = mService.makeSolRsp(serial); 223 try { 224 mRadioModemResponse.getDeviceIdentityResponse(rsp, imei, imeisv, esn, meid); 225 } catch (RemoteException ex) { 226 Log.e(mTag, "Failed to getDeviceIdentity from AIDL. Exception" + ex); 227 } 228 } 229 230 @Override getImei(int serial)231 public void getImei(int serial) { 232 Log.d(mTag, "getImei"); 233 android.hardware.radio.modem.ImeiInfo imeiInfo = 234 new android.hardware.radio.modem.ImeiInfo(); 235 synchronized (mCacheUpdateMutex) { 236 imeiInfo.type = mImeiType; 237 imeiInfo.imei = mImei; 238 imeiInfo.svn = mImeiSv; 239 } 240 RadioResponseInfo rsp = mService.makeSolRsp(serial); 241 try { 242 mRadioModemResponse.getImeiResponse(rsp, imeiInfo); 243 } catch (RemoteException ex) { 244 Log.e(mTag, "Failed to getImeiResponse from AIDL. Exception" + ex); 245 } 246 } 247 248 @Override getHardwareConfig(int serial)249 public void getHardwareConfig(int serial) { 250 Log.d(mTag, "getHardwareConfig"); 251 252 android.hardware.radio.modem.HardwareConfig[] config = 253 new android.hardware.radio.modem.HardwareConfig[0]; 254 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 255 try { 256 mRadioModemResponse.getHardwareConfigResponse(rsp, config); 257 } catch (RemoteException ex) { 258 Log.e(mTag, "Failed to getHardwareConfig from AIDL. Exception" + ex); 259 } 260 } 261 262 @Override getModemActivityInfo(int serial)263 public void getModemActivityInfo(int serial) { 264 Log.d(mTag, "getModemActivityInfo"); 265 266 android.hardware.radio.modem.ActivityStatsInfo activityInfo = 267 new android.hardware.radio.modem.ActivityStatsInfo(); 268 269 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 270 try { 271 mRadioModemResponse.getModemActivityInfoResponse(rsp, activityInfo); 272 } catch (RemoteException ex) { 273 Log.e(mTag, "Failed to getModemActivityInfo from AIDL. Exception" + ex); 274 } 275 } 276 277 @Override getModemStackStatus(int serial)278 public void getModemStackStatus(int serial) { 279 Log.d(mTag, "getModemStackStatus"); 280 281 boolean isEnabled = false; 282 283 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 284 try { 285 mRadioModemResponse.getModemStackStatusResponse(rsp, isEnabled); 286 } catch (RemoteException ex) { 287 Log.e(mTag, "Failed to getModemStackStatus from AIDL. Exception" + ex); 288 } 289 } 290 291 @Override getRadioCapability(int serial)292 public void getRadioCapability(int serial) { 293 Log.d(mTag, "getRadioCapability"); 294 295 android.hardware.radio.modem.RadioCapability rc = 296 new android.hardware.radio.modem.RadioCapability(); 297 298 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 299 try { 300 mRadioModemResponse.getRadioCapabilityResponse(rsp, rc); 301 } catch (RemoteException ex) { 302 Log.e(mTag, "Failed to getRadioCapability from AIDL. Exception" + ex); 303 } 304 } 305 306 @Override nvReadItem(int serial, int itemId)307 public void nvReadItem(int serial, int itemId) { 308 Log.d(mTag, "nvReadItem"); 309 310 // TODO: cache value 311 String result = ""; 312 313 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 314 try { 315 mRadioModemResponse.nvReadItemResponse(rsp, result); 316 } catch (RemoteException ex) { 317 Log.e(mTag, "Failed to nvReadItem from AIDL. Exception" + ex); 318 } 319 } 320 321 @Override nvResetConfig(int serial, int resetType)322 public void nvResetConfig(int serial, int resetType) { 323 Log.d(mTag, "nvResetConfig"); 324 325 // TODO: cache value 326 327 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 328 try { 329 mRadioModemResponse.nvResetConfigResponse(rsp); 330 } catch (RemoteException ex) { 331 Log.e(mTag, "Failed to nvResetConfig from AIDL. Exception" + ex); 332 } 333 } 334 335 @Override nvWriteCdmaPrl(int serial, byte[] prl)336 public void nvWriteCdmaPrl(int serial, byte[] prl) { 337 Log.d(mTag, "nvWriteCdmaPrl"); 338 339 // TODO: cache value 340 341 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 342 try { 343 mRadioModemResponse.nvWriteCdmaPrlResponse(rsp); 344 } catch (RemoteException ex) { 345 Log.e(mTag, "Failed to nvWriteCdmaPrl from AIDL. Exception" + ex); 346 } 347 } 348 349 @Override nvWriteItem(int serial, android.hardware.radio.modem.NvWriteItem item)350 public void nvWriteItem(int serial, android.hardware.radio.modem.NvWriteItem item) { 351 Log.d(mTag, "nvWriteItem"); 352 353 // TODO: cache value 354 355 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 356 try { 357 mRadioModemResponse.nvWriteItemResponse(rsp); 358 } catch (RemoteException ex) { 359 Log.e(mTag, "Failed to nvWriteItem from AIDL. Exception" + ex); 360 } 361 } 362 363 @Override requestShutdown(int serial)364 public void requestShutdown(int serial) { 365 Log.d(mTag, "requestShutdown"); 366 367 RadioResponseInfo rsp = mService.makeSolRsp(serial); 368 try { 369 mRadioModemResponse.requestShutdownResponse(rsp); 370 } catch (RemoteException ex) { 371 Log.e(mTag, "Failed to requestShutdown from AIDL. Exception" + ex); 372 } 373 } 374 375 @Override sendDeviceState(int serial, int deviceStateType, boolean state)376 public void sendDeviceState(int serial, int deviceStateType, boolean state) { 377 Log.d(mTag, "sendDeviceState"); 378 379 // TODO: cache value 380 381 RadioResponseInfo rsp = mService.makeSolRsp(serial); 382 try { 383 mRadioModemResponse.sendDeviceStateResponse(rsp); 384 } catch (RemoteException ex) { 385 Log.e(mTag, "Failed to sendDeviceState from AIDL. Exception" + ex); 386 } 387 } 388 389 @Override responseAcknowledgement()390 public void responseAcknowledgement() { 391 Log.d(mTag, "responseAcknowledgement"); 392 } 393 394 @Override setRadioCapability(int serial, android.hardware.radio.modem.RadioCapability rc)395 public void setRadioCapability(int serial, android.hardware.radio.modem.RadioCapability rc) { 396 Log.d(mTag, "setRadioCapability"); 397 398 // TODO: cache value 399 android.hardware.radio.modem.RadioCapability respRc = 400 new android.hardware.radio.modem.RadioCapability(); 401 402 RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED); 403 try { 404 mRadioModemResponse.setRadioCapabilityResponse(rsp, respRc); 405 } catch (RemoteException ex) { 406 Log.e(mTag, "Failed to setRadioCapability from AIDL. Exception" + ex); 407 } 408 } 409 410 @Override setRadioPower( int serial, boolean powerOn, boolean forEmergencyCall, boolean preferredForEmergencyCall)411 public void setRadioPower( 412 int serial, 413 boolean powerOn, 414 boolean forEmergencyCall, 415 boolean preferredForEmergencyCall) { 416 Log.d(mTag, "setRadioPower"); 417 RadioResponseInfo rsp = null; 418 419 // Check if the error response needs to be modified 420 if (mForceRadioPowerError != -1) { 421 rsp = mService.makeSolRsp(serial, mForceRadioPowerError); 422 } else { 423 synchronized (mCacheUpdateMutex) { 424 if (powerOn) { 425 mRadioState = MockModemConfigInterface.RADIO_STATE_ON; 426 } else { 427 mRadioState = MockModemConfigInterface.RADIO_STATE_OFF; 428 } 429 mMockModemConfigInterface.setRadioState(mSubId, mRadioState, mTag); 430 } 431 rsp = mService.makeSolRsp(serial); 432 } 433 434 try { 435 mRadioModemResponse.setRadioPowerResponse(rsp); 436 } catch (RemoteException ex) { 437 Log.e(mTag, "Failed to setRadioPower from AIDL. Exception" + ex); 438 } 439 440 if (rsp.error == RadioError.NONE) { 441 if (powerOn) { 442 radioStateChanged(RadioState.ON); 443 } else { 444 radioStateChanged(RadioState.OFF); 445 } 446 } 447 } 448 449 /** 450 * Sent when setRadioCapability() completes. Returns the same RadioCapability as 451 * getRadioCapability() and is the same as the one sent by setRadioCapability(). 452 * 453 * @param radioCapability Current radio capability 454 */ radioCapabilityIndication( android.hardware.radio.modem.RadioCapability radioCapability)455 public void radioCapabilityIndication( 456 android.hardware.radio.modem.RadioCapability radioCapability) { 457 Log.d(mTag, "radioCapabilityIndication"); 458 459 if (mRadioModemIndication != null) { 460 try { 461 mRadioModemIndication.radioCapabilityIndication( 462 RadioIndicationType.UNSOLICITED, radioCapability); 463 } catch (RemoteException ex) { 464 Log.e(mTag, "Failed to radioCapabilityIndication from AIDL. Exception" + ex); 465 } 466 } else { 467 468 Log.e(mTag, "null mRadioModemIndication"); 469 } 470 } 471 472 /** 473 * Sent when there is a change in the IMEI mapping with respect to sim slot. 474 * 475 * @param imeiInfo : change in the imeiInfo value 476 */ onImeiMappingChanged(ImeiInfo imeiInfo)477 public void onImeiMappingChanged(ImeiInfo imeiInfo) { 478 Log.d(TAG, "onImeiMappingChanged"); 479 480 if (mRadioModemIndication != null) { 481 try { 482 mRadioModemIndication.onImeiMappingChanged(RadioIndicationType.UNSOLICITED, 483 imeiInfo); 484 } catch (RemoteException ex) { 485 Log.e(TAG, "Failed to onImeiMappingChanged from AIDL. Exception" + ex); 486 } 487 } else { 488 Log.e(TAG, "null mRadioModemIndication"); 489 } 490 } 491 492 /** 493 * Indicates when radio state changes. 494 * 495 * @param radioState Current radio state 496 */ radioStateChanged(int radioState)497 public void radioStateChanged(int radioState) { 498 Log.d(mTag, "radioStateChanged"); 499 500 if (mRadioModemIndication != null) { 501 try { 502 mRadioModemIndication.radioStateChanged( 503 RadioIndicationType.UNSOLICITED, radioState); 504 } catch (RemoteException ex) { 505 Log.e(mTag, "Failed to radioStateChanged from AIDL. Exception" + ex); 506 } 507 } else { 508 509 Log.e(mTag, "null mRadioModemIndication"); 510 } 511 } 512 513 /** Indicates the ril connects and returns the version. */ rilConnected()514 public void rilConnected() { 515 Log.d(mTag, "rilConnected"); 516 517 if (mRadioModemIndication != null) { 518 try { 519 mRadioModemIndication.rilConnected(RadioIndicationType.UNSOLICITED); 520 } catch (RemoteException ex) { 521 Log.e(mTag, "Failed to rilConnected from AIDL. Exception" + ex); 522 } 523 } else { 524 525 Log.e(mTag, "null mRadioModemIndication"); 526 } 527 } 528 forceErrorResponse(int requestId, int error)529 public void forceErrorResponse(int requestId, int error) { 530 switch (requestId) { 531 case RIL_REQUEST_RADIO_POWER: 532 mForceRadioPowerError = error; 533 break; 534 default: 535 break; 536 } 537 } 538 539 @Override getInterfaceHash()540 public String getInterfaceHash() { 541 return IRadioModem.HASH; 542 } 543 544 @Override getInterfaceVersion()545 public int getInterfaceVersion() { 546 return IRadioModem.VERSION; 547 } 548 549 /** 550 * Helper method that will change the Imei mapping to another slot. 551 */ changeImeiMapping()552 public void changeImeiMapping() { 553 if (mImeiType == ImeiInfo.ImeiType.PRIMARY 554 && MockModemConfigInterface.DEFAULT_PHONE1_IMEI.equals(mImei)) { 555 mImeiType = ImeiInfo.ImeiType.SECONDARY; 556 mImei = MockModemConfigInterface.DEFAULT_PHONE2_IMEI; 557 } else { 558 mImeiType = ImeiInfo.ImeiType.PRIMARY; 559 mImei = MockModemConfigInterface.DEFAULT_PHONE1_IMEI; 560 } 561 562 Log.d(TAG, "changeImeiMapping"); 563 android.hardware.radio.modem.ImeiInfo imeiInfo = 564 new android.hardware.radio.modem.ImeiInfo(); 565 synchronized (mCacheUpdateMutex) { 566 imeiInfo.type = mImeiType; 567 imeiInfo.imei = mImei; 568 imeiInfo.svn = mImeiSv; 569 } 570 onImeiMappingChanged(imeiInfo); 571 } 572 573 /** 574 * Waits for the event of voice service. 575 * 576 * @param latchIndex The index of the event. 577 * @param waitMs The timeout in milliseconds. 578 * @return {@code true} if the event happens. 579 */ waitForLatchCountdown(int latchIndex, long waitMs)580 public boolean waitForLatchCountdown(int latchIndex, long waitMs) { 581 boolean complete = false; 582 try { 583 CountDownLatch latch; 584 synchronized (mLatches) { 585 latch = mLatches[latchIndex]; 586 } 587 long startTime = System.currentTimeMillis(); 588 complete = latch.await(waitMs, TimeUnit.MILLISECONDS); 589 Log.i(TAG, "Latch " + latchIndex + " took " 590 + (System.currentTimeMillis() - startTime) + " ms to count down."); 591 } catch (InterruptedException e) { 592 Log.e(TAG, "Waiting latch " + latchIndex + " interrupted, e=" + e); 593 } 594 synchronized (mLatches) { 595 mLatches[latchIndex] = new CountDownLatch(1); 596 } 597 return complete; 598 } 599 600 /** 601 * Resets the CountDownLatches 602 */ resetAllLatchCountdown()603 public void resetAllLatchCountdown() { 604 synchronized (mLatches) { 605 for (int i = 0; i < LATCH_MAX; i++) { 606 mLatches[i] = new CountDownLatch(1); 607 } 608 } 609 } 610 } 611