1 /* 2 * Copyright (C) 2018 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 com.android.internal.telephony; 18 19 import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_NSA; 20 import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_SA; 21 22 import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE; 23 import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED; 24 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES; 25 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PHONE_CAPABILITY; 26 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT; 27 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS; 28 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING; 29 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM; 30 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG; 31 32 import android.content.Context; 33 import android.os.AsyncResult; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Message; 37 import android.os.Registrant; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.Trace; 41 import android.os.WorkSource; 42 import android.telephony.TelephonyManager; 43 import android.telephony.UiccSlotMapping; 44 import android.util.SparseArray; 45 46 import com.android.telephony.Rlog; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.NoSuchElementException; 51 import java.util.concurrent.atomic.AtomicLong; 52 53 /** 54 * This class provides wrapper APIs for IRadioConfig interface. 55 */ 56 public class RadioConfig extends Handler { 57 private static final String TAG = "RadioConfig"; 58 private static final boolean DBG = true; 59 private static final boolean VDBG = false; //STOPSHIP if true 60 private static final Object sLock = new Object(); 61 62 static final int EVENT_HIDL_SERVICE_DEAD = 1; 63 static final int EVENT_AIDL_SERVICE_DEAD = 2; 64 65 private final boolean mIsMobileNetworkSupported; 66 private final SparseArray<RILRequest> mRequestList = new SparseArray<>(); 67 /* default work source which will blame phone process */ 68 private final WorkSource mDefaultWorkSource; 69 private final int[] mDeviceNrCapabilities; 70 private final AtomicLong mRadioConfigProxyCookie = new AtomicLong(0); 71 private final RadioConfigProxy mRadioConfigProxy; 72 private MockModem mMockModem; 73 private static Context sContext; 74 75 private static RadioConfig sRadioConfig; 76 77 protected Registrant mSimSlotStatusRegistrant; 78 79 protected Registrant mSimultaneousCallingSupportStatusRegistrant; 80 isMobileDataCapable(Context context)81 private boolean isMobileDataCapable(Context context) { 82 final TelephonyManager tm = context.getSystemService(TelephonyManager.class); 83 return tm != null && tm.isDataCapable(); 84 } 85 RadioConfig(Context context, HalVersion radioHalVersion)86 private RadioConfig(Context context, HalVersion radioHalVersion) { 87 mIsMobileNetworkSupported = isMobileDataCapable(context); 88 mRadioConfigProxy = new RadioConfigProxy(this, radioHalVersion); 89 mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid, 90 context.getPackageName()); 91 92 boolean is5gStandalone = context.getResources().getBoolean( 93 com.android.internal.R.bool.config_telephony5gStandalone); 94 boolean is5gNonStandalone = context.getResources().getBoolean( 95 com.android.internal.R.bool.config_telephony5gNonStandalone); 96 97 if (!is5gStandalone && !is5gNonStandalone) { 98 mDeviceNrCapabilities = new int[0]; 99 } else { 100 List<Integer> list = new ArrayList<>(); 101 if (is5gNonStandalone) { 102 list.add(DEVICE_NR_CAPABILITY_NSA); 103 } 104 if (is5gStandalone) { 105 list.add(DEVICE_NR_CAPABILITY_SA); 106 } 107 mDeviceNrCapabilities = list.stream().mapToInt(Integer::valueOf).toArray(); 108 } 109 } 110 111 /** 112 * Returns the singleton static instance of RadioConfig 113 */ getInstance()114 public static RadioConfig getInstance() { 115 synchronized (sLock) { 116 if (sRadioConfig == null) { 117 throw new RuntimeException( 118 "RadioConfig.getInstance can't be called before make()"); 119 } 120 return sRadioConfig; 121 } 122 } 123 124 /** 125 * Makes the radio config based on the context and the radio hal version passed in 126 */ make(Context c, HalVersion radioHalVersion)127 public static RadioConfig make(Context c, HalVersion radioHalVersion) { 128 synchronized (sLock) { 129 if (sRadioConfig != null) { 130 throw new RuntimeException("RadioConfig.make() should only be called once"); 131 } 132 sContext = c; 133 sRadioConfig = new RadioConfig(c, radioHalVersion); 134 return sRadioConfig; 135 } 136 } 137 138 @Override handleMessage(Message message)139 public void handleMessage(Message message) { 140 if (message.what == EVENT_HIDL_SERVICE_DEAD) { 141 logd("handleMessage: EVENT_HIDL_SERVICE_DEAD cookie = " + message.obj 142 + " mRadioConfigProxyCookie = " + mRadioConfigProxyCookie.get()); 143 if ((long) message.obj == mRadioConfigProxyCookie.get()) { 144 resetProxyAndRequestList("EVENT_HIDL_SERVICE_DEAD", null); 145 } 146 } else if (message.what == EVENT_AIDL_SERVICE_DEAD) { 147 logd("handleMessage: EVENT_AIDL_SERVICE_DEAD mRadioConfigProxyCookie = " 148 + mRadioConfigProxyCookie.get()); 149 resetProxyAndRequestList("EVENT_AIDL_SERVICE_DEAD", null); 150 } 151 } 152 153 /** 154 * Release each request in mRequestList then clear the list 155 * @param error is the RIL_Errno sent back 156 * @param loggable true means to print all requests in mRequestList 157 */ clearRequestList(int error, boolean loggable)158 private void clearRequestList(int error, boolean loggable) { 159 RILRequest rr; 160 synchronized (mRequestList) { 161 int count = mRequestList.size(); 162 if (DBG && loggable) { 163 logd("clearRequestList: mRequestList=" + count); 164 } 165 166 for (int i = 0; i < count; i++) { 167 rr = mRequestList.valueAt(i); 168 if (DBG && loggable) { 169 logd(i + ": [" + rr.mSerial + "] " + RILUtils.requestToString(rr.mRequest)); 170 } 171 rr.onError(error, null); 172 rr.release(); 173 } 174 mRequestList.clear(); 175 } 176 } 177 resetProxyAndRequestList(String caller, Exception e)178 private void resetProxyAndRequestList(String caller, Exception e) { 179 loge(caller + ": " + e); 180 mRadioConfigProxy.clear(); 181 182 // increment the cookie so that death notification can be ignored 183 mRadioConfigProxyCookie.incrementAndGet(); 184 185 RILRequest.resetSerial(); 186 // Clear request list on close 187 clearRequestList(RADIO_NOT_AVAILABLE, false); 188 189 getRadioConfigProxy(null); 190 } 191 192 /** 193 * Returns a holder that has either: 194 * - getV1() -> {@link android.hardware.radio.config.V1_0.IRadioConfig} 195 * - getV2() -> {@link android.hardware.radio.config.IRadioConfig} 196 * that returns corresponding hal implementation 197 */ getRadioConfigProxy(Message result)198 public RadioConfigProxy getRadioConfigProxy(Message result) { 199 if (!mIsMobileNetworkSupported) { 200 if (VDBG) logd("getRadioConfigProxy: Not calling getService(): wifi-only"); 201 if (result != null) { 202 AsyncResult.forMessage(result, null, 203 CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 204 result.sendToTarget(); 205 } 206 mRadioConfigProxy.clear(); 207 return mRadioConfigProxy; 208 } 209 210 if (!mRadioConfigProxy.isEmpty()) { 211 return mRadioConfigProxy; 212 } 213 214 updateRadioConfigProxy(); 215 216 if (mRadioConfigProxy.isEmpty() && result != null) { 217 AsyncResult.forMessage( 218 result, null, CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 219 result.sendToTarget(); 220 } 221 222 return mRadioConfigProxy; 223 } 224 225 /** 226 * Request to enable/disable the mock modem service. 227 * This is invoked from shell commands during CTS testing only. 228 * 229 * @param serviceName the service name we want to bind to 230 */ setModemService(String serviceName)231 public boolean setModemService(String serviceName) { 232 boolean serviceBound = true; 233 234 if (serviceName != null) { 235 logd("Overriding connected service to MockModemService"); 236 mMockModem = null; 237 238 mMockModem = new MockModem(sContext, serviceName); 239 if (mMockModem == null) { 240 loge("MockModem creation failed."); 241 return false; 242 } 243 244 mMockModem.bindToMockModemService(MockModem.RADIOCONFIG_SERVICE); 245 246 int retryCount = 0; 247 IBinder binder; 248 do { 249 binder = mMockModem.getServiceBinder(MockModem.RADIOCONFIG_SERVICE); 250 251 retryCount++; 252 if (binder == null) { 253 logd("Retry(" + retryCount + ") Mock RadioConfig"); 254 try { 255 Thread.sleep(MockModem.BINDER_RETRY_MILLIS); 256 } catch (InterruptedException e) { 257 } 258 } 259 } while ((binder == null) && (retryCount < MockModem.BINDER_MAX_RETRY)); 260 261 if (binder == null) { 262 loge("Mock RadioConfig bind fail"); 263 serviceBound = false; 264 } 265 266 if (serviceBound) resetProxyAndRequestList("EVENT_HIDL_SERVICE_DEAD", null); 267 } 268 269 if ((serviceName == null) || (!serviceBound)) { 270 if (serviceBound) logd("Unbinding to mock RadioConfig service"); 271 272 if (mMockModem != null) { 273 mMockModem = null; 274 resetProxyAndRequestList("EVENT_AIDL_SERVICE_DEAD", null); 275 } 276 } 277 278 return serviceBound; 279 } 280 updateRadioConfigProxy()281 private void updateRadioConfigProxy() { 282 IBinder service; 283 if (mMockModem == null) { 284 service = ServiceManager.waitForDeclaredService( 285 android.hardware.radio.config.IRadioConfig.DESCRIPTOR + "/default"); 286 } else { 287 // Binds to Mock RadioConfig Service 288 service = mMockModem.getServiceBinder(MockModem.RADIOCONFIG_SERVICE); 289 } 290 291 if (service != null) { 292 mRadioConfigProxy.setAidl( 293 android.hardware.radio.config.IRadioConfig.Stub.asInterface(service)); 294 } 295 296 if (mRadioConfigProxy.isEmpty()) { 297 try { 298 mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_3, 299 android.hardware.radio.config.V1_3.IRadioConfig.getService(true)); 300 } catch (RemoteException | NoSuchElementException e) { 301 mRadioConfigProxy.clear(); 302 loge("getHidlRadioConfigProxy1_3: RadioConfigProxy getService: " + e); 303 } 304 } 305 306 if (mRadioConfigProxy.isEmpty()) { 307 try { 308 mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_1, 309 android.hardware.radio.config.V1_1.IRadioConfig.getService(true)); 310 } catch (RemoteException | NoSuchElementException e) { 311 mRadioConfigProxy.clear(); 312 loge("getHidlRadioConfigProxy1_1: RadioConfigProxy getService | linkToDeath: " + e); 313 } 314 } 315 316 if (mRadioConfigProxy.isEmpty()) { 317 loge("IRadioConfig <1.1 is no longer supported."); 318 } 319 320 if (!mRadioConfigProxy.isEmpty()) { 321 try { 322 mRadioConfigProxy.linkToDeath(mRadioConfigProxyCookie.incrementAndGet()); 323 mRadioConfigProxy.setResponseFunctions(this); 324 return; 325 } catch (RemoteException e) { 326 mRadioConfigProxy.clear(); 327 loge("RadioConfigProxy: failed to linkToDeath() or setResponseFunction()"); 328 } 329 } 330 331 loge("getRadioConfigProxy: mRadioConfigProxy == null"); 332 } 333 obtainRequest(int request, Message result, WorkSource workSource)334 private RILRequest obtainRequest(int request, Message result, WorkSource workSource) { 335 RILRequest rr = RILRequest.obtain(request, result, workSource); 336 Trace.asyncTraceForTrackBegin( 337 Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial); 338 339 synchronized (mRequestList) { 340 mRequestList.append(rr.mSerial, rr); 341 } 342 return rr; 343 } 344 findAndRemoveRequestFromList(int serial)345 private RILRequest findAndRemoveRequestFromList(int serial) { 346 RILRequest rr; 347 synchronized (mRequestList) { 348 rr = mRequestList.get(serial); 349 350 if (rr != null) { 351 Trace.asyncTraceForTrackEnd( 352 Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial); 353 mRequestList.remove(serial); 354 } 355 } 356 357 return rr; 358 } 359 360 /** 361 * This is a helper function to be called when a RadioConfigResponse callback is called. 362 * It finds and returns RILRequest corresponding to the response if one is found. 363 * @param responseInfo RadioResponseInfo received in response callback 364 * @return RILRequest corresponding to the response 365 */ processResponse(android.hardware.radio.RadioResponseInfo responseInfo)366 public RILRequest processResponse(android.hardware.radio.RadioResponseInfo responseInfo) { 367 int serial = responseInfo.serial; 368 int error = responseInfo.error; 369 int type = responseInfo.type; 370 371 if (type != android.hardware.radio.RadioResponseType.SOLICITED) { 372 loge("processResponse: Unexpected response type " + type); 373 } 374 375 RILRequest rr = findAndRemoveRequestFromList(serial); 376 if (rr == null) { 377 loge("processResponse: Unexpected response! serial: " + serial + " error: " + error); 378 return null; 379 } 380 381 return rr; 382 } 383 384 /** 385 * This is a helper function to be called when a RadioConfigResponse callback is called. 386 * It finds and returns RILRequest corresponding to the response if one is found. 387 * @param responseInfo RadioResponseInfo received in response callback 388 * @return RILRequest corresponding to the response 389 */ processResponse(android.hardware.radio.V1_0.RadioResponseInfo responseInfo)390 public RILRequest processResponse(android.hardware.radio.V1_0.RadioResponseInfo responseInfo) { 391 int serial = responseInfo.serial; 392 int error = responseInfo.error; 393 int type = responseInfo.type; 394 395 if (type != android.hardware.radio.RadioResponseType.SOLICITED) { 396 loge("processResponse: Unexpected response type " + type); 397 } 398 399 RILRequest rr = findAndRemoveRequestFromList(serial); 400 if (rr == null) { 401 loge("processResponse: Unexpected response! serial: " + serial + " error: " + error); 402 return null; 403 } 404 405 return rr; 406 } 407 408 /** 409 * This is a helper function to be called when a RadioConfigResponse callback is called. 410 * It finds and returns RILRequest corresponding to the response if one is found. 411 * @param responseInfo RadioResponseInfo received in response callback 412 * @return RILRequest corresponding to the response 413 */ processResponse_1_6( android.hardware.radio.V1_6.RadioResponseInfo responseInfo)414 public RILRequest processResponse_1_6( 415 android.hardware.radio.V1_6.RadioResponseInfo responseInfo) { 416 int serial = responseInfo.serial; 417 int error = responseInfo.error; 418 int type = responseInfo.type; 419 if (type != android.hardware.radio.RadioResponseType.SOLICITED) { 420 loge("processResponse: Unexpected response type " + type); 421 } 422 423 RILRequest rr = findAndRemoveRequestFromList(serial); 424 if (rr == null) { 425 loge("processResponse: Unexpected response! serial: " + serial + " error: " + error); 426 return null; 427 } 428 429 return rr; 430 } 431 432 /** 433 * Wrapper function for IRadioConfig.getSimSlotsStatus(). 434 */ getSimSlotsStatus(Message result)435 public void getSimSlotsStatus(Message result) { 436 RadioConfigProxy proxy = getRadioConfigProxy(result); 437 if (proxy.isEmpty()) return; 438 439 RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLOT_STATUS, result, mDefaultWorkSource); 440 if (DBG) { 441 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); 442 } 443 try { 444 proxy.getSimSlotStatus(rr.mSerial); 445 } catch (RemoteException | RuntimeException e) { 446 resetProxyAndRequestList("getSimSlotsStatus", e); 447 } 448 } 449 450 /** 451 * Wrapper function for IRadioConfig.setPreferredDataModem(int modemId). 452 */ setPreferredDataModem(int modemId, Message result)453 public void setPreferredDataModem(int modemId, Message result) { 454 RadioConfigProxy proxy = getRadioConfigProxy(null); 455 if (proxy.isEmpty()) return; 456 457 if (!isSetPreferredDataCommandSupported()) { 458 if (result != null) { 459 AsyncResult.forMessage(result, null, 460 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 461 result.sendToTarget(); 462 } 463 return; 464 } 465 466 RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_DATA_MODEM, 467 result, mDefaultWorkSource); 468 if (DBG) { 469 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); 470 } 471 try { 472 proxy.setPreferredDataModem(rr.mSerial, modemId); 473 } catch (RemoteException | RuntimeException e) { 474 resetProxyAndRequestList("setPreferredDataModem", e); 475 } 476 } 477 478 /** 479 * Wrapper function for IRadioConfig.getSimultaneousCallingSupport(). 480 */ updateSimultaneousCallingSupport(Message result)481 public void updateSimultaneousCallingSupport(Message result) { 482 RadioConfigProxy proxy = getRadioConfigProxy(null); 483 if (proxy.isEmpty()) return; 484 485 if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_2_2)) { 486 if (result != null) { 487 AsyncResult.forMessage(result, null, 488 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 489 result.sendToTarget(); 490 } 491 return; 492 } 493 494 RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT, result, 495 mDefaultWorkSource); 496 if (DBG) { 497 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); 498 } 499 try { 500 proxy.updateSimultaneousCallingSupport(rr.mSerial); 501 } catch (RemoteException | RuntimeException e) { 502 resetProxyAndRequestList("updateSimultaneousCallingSupport", e); 503 } 504 } 505 506 /** 507 * Wrapper function for IRadioConfig.getPhoneCapability(). 508 */ getPhoneCapability(Message result)509 public void getPhoneCapability(Message result) { 510 RadioConfigProxy proxy = getRadioConfigProxy(null); 511 if (proxy.isEmpty()) return; 512 513 if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) { 514 if (result != null) { 515 AsyncResult.forMessage(result, null, 516 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 517 result.sendToTarget(); 518 } 519 return; 520 } 521 522 RILRequest rr = obtainRequest(RIL_REQUEST_GET_PHONE_CAPABILITY, result, mDefaultWorkSource); 523 if (DBG) { 524 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); 525 } 526 try { 527 proxy.getPhoneCapability(rr.mSerial); 528 } catch (RemoteException | RuntimeException e) { 529 resetProxyAndRequestList("getPhoneCapability", e); 530 } 531 } 532 533 /** 534 * @return whether current radio config version supports SET_PREFERRED_DATA_MODEM command. 535 * If yes, we'll use RIL_REQUEST_SET_PREFERRED_DATA_MODEM to indicate which modem is preferred. 536 * If not, we shall use RIL_REQUEST_ALLOW_DATA for on-demand PS attach / detach. 537 * See PhoneSwitcher for more details. 538 */ isSetPreferredDataCommandSupported()539 public boolean isSetPreferredDataCommandSupported() { 540 RadioConfigProxy proxy = getRadioConfigProxy(null); 541 return !proxy.isEmpty() && proxy.getVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_1); 542 } 543 544 /** 545 * Wrapper function for IRadioConfig.setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap). 546 */ setSimSlotsMapping(List<UiccSlotMapping> slotMapping, Message result)547 public void setSimSlotsMapping(List<UiccSlotMapping> slotMapping, Message result) { 548 RadioConfigProxy proxy = getRadioConfigProxy(result); 549 if (proxy.isEmpty()) return; 550 551 RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING, result, 552 mDefaultWorkSource); 553 if (DBG) { 554 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " " 555 + slotMapping); 556 } 557 try { 558 proxy.setSimSlotsMapping(rr.mSerial, slotMapping); 559 } catch (RemoteException | RuntimeException e) { 560 resetProxyAndRequestList("setSimSlotsMapping", e); 561 } 562 } 563 564 /** 565 * Wrapper function for using IRadioConfig.setNumOfLiveModems(int32_t serial, 566 * byte numOfLiveModems) to switch between single-sim and multi-sim. 567 */ setNumOfLiveModems(int numOfLiveModems, Message result)568 public void setNumOfLiveModems(int numOfLiveModems, Message result) { 569 RadioConfigProxy proxy = getRadioConfigProxy(result); 570 if (proxy.isEmpty()) return; 571 572 if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) { 573 if (result != null) { 574 AsyncResult.forMessage( 575 result, null, CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 576 result.sendToTarget(); 577 } 578 return; 579 } 580 581 RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG, 582 result, mDefaultWorkSource); 583 if (DBG) { 584 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) 585 + ", numOfLiveModems = " + numOfLiveModems); 586 } 587 try { 588 proxy.setNumOfLiveModems(rr.mSerial, numOfLiveModems); 589 } catch (RemoteException | RuntimeException e) { 590 resetProxyAndRequestList("setNumOfLiveModems", e); 591 } 592 } 593 594 /** 595 * Register a handler to get SIM slots that support simultaneous calling changed notifications. 596 */ registerForSimultaneousCallingSupportStatusChanged(Handler h, int what, Object obj)597 public void registerForSimultaneousCallingSupportStatusChanged(Handler h, int what, 598 Object obj) { 599 mSimultaneousCallingSupportStatusRegistrant = new Registrant(h, what, obj); 600 } 601 602 /** 603 * Register a handler to get SIM slot status changed notifications. 604 */ registerForSimSlotStatusChanged(Handler h, int what, Object obj)605 public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) { 606 mSimSlotStatusRegistrant = new Registrant(h, what, obj); 607 } 608 609 /** 610 * Unregister corresponding to registerForSimSlotStatusChanged(). 611 */ unregisterForSimSlotStatusChanged(Handler h)612 public void unregisterForSimSlotStatusChanged(Handler h) { 613 if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) { 614 mSimSlotStatusRegistrant.clear(); 615 mSimSlotStatusRegistrant = null; 616 } 617 } 618 619 /** 620 * Gets the hal capabilities from the device. 621 */ getHalDeviceCapabilities(Message result)622 public void getHalDeviceCapabilities(Message result) { 623 RadioConfigProxy proxy = getRadioConfigProxy(Message.obtain(result)); 624 if (proxy.isEmpty()) return; 625 626 if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_3)) { 627 if (result != null) { 628 if (DBG) { 629 logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED"); 630 } 631 AsyncResult.forMessage(result, 632 /* Send response such that all capabilities are supported (depending on 633 the hal version of course.) */ 634 proxy.getFullCapabilitySet(), 635 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 636 result.sendToTarget(); 637 } else { 638 if (DBG) { 639 logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED " 640 + "on complete message not set."); 641 } 642 } 643 return; 644 } 645 646 RILRequest rr = obtainRequest(RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES, 647 result, mDefaultWorkSource); 648 if (DBG) { 649 logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)); 650 } 651 try { 652 proxy.getHalDeviceCapabilities(rr.mSerial); 653 } catch (RemoteException | RuntimeException e) { 654 resetProxyAndRequestList("getHalDeviceCapabilities", e); 655 } 656 } 657 658 /** 659 * Returns the device's nr capability. 660 */ getDeviceNrCapabilities()661 public int[] getDeviceNrCapabilities() { 662 return mDeviceNrCapabilities; 663 } 664 logd(String log)665 private static void logd(String log) { 666 Rlog.d(TAG, log); 667 } 668 loge(String log)669 private static void loge(String log) { 670 Rlog.e(TAG, log); 671 } 672 673 @Override toString()674 public String toString() { 675 return "RadioConfig[" + "mRadioConfigProxy=" + mRadioConfigProxy + ']'; 676 } 677 } 678