• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.services.telephony;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.UserHandle;
22 import android.provider.Settings;
23 import android.telephony.TelephonyManager;
24 
25 import com.android.internal.telephony.Phone;
26 import com.android.internal.telephony.PhoneFactory;
27 
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
31 
32 /**
33  * Helper class that implements special behavior related to emergency calls. Specifically, this
34  * class handles the case of the user trying to dial an emergency number while the radio is off
35  * (i.e. the device is in airplane mode), by forcibly turning the radio back on, waiting for it to
36  * come up, and then retrying the emergency call.
37  */
38 public class EmergencyCallHelper implements EmergencyCallStateListener.Callback {
39 
40     private final Context mContext;
41     private EmergencyCallStateListener.Callback mCallback;
42     private List<EmergencyCallStateListener> mListeners;
43     private List<EmergencyCallStateListener> mInProgressListeners;
44     private boolean mIsEmergencyCallingEnabled;
45 
46 
EmergencyCallHelper(Context context)47     public EmergencyCallHelper(Context context) {
48         mContext = context;
49         mInProgressListeners = new ArrayList<>(2);
50     }
51 
setupListeners()52     private void setupListeners() {
53         if (mListeners != null) {
54             return;
55         }
56         mListeners = new ArrayList<>(2);
57         for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
58             mListeners.add(new EmergencyCallStateListener());
59         }
60     }
61     /**
62      * Starts the "turn on radio" sequence. This is the (single) external API of the
63      * EmergencyCallHelper class.
64      *
65      * This method kicks off the following sequence:
66      * - Power on the radio for each Phone
67      * - Listen for the service state change event telling us the radio has come up.
68      * - Retry if we've gone a significant amount of time without any response from the radio.
69      * - Finally, clean up any leftover state.
70      *
71      * This method is safe to call from any thread, since it simply posts a message to the
72      * EmergencyCallHelper's handler (thus ensuring that the rest of the sequence is entirely
73      * serialized, and runs on the main looper.)
74      */
enableEmergencyCalling(EmergencyCallStateListener.Callback callback)75     public void enableEmergencyCalling(EmergencyCallStateListener.Callback callback) {
76         setupListeners();
77         mCallback = callback;
78         mInProgressListeners.clear();
79         mIsEmergencyCallingEnabled = false;
80         for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
81             Phone phone = PhoneFactory.getPhone(i);
82             if (phone == null)
83                 continue;
84 
85             mInProgressListeners.add(mListeners.get(i));
86             mListeners.get(i).waitForRadioOn(phone, this);
87         }
88 
89         powerOnRadio();
90     }
91     /**
92      * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
93      * get an onServiceStateChanged() callback when the radio successfully comes up.
94      */
powerOnRadio()95     private void powerOnRadio() {
96         Log.d(this, "powerOnRadio().");
97 
98         // If airplane mode is on, we turn it off the same way that the Settings activity turns it
99         // off.
100         if (Settings.Global.getInt(mContext.getContentResolver(),
101                 Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
102             Log.d(this, "==> Turning off airplane mode.");
103 
104             // Change the system setting
105             Settings.Global.putInt(mContext.getContentResolver(),
106                     Settings.Global.AIRPLANE_MODE_ON, 0);
107 
108             // Post the broadcast intend for change in airplane mode
109             // TODO: We really should not be in charge of sending this broadcast.
110             //     If changing the setting is sufficent to trigger all of the rest of the logic,
111             //     then that should also trigger the broadcast intent.
112             Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
113             intent.putExtra("state", false);
114             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
115         }
116     }
117 
118     /**
119      * This method is called from multiple Listeners on the Main Looper.
120      * Synchronization is not necessary.
121      */
122     @Override
onComplete(EmergencyCallStateListener listener, boolean isRadioReady)123     public void onComplete(EmergencyCallStateListener listener, boolean isRadioReady) {
124         mIsEmergencyCallingEnabled |= isRadioReady;
125         mInProgressListeners.remove(listener);
126         if (mCallback != null && mInProgressListeners.isEmpty()) {
127             mCallback.onComplete(null, mIsEmergencyCallingEnabled);
128         }
129     }
130 }
131