Home | History | Annotate | Download | only in telephony
      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
     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 
     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      */
     93     public static RadioConfig getInstance(Context context) {
     94         if (sRadioConfig == null) {
     95             sRadioConfig = new RadioConfig(context);
     96         }
     97         return sRadioConfig;
     98     }
     99 
    100     @Override
    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      */
    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 
    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. */
    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 
    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 
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    312     public void unregisterForSimSlotStatusChanged(Handler h) {
    313         if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) {
    314             mSimSlotStatusRegistrant.clear();
    315             mSimSlotStatusRegistrant = null;
    316         }
    317     }
    318 
    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 
    334     private static void logd(String log) {
    335         Rlog.d(TAG, log);
    336     }
    337 
    338     private static void loge(String log) {
    339         Rlog.e(TAG, log);
    340     }
    341 }
    342