Home | History | Annotate | Download | only in phone
      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 
    240     public PhoneGlobals(Context context) {
    241         super(context);
    242         sMe = this;
    243     }
    244 
    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      */
    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      */
    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      */
    397     public static Phone getPhone() {
    398         return PhoneFactory.getDefaultPhone();
    399     }
    400 
    401     public static Phone getPhone(int subId) {
    402         return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
    403     }
    404 
    405     /* package */ CallManager getCallManager() {
    406         return mCM;
    407     }
    408 
    409     public PersistableBundle getCarrierConfig() {
    410         return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
    411     }
    412 
    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      */
    429     void setPukEntryActivity(Activity activity) {
    430         mPUKEntryActivity = activity;
    431     }
    432 
    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      */
    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      */
    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      */
    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      */
    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 
    555     KeyguardManager getKeyguardManager() {
    556         return mKeyguardManager;
    557     }
    558 
    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 
    565     private void initForNewRadioTechnology() {
    566         if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
    567         notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
    568     }
    569 
    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      */
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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      */
    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      */
    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      */
    805     public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
    806         notificationMgr.setShouldCheckVisualVoicemailConfigurationForMwi(subId, enabled);
    807     }
    808 }
    809