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