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 com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE; 20 import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED; 21 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PHONE_CAPABILITY; 22 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS; 23 import static com.android.internal.telephony.RILConstants 24 .RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING; 25 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM; 26 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG; 27 28 import android.content.Context; 29 import android.hardware.radio.V1_0.RadioResponseInfo; 30 import android.hardware.radio.V1_0.RadioResponseType; 31 import android.hardware.radio.config.V1_0.IRadioConfig; 32 import android.hardware.radio.config.V1_1.ModemsConfig; 33 import android.net.ConnectivityManager; 34 import android.os.AsyncResult; 35 import android.os.Handler; 36 import android.os.HwBinder; 37 import android.os.Message; 38 import android.os.Registrant; 39 import android.os.RemoteException; 40 import android.os.WorkSource; 41 import android.util.SparseArray; 42 43 import com.android.internal.telephony.uicc.IccSlotStatus; 44 import com.android.telephony.Rlog; 45 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.NoSuchElementException; 49 import java.util.concurrent.atomic.AtomicLong; 50 51 /** 52 * This class provides wrapper APIs for IRadioConfig interface. 53 */ 54 public class RadioConfig extends Handler { 55 private static final String TAG = "RadioConfig"; 56 private static final boolean DBG = true; 57 private static final boolean VDBG = false; //STOPSHIP if true 58 59 private static final int EVENT_SERVICE_DEAD = 1; 60 61 private static final HalVersion RADIO_CONFIG_HAL_VERSION_UNKNOWN = new HalVersion(-1, -1); 62 63 private static final HalVersion RADIO_CONFIG_HAL_VERSION_1_0 = new HalVersion(1, 0); 64 65 private static final HalVersion RADIO_CONFIG_HAL_VERSION_1_1 = new HalVersion(1, 1); 66 67 private final boolean mIsMobileNetworkSupported; 68 private volatile IRadioConfig mRadioConfigProxy = null; 69 // IRadioConfig version 70 private HalVersion mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_UNKNOWN; 71 private final ServiceDeathRecipient mServiceDeathRecipient; 72 private final AtomicLong mRadioConfigProxyCookie = new AtomicLong(0); 73 private final RadioConfigResponse mRadioConfigResponse; 74 private final RadioConfigIndication mRadioConfigIndication; 75 private final SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>(); 76 /* default work source which will blame phone process */ 77 private final WorkSource mDefaultWorkSource; 78 private static RadioConfig sRadioConfig; 79 80 protected Registrant mSimSlotStatusRegistrant; 81 82 final class ServiceDeathRecipient implements HwBinder.DeathRecipient { 83 @Override serviceDied(long cookie)84 public void serviceDied(long cookie) { 85 // Deal with service going away 86 logd("serviceDied"); 87 sendMessage(obtainMessage(EVENT_SERVICE_DEAD, cookie)); 88 } 89 } 90 RadioConfig(Context context)91 private RadioConfig(Context context) { 92 ConnectivityManager cm = (ConnectivityManager) context.getSystemService( 93 Context.CONNECTIVITY_SERVICE); 94 mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 95 96 mRadioConfigResponse = new RadioConfigResponse(this); 97 mRadioConfigIndication = new RadioConfigIndication(this); 98 mServiceDeathRecipient = new ServiceDeathRecipient(); 99 100 mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid, 101 context.getPackageName()); 102 } 103 104 /** 105 * Returns the singleton static instance of RadioConfig 106 */ getInstance(Context context)107 public static RadioConfig getInstance(Context context) { 108 if (sRadioConfig == null) { 109 sRadioConfig = new RadioConfig(context); 110 } 111 return sRadioConfig; 112 } 113 114 @Override handleMessage(Message message)115 public void handleMessage(Message message) { 116 switch (message.what) { 117 case EVENT_SERVICE_DEAD: 118 logd("handleMessage: EVENT_SERVICE_DEAD cookie = " + message.obj 119 + " mRadioConfigProxyCookie = " + mRadioConfigProxyCookie.get()); 120 if ((long) message.obj == mRadioConfigProxyCookie.get()) { 121 resetProxyAndRequestList("EVENT_SERVICE_DEAD", null); 122 } 123 break; 124 } 125 } 126 127 /** 128 * Release each request in mRequestList then clear the list 129 * @param error is the RIL_Errno sent back 130 * @param loggable true means to print all requests in mRequestList 131 */ clearRequestList(int error, boolean loggable)132 private void clearRequestList(int error, boolean loggable) { 133 RILRequest rr; 134 synchronized (mRequestList) { 135 int count = mRequestList.size(); 136 if (DBG && loggable) { 137 logd("clearRequestList: mRequestList=" + count); 138 } 139 140 for (int i = 0; i < count; i++) { 141 rr = mRequestList.valueAt(i); 142 if (DBG && loggable) { 143 logd(i + ": [" + rr.mSerial + "] " + requestToString(rr.mRequest)); 144 } 145 rr.onError(error, null); 146 rr.release(); 147 } 148 mRequestList.clear(); 149 } 150 } 151 resetProxyAndRequestList(String caller, Exception e)152 private void resetProxyAndRequestList(String caller, Exception e) { 153 loge(caller + ": " + e); 154 mRadioConfigProxy = null; 155 156 // increment the cookie so that death notification can be ignored 157 mRadioConfigProxyCookie.incrementAndGet(); 158 159 RILRequest.resetSerial(); 160 // Clear request list on close 161 clearRequestList(RADIO_NOT_AVAILABLE, false); 162 163 getRadioConfigProxy(null); 164 } 165 166 /** Returns a {@link IRadioConfig} instance or null if the service is not available. */ getRadioConfigProxy(Message result)167 public IRadioConfig getRadioConfigProxy(Message result) { 168 if (!mIsMobileNetworkSupported) { 169 if (VDBG) logd("getRadioConfigProxy: Not calling getService(): wifi-only"); 170 if (result != null) { 171 AsyncResult.forMessage(result, null, 172 CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 173 result.sendToTarget(); 174 } 175 return null; 176 } 177 178 if (mRadioConfigProxy != null) { 179 return mRadioConfigProxy; 180 } 181 182 updateRadioConfigProxy(); 183 184 if (mRadioConfigProxy == null) { 185 if (result != null) { 186 AsyncResult.forMessage(result, null, 187 CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 188 result.sendToTarget(); 189 } 190 } 191 192 return mRadioConfigProxy; 193 } 194 updateRadioConfigProxy()195 private void updateRadioConfigProxy() { 196 try { 197 // Try to get service from different versions. 198 try { 199 mRadioConfigProxy = android.hardware.radio.config.V1_1.IRadioConfig.getService( 200 true); 201 mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_1; 202 } catch (NoSuchElementException e) { 203 } 204 205 if (mRadioConfigProxy == null) { 206 try { 207 mRadioConfigProxy = android.hardware.radio.config.V1_0 208 .IRadioConfig.getService(true); 209 mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_0; 210 } catch (NoSuchElementException e) { 211 } 212 } 213 214 if (mRadioConfigProxy == null) { 215 loge("getRadioConfigProxy: mRadioConfigProxy == null"); 216 return; 217 } 218 219 // Link to death recipient and set response. If fails, set proxy to null and return. 220 mRadioConfigProxy.linkToDeath(mServiceDeathRecipient, 221 mRadioConfigProxyCookie.incrementAndGet()); 222 mRadioConfigProxy.setResponseFunctions(mRadioConfigResponse, 223 mRadioConfigIndication); 224 } catch (RemoteException | RuntimeException e) { 225 mRadioConfigProxy = null; 226 loge("getRadioConfigProxy: RadioConfigProxy setResponseFunctions: " + e); 227 return; 228 } 229 } 230 obtainRequest(int request, Message result, WorkSource workSource)231 private RILRequest obtainRequest(int request, Message result, WorkSource workSource) { 232 RILRequest rr = RILRequest.obtain(request, result, workSource); 233 synchronized (mRequestList) { 234 mRequestList.append(rr.mSerial, rr); 235 } 236 return rr; 237 } 238 findAndRemoveRequestFromList(int serial)239 private RILRequest findAndRemoveRequestFromList(int serial) { 240 RILRequest rr; 241 synchronized (mRequestList) { 242 rr = mRequestList.get(serial); 243 if (rr != null) { 244 mRequestList.remove(serial); 245 } 246 } 247 248 return rr; 249 } 250 251 /** 252 * This is a helper function to be called when a RadioConfigResponse callback is called. 253 * It finds and returns RILRequest corresponding to the response if one is found. 254 * @param responseInfo RadioResponseInfo received in response callback 255 * @return RILRequest corresponding to the response 256 */ processResponse(RadioResponseInfo responseInfo)257 public RILRequest processResponse(RadioResponseInfo responseInfo) { 258 int serial = responseInfo.serial; 259 int error = responseInfo.error; 260 int type = responseInfo.type; 261 262 if (type != RadioResponseType.SOLICITED) { 263 loge("processResponse: Unexpected response type " + type); 264 } 265 266 RILRequest rr = findAndRemoveRequestFromList(serial); 267 if (rr == null) { 268 loge("processResponse: Unexpected response! serial: " + serial + " error: " + error); 269 return null; 270 } 271 272 return rr; 273 } 274 275 /** 276 * Wrapper function for IRadioConfig.getSimSlotsStatus(). 277 */ getSimSlotsStatus(Message result)278 public void getSimSlotsStatus(Message result) { 279 IRadioConfig radioConfigProxy = getRadioConfigProxy(result); 280 if (radioConfigProxy != null) { 281 RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLOT_STATUS, result, mDefaultWorkSource); 282 283 if (DBG) { 284 logd(rr.serialString() + "> " + requestToString(rr.mRequest)); 285 } 286 287 try { 288 radioConfigProxy.getSimSlotsStatus(rr.mSerial); 289 } catch (RemoteException | RuntimeException e) { 290 resetProxyAndRequestList("getSimSlotsStatus", e); 291 } 292 } 293 } 294 295 /** 296 * Wrapper function for IRadioConfig.setPreferredDataModem(int modemId). 297 */ setPreferredDataModem(int modemId, Message result)298 public void setPreferredDataModem(int modemId, Message result) { 299 if (!isSetPreferredDataCommandSupported()) { 300 if (result != null) { 301 AsyncResult.forMessage(result, null, 302 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 303 result.sendToTarget(); 304 } 305 return; 306 } 307 308 RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_DATA_MODEM, 309 result, mDefaultWorkSource); 310 311 if (DBG) { 312 logd(rr.serialString() + "> " + requestToString(rr.mRequest)); 313 } 314 315 try { 316 ((android.hardware.radio.config.V1_1.IRadioConfig) mRadioConfigProxy) 317 .setPreferredDataModem(rr.mSerial, (byte) modemId); 318 } catch (RemoteException | RuntimeException e) { 319 resetProxyAndRequestList("setPreferredDataModem", e); 320 } 321 } 322 323 /** 324 * Wrapper function for IRadioConfig.getPhoneCapability(). 325 */ getPhoneCapability(Message result)326 public void getPhoneCapability(Message result) { 327 IRadioConfig radioConfigProxy = getRadioConfigProxy(null); 328 if (radioConfigProxy == null || mRadioConfigVersion.less(RADIO_CONFIG_HAL_VERSION_1_1)) { 329 if (result != null) { 330 AsyncResult.forMessage(result, null, 331 CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED)); 332 result.sendToTarget(); 333 } 334 return; 335 } 336 337 RILRequest rr = obtainRequest(RIL_REQUEST_GET_PHONE_CAPABILITY, result, mDefaultWorkSource); 338 339 if (DBG) { 340 logd(rr.serialString() + "> " + requestToString(rr.mRequest)); 341 } 342 343 try { 344 ((android.hardware.radio.config.V1_1.IRadioConfig) mRadioConfigProxy) 345 .getPhoneCapability(rr.mSerial); 346 } catch (RemoteException | RuntimeException e) { 347 resetProxyAndRequestList("getPhoneCapability", e); 348 } 349 } 350 351 /** 352 * @return whether current radio config version supports SET_PREFERRED_DATA_MODEM command. 353 * If yes, we'll use RIL_REQUEST_SET_PREFERRED_DATA_MODEM to indicate which modem is preferred. 354 * If not, we shall use RIL_REQUEST_ALLOW_DATA for on-demand PS attach / detach. 355 * See PhoneSwitcher for more details. 356 */ isSetPreferredDataCommandSupported()357 public boolean isSetPreferredDataCommandSupported() { 358 IRadioConfig radioConfigProxy = getRadioConfigProxy(null); 359 return radioConfigProxy != null && mRadioConfigVersion 360 .greaterOrEqual(RADIO_CONFIG_HAL_VERSION_1_1); 361 } 362 363 /** 364 * Wrapper function for IRadioConfig.setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap). 365 */ setSimSlotsMapping(int[] physicalSlots, Message result)366 public void setSimSlotsMapping(int[] physicalSlots, Message result) { 367 IRadioConfig radioConfigProxy = getRadioConfigProxy(result); 368 if (radioConfigProxy != null) { 369 RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING, result, 370 mDefaultWorkSource); 371 372 if (DBG) { 373 logd(rr.serialString() + "> " + requestToString(rr.mRequest) 374 + " " + Arrays.toString(physicalSlots)); 375 } 376 377 try { 378 radioConfigProxy.setSimSlotsMapping(rr.mSerial, 379 primitiveArrayToArrayList(physicalSlots)); 380 } catch (RemoteException | RuntimeException e) { 381 resetProxyAndRequestList("setSimSlotsMapping", e); 382 } 383 } 384 } 385 primitiveArrayToArrayList(int[] arr)386 private static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) { 387 ArrayList<Integer> arrayList = new ArrayList<>(arr.length); 388 for (int i : arr) { 389 arrayList.add(i); 390 } 391 return arrayList; 392 } 393 requestToString(int request)394 static String requestToString(int request) { 395 switch (request) { 396 case RIL_REQUEST_GET_PHONE_CAPABILITY: 397 return "GET_PHONE_CAPABILITY"; 398 case RIL_REQUEST_GET_SLOT_STATUS: 399 return "GET_SLOT_STATUS"; 400 case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING: 401 return "SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING"; 402 case RIL_REQUEST_SET_PREFERRED_DATA_MODEM: 403 return "SET_PREFERRED_DATA_MODEM"; 404 case RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG: 405 return "SWITCH_DUAL_SIM_CONFIG"; 406 default: 407 return "<unknown request " + request + ">"; 408 } 409 } 410 411 /** 412 * Wrapper function for using IRadioConfig.setModemsConfig(int32_t serial, 413 * ModemsConfig modemsConfig) to switch between single-sim and multi-sim. 414 */ setModemsConfig(int numOfLiveModems, Message result)415 public void setModemsConfig(int numOfLiveModems, Message result) { 416 IRadioConfig radioConfigProxy = getRadioConfigProxy(result); 417 if (radioConfigProxy != null 418 && mRadioConfigVersion.greaterOrEqual(RADIO_CONFIG_HAL_VERSION_1_1)) { 419 android.hardware.radio.config.V1_1.IRadioConfig radioConfigProxy11 = 420 (android.hardware.radio.config.V1_1.IRadioConfig) radioConfigProxy; 421 RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG, 422 result, mDefaultWorkSource); 423 424 if (DBG) { 425 logd(rr.serialString() + "> " + requestToString(rr.mRequest) 426 + ", numOfLiveModems = " + numOfLiveModems); 427 } 428 429 try { 430 ModemsConfig modemsConfig = new ModemsConfig(); 431 modemsConfig.numOfLiveModems = (byte) numOfLiveModems; 432 radioConfigProxy11.setModemsConfig(rr.mSerial, modemsConfig); 433 } catch (RemoteException | RuntimeException e) { 434 resetProxyAndRequestList("setModemsConfig", e); 435 } 436 } 437 } 438 439 // TODO: not needed for now, but if we don't want to use System Properties any more, 440 // we need to implement a wrapper function for getModemsConfig as well 441 442 /** 443 * Register a handler to get SIM slot status changed notifications. 444 */ registerForSimSlotStatusChanged(Handler h, int what, Object obj)445 public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) { 446 mSimSlotStatusRegistrant = new Registrant(h, what, obj); 447 } 448 449 /** 450 * Unregister corresponding to registerForSimSlotStatusChanged(). 451 */ unregisterForSimSlotStatusChanged(Handler h)452 public void unregisterForSimSlotStatusChanged(Handler h) { 453 if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) { 454 mSimSlotStatusRegistrant.clear(); 455 mSimSlotStatusRegistrant = null; 456 } 457 } 458 convertHalSlotStatus( ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> halSlotStatusList)459 static ArrayList<IccSlotStatus> convertHalSlotStatus( 460 ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> halSlotStatusList) { 461 ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size()); 462 for (android.hardware.radio.config.V1_0.SimSlotStatus slotStatus : halSlotStatusList) { 463 IccSlotStatus iccSlotStatus = new IccSlotStatus(); 464 iccSlotStatus.setCardState(slotStatus.cardState); 465 iccSlotStatus.setSlotState(slotStatus.slotState); 466 iccSlotStatus.logicalSlotIndex = slotStatus.logicalSlotId; 467 iccSlotStatus.atr = slotStatus.atr; 468 iccSlotStatus.iccid = slotStatus.iccid; 469 response.add(iccSlotStatus); 470 } 471 return response; 472 } 473 convertHalSlotStatus_1_2( ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> halSlotStatusList)474 static ArrayList<IccSlotStatus> convertHalSlotStatus_1_2( 475 ArrayList<android.hardware.radio.config.V1_2.SimSlotStatus> halSlotStatusList) { 476 ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size()); 477 for (android.hardware.radio.config.V1_2.SimSlotStatus slotStatus : halSlotStatusList) { 478 IccSlotStatus iccSlotStatus = new IccSlotStatus(); 479 iccSlotStatus.setCardState(slotStatus.base.cardState); 480 iccSlotStatus.setSlotState(slotStatus.base.slotState); 481 iccSlotStatus.logicalSlotIndex = slotStatus.base.logicalSlotId; 482 iccSlotStatus.atr = slotStatus.base.atr; 483 iccSlotStatus.iccid = slotStatus.base.iccid; 484 iccSlotStatus.eid = slotStatus.eid; 485 response.add(iccSlotStatus); 486 } 487 return response; 488 } 489 logd(String log)490 private static void logd(String log) { 491 Rlog.d(TAG, log); 492 } 493 loge(String log)494 private static void loge(String log) { 495 Rlog.e(TAG, log); 496 } 497 } 498