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