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