Home | History | Annotate | Download | only in vvm
      1 /*
      2  * Copyright (C) 2016 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.phone.vvm;
     18 
     19 import android.annotation.Nullable;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.os.SystemProperties;
     24 import android.telecom.PhoneAccountHandle;
     25 import android.telecom.TelecomManager;
     26 import android.telephony.CarrierConfigManager;
     27 import android.telephony.PhoneStateListener;
     28 import android.telephony.ServiceState;
     29 import android.telephony.SubscriptionManager;
     30 import android.telephony.TelephonyManager;
     31 import android.util.ArrayMap;
     32 import android.util.ArraySet;
     33 
     34 import com.android.internal.telephony.IccCardConstants;
     35 import com.android.internal.telephony.PhoneConstants;
     36 import com.android.internal.telephony.TelephonyIntents;
     37 import com.android.phone.PhoneUtils;
     38 
     39 import java.util.Map;
     40 import java.util.Set;
     41 
     42 /**
     43  * Tracks the status of all inserted SIMs. Will notify {@link RemoteVvmTaskManager} of when a SIM
     44  * connected to the service for the first time after it was inserted or the system booted, and when
     45  * the SIM is removed. Losing cell signal or entering airplane mode will not cause the connected
     46  * event to be triggered again. Reinserting the SIM will trigger the connected event. Changing the
     47  * carrier config will also trigger the connected event. Events will be delayed until the device has
     48  * been fully booted (and left FBE mode).
     49  */
     50 public class VvmSimStateTracker extends BroadcastReceiver {
     51 
     52     private static final String TAG = "VvmSimStateTracker";
     53 
     54     /**
     55      * Map to keep track of currently inserted SIMs. If the SIM hasn't been connected to the service
     56      * before the value will be a {@link ServiceStateListener} that is still waiting for the
     57      * connection. A value of {@code null} means the SIM has been connected to the service before.
     58      */
     59     private static Map<PhoneAccountHandle, ServiceStateListener> sListeners = new ArrayMap<>();
     60 
     61     /**
     62      * Accounts that has events before the device is booted. The events should be regenerated after
     63      * the device has fully booted.
     64      */
     65     private static Set<PhoneAccountHandle> sPreBootHandles = new ArraySet<>();
     66 
     67     /**
     68      * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
     69      * connected event. Will unregister itself once the event has been triggered.
     70      */
     71     private class ServiceStateListener extends PhoneStateListener {
     72 
     73         private final PhoneAccountHandle mPhoneAccountHandle;
     74         private final Context mContext;
     75 
     76         public ServiceStateListener(Context context, PhoneAccountHandle phoneAccountHandle) {
     77             mContext = context;
     78             mPhoneAccountHandle = phoneAccountHandle;
     79         }
     80 
     81         public void listen() {
     82             TelephonyManager telephonyManager = getTelephonyManager(mContext, mPhoneAccountHandle);
     83             if(telephonyManager == null){
     84                 VvmLog.e(TAG, "Cannot create TelephonyManager from " + mPhoneAccountHandle);
     85                 return;
     86             }
     87             telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
     88         }
     89 
     90         public void unlisten() {
     91             // TelephonyManager does not need to be pinned to an account when removing a
     92             // PhoneStateListener, and mPhoneAccountHandle might be invalid at this point
     93             // (e.g. SIM removal)
     94             mContext.getSystemService(TelephonyManager.class)
     95                     .listen(this, PhoneStateListener.LISTEN_NONE);
     96             sListeners.put(mPhoneAccountHandle, null);
     97         }
     98 
     99         @Override
    100         public void onServiceStateChanged(ServiceState serviceState) {
    101             if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
    102                 VvmLog.i(TAG, "in service");
    103                 sendConnected(mContext, mPhoneAccountHandle);
    104                 unlisten();
    105             }
    106         }
    107     }
    108 
    109     @Override
    110     public void onReceive(Context context, Intent intent) {
    111 
    112         final String action = intent.getAction();
    113         if (action == null) {
    114             VvmLog.w(TAG, "Null action for intent.");
    115             return;
    116         }
    117         VvmLog.i(TAG, action);
    118         switch (action) {
    119             case Intent.ACTION_BOOT_COMPLETED:
    120                 onBootCompleted(context);
    121                 break;
    122             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
    123                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
    124                         intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
    125                     // checkRemovedSim will scan all known accounts with isPhoneAccountActive() to find
    126                     // which SIM is removed.
    127                     // ACTION_SIM_STATE_CHANGED only provides subId which cannot be converted to a
    128                     // PhoneAccountHandle when the SIM is absent.
    129                     checkRemovedSim(context);
    130                 }
    131                 break;
    132             case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
    133                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
    134                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
    135 
    136                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
    137                     VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
    138                     checkRemovedSim(context);
    139                     return;
    140                 }
    141 
    142                 PhoneAccountHandle phoneAccountHandle =
    143                         PhoneAccountHandleConverter.fromSubId(subId);
    144 
    145                 if ("null".equals(phoneAccountHandle.getId())) {
    146                     VvmLog.e(TAG,
    147                             "null phone account handle ID, possible modem crash."
    148                                     + " Ignoring carrier config changed event");
    149                     return;
    150                 }
    151                 onCarrierConfigChanged(context, phoneAccountHandle);
    152         }
    153     }
    154 
    155     private void onBootCompleted(Context context) {
    156         for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
    157             TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
    158             if (telephonyManager == null) {
    159                 continue;
    160             }
    161             if (telephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
    162                 sListeners.put(phoneAccountHandle, null);
    163                 sendConnected(context, phoneAccountHandle);
    164             } else {
    165                 listenToAccount(context, phoneAccountHandle);
    166             }
    167         }
    168         sPreBootHandles.clear();
    169     }
    170 
    171     private void sendConnected(Context context, PhoneAccountHandle phoneAccountHandle) {
    172         VvmLog.i(TAG, "Service connected on " + phoneAccountHandle);
    173         RemoteVvmTaskManager.startCellServiceConnected(context, phoneAccountHandle);
    174     }
    175 
    176     private void checkRemovedSim(Context context) {
    177         SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
    178         if (!isBootCompleted()) {
    179             for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
    180                 if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
    181                     sPreBootHandles.remove(phoneAccountHandle);
    182                 }
    183             }
    184             return;
    185         }
    186         Set<PhoneAccountHandle> removeList = new ArraySet<>();
    187         for (PhoneAccountHandle phoneAccountHandle : sListeners.keySet()) {
    188             if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
    189                 removeList.add(phoneAccountHandle);
    190                 ServiceStateListener listener = sListeners.get(phoneAccountHandle);
    191                 if (listener != null) {
    192                     listener.unlisten();
    193                 }
    194                 sendSimRemoved(context, phoneAccountHandle);
    195             }
    196         }
    197 
    198         for (PhoneAccountHandle phoneAccountHandle : removeList) {
    199             sListeners.remove(phoneAccountHandle);
    200         }
    201     }
    202 
    203     private boolean isBootCompleted() {
    204         return SystemProperties.getBoolean("sys.boot_completed", false);
    205     }
    206 
    207     private void sendSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
    208         VvmLog.i(TAG, "Sim removed on " + phoneAccountHandle);
    209         RemoteVvmTaskManager.startSimRemoved(context, phoneAccountHandle);
    210     }
    211 
    212     private void onCarrierConfigChanged(Context context, PhoneAccountHandle phoneAccountHandle) {
    213         if (!isBootCompleted()) {
    214             sPreBootHandles.add(phoneAccountHandle);
    215             return;
    216         }
    217         TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
    218         if(telephonyManager == null){
    219             int subId = context.getSystemService(TelephonyManager.class).getSubIdForPhoneAccount(
    220                     context.getSystemService(TelecomManager.class)
    221                             .getPhoneAccount(phoneAccountHandle));
    222             VvmLog.e(TAG, "Cannot create TelephonyManager from " + phoneAccountHandle + ", subId="
    223                     + subId);
    224             // TODO(b/33945549): investigate more why this is happening. The PhoneAccountHandle was
    225             // just converted from a valid subId so createForPhoneAccountHandle shouldn't really
    226             // return null.
    227             return;
    228         }
    229         if (telephonyManager.getServiceState().getState()
    230                 == ServiceState.STATE_IN_SERVICE) {
    231             sendConnected(context, phoneAccountHandle);
    232             sListeners.put(phoneAccountHandle, null);
    233         } else {
    234             listenToAccount(context, phoneAccountHandle);
    235         }
    236     }
    237 
    238     private void listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle) {
    239         ServiceStateListener listener = new ServiceStateListener(context, phoneAccountHandle);
    240         listener.listen();
    241         sListeners.put(phoneAccountHandle, listener);
    242     }
    243 
    244     @Nullable
    245     private static TelephonyManager getTelephonyManager(Context context,
    246             PhoneAccountHandle phoneAccountHandle) {
    247         return context.getSystemService(TelephonyManager.class)
    248                 .createForPhoneAccountHandle(phoneAccountHandle);
    249     }
    250 }
    251