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