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