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 17package com.android.server.emergency; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.os.Handler; 24import android.os.HandlerThread; 25import android.os.Looper; 26import android.os.Message; 27import android.provider.Settings; 28import android.telephony.CellInfo; 29import android.telephony.CellInfoGsm; 30import android.telephony.CellInfoLte; 31import android.telephony.CellInfoWcdma; 32import android.telephony.CellLocation; 33import android.telephony.PhoneStateListener; 34import android.telephony.SubscriptionInfo; 35import android.telephony.SubscriptionManager; 36import android.telephony.TelephonyManager; 37 38import com.android.server.SystemService; 39 40import java.util.ArrayList; 41import java.util.Arrays; 42import java.util.List; 43 44/** 45 * A service that listens to connectivity and SIM card changes and determines if the emergency mode 46 * should be enabled 47 */ 48public class EmergencyAffordanceService extends SystemService { 49 50 private static final String TAG = "EmergencyAffordanceService"; 51 52 private static final int NUM_SCANS_UNTIL_ABORT = 4; 53 54 private static final int INITIALIZE_STATE = 1; 55 private static final int CELL_INFO_STATE_CHANGED = 2; 56 private static final int SUBSCRIPTION_CHANGED = 3; 57 58 /** 59 * Global setting, whether the last scan of the sim cards reveal that a sim was inserted that 60 * requires the emergency affordance. The value is a boolean (1 or 0). 61 * @hide 62 */ 63 private static final String EMERGENCY_SIM_INSERTED_SETTING = "emergency_sim_inserted_before"; 64 65 private final Context mContext; 66 private final ArrayList<Integer> mEmergencyCallMccNumbers; 67 68 private final Object mLock = new Object(); 69 70 private TelephonyManager mTelephonyManager; 71 private SubscriptionManager mSubscriptionManager; 72 private boolean mEmergencyAffordanceNeeded; 73 private MyHandler mHandler; 74 private int mScansCompleted; 75 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 76 @Override 77 public void onCellInfoChanged(List<CellInfo> cellInfo) { 78 if (!isEmergencyAffordanceNeeded()) { 79 requestCellScan(); 80 } 81 } 82 83 @Override 84 public void onCellLocationChanged(CellLocation location) { 85 if (!isEmergencyAffordanceNeeded()) { 86 requestCellScan(); 87 } 88 } 89 }; 90 private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() { 91 @Override 92 public void onReceive(Context context, Intent intent) { 93 if (Settings.Global.getInt(context.getContentResolver(), 94 Settings.Global.AIRPLANE_MODE_ON, 0) == 0) { 95 startScanning(); 96 requestCellScan(); 97 } 98 } 99 }; 100 private boolean mSimNeedsEmergencyAffordance; 101 private boolean mNetworkNeedsEmergencyAffordance; 102 private boolean mVoiceCapable; 103 104 private void requestCellScan() { 105 mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget(); 106 } 107 108 private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener 109 = new SubscriptionManager.OnSubscriptionsChangedListener() { 110 @Override 111 public void onSubscriptionsChanged() { 112 mHandler.obtainMessage(SUBSCRIPTION_CHANGED).sendToTarget(); 113 } 114 }; 115 116 public EmergencyAffordanceService(Context context) { 117 super(context); 118 mContext = context; 119 int[] numbers = context.getResources().getIntArray( 120 com.android.internal.R.array.config_emergency_mcc_codes); 121 mEmergencyCallMccNumbers = new ArrayList<>(numbers.length); 122 for (int i = 0; i < numbers.length; i++) { 123 mEmergencyCallMccNumbers.add(numbers[i]); 124 } 125 } 126 127 private void updateEmergencyAffordanceNeeded() { 128 synchronized (mLock) { 129 mEmergencyAffordanceNeeded = mVoiceCapable && (mSimNeedsEmergencyAffordance || 130 mNetworkNeedsEmergencyAffordance); 131 Settings.Global.putInt(mContext.getContentResolver(), 132 Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, 133 mEmergencyAffordanceNeeded ? 1 : 0); 134 if (mEmergencyAffordanceNeeded) { 135 stopScanning(); 136 } 137 } 138 } 139 140 private void stopScanning() { 141 synchronized (mLock) { 142 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 143 mScansCompleted = 0; 144 } 145 } 146 147 private boolean isEmergencyAffordanceNeeded() { 148 synchronized (mLock) { 149 return mEmergencyAffordanceNeeded; 150 } 151 } 152 153 @Override 154 public void onStart() { 155 } 156 157 @Override 158 public void onBootPhase(int phase) { 159 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 160 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 161 mVoiceCapable = mTelephonyManager.isVoiceCapable(); 162 if (!mVoiceCapable) { 163 updateEmergencyAffordanceNeeded(); 164 return; 165 } 166 mSubscriptionManager = SubscriptionManager.from(mContext); 167 HandlerThread thread = new HandlerThread(TAG); 168 thread.start(); 169 mHandler = new MyHandler(thread.getLooper()); 170 mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget(); 171 startScanning(); 172 IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); 173 mContext.registerReceiver(mAirplaneModeReceiver, filter); 174 mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener); 175 } 176 } 177 178 private void startScanning() { 179 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_INFO 180 | PhoneStateListener.LISTEN_CELL_LOCATION); 181 } 182 183 /** Handler to do the heavier work on */ 184 private class MyHandler extends Handler { 185 186 public MyHandler(Looper l) { 187 super(l); 188 } 189 190 @Override 191 public void handleMessage(Message msg) { 192 switch (msg.what) { 193 case INITIALIZE_STATE: 194 handleInitializeState(); 195 break; 196 case CELL_INFO_STATE_CHANGED: 197 handleUpdateCellInfo(); 198 break; 199 case SUBSCRIPTION_CHANGED: 200 handleUpdateSimSubscriptionInfo(); 201 break; 202 } 203 } 204 } 205 206 private void handleInitializeState() { 207 if (handleUpdateSimSubscriptionInfo()) { 208 return; 209 } 210 if (handleUpdateCellInfo()) { 211 return; 212 } 213 updateEmergencyAffordanceNeeded(); 214 } 215 216 private boolean handleUpdateSimSubscriptionInfo() { 217 boolean neededBefore = simNeededAffordanceBefore(); 218 boolean neededNow = neededBefore; 219 List<SubscriptionInfo> activeSubscriptionInfoList = 220 mSubscriptionManager.getActiveSubscriptionInfoList(); 221 if (activeSubscriptionInfoList == null) { 222 return neededNow; 223 } 224 for (SubscriptionInfo info : activeSubscriptionInfoList) { 225 int mcc = info.getMcc(); 226 if (mccRequiresEmergencyAffordance(mcc)) { 227 neededNow = true; 228 break; 229 } else if (mcc != 0 && mcc != Integer.MAX_VALUE){ 230 // a Sim with a different mcc code was found 231 neededNow = false; 232 } 233 String simOperator = mTelephonyManager.getSimOperator(info.getSubscriptionId()); 234 mcc = 0; 235 if (simOperator != null && simOperator.length() >= 3) { 236 mcc = Integer.parseInt(simOperator.substring(0, 3)); 237 } 238 if (mcc != 0) { 239 if (mccRequiresEmergencyAffordance(mcc)) { 240 neededNow = true; 241 break; 242 } else { 243 // a Sim with a different mcc code was found 244 neededNow = false; 245 } 246 } 247 } 248 if (neededNow != neededBefore) { 249 setSimNeedsEmergencyAffordance(neededNow); 250 } 251 return neededNow; 252 } 253 254 private void setSimNeedsEmergencyAffordance(boolean simNeedsEmergencyAffordance) { 255 mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance; 256 Settings.Global.putInt(mContext.getContentResolver(), 257 EMERGENCY_SIM_INSERTED_SETTING, 258 simNeedsEmergencyAffordance ? 1 : 0); 259 updateEmergencyAffordanceNeeded(); 260 } 261 262 private boolean simNeededAffordanceBefore() { 263 return Settings.Global.getInt(mContext.getContentResolver(), 264 "emergency_sim_inserted_before", 0) != 0; 265 } 266 267 private boolean handleUpdateCellInfo() { 268 List<CellInfo> cellInfos = mTelephonyManager.getAllCellInfo(); 269 if (cellInfos == null) { 270 return false; 271 } 272 boolean stopScanningAfterScan = false; 273 for (CellInfo cellInfo : cellInfos) { 274 int mcc = 0; 275 if (cellInfo instanceof CellInfoGsm) { 276 mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMcc(); 277 } else if (cellInfo instanceof CellInfoLte) { 278 mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMcc(); 279 } else if (cellInfo instanceof CellInfoWcdma) { 280 mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMcc(); 281 } 282 if (mccRequiresEmergencyAffordance(mcc)) { 283 setNetworkNeedsEmergencyAffordance(true); 284 return true; 285 } else if (mcc != 0 && mcc != Integer.MAX_VALUE) { 286 // we found an mcc that isn't in the list, abort 287 stopScanningAfterScan = true; 288 } 289 } 290 if (stopScanningAfterScan) { 291 stopScanning(); 292 } else { 293 onCellScanFinishedUnsuccessful(); 294 } 295 setNetworkNeedsEmergencyAffordance(false); 296 return false; 297 } 298 299 private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) { 300 synchronized (mLock) { 301 mNetworkNeedsEmergencyAffordance = needsAffordance; 302 updateEmergencyAffordanceNeeded(); 303 } 304 } 305 306 private void onCellScanFinishedUnsuccessful() { 307 synchronized (mLock) { 308 mScansCompleted++; 309 if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) { 310 stopScanning(); 311 } 312 } 313 } 314 315 private boolean mccRequiresEmergencyAffordance(int mcc) { 316 return mEmergencyCallMccNumbers.contains(mcc); 317 } 318} 319