• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(TelephonyManager.INCLUDE_LOCATION_DATA_NONE,
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, "onReceive: Null action for intent.");
120             return;
121         }
122         VvmLog.i(TAG, action);
123         switch (action) {
124             case Intent.ACTION_BOOT_COMPLETED:
125                 VvmLog.i(TAG, "onReceive: ACTION_BOOT_COMPLETED");
126                 onBootCompleted(context);
127                 break;
128             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
129                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
130                         intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
131                     // checkRemovedSim will scan all known accounts with isPhoneAccountActive() to find
132                     // which SIM is removed.
133                     // ACTION_SIM_STATE_CHANGED only provides subId which cannot be converted to a
134                     // PhoneAccountHandle when the SIM is absent.
135                     VvmLog.i(TAG, "onReceive: ACTION_SIM_STATE_CHANGED");
136                     checkRemovedSim(context);
137                 }
138                 break;
139             case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
140                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
141                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
142 
143                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
144                     VvmLog.i(TAG, "onReceive: Received carrier config for invalid sub id.");
145                     checkRemovedSim(context);
146                     return;
147                 }
148 
149                 PhoneAccountHandle phoneAccountHandle =
150                         PhoneAccountHandleConverter.fromSubId(subId);
151 
152                 if ("null".equals(phoneAccountHandle.getId())) {
153                     VvmLog.e(TAG,
154                             "onReceive: null phone account handle ID, possible modem crash."
155                                     + " Ignoring carrier config changed event");
156                     return;
157                 }
158                 VvmLog.i(TAG, "onReceive: ACTION_CARRIER_CONFIG_CHANGED; subId=" + subId);
159                 onCarrierConfigChanged(context, phoneAccountHandle);
160         }
161     }
162 
onBootCompleted(Context context)163     private void onBootCompleted(Context context) {
164         for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
165             TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
166             if (telephonyManager == null) {
167                 continue;
168             }
169             if (telephonyManager.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
170                 sListeners.put(phoneAccountHandle, null);
171                 sendConnected(context, phoneAccountHandle);
172             } else {
173                 listenToAccount(context, phoneAccountHandle);
174             }
175         }
176         sPreBootHandles.clear();
177     }
178 
sendConnected(Context context, PhoneAccountHandle phoneAccountHandle)179     private void sendConnected(Context context, PhoneAccountHandle phoneAccountHandle) {
180         VvmLog.i(TAG, "sendConnected: Service connected on " + phoneAccountHandle);
181         RemoteVvmTaskManager.startCellServiceConnected(context, phoneAccountHandle);
182     }
183 
checkRemovedSim(Context context)184     private void checkRemovedSim(Context context) {
185         SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
186         if (!isBootCompleted()) {
187             for (PhoneAccountHandle phoneAccountHandle : sPreBootHandles) {
188                 if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
189                     sPreBootHandles.remove(phoneAccountHandle);
190                 }
191             }
192             return;
193         }
194         Set<PhoneAccountHandle> removeList = new ArraySet<>();
195         for (PhoneAccountHandle phoneAccountHandle : sListeners.keySet()) {
196             if (!PhoneUtils.isPhoneAccountActive(subscriptionManager, phoneAccountHandle)) {
197                 removeList.add(phoneAccountHandle);
198                 ServiceStateListener listener = sListeners.get(phoneAccountHandle);
199                 if (listener != null) {
200                     listener.unlisten();
201                 }
202                 sendSimRemoved(context, phoneAccountHandle);
203             }
204         }
205 
206         for (PhoneAccountHandle phoneAccountHandle : removeList) {
207             sListeners.remove(phoneAccountHandle);
208         }
209     }
210 
isBootCompleted()211     private boolean isBootCompleted() {
212         return SystemProperties.getBoolean("sys.boot_completed", false);
213     }
214 
sendSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle)215     private void sendSimRemoved(Context context, PhoneAccountHandle phoneAccountHandle) {
216         VvmLog.i(TAG, "sendSimRemoved: Sim removed on " + phoneAccountHandle);
217         RemoteVvmTaskManager.startSimRemoved(context, phoneAccountHandle);
218     }
219 
onCarrierConfigChanged(Context context, PhoneAccountHandle phoneAccountHandle)220     private void onCarrierConfigChanged(Context context, PhoneAccountHandle phoneAccountHandle) {
221         if (!isBootCompleted()) {
222             sPreBootHandles.add(phoneAccountHandle);
223             return;
224         }
225         TelephonyManager telephonyManager = getTelephonyManager(context, phoneAccountHandle);
226         if(telephonyManager == null){
227             int subId = context.getSystemService(TelephonyManager.class).getSubIdForPhoneAccount(
228                     context.getSystemService(TelecomManager.class)
229                             .getPhoneAccount(phoneAccountHandle));
230             VvmLog.e(TAG, "Cannot create TelephonyManager from " + phoneAccountHandle + ", subId="
231                     + subId);
232             // TODO(b/33945549): investigate more why this is happening. The PhoneAccountHandle was
233             // just converted from a valid subId so createForPhoneAccountHandle shouldn't really
234             // return null.
235             return;
236         }
237         ServiceState currentServiceState = telephonyManager.getServiceState();
238         if (currentServiceState != null && currentServiceState.getState()
239                 == ServiceState.STATE_IN_SERVICE) {
240             VvmLog.i(TAG, "onCarrierConfigChanged: in service; send connected "
241                     + phoneAccountHandle);
242             sendConnected(context, phoneAccountHandle);
243             sListeners.put(phoneAccountHandle, null);
244         } else {
245             listenToAccount(context, phoneAccountHandle);
246         }
247     }
248 
listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle)249     private void listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle) {
250         ServiceStateListener listener = new ServiceStateListener(context, phoneAccountHandle);
251         listener.listen();
252         VvmLog.i(TAG, "listenToAccount: " + phoneAccountHandle);
253         sListeners.put(phoneAccountHandle, listener);
254     }
255 
256     @Nullable
getTelephonyManager(Context context, PhoneAccountHandle phoneAccountHandle)257     private static TelephonyManager getTelephonyManager(Context context,
258             PhoneAccountHandle phoneAccountHandle) {
259         return context.getSystemService(TelephonyManager.class)
260                 .createForPhoneAccountHandle(phoneAccountHandle);
261     }
262 }
263