• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.telephony.Rlog;
42  import android.util.SparseArray;
43  
44  import com.android.internal.telephony.uicc.IccSlotStatus;
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