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.RIL_REQUEST_GET_SLOT_STATUS; 21 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING; 22 23 import android.content.Context; 24 import android.hardware.radio.V1_0.RadioResponseInfo; 25 import android.hardware.radio.V1_0.RadioResponseType; 26 import android.hardware.radio.config.V1_0.IRadioConfig; 27 import android.hardware.radio.config.V1_0.SimSlotStatus; 28 import android.net.ConnectivityManager; 29 import android.os.AsyncResult; 30 import android.os.Handler; 31 import android.os.HwBinder; 32 import android.os.Message; 33 import android.os.Registrant; 34 import android.os.RemoteException; 35 import android.os.WorkSource; 36 import android.telephony.Rlog; 37 import android.util.SparseArray; 38 39 import com.android.internal.telephony.uicc.IccSlotStatus; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.concurrent.atomic.AtomicLong; 44 45 /** 46 * This class provides wrapper APIs for IRadioConfig interface. 47 */ 48 public class RadioConfig extends Handler { 49 private static final String TAG = "RadioConfig"; 50 private static final boolean DBG = true; 51 private static final boolean VDBG = false; //STOPSHIP if true 52 53 private static final int EVENT_SERVICE_DEAD = 1; 54 55 private final boolean mIsMobileNetworkSupported; 56 private volatile IRadioConfig mRadioConfigProxy = null; 57 private final ServiceDeathRecipient mServiceDeathRecipient; 58 private final AtomicLong mRadioConfigProxyCookie = new AtomicLong(0); 59 private final RadioConfigResponse mRadioConfigResponse; 60 private final RadioConfigIndication mRadioConfigIndication; 61 private final SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>(); 62 /* default work source which will blame phone process */ 63 private final WorkSource mDefaultWorkSource; 64 private static RadioConfig sRadioConfig; 65 66 protected Registrant mSimSlotStatusRegistrant; 67 68 final class ServiceDeathRecipient implements HwBinder.DeathRecipient { 69 @Override serviceDied(long cookie)70 public void serviceDied(long cookie) { 71 // Deal with service going away 72 logd("serviceDied"); 73 sendMessage(obtainMessage(EVENT_SERVICE_DEAD, cookie)); 74 } 75 } 76 RadioConfig(Context context)77 private RadioConfig(Context context) { 78 ConnectivityManager cm = (ConnectivityManager) context.getSystemService( 79 Context.CONNECTIVITY_SERVICE); 80 mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); 81 82 mRadioConfigResponse = new RadioConfigResponse(this); 83 mRadioConfigIndication = new RadioConfigIndication(this); 84 mServiceDeathRecipient = new ServiceDeathRecipient(); 85 86 mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid, 87 context.getPackageName()); 88 } 89 90 /** 91 * Returns the singleton static instance of RadioConfig 92 */ getInstance(Context context)93 public static RadioConfig getInstance(Context context) { 94 if (sRadioConfig == null) { 95 sRadioConfig = new RadioConfig(context); 96 } 97 return sRadioConfig; 98 } 99 100 @Override handleMessage(Message message)101 public void handleMessage(Message message) { 102 switch (message.what) { 103 case EVENT_SERVICE_DEAD: 104 logd("handleMessage: EVENT_SERVICE_DEAD cookie = " + message.obj 105 + " mRadioConfigProxyCookie = " + mRadioConfigProxyCookie.get()); 106 if ((long) message.obj == mRadioConfigProxyCookie.get()) { 107 resetProxyAndRequestList("EVENT_SERVICE_DEAD", null); 108 } 109 break; 110 } 111 } 112 113 /** 114 * Release each request in mRequestList then clear the list 115 * @param error is the RIL_Errno sent back 116 * @param loggable true means to print all requests in mRequestList 117 */ clearRequestList(int error, boolean loggable)118 private void clearRequestList(int error, boolean loggable) { 119 RILRequest rr; 120 synchronized (mRequestList) { 121 int count = mRequestList.size(); 122 if (DBG && loggable) { 123 logd("clearRequestList: mRequestList=" + count); 124 } 125 126 for (int i = 0; i < count; i++) { 127 rr = mRequestList.valueAt(i); 128 if (DBG && loggable) { 129 logd(i + ": [" + rr.mSerial + "] " + requestToString(rr.mRequest)); 130 } 131 rr.onError(error, null); 132 rr.release(); 133 } 134 mRequestList.clear(); 135 } 136 } 137 resetProxyAndRequestList(String caller, Exception e)138 private void resetProxyAndRequestList(String caller, Exception e) { 139 loge(caller + ": " + e); 140 mRadioConfigProxy = null; 141 142 // increment the cookie so that death notification can be ignored 143 mRadioConfigProxyCookie.incrementAndGet(); 144 145 RILRequest.resetSerial(); 146 // Clear request list on close 147 clearRequestList(RADIO_NOT_AVAILABLE, false); 148 149 getRadioConfigProxy(null); 150 } 151 152 /** Returns a {@link IRadioConfig} instance or null if the service is not available. */ getRadioConfigProxy(Message result)153 public IRadioConfig getRadioConfigProxy(Message result) { 154 if (!mIsMobileNetworkSupported) { 155 if (VDBG) logd("getRadioConfigProxy: Not calling getService(): wifi-only"); 156 if (result != null) { 157 AsyncResult.forMessage(result, null, 158 CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 159 result.sendToTarget(); 160 } 161 return null; 162 } 163 164 if (mRadioConfigProxy != null) { 165 return mRadioConfigProxy; 166 } 167 168 try { 169 mRadioConfigProxy = IRadioConfig.getService(true); 170 if (mRadioConfigProxy != null) { 171 mRadioConfigProxy.linkToDeath(mServiceDeathRecipient, 172 mRadioConfigProxyCookie.incrementAndGet()); 173 mRadioConfigProxy.setResponseFunctions(mRadioConfigResponse, 174 mRadioConfigIndication); 175 } else { 176 loge("getRadioConfigProxy: mRadioConfigProxy == null"); 177 } 178 } catch (RemoteException | RuntimeException e) { 179 mRadioConfigProxy = null; 180 loge("getRadioConfigProxy: RadioConfigProxy getService/setResponseFunctions: " + e); 181 } 182 183 if (mRadioConfigProxy == null) { 184 // getService() is a blocking call, so this should never happen 185 loge("getRadioConfigProxy: mRadioConfigProxy == null"); 186 if (result != null) { 187 AsyncResult.forMessage(result, null, 188 CommandException.fromRilErrno(RADIO_NOT_AVAILABLE)); 189 result.sendToTarget(); 190 } 191 } 192 193 return mRadioConfigProxy; 194 } 195 obtainRequest(int request, Message result, WorkSource workSource)196 private RILRequest obtainRequest(int request, Message result, WorkSource workSource) { 197 RILRequest rr = RILRequest.obtain(request, result, workSource); 198 synchronized (mRequestList) { 199 mRequestList.append(rr.mSerial, rr); 200 } 201 return rr; 202 } 203 findAndRemoveRequestFromList(int serial)204 private RILRequest findAndRemoveRequestFromList(int serial) { 205 RILRequest rr; 206 synchronized (mRequestList) { 207 rr = mRequestList.get(serial); 208 if (rr != null) { 209 mRequestList.remove(serial); 210 } 211 } 212 213 return rr; 214 } 215 216 /** 217 * This is a helper function to be called when a RadioConfigResponse callback is called. 218 * It finds and returns RILRequest corresponding to the response if one is found. 219 * @param responseInfo RadioResponseInfo received in response callback 220 * @return RILRequest corresponding to the response 221 */ processResponse(RadioResponseInfo responseInfo)222 public RILRequest processResponse(RadioResponseInfo responseInfo) { 223 int serial = responseInfo.serial; 224 int error = responseInfo.error; 225 int type = responseInfo.type; 226 227 if (type != RadioResponseType.SOLICITED) { 228 loge("processResponse: Unexpected response type " + type); 229 } 230 231 RILRequest rr = findAndRemoveRequestFromList(serial); 232 if (rr == null) { 233 loge("processResponse: Unexpected response! serial: " + serial + " error: " + error); 234 return null; 235 } 236 237 return rr; 238 } 239 240 /** 241 * Wrapper function for IRadioConfig.getSimSlotsStatus(). 242 */ getSimSlotsStatus(Message result)243 public void getSimSlotsStatus(Message result) { 244 IRadioConfig radioConfigProxy = getRadioConfigProxy(result); 245 if (radioConfigProxy != null) { 246 RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLOT_STATUS, result, mDefaultWorkSource); 247 248 if (DBG) { 249 logd(rr.serialString() + "> " + requestToString(rr.mRequest)); 250 } 251 252 try { 253 radioConfigProxy.getSimSlotsStatus(rr.mSerial); 254 } catch (RemoteException | RuntimeException e) { 255 resetProxyAndRequestList("getSimSlotsStatus", e); 256 } 257 } 258 } 259 260 /** 261 * Wrapper function for IRadioConfig.getSimSlotsStatus(). 262 */ setSimSlotsMapping(int[] physicalSlots, Message result)263 public void setSimSlotsMapping(int[] physicalSlots, Message result) { 264 IRadioConfig radioConfigProxy = getRadioConfigProxy(result); 265 if (radioConfigProxy != null) { 266 RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING, result, 267 mDefaultWorkSource); 268 269 if (DBG) { 270 logd(rr.serialString() + "> " + requestToString(rr.mRequest) 271 + " " + Arrays.toString(physicalSlots)); 272 } 273 274 try { 275 radioConfigProxy.setSimSlotsMapping(rr.mSerial, 276 primitiveArrayToArrayList(physicalSlots)); 277 } catch (RemoteException | RuntimeException e) { 278 resetProxyAndRequestList("setSimSlotsMapping", e); 279 } 280 } 281 } 282 primitiveArrayToArrayList(int[] arr)283 private static ArrayList<Integer> primitiveArrayToArrayList(int[] arr) { 284 ArrayList<Integer> arrayList = new ArrayList<>(arr.length); 285 for (int i : arr) { 286 arrayList.add(i); 287 } 288 return arrayList; 289 } 290 requestToString(int request)291 static String requestToString(int request) { 292 switch (request) { 293 case RIL_REQUEST_GET_SLOT_STATUS: 294 return "GET_SLOT_STATUS"; 295 case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING: 296 return "SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING"; 297 default: 298 return "<unknown request>"; 299 } 300 } 301 302 /** 303 * Register a handler to get SIM slot status changed notifications. 304 */ registerForSimSlotStatusChanged(Handler h, int what, Object obj)305 public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) { 306 mSimSlotStatusRegistrant = new Registrant(h, what, obj); 307 } 308 309 /** 310 * Unregister corresponding to registerForSimSlotStatusChanged(). 311 */ unregisterForSimSlotStatusChanged(Handler h)312 public void unregisterForSimSlotStatusChanged(Handler h) { 313 if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) { 314 mSimSlotStatusRegistrant.clear(); 315 mSimSlotStatusRegistrant = null; 316 } 317 } 318 convertHalSlotStatus( ArrayList<SimSlotStatus> halSlotStatusList)319 static ArrayList<IccSlotStatus> convertHalSlotStatus( 320 ArrayList<SimSlotStatus> halSlotStatusList) { 321 ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size()); 322 for (SimSlotStatus slotStatus : halSlotStatusList) { 323 IccSlotStatus iccSlotStatus = new IccSlotStatus(); 324 iccSlotStatus.setCardState(slotStatus.cardState); 325 iccSlotStatus.setSlotState(slotStatus.slotState); 326 iccSlotStatus.logicalSlotIndex = slotStatus.logicalSlotId; 327 iccSlotStatus.atr = slotStatus.atr; 328 iccSlotStatus.iccid = slotStatus.iccid; 329 response.add(iccSlotStatus); 330 } 331 return response; 332 } 333 logd(String log)334 private static void logd(String log) { 335 Rlog.d(TAG, log); 336 } 337 loge(String log)338 private static void loge(String log) { 339 Rlog.e(TAG, log); 340 } 341 } 342