Home | History | Annotate | Download | only in emergency
      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.server.emergency;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.os.Handler;
     24 import android.os.HandlerThread;
     25 import android.os.Looper;
     26 import android.os.Message;
     27 import android.provider.Settings;
     28 import android.telephony.CellInfo;
     29 import android.telephony.CellInfoGsm;
     30 import android.telephony.CellInfoLte;
     31 import android.telephony.CellInfoWcdma;
     32 import android.telephony.CellLocation;
     33 import android.telephony.PhoneStateListener;
     34 import android.telephony.SubscriptionInfo;
     35 import android.telephony.SubscriptionManager;
     36 import android.telephony.TelephonyManager;
     37 
     38 import com.android.server.SystemService;
     39 
     40 import java.util.ArrayList;
     41 import java.util.Arrays;
     42 import 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  */
     48 public 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             setSimNeedsEmergencyAffordance(neededNow);
    223             return neededNow;
    224         }
    225         for (SubscriptionInfo info : activeSubscriptionInfoList) {
    226             int mcc = info.getMcc();
    227             if (mccRequiresEmergencyAffordance(mcc)) {
    228                 neededNow = true;
    229                 break;
    230             } else if (mcc != 0 && mcc != Integer.MAX_VALUE){
    231                 // a Sim with a different mcc code was found
    232                 neededNow = false;
    233             }
    234             String simOperator  = mTelephonyManager.getSimOperator(info.getSubscriptionId());
    235             mcc = 0;
    236             if (simOperator != null && simOperator.length() >= 3) {
    237                 mcc = Integer.parseInt(simOperator.substring(0, 3));
    238             }
    239             if (mcc != 0) {
    240                 if (mccRequiresEmergencyAffordance(mcc)) {
    241                     neededNow = true;
    242                     break;
    243                 } else {
    244                     // a Sim with a different mcc code was found
    245                     neededNow = false;
    246                 }
    247             }
    248         }
    249         setSimNeedsEmergencyAffordance(neededNow);
    250         return neededNow;
    251     }
    252 
    253     private void setSimNeedsEmergencyAffordance(boolean simNeedsEmergencyAffordance) {
    254         if (simNeededAffordanceBefore() != simNeedsEmergencyAffordance) {
    255             Settings.Global.putInt(mContext.getContentResolver(),
    256                     EMERGENCY_SIM_INSERTED_SETTING,
    257                     simNeedsEmergencyAffordance ? 1 : 0);
    258         }
    259         if (simNeedsEmergencyAffordance != mSimNeedsEmergencyAffordance) {
    260             mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance;
    261             updateEmergencyAffordanceNeeded();
    262         }
    263     }
    264 
    265     private boolean simNeededAffordanceBefore() {
    266         return Settings.Global.getInt(mContext.getContentResolver(),
    267                 EMERGENCY_SIM_INSERTED_SETTING, 0) != 0;
    268     }
    269 
    270     private boolean handleUpdateCellInfo() {
    271         List<CellInfo> cellInfos = mTelephonyManager.getAllCellInfo();
    272         if (cellInfos == null) {
    273             return false;
    274         }
    275         boolean stopScanningAfterScan = false;
    276         for (CellInfo cellInfo : cellInfos) {
    277             int mcc = 0;
    278             if (cellInfo instanceof CellInfoGsm) {
    279                 mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMcc();
    280             } else if (cellInfo instanceof CellInfoLte) {
    281                 mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMcc();
    282             } else if (cellInfo instanceof CellInfoWcdma) {
    283                 mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMcc();
    284             }
    285             if (mccRequiresEmergencyAffordance(mcc)) {
    286                 setNetworkNeedsEmergencyAffordance(true);
    287                 return true;
    288             } else if (mcc != 0 && mcc != Integer.MAX_VALUE) {
    289                 // we found an mcc that isn't in the list, abort
    290                 stopScanningAfterScan = true;
    291             }
    292         }
    293         if (stopScanningAfterScan) {
    294             stopScanning();
    295         } else {
    296             onCellScanFinishedUnsuccessful();
    297         }
    298         setNetworkNeedsEmergencyAffordance(false);
    299         return false;
    300     }
    301 
    302     private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) {
    303         synchronized (mLock) {
    304             mNetworkNeedsEmergencyAffordance = needsAffordance;
    305             updateEmergencyAffordanceNeeded();
    306         }
    307     }
    308 
    309     private void onCellScanFinishedUnsuccessful() {
    310         synchronized (mLock) {
    311             mScansCompleted++;
    312             if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) {
    313                 stopScanning();
    314             }
    315         }
    316     }
    317 
    318     private boolean mccRequiresEmergencyAffordance(int mcc) {
    319         return mEmergencyCallMccNumbers.contains(mcc);
    320     }
    321 }
    322