1 /*
2  * Copyright (C) 2006 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;
18 
19 import android.app.Activity;
20 import android.app.KeyguardManager;
21 import android.app.ProgressDialog;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.ContextWrapper;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.media.AudioManager;
29 import android.net.ConnectivityManager;
30 import android.net.Uri;
31 import android.os.AsyncResult;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Message;
35 import android.os.PersistableBundle;
36 import android.os.PowerManager;
37 import android.os.SystemClock;
38 import android.os.SystemProperties;
39 import android.os.UpdateLock;
40 import android.os.UserManager;
41 import android.preference.PreferenceManager;
42 import android.provider.Settings;
43 import android.telephony.CarrierConfigManager;
44 import android.telephony.ServiceState;
45 import android.telephony.SubscriptionManager;
46 import android.util.Log;
47 import android.widget.Toast;
48 
49 import com.android.internal.telephony.Call;
50 import com.android.internal.telephony.CallManager;
51 import com.android.internal.telephony.IccCardConstants;
52 import com.android.internal.telephony.MmiCode;
53 import com.android.internal.telephony.Phone;
54 import com.android.internal.telephony.PhoneConstants;
55 import com.android.internal.telephony.PhoneFactory;
56 import com.android.internal.telephony.TelephonyCapabilities;
57 import com.android.internal.telephony.TelephonyIntents;
58 import com.android.phone.common.CallLogAsync;
59 import com.android.phone.settings.SettingsConstants;
60 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
61 import com.android.services.telephony.sip.SipUtil;
62 
63 /**
64  * Global state for the telephony subsystem when running in the primary
65  * phone process.
66  */
67 public class PhoneGlobals extends ContextWrapper {
68     public static final String LOG_TAG = "PhoneApp";
69 
70     /**
71      * Phone app-wide debug level:
72      *   0 - no debug logging
73      *   1 - normal debug logging if ro.debuggable is set (which is true in
74      *       "eng" and "userdebug" builds but not "user" builds)
75      *   2 - ultra-verbose debug logging
76      *
77      * Most individual classes in the phone app have a local DBG constant,
78      * typically set to
79      *   (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1)
80      * or else
81      *   (PhoneApp.DBG_LEVEL >= 2)
82      * depending on the desired verbosity.
83      *
84      * ***** DO NOT SUBMIT WITH DBG_LEVEL > 0 *************
85      */
86     public static final int DBG_LEVEL = 0;
87 
88     private static final boolean DBG =
89             (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
90     private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
91 
92     // Message codes; see mHandler below.
93     private static final int EVENT_SIM_NETWORK_LOCKED = 3;
94     private static final int EVENT_SIM_STATE_CHANGED = 8;
95     private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
96     private static final int EVENT_DATA_ROAMING_OK = 11;
97     private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
98     private static final int EVENT_RESTART_SIP = 13;
99 
100     // The MMI codes are also used by the InCallScreen.
101     public static final int MMI_INITIATE = 51;
102     public static final int MMI_COMPLETE = 52;
103     public static final int MMI_CANCEL = 53;
104     // Don't use message codes larger than 99 here; those are reserved for
105     // the individual Activities of the Phone UI.
106 
107     public static final int AIRPLANE_ON = 1;
108     public static final int AIRPLANE_OFF = 0;
109 
110     /**
111      * Allowable values for the wake lock code.
112      *   SLEEP means the device can be put to sleep.
113      *   PARTIAL means wake the processor, but we display can be kept off.
114      *   FULL means wake both the processor and the display.
115      */
116     public enum WakeState {
117         SLEEP,
118         PARTIAL,
119         FULL
120     }
121 
122     private static PhoneGlobals sMe;
123 
124     // A few important fields we expose to the rest of the package
125     // directly (rather than thru set/get methods) for efficiency.
126     CallController callController;
127     CallManager mCM;
128     CallNotifier notifier;
129     CallerInfoCache callerInfoCache;
130     NotificationMgr notificationMgr;
131     public PhoneInterfaceManager phoneMgr;
132     CarrierConfigLoader configLoader;
133 
134     private CallGatewayManager callGatewayManager;
135     private Phone phoneInEcm;
136 
137     static boolean sVoiceCapable = true;
138 
139     // TODO: Remove, no longer used.
140     CdmaPhoneCallState cdmaPhoneCallState;
141 
142     // The currently-active PUK entry activity and progress dialog.
143     // Normally, these are the Emergency Dialer and the subsequent
144     // progress dialog.  null if there is are no such objects in
145     // the foreground.
146     private Activity mPUKEntryActivity;
147     private ProgressDialog mPUKEntryProgressDialog;
148 
149     private boolean mDataDisconnectedDueToRoaming = false;
150 
151     private WakeState mWakeState = WakeState.SLEEP;
152 
153     private PowerManager mPowerManager;
154     private PowerManager.WakeLock mWakeLock;
155     private PowerManager.WakeLock mPartialWakeLock;
156     private KeyguardManager mKeyguardManager;
157 
158     private UpdateLock mUpdateLock;
159 
160     // Broadcast receiver for various intent broadcasts (see onCreate())
161     private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
162 
163     private final CarrierVvmPackageInstalledReceiver mCarrierVvmPackageInstalledReceiver =
164             new CarrierVvmPackageInstalledReceiver();
165 
166     Handler mHandler = new Handler() {
167         @Override
168         public void handleMessage(Message msg) {
169             PhoneConstants.State phoneState;
170             switch (msg.what) {
171                 // TODO: This event should be handled by the lock screen, just
172                 // like the "SIM missing" and "Sim locked" cases (bug 1804111).
173                 case EVENT_SIM_NETWORK_LOCKED:
174                     if (getCarrierConfig().getBoolean(
175                             CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
176                         // Some products don't have the concept of a "SIM network lock"
177                         Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
178                               + "not showing 'SIM network unlock' PIN entry screen");
179                     } else {
180                         // Normal case: show the "SIM network unlock" PIN entry screen.
181                         // The user won't be able to do anything else until
182                         // they enter a valid SIM network PIN.
183                         Log.i(LOG_TAG, "show sim depersonal panel");
184                         IccNetworkDepersonalizationPanel.showDialog();
185                     }
186                     break;
187 
188                 case EVENT_DATA_ROAMING_DISCONNECTED:
189                     notificationMgr.showDataDisconnectedRoaming();
190                     break;
191 
192                 case EVENT_DATA_ROAMING_OK:
193                     notificationMgr.hideDataDisconnectedRoaming();
194                     break;
195 
196                 case MMI_COMPLETE:
197                     onMMIComplete((AsyncResult) msg.obj);
198                     break;
199 
200                 case MMI_CANCEL:
201                     PhoneUtils.cancelMmiCode(mCM.getFgPhone());
202                     break;
203 
204                 case EVENT_SIM_STATE_CHANGED:
205                     // Marks the event where the SIM goes into ready state.
206                     // Right now, this is only used for the PUK-unlocking
207                     // process.
208                     if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)) {
209                         // when the right event is triggered and there
210                         // are UI objects in the foreground, we close
211                         // them to display the lock panel.
212                         if (mPUKEntryActivity != null) {
213                             mPUKEntryActivity.finish();
214                             mPUKEntryActivity = null;
215                         }
216                         if (mPUKEntryProgressDialog != null) {
217                             mPUKEntryProgressDialog.dismiss();
218                             mPUKEntryProgressDialog = null;
219                         }
220                     }
221                     break;
222 
223                 case EVENT_UNSOL_CDMA_INFO_RECORD:
224                     //TODO: handle message here;
225                     break;
226                 case EVENT_RESTART_SIP:
227                     // This should only run if the Phone process crashed and was restarted. We do
228                     // not want this running if the device is still in the FBE encrypted state.
229                     // This is the same procedure that is triggered in the SipBroadcastReceiver
230                     // upon BOOT_COMPLETED.
231                     UserManager userManager = UserManager.get(sMe);
232                     if (userManager != null && userManager.isUserUnlocked()) {
233                         SipUtil.startSipService();
234                     }
235                     break;
236             }
237         }
238     };
239 
PhoneGlobals(Context context)240     public PhoneGlobals(Context context) {
241         super(context);
242         sMe = this;
243     }
244 
onCreate()245     public void onCreate() {
246         if (VDBG) Log.v(LOG_TAG, "onCreate()...");
247 
248         ContentResolver resolver = getContentResolver();
249 
250         // Cache the "voice capable" flag.
251         // This flag currently comes from a resource (which is
252         // overrideable on a per-product basis):
253         sVoiceCapable =
254                 getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
255         // ...but this might eventually become a PackageManager "system
256         // feature" instead, in which case we'd do something like:
257         // sVoiceCapable =
258         //   getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
259 
260         if (mCM == null) {
261             // Initialize the telephony framework
262             PhoneFactory.makeDefaultPhones(this);
263 
264             // Start TelephonyDebugService After the default phone is created.
265             Intent intent = new Intent(this, TelephonyDebugService.class);
266             startService(intent);
267 
268             mCM = CallManager.getInstance();
269             for (Phone phone : PhoneFactory.getPhones()) {
270                 mCM.registerPhone(phone);
271             }
272 
273             // Create the NotificationMgr singleton, which is used to display
274             // status bar icons and control other status bar behavior.
275             notificationMgr = NotificationMgr.init(this);
276 
277             // If PhoneGlobals has crashed and is being restarted, then restart.
278             mHandler.sendEmptyMessage(EVENT_RESTART_SIP);
279 
280             // Create an instance of CdmaPhoneCallState and initialize it to IDLE
281             cdmaPhoneCallState = new CdmaPhoneCallState();
282             cdmaPhoneCallState.CdmaPhoneCallStateInit();
283 
284             // before registering for phone state changes
285             mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
286             mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG);
287             // lock used to keep the processor awake, when we don't care for the display.
288             mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
289                     | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
290 
291             mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
292 
293             // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
294             // during phone calls.
295             mUpdateLock = new UpdateLock("phone");
296 
297             if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
298 
299             CallLogger callLogger = new CallLogger(this, new CallLogAsync());
300 
301             callGatewayManager = CallGatewayManager.getInstance();
302 
303             // Create the CallController singleton, which is the interface
304             // to the telephony layer for user-initiated telephony functionality
305             // (like making outgoing calls.)
306             callController = CallController.init(this, callLogger, callGatewayManager);
307 
308             // Create the CallerInfoCache singleton, which remembers custom ring tone and
309             // send-to-voicemail settings.
310             //
311             // The asynchronous caching will start just after this call.
312             callerInfoCache = CallerInfoCache.init(this);
313 
314             phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
315 
316             configLoader = CarrierConfigLoader.init(this);
317 
318             // Create the CallNotifer singleton, which handles
319             // asynchronous events from the telephony layer (like
320             // launching the incoming-call UI when an incoming call comes
321             // in.)
322             notifier = CallNotifier.init(this);
323 
324             PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED);
325 
326             // register for MMI/USSD
327             mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
328 
329             // register connection tracking to PhoneUtils
330             PhoneUtils.initializeConnectionHandler(mCM);
331 
332             // Register for misc other intent broadcasts.
333             IntentFilter intentFilter =
334                     new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
335             intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
336             intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
337             intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
338             intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
339             intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
340             registerReceiver(mReceiver, intentFilter);
341 
342             mCarrierVvmPackageInstalledReceiver.register(this);
343 
344             //set the default values for the preferences in the phone.
345             PreferenceManager.setDefaultValues(this, R.xml.network_setting_fragment, false);
346 
347             PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);
348 
349             // Make sure the audio mode (along with some
350             // audio-mode-related state of our own) is initialized
351             // correctly, given the current state of the phone.
352             PhoneUtils.setAudioMode(mCM);
353         }
354 
355         // XXX pre-load the SimProvider so that it's ready
356         resolver.getType(Uri.parse("content://icc/adn"));
357 
358         // TODO: Register for Cdma Information Records
359         // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null);
360 
361         // Read HAC settings and configure audio hardware
362         if (getResources().getBoolean(R.bool.hac_enabled)) {
363             int hac = android.provider.Settings.System.getInt(
364                     getContentResolver(),
365                     android.provider.Settings.System.HEARING_AID,
366                     0);
367             AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
368             audioManager.setParameter(SettingsConstants.HAC_KEY,
369                     hac == SettingsConstants.HAC_ENABLED
370                             ? SettingsConstants.HAC_VAL_ON : SettingsConstants.HAC_VAL_OFF);
371         }
372     }
373 
374     /**
375      * Returns the singleton instance of the PhoneApp.
376      */
getInstance()377     public static PhoneGlobals getInstance() {
378         if (sMe == null) {
379             throw new IllegalStateException("No PhoneGlobals here!");
380         }
381         return sMe;
382     }
383 
384     /**
385      * Returns the singleton instance of the PhoneApp if running as the
386      * primary user, otherwise null.
387      */
getInstanceIfPrimary()388     static PhoneGlobals getInstanceIfPrimary() {
389         return sMe;
390     }
391 
392     /**
393      * Returns the default phone.
394      *
395      * WARNING: This method should be used carefully, now that there may be multiple phones.
396      */
getPhone()397     public static Phone getPhone() {
398         return PhoneFactory.getDefaultPhone();
399     }
400 
getPhone(int subId)401     public static Phone getPhone(int subId) {
402         return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
403     }
404 
getCallManager()405     /* package */ CallManager getCallManager() {
406         return mCM;
407     }
408 
getCarrierConfig()409     public PersistableBundle getCarrierConfig() {
410         return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
411     }
412 
getCarrierConfigForSubId(int subId)413     public PersistableBundle getCarrierConfigForSubId(int subId) {
414         return configLoader.getConfigForSubId(subId);
415     }
416 
417     /**
418      * Sets the activity responsible for un-PUK-blocking the device
419      * so that we may close it when we receive a positive result.
420      * mPUKEntryActivity is also used to indicate to the device that
421      * we are trying to un-PUK-lock the phone. In other words, iff
422      * it is NOT null, then we are trying to unlock and waiting for
423      * the SIM to move to READY state.
424      *
425      * @param activity is the activity to close when PUK has
426      * finished unlocking. Can be set to null to indicate the unlock
427      * or SIM READYing process is over.
428      */
setPukEntryActivity(Activity activity)429     void setPukEntryActivity(Activity activity) {
430         mPUKEntryActivity = activity;
431     }
432 
getPUKEntryActivity()433     Activity getPUKEntryActivity() {
434         return mPUKEntryActivity;
435     }
436 
437     /**
438      * Sets the dialog responsible for notifying the user of un-PUK-
439      * blocking - SIM READYing progress, so that we may dismiss it
440      * when we receive a positive result.
441      *
442      * @param dialog indicates the progress dialog informing the user
443      * of the state of the device.  Dismissed upon completion of
444      * READYing process
445      */
setPukEntryProgressDialog(ProgressDialog dialog)446     void setPukEntryProgressDialog(ProgressDialog dialog) {
447         mPUKEntryProgressDialog = dialog;
448     }
449 
450     /**
451      * Controls whether or not the screen is allowed to sleep.
452      *
453      * Once sleep is allowed (WakeState is SLEEP), it will rely on the
454      * settings for the poke lock to determine when to timeout and let
455      * the device sleep {@link PhoneGlobals#setScreenTimeout}.
456      *
457      * @param ws tells the device to how to wake.
458      */
requestWakeState(WakeState ws)459     /* package */ void requestWakeState(WakeState ws) {
460         if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")...");
461         synchronized (this) {
462             if (mWakeState != ws) {
463                 switch (ws) {
464                     case PARTIAL:
465                         // acquire the processor wake lock, and release the FULL
466                         // lock if it is being held.
467                         mPartialWakeLock.acquire();
468                         if (mWakeLock.isHeld()) {
469                             mWakeLock.release();
470                         }
471                         break;
472                     case FULL:
473                         // acquire the full wake lock, and release the PARTIAL
474                         // lock if it is being held.
475                         mWakeLock.acquire();
476                         if (mPartialWakeLock.isHeld()) {
477                             mPartialWakeLock.release();
478                         }
479                         break;
480                     case SLEEP:
481                     default:
482                         // release both the PARTIAL and FULL locks.
483                         if (mWakeLock.isHeld()) {
484                             mWakeLock.release();
485                         }
486                         if (mPartialWakeLock.isHeld()) {
487                             mPartialWakeLock.release();
488                         }
489                         break;
490                 }
491                 mWakeState = ws;
492             }
493         }
494     }
495 
496     /**
497      * If we are not currently keeping the screen on, then poke the power
498      * manager to wake up the screen for the user activity timeout duration.
499      */
wakeUpScreen()500     /* package */ void wakeUpScreen() {
501         synchronized (this) {
502             if (mWakeState == WakeState.SLEEP) {
503                 if (DBG) Log.d(LOG_TAG, "pulse screen lock");
504                 mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.phone:WAKE");
505             }
506         }
507     }
508 
509     /**
510      * Sets the wake state and screen timeout based on the current state
511      * of the phone, and the current state of the in-call UI.
512      *
513      * This method is a "UI Policy" wrapper around
514      * {@link PhoneGlobals#requestWakeState} and {@link PhoneGlobals#setScreenTimeout}.
515      *
516      * It's safe to call this method regardless of the state of the Phone
517      * (e.g. whether or not it's idle), and regardless of the state of the
518      * Phone UI (e.g. whether or not the InCallScreen is active.)
519      */
updateWakeState()520     /* package */ void updateWakeState() {
521         PhoneConstants.State state = mCM.getState();
522 
523         // True if the speakerphone is in use.  (If so, we *always* use
524         // the default timeout.  Since the user is obviously not holding
525         // the phone up to his/her face, we don't need to worry about
526         // false touches, and thus don't need to turn the screen off so
527         // aggressively.)
528         // Note that we need to make a fresh call to this method any
529         // time the speaker state changes.  (That happens in
530         // PhoneUtils.turnOnSpeaker().)
531         boolean isSpeakerInUse = (state == PhoneConstants.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this);
532 
533         // TODO (bug 1440854): The screen timeout *might* also need to
534         // depend on the bluetooth state, but this isn't as clear-cut as
535         // the speaker state (since while using BT it's common for the
536         // user to put the phone straight into a pocket, in which case the
537         // timeout should probably still be short.)
538 
539         // Decide whether to force the screen on or not.
540         //
541         // Force the screen to be on if the phone is ringing or dialing,
542         // or if we're displaying the "Call ended" UI for a connection in
543         // the "disconnected" state.
544         // However, if the phone is disconnected while the user is in the
545         // middle of selecting a quick response message, we should not force
546         // the screen to be on.
547         //
548         boolean isRinging = (state == PhoneConstants.State.RINGING);
549         boolean isDialing = (mCM.getFgPhone().getForegroundCall().getState() == Call.State.DIALING);
550         boolean keepScreenOn = isRinging || isDialing;
551         // keepScreenOn == true means we'll hold a full wake lock:
552         requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
553     }
554 
getKeyguardManager()555     KeyguardManager getKeyguardManager() {
556         return mKeyguardManager;
557     }
558 
onMMIComplete(AsyncResult r)559     private void onMMIComplete(AsyncResult r) {
560         if (VDBG) Log.d(LOG_TAG, "onMMIComplete()...");
561         MmiCode mmiCode = (MmiCode) r.result;
562         PhoneUtils.displayMMIComplete(mmiCode.getPhone(), getInstance(), mmiCode, null, null);
563     }
564 
initForNewRadioTechnology()565     private void initForNewRadioTechnology() {
566         if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
567         notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
568     }
569 
handleAirplaneModeChange(Context context, int newMode)570     private void handleAirplaneModeChange(Context context, int newMode) {
571         int cellState = Settings.Global.getInt(context.getContentResolver(),
572                 Settings.Global.CELL_ON, PhoneConstants.CELL_ON_FLAG);
573         boolean isAirplaneNewlyOn = (newMode == 1);
574         switch (cellState) {
575             case PhoneConstants.CELL_OFF_FLAG:
576                 // Airplane mode does not affect the cell radio if user
577                 // has turned it off.
578                 break;
579             case PhoneConstants.CELL_ON_FLAG:
580                 maybeTurnCellOff(context, isAirplaneNewlyOn);
581                 break;
582             case PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG:
583                 maybeTurnCellOn(context, isAirplaneNewlyOn);
584                 break;
585         }
586     }
587 
588     /*
589      * Returns true if the radio must be turned off when entering airplane mode.
590      */
isCellOffInAirplaneMode(Context context)591     private boolean isCellOffInAirplaneMode(Context context) {
592         String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
593                 Settings.Global.AIRPLANE_MODE_RADIOS);
594         return airplaneModeRadios == null
595                 || airplaneModeRadios.contains(Settings.Global.RADIO_CELL);
596     }
597 
setRadioPowerOff(Context context)598     private void setRadioPowerOff(Context context) {
599         Log.i(LOG_TAG, "Turning radio off - airplane");
600         Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
601                  PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
602         SystemProperties.set("persist.radio.airplane_mode_on", "1");
603         Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
604         PhoneUtils.setRadioPower(false);
605     }
606 
setRadioPowerOn(Context context)607     private void setRadioPowerOn(Context context) {
608         Log.i(LOG_TAG, "Turning radio on - airplane");
609         Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
610                 PhoneConstants.CELL_ON_FLAG);
611         Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
612                 1);
613         SystemProperties.set("persist.radio.airplane_mode_on", "0");
614         PhoneUtils.setRadioPower(true);
615     }
616 
maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn)617     private void maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn) {
618         if (isAirplaneNewlyOn) {
619             // If we are trying to turn off the radio, make sure there are no active
620             // emergency calls.  If there are, switch airplane mode back to off.
621             if (PhoneUtils.isInEmergencyCall(mCM)) {
622                 // Switch airplane mode back to off.
623                 ConnectivityManager.from(this).setAirplaneMode(false);
624                 Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
625                         .show();
626                 Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
627             } else if (isCellOffInAirplaneMode(context)) {
628                 setRadioPowerOff(context);
629             } else {
630                 Log.i(LOG_TAG, "Ignoring airplane mode: settings prevent cell radio power off");
631             }
632         }
633     }
634 
maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn)635     private void maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn) {
636         if (!isAirplaneNewlyOn) {
637             setRadioPowerOn(context);
638         }
639     }
640 
641     /**
642      * Receiver for misc intent broadcasts the Phone app cares about.
643      */
644     private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
645         @Override
onReceive(Context context, Intent intent)646         public void onReceive(Context context, Intent intent) {
647             String action = intent.getAction();
648             if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
649                 int airplaneMode = Settings.Global.getInt(getContentResolver(),
650                         Settings.Global.AIRPLANE_MODE_ON, AIRPLANE_OFF);
651                 // Treat any non-OFF values as ON.
652                 if (airplaneMode != AIRPLANE_OFF) {
653                     airplaneMode = AIRPLANE_ON;
654                 }
655                 handleAirplaneModeChange(context, airplaneMode);
656             } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
657                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
658                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
659                 int phoneId = SubscriptionManager.getPhoneId(subId);
660                 final String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
661                 final String state = intent.getStringExtra(PhoneConstants.STATE_KEY);
662                 final String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
663                 if (VDBG) {
664                     Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
665                     Log.d(LOG_TAG, "- state: " + state);
666                     Log.d(LOG_TAG, "- reason: " + reason);
667                     Log.d(LOG_TAG, "- subId: " + subId);
668                 }
669                 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
670                         PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
671 
672                 // If the apn type of data connection state changed event is NOT default,
673                 // ignore the broadcast intent and avoid action.
674                 if (!PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
675                     if (VDBG) Log.d(LOG_TAG, "Ignore broadcast intent as not default apn type");
676                     return;
677                 }
678 
679                 // The "data disconnected due to roaming" notification is shown
680                 // if (a) you have the "data roaming" feature turned off, and
681                 // (b) you just lost data connectivity because you're roaming.
682                 // (c) if we haven't shown the notification for this disconnection earlier.
683                 if (!mDataDisconnectedDueToRoaming
684                         && PhoneConstants.DataState.DISCONNECTED.name().equals(state)
685                         && Phone.REASON_ROAMING_ON.equals(reason)
686                         && !phone.getDataRoamingEnabled()) {
687                     // Notify the user that data call is disconnected due to roaming. Note that
688                     // calling this multiple times will not cause multiple notifications.
689                     mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_DISCONNECTED);
690                     mDataDisconnectedDueToRoaming = true;
691                 } else if (mDataDisconnectedDueToRoaming
692                         && PhoneConstants.DataState.CONNECTED.name().equals(state)) {
693                     // Cancel the notification when data is available. Note it is okay to call this
694                     // even if the notification doesn't exist.
695                     mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_OK);
696                     mDataDisconnectedDueToRoaming = false;
697                 }
698             } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
699                     (mPUKEntryActivity != null)) {
700                 // if an attempt to un-PUK-lock the device was made, while we're
701                 // receiving this state change notification, notify the handler.
702                 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
703                 // been attempted.
704                 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
705                         intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
706             } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
707                 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
708                 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
709                 initForNewRadioTechnology();
710             } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
711                 handleServiceStateChanged(intent);
712             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
713                 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
714                 phoneInEcm = PhoneFactory.getPhone(phoneId);
715                 Log.d(LOG_TAG, "Emergency Callback Mode. phoneId:" + phoneId);
716                 if (phoneInEcm != null) {
717                     if (TelephonyCapabilities.supportsEcm(phoneInEcm)) {
718                         Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
719                         // Start Emergency Callback Mode service
720                         if (intent.getBooleanExtra("phoneinECMState", false)) {
721                             context.startService(new Intent(context,
722                                     EmergencyCallbackModeService.class));
723                         } else {
724                             phoneInEcm = null;
725                         }
726                     } else {
727                         // It doesn't make sense to get ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
728                         // on a device that doesn't support ECM in the first place.
729                         Log.e(LOG_TAG, "Got ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, but "
730                                 + "ECM isn't supported for phone: " + phoneInEcm.getPhoneName());
731                         phoneInEcm = null;
732                     }
733                 } else {
734                     Log.w(LOG_TAG, "phoneInEcm is null.");
735                 }
736             }
737         }
738     }
739 
handleServiceStateChanged(Intent intent)740     private void handleServiceStateChanged(Intent intent) {
741         /**
742          * This used to handle updating EriTextWidgetProvider this routine
743          * and and listening for ACTION_SERVICE_STATE_CHANGED intents could
744          * be removed. But leaving just in case it might be needed in the near
745          * future.
746          */
747 
748         // If service just returned, start sending out the queued messages
749         Bundle extras = intent.getExtras();
750         if (extras != null) {
751             ServiceState ss = ServiceState.newFromBundle(extras);
752             if (ss != null) {
753                 int state = ss.getState();
754                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
755                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
756                 notificationMgr.updateNetworkSelection(state, subId);
757             }
758         }
759     }
760 
getPhoneInEcm()761     public Phone getPhoneInEcm() {
762         return phoneInEcm;
763     }
764 
765     /**
766      * Triggers a refresh of the message waiting (voicemail) indicator.
767      *
768      * @param subId the subscription id we should refresh the notification for.
769      */
refreshMwiIndicator(int subId)770     public void refreshMwiIndicator(int subId) {
771         notificationMgr.refreshMwi(subId);
772     }
773 
774     /**
775      * Dismisses the message waiting (voicemail) indicator.
776      *
777      * @param subId the subscription id we should dismiss the notification for.
778      */
clearMwiIndicator(int subId)779     public void clearMwiIndicator(int subId) {
780         // Setting voiceMessageCount to 0 will remove the current notification and clear the system
781         // cached value.
782         Phone phone = getPhone(subId);
783         if (phone == null) {
784             Log.w(LOG_TAG, "clearMwiIndicator on null phone, subId:" + subId);
785         } else {
786             phone.setVoiceMessageCount(0);
787         }
788     }
789 
790     /**
791      * Enables or disables the visual voicemail check for message waiting indicator. Default value
792      * is true. MWI is the traditional voicemail notification which should be suppressed if visual
793      * voicemail is active. {@link NotificationMgr#updateMwi(int, boolean, boolean)} currently
794      * checks the {@link android.provider.VoicemailContract.Status#CONFIGURATION_STATE} to suppress
795      * the MWI, but there are several issues. b/31229016 is a bug that when the device boots the
796      * configuration state will be cleared and the MWI for voicemail that arrives when the device
797      * is offline will be cleared, even if the account cannot be activated. A full solution will be
798      * adding a setMwiEnabled() method and stop checking the configuration state, but that is too
799      * risky at this moment. This is a temporary workaround to shut down the configuration state
800      * check if visual voicemail cannot be activated.
801      * <p>TODO(twyen): implement the setMwiEnabled() mentioned above.
802      *
803      * @param subId the account to set the enabled state
804      */
setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled)805     public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
806         notificationMgr.setShouldCheckVisualVoicemailConfigurationForMwi(subId, enabled);
807     }
808 }
809