| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.phone; |
| |
| import android.app.Activity; |
| import android.app.KeyguardManager; |
| import android.app.PendingIntent; |
| import android.app.ProgressDialog; |
| import android.app.TaskStackBuilder; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.IBluetoothHeadsetPhone; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.ContextWrapper; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.ServiceConnection; |
| import android.media.AudioManager; |
| import android.net.Uri; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IPowerManager; |
| import android.os.Message; |
| import android.os.PowerManager; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.UpdateLock; |
| import android.os.UserHandle; |
| import android.preference.PreferenceManager; |
| import android.provider.Settings.System; |
| import android.telephony.ServiceState; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.util.Log; |
| |
| import com.android.internal.telephony.Call; |
| import com.android.internal.telephony.CallManager; |
| import com.android.internal.telephony.IccCard; |
| import com.android.internal.telephony.IccCardConstants; |
| import com.android.internal.telephony.MmiCode; |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.PhoneFactory; |
| import com.android.internal.telephony.SubscriptionController; |
| import com.android.internal.telephony.TelephonyCapabilities; |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.phone.common.CallLogAsync; |
| import com.android.server.sip.SipService; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Global state for the telephony subsystem when running in the primary |
| * phone process. |
| */ |
| public class PhoneGlobals extends ContextWrapper { |
| public static final String LOG_TAG = "PhoneApp"; |
| |
| /** |
| * Phone app-wide debug level: |
| * 0 - no debug logging |
| * 1 - normal debug logging if ro.debuggable is set (which is true in |
| * "eng" and "userdebug" builds but not "user" builds) |
| * 2 - ultra-verbose debug logging |
| * |
| * Most individual classes in the phone app have a local DBG constant, |
| * typically set to |
| * (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1) |
| * or else |
| * (PhoneApp.DBG_LEVEL >= 2) |
| * depending on the desired verbosity. |
| * |
| * ***** DO NOT SUBMIT WITH DBG_LEVEL > 0 ************* |
| */ |
| public static final int DBG_LEVEL = 0; |
| |
| private static final boolean DBG = |
| (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); |
| private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2); |
| |
| // Message codes; see mHandler below. |
| private static final int EVENT_SIM_NETWORK_LOCKED = 3; |
| private static final int EVENT_SIM_STATE_CHANGED = 8; |
| private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10; |
| private static final int EVENT_DATA_ROAMING_OK = 11; |
| private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12; |
| private static final int EVENT_DOCK_STATE_CHANGED = 13; |
| private static final int EVENT_START_SIP_SERVICE = 14; |
| |
| // The MMI codes are also used by the InCallScreen. |
| public static final int MMI_INITIATE = 51; |
| public static final int MMI_COMPLETE = 52; |
| public static final int MMI_CANCEL = 53; |
| // Don't use message codes larger than 99 here; those are reserved for |
| // the individual Activities of the Phone UI. |
| |
| /** |
| * Allowable values for the wake lock code. |
| * SLEEP means the device can be put to sleep. |
| * PARTIAL means wake the processor, but we display can be kept off. |
| * FULL means wake both the processor and the display. |
| */ |
| public enum WakeState { |
| SLEEP, |
| PARTIAL, |
| FULL |
| } |
| |
| /** |
| * Intent Action used for hanging up the current call from Notification bar. This will |
| * choose first ringing call, first active call, or first background call (typically in |
| * HOLDING state). |
| */ |
| public static final String ACTION_HANG_UP_ONGOING_CALL = |
| "com.android.phone.ACTION_HANG_UP_ONGOING_CALL"; |
| |
| private static PhoneGlobals sMe; |
| |
| // A few important fields we expose to the rest of the package |
| // directly (rather than thru set/get methods) for efficiency. |
| CallController callController; |
| CallManager mCM; |
| CallNotifier notifier; |
| CallerInfoCache callerInfoCache; |
| NotificationMgr notificationMgr; |
| PhoneInterfaceManager phoneMgr; |
| |
| private BluetoothManager bluetoothManager; |
| private CallGatewayManager callGatewayManager; |
| private CallStateMonitor callStateMonitor; |
| |
| static int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; |
| static boolean sVoiceCapable = true; |
| |
| // Internal PhoneApp Call state tracker |
| CdmaPhoneCallState cdmaPhoneCallState; |
| |
| // The currently-active PUK entry activity and progress dialog. |
| // Normally, these are the Emergency Dialer and the subsequent |
| // progress dialog. null if there is are no such objects in |
| // the foreground. |
| private Activity mPUKEntryActivity; |
| private ProgressDialog mPUKEntryProgressDialog; |
| |
| private boolean mIsSimPinEnabled; |
| private String mCachedSimPin; |
| |
| // True if we are beginning a call, but the phone state has not changed yet |
| private boolean mBeginningCall; |
| private boolean mDataDisconnectedDueToRoaming = false; |
| |
| // Last phone state seen by updatePhoneState() |
| private PhoneConstants.State mLastPhoneState = PhoneConstants.State.IDLE; |
| |
| private WakeState mWakeState = WakeState.SLEEP; |
| |
| private PowerManager mPowerManager; |
| private IPowerManager mPowerManagerService; |
| private PowerManager.WakeLock mWakeLock; |
| private PowerManager.WakeLock mPartialWakeLock; |
| private KeyguardManager mKeyguardManager; |
| |
| private UpdateLock mUpdateLock; |
| |
| // Broadcast receiver for various intent broadcasts (see onCreate()) |
| private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver(); |
| |
| /** boolean indicating restoring mute state on InCallScreen.onResume() */ |
| private boolean mShouldRestoreMuteOnInCallResume; |
| |
| /** |
| * The singleton OtaUtils instance used for OTASP calls. |
| * |
| * The OtaUtils instance is created lazily the first time we need to |
| * make an OTASP call, regardless of whether it's an interactive or |
| * non-interactive OTASP call. |
| */ |
| public OtaUtils otaUtils; |
| |
| // Following are the CDMA OTA information Objects used during OTA Call. |
| // cdmaOtaProvisionData object store static OTA information that needs |
| // to be maintained even during Slider open/close scenarios. |
| // cdmaOtaConfigData object stores configuration info to control visiblity |
| // of each OTA Screens. |
| // cdmaOtaScreenState object store OTA Screen State information. |
| public OtaUtils.CdmaOtaProvisionData cdmaOtaProvisionData; |
| public OtaUtils.CdmaOtaConfigData cdmaOtaConfigData; |
| public OtaUtils.CdmaOtaScreenState cdmaOtaScreenState; |
| public OtaUtils.CdmaOtaInCallScreenUiState cdmaOtaInCallScreenUiState; |
| |
| /** |
| * Set the restore mute state flag. Used when we are setting the mute state |
| * OUTSIDE of user interaction {@link PhoneUtils#startNewCall(Phone)} |
| */ |
| /*package*/void setRestoreMuteOnInCallResume (boolean mode) { |
| mShouldRestoreMuteOnInCallResume = mode; |
| } |
| |
| Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| PhoneConstants.State phoneState; |
| switch (msg.what) { |
| // Starts the SIP service. It's a no-op if SIP API is not supported |
| // on the deivce. |
| // TODO: Having the phone process host the SIP service is only |
| // temporary. Will move it to a persistent communication process |
| // later. |
| case EVENT_START_SIP_SERVICE: |
| SipService.start(getApplicationContext()); |
| break; |
| |
| // TODO: This event should be handled by the lock screen, just |
| // like the "SIM missing" and "Sim locked" cases (bug 1804111). |
| case EVENT_SIM_NETWORK_LOCKED: |
| if (getResources().getBoolean(R.bool.ignore_sim_network_locked_events)) { |
| // Some products don't have the concept of a "SIM network lock" |
| Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; " |
| + "not showing 'SIM network unlock' PIN entry screen"); |
| } else { |
| // Normal case: show the "SIM network unlock" PIN entry screen. |
| // The user won't be able to do anything else until |
| // they enter a valid SIM network PIN. |
| Log.i(LOG_TAG, "show sim depersonal panel"); |
| IccNetworkDepersonalizationPanel ndpPanel = |
| new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance()); |
| ndpPanel.show(); |
| } |
| break; |
| |
| case EVENT_DATA_ROAMING_DISCONNECTED: |
| notificationMgr.showDataDisconnectedRoaming(); |
| break; |
| |
| case EVENT_DATA_ROAMING_OK: |
| notificationMgr.hideDataDisconnectedRoaming(); |
| break; |
| |
| case MMI_COMPLETE: |
| onMMIComplete((AsyncResult) msg.obj); |
| break; |
| |
| case MMI_CANCEL: |
| PhoneUtils.cancelMmiCode(mCM.getFgPhone()); |
| break; |
| |
| case EVENT_SIM_STATE_CHANGED: |
| // Marks the event where the SIM goes into ready state. |
| // Right now, this is only used for the PUK-unlocking |
| // process. |
| if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)) { |
| // when the right event is triggered and there |
| // are UI objects in the foreground, we close |
| // them to display the lock panel. |
| if (mPUKEntryActivity != null) { |
| mPUKEntryActivity.finish(); |
| mPUKEntryActivity = null; |
| } |
| if (mPUKEntryProgressDialog != null) { |
| mPUKEntryProgressDialog.dismiss(); |
| mPUKEntryProgressDialog = null; |
| } |
| } |
| break; |
| |
| case EVENT_UNSOL_CDMA_INFO_RECORD: |
| //TODO: handle message here; |
| break; |
| |
| case EVENT_DOCK_STATE_CHANGED: |
| // If the phone is docked/undocked during a call, and no wired or BT headset |
| // is connected: turn on/off the speaker accordingly. |
| boolean inDockMode = false; |
| if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { |
| inDockMode = true; |
| } |
| if (VDBG) Log.d(LOG_TAG, "received EVENT_DOCK_STATE_CHANGED. Phone inDock = " |
| + inDockMode); |
| |
| phoneState = mCM.getState(); |
| if (phoneState == PhoneConstants.State.OFFHOOK && |
| !bluetoothManager.isBluetoothHeadsetAudioOn()) { |
| PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true); |
| } |
| break; |
| } |
| } |
| }; |
| |
| public PhoneGlobals(Context context) { |
| super(context); |
| sMe = this; |
| } |
| |
| public void onCreate() { |
| if (VDBG) Log.v(LOG_TAG, "onCreate()..."); |
| |
| ContentResolver resolver = getContentResolver(); |
| |
| // Cache the "voice capable" flag. |
| // This flag currently comes from a resource (which is |
| // overrideable on a per-product basis): |
| sVoiceCapable = |
| getResources().getBoolean(com.android.internal.R.bool.config_voice_capable); |
| // ...but this might eventually become a PackageManager "system |
| // feature" instead, in which case we'd do something like: |
| // sVoiceCapable = |
| // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS); |
| |
| if (mCM == null) { |
| // Initialize the telephony framework |
| PhoneFactory.makeDefaultPhones(this); |
| |
| // Start TelephonyDebugService After the default phone is created. |
| Intent intent = new Intent(this, TelephonyDebugService.class); |
| startService(intent); |
| |
| mCM = CallManager.getInstance(); |
| boolean hasCdmaPhoneType = false; |
| for (Phone phone : PhoneFactory.getPhones()) { |
| mCM.registerPhone(phone); |
| if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { |
| hasCdmaPhoneType = true; |
| } |
| } |
| |
| // Create the NotificationMgr singleton, which is used to display |
| // status bar icons and control other status bar behavior. |
| notificationMgr = NotificationMgr.init(this); |
| |
| mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE); |
| |
| if (hasCdmaPhoneType) { |
| // Create an instance of CdmaPhoneCallState and initialize it to IDLE |
| cdmaPhoneCallState = new CdmaPhoneCallState(); |
| cdmaPhoneCallState.CdmaPhoneCallStateInit(); |
| } |
| |
| // before registering for phone state changes |
| mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); |
| mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG); |
| // lock used to keep the processor awake, when we don't care for the display. |
| mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK |
| | PowerManager.ON_AFTER_RELEASE, LOG_TAG); |
| |
| mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); |
| |
| // get a handle to the service so that we can use it later when we |
| // want to set the poke lock. |
| mPowerManagerService = IPowerManager.Stub.asInterface( |
| ServiceManager.getService("power")); |
| |
| // Get UpdateLock to suppress system-update related events (e.g. dialog show-up) |
| // during phone calls. |
| mUpdateLock = new UpdateLock("phone"); |
| |
| if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock); |
| |
| CallLogger callLogger = new CallLogger(this, new CallLogAsync()); |
| |
| callGatewayManager = CallGatewayManager.getInstance(); |
| |
| // Create the CallController singleton, which is the interface |
| // to the telephony layer for user-initiated telephony functionality |
| // (like making outgoing calls.) |
| callController = CallController.init(this, callLogger, callGatewayManager); |
| |
| // Create the CallerInfoCache singleton, which remembers custom ring tone and |
| // send-to-voicemail settings. |
| // |
| // The asynchronous caching will start just after this call. |
| callerInfoCache = CallerInfoCache.init(this); |
| |
| // Monitors call activity from the telephony layer |
| callStateMonitor = new CallStateMonitor(mCM); |
| |
| // Bluetooth manager |
| bluetoothManager = new BluetoothManager(); |
| |
| phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone()); |
| |
| // Create the CallNotifer singleton, which handles |
| // asynchronous events from the telephony layer (like |
| // launching the incoming-call UI when an incoming call comes |
| // in.) |
| notifier = CallNotifier.init(this, callLogger, callStateMonitor, bluetoothManager); |
| |
| PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED); |
| |
| // register for MMI/USSD |
| mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null); |
| |
| // register connection tracking to PhoneUtils |
| PhoneUtils.initializeConnectionHandler(mCM); |
| |
| // Register for misc other intent broadcasts. |
| IntentFilter intentFilter = |
| new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); |
| intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); |
| intentFilter.addAction(Intent.ACTION_DOCK_EVENT); |
| intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); |
| intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); |
| intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); |
| intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); |
| registerReceiver(mReceiver, intentFilter); |
| |
| //set the default values for the preferences in the phone. |
| PreferenceManager.setDefaultValues(this, R.xml.network_setting, false); |
| |
| PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false); |
| |
| // Make sure the audio mode (along with some |
| // audio-mode-related state of our own) is initialized |
| // correctly, given the current state of the phone. |
| PhoneUtils.setAudioMode(mCM); |
| } |
| |
| cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData(); |
| cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData(); |
| cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState(); |
| cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState(); |
| |
| // XXX pre-load the SimProvider so that it's ready |
| resolver.getType(Uri.parse("content://icc/adn")); |
| |
| // start with the default value to set the mute state. |
| mShouldRestoreMuteOnInCallResume = false; |
| |
| // TODO: Register for Cdma Information Records |
| // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null); |
| |
| // Read HAC settings and configure audio hardware |
| if (getResources().getBoolean(R.bool.hac_enabled)) { |
| int hac = android.provider.Settings.System.getInt( |
| getContentResolver(), |
| android.provider.Settings.System.HEARING_AID, |
| 0); |
| AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
| audioManager.setParameter(CallFeaturesSetting.HAC_KEY, hac != 0 ? |
| CallFeaturesSetting.HAC_VAL_ON : |
| CallFeaturesSetting.HAC_VAL_OFF); |
| } |
| } |
| |
| /** |
| * Returns the singleton instance of the PhoneApp. |
| */ |
| public static PhoneGlobals getInstance() { |
| if (sMe == null) { |
| throw new IllegalStateException("No PhoneGlobals here!"); |
| } |
| return sMe; |
| } |
| |
| /** |
| * Returns the singleton instance of the PhoneApp if running as the |
| * primary user, otherwise null. |
| */ |
| static PhoneGlobals getInstanceIfPrimary() { |
| return sMe; |
| } |
| |
| /** |
| * Returns the default phone. |
| * |
| * WARNING: This method should be used carefully, now that there may be multiple phones. |
| */ |
| public static Phone getPhone() { |
| return PhoneFactory.getDefaultPhone(); |
| } |
| |
| public static Phone getPhone(int subId) { |
| return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); |
| } |
| |
| /* package */ BluetoothManager getBluetoothManager() { |
| return bluetoothManager; |
| } |
| |
| /* package */ CallManager getCallManager() { |
| return mCM; |
| } |
| |
| /** |
| * Returns PendingIntent for hanging up ongoing phone call. This will typically be used from |
| * Notification context. |
| */ |
| /* package */ static PendingIntent createHangUpOngoingCallPendingIntent(Context context) { |
| Intent intent = new Intent(PhoneGlobals.ACTION_HANG_UP_ONGOING_CALL, null, |
| context, NotificationBroadcastReceiver.class); |
| return PendingIntent.getBroadcast(context, 0, intent, 0); |
| } |
| |
| boolean isSimPinEnabled() { |
| return mIsSimPinEnabled; |
| } |
| |
| boolean authenticateAgainstCachedSimPin(String pin) { |
| return (mCachedSimPin != null && mCachedSimPin.equals(pin)); |
| } |
| |
| void setCachedSimPin(String pin) { |
| mCachedSimPin = pin; |
| } |
| |
| /** |
| * Handles OTASP-related events from the telephony layer. |
| * |
| * While an OTASP call is active, the CallNotifier forwards |
| * OTASP-related telephony events to this method. |
| */ |
| void handleOtaspEvent(Message msg) { |
| if (DBG) Log.d(LOG_TAG, "handleOtaspEvent(message " + msg + ")..."); |
| |
| if (otaUtils == null) { |
| // We shouldn't be getting OTASP events without ever |
| // having started the OTASP call in the first place! |
| Log.w(LOG_TAG, "handleOtaEvents: got an event but otaUtils is null! " |
| + "message = " + msg); |
| return; |
| } |
| |
| otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj); |
| } |
| |
| /** |
| * Similarly, handle the disconnect event of an OTASP call |
| * by forwarding it to the OtaUtils instance. |
| */ |
| /* package */ void handleOtaspDisconnect() { |
| if (DBG) Log.d(LOG_TAG, "handleOtaspDisconnect()..."); |
| |
| if (otaUtils == null) { |
| // We shouldn't be getting OTASP events without ever |
| // having started the OTASP call in the first place! |
| Log.w(LOG_TAG, "handleOtaspDisconnect: otaUtils is null!"); |
| return; |
| } |
| |
| otaUtils.onOtaspDisconnect(); |
| } |
| |
| /** |
| * Sets the activity responsible for un-PUK-blocking the device |
| * so that we may close it when we receive a positive result. |
| * mPUKEntryActivity is also used to indicate to the device that |
| * we are trying to un-PUK-lock the phone. In other words, iff |
| * it is NOT null, then we are trying to unlock and waiting for |
| * the SIM to move to READY state. |
| * |
| * @param activity is the activity to close when PUK has |
| * finished unlocking. Can be set to null to indicate the unlock |
| * or SIM READYing process is over. |
| */ |
| void setPukEntryActivity(Activity activity) { |
| mPUKEntryActivity = activity; |
| } |
| |
| Activity getPUKEntryActivity() { |
| return mPUKEntryActivity; |
| } |
| |
| /** |
| * Sets the dialog responsible for notifying the user of un-PUK- |
| * blocking - SIM READYing progress, so that we may dismiss it |
| * when we receive a positive result. |
| * |
| * @param dialog indicates the progress dialog informing the user |
| * of the state of the device. Dismissed upon completion of |
| * READYing process |
| */ |
| void setPukEntryProgressDialog(ProgressDialog dialog) { |
| mPUKEntryProgressDialog = dialog; |
| } |
| |
| ProgressDialog getPUKEntryProgressDialog() { |
| return mPUKEntryProgressDialog; |
| } |
| |
| /** |
| * Controls whether or not the screen is allowed to sleep. |
| * |
| * Once sleep is allowed (WakeState is SLEEP), it will rely on the |
| * settings for the poke lock to determine when to timeout and let |
| * the device sleep {@link PhoneGlobals#setScreenTimeout}. |
| * |
| * @param ws tells the device to how to wake. |
| */ |
| /* package */ void requestWakeState(WakeState ws) { |
| if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")..."); |
| synchronized (this) { |
| if (mWakeState != ws) { |
| switch (ws) { |
| case PARTIAL: |
| // acquire the processor wake lock, and release the FULL |
| // lock if it is being held. |
| mPartialWakeLock.acquire(); |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| break; |
| case FULL: |
| // acquire the full wake lock, and release the PARTIAL |
| // lock if it is being held. |
| mWakeLock.acquire(); |
| if (mPartialWakeLock.isHeld()) { |
| mPartialWakeLock.release(); |
| } |
| break; |
| case SLEEP: |
| default: |
| // release both the PARTIAL and FULL locks. |
| if (mWakeLock.isHeld()) { |
| mWakeLock.release(); |
| } |
| if (mPartialWakeLock.isHeld()) { |
| mPartialWakeLock.release(); |
| } |
| break; |
| } |
| mWakeState = ws; |
| } |
| } |
| } |
| |
| /** |
| * If we are not currently keeping the screen on, then poke the power |
| * manager to wake up the screen for the user activity timeout duration. |
| */ |
| /* package */ void wakeUpScreen() { |
| synchronized (this) { |
| if (mWakeState == WakeState.SLEEP) { |
| if (DBG) Log.d(LOG_TAG, "pulse screen lock"); |
| mPowerManager.wakeUp(SystemClock.uptimeMillis()); |
| } |
| } |
| } |
| |
| /** |
| * Sets the wake state and screen timeout based on the current state |
| * of the phone, and the current state of the in-call UI. |
| * |
| * This method is a "UI Policy" wrapper around |
| * {@link PhoneGlobals#requestWakeState} and {@link PhoneGlobals#setScreenTimeout}. |
| * |
| * It's safe to call this method regardless of the state of the Phone |
| * (e.g. whether or not it's idle), and regardless of the state of the |
| * Phone UI (e.g. whether or not the InCallScreen is active.) |
| */ |
| /* package */ void updateWakeState() { |
| PhoneConstants.State state = mCM.getState(); |
| |
| // True if the speakerphone is in use. (If so, we *always* use |
| // the default timeout. Since the user is obviously not holding |
| // the phone up to his/her face, we don't need to worry about |
| // false touches, and thus don't need to turn the screen off so |
| // aggressively.) |
| // Note that we need to make a fresh call to this method any |
| // time the speaker state changes. (That happens in |
| // PhoneUtils.turnOnSpeaker().) |
| boolean isSpeakerInUse = (state == PhoneConstants.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this); |
| |
| // TODO (bug 1440854): The screen timeout *might* also need to |
| // depend on the bluetooth state, but this isn't as clear-cut as |
| // the speaker state (since while using BT it's common for the |
| // user to put the phone straight into a pocket, in which case the |
| // timeout should probably still be short.) |
| |
| // Decide whether to force the screen on or not. |
| // |
| // Force the screen to be on if the phone is ringing or dialing, |
| // or if we're displaying the "Call ended" UI for a connection in |
| // the "disconnected" state. |
| // However, if the phone is disconnected while the user is in the |
| // middle of selecting a quick response message, we should not force |
| // the screen to be on. |
| // |
| boolean isRinging = (state == PhoneConstants.State.RINGING); |
| boolean isDialing = (mCM.getFgPhone().getForegroundCall().getState() == Call.State.DIALING); |
| boolean keepScreenOn = isRinging || isDialing; |
| // keepScreenOn == true means we'll hold a full wake lock: |
| requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP); |
| } |
| |
| /** |
| * Manually pokes the PowerManager's userActivity method. Since we |
| * set the {@link WindowManager.LayoutParams#INPUT_FEATURE_DISABLE_USER_ACTIVITY} |
| * flag while the InCallScreen is active when there is no proximity sensor, |
| * we need to do this for touch events that really do count as user activity |
| * (like pressing any onscreen UI elements.) |
| */ |
| /* package */ void pokeUserActivity() { |
| if (VDBG) Log.d(LOG_TAG, "pokeUserActivity()..."); |
| mPowerManager.userActivity(SystemClock.uptimeMillis(), false); |
| } |
| |
| /** |
| * Notifies the phone app when the phone state changes. |
| * |
| * This method will updates various states inside Phone app (e.g. update-lock state, etc.) |
| */ |
| /* package */ void updatePhoneState(PhoneConstants.State state) { |
| if (state != mLastPhoneState) { |
| mLastPhoneState = state; |
| |
| // Try to acquire or release UpdateLock. |
| // |
| // Watch out: we don't release the lock here when the screen is still in foreground. |
| // At that time InCallScreen will release it on onPause(). |
| if (state != PhoneConstants.State.IDLE) { |
| // UpdateLock is a recursive lock, while we may get "acquire" request twice and |
| // "release" request once for a single call (RINGING + OFFHOOK and IDLE). |
| // We need to manually ensure the lock is just acquired once for each (and this |
| // will prevent other possible buggy situations too). |
| if (!mUpdateLock.isHeld()) { |
| mUpdateLock.acquire(); |
| } |
| } else { |
| if (mUpdateLock.isHeld()) { |
| mUpdateLock.release(); |
| } |
| } |
| } |
| } |
| |
| /* package */ PhoneConstants.State getPhoneState() { |
| return mLastPhoneState; |
| } |
| |
| KeyguardManager getKeyguardManager() { |
| return mKeyguardManager; |
| } |
| |
| private void onMMIComplete(AsyncResult r) { |
| if (VDBG) Log.d(LOG_TAG, "onMMIComplete()..."); |
| MmiCode mmiCode = (MmiCode) r.result; |
| PhoneUtils.displayMMIComplete(mmiCode.getPhone(), getInstance(), mmiCode, null, null); |
| } |
| |
| private void initForNewRadioTechnology(int phoneId) { |
| if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology..."); |
| |
| final Phone phone = PhoneFactory.getPhone(phoneId); |
| |
| if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { |
| // Create an instance of CdmaPhoneCallState and initialize it to IDLE |
| cdmaPhoneCallState = new CdmaPhoneCallState(); |
| cdmaPhoneCallState.CdmaPhoneCallStateInit(); |
| } |
| if (!TelephonyCapabilities.supportsOtasp(phone)) { |
| //Clean up OTA data in GSM/UMTS. It is valid only for CDMA |
| clearOtaState(); |
| } |
| |
| notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange(); |
| callStateMonitor.updateAfterRadioTechnologyChange(); |
| |
| // Update registration for ICC status after radio technology change |
| IccCard sim = phone.getIccCard(); |
| if (sim != null) { |
| if (DBG) Log.d(LOG_TAG, "Update registration for ICC status..."); |
| |
| //Register all events new to the new active phone |
| sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null); |
| } |
| } |
| |
| /** |
| * Receiver for misc intent broadcasts the Phone app cares about. |
| */ |
| private class PhoneAppBroadcastReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { |
| boolean enabled = System.getInt(getContentResolver(), |
| System.AIRPLANE_MODE_ON, 0) == 0; |
| PhoneUtils.setRadioPower(enabled); |
| } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { |
| int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, |
| SubscriptionManager.INVALID_SUBSCRIPTION_ID); |
| int phoneId = SubscriptionManager.getPhoneId(subId); |
| String state = intent.getStringExtra(PhoneConstants.STATE_KEY); |
| if (VDBG) { |
| Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"); |
| Log.d(LOG_TAG, "- state: " + state); |
| Log.d(LOG_TAG, "- reason: " |
| + intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY)); |
| Log.d(LOG_TAG, "- subId: " + subId); |
| Log.d(LOG_TAG, "- phoneId: " + phoneId); |
| } |
| Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ? |
| PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone(); |
| // The "data disconnected due to roaming" notification is shown |
| // if (a) you have the "data roaming" feature turned off, and |
| // (b) you just lost data connectivity because you're roaming. |
| boolean disconnectedDueToRoaming = |
| !phone.getDataRoamingEnabled() |
| && PhoneConstants.DataState.DISCONNECTED.equals(state) |
| && Phone.REASON_ROAMING_ON.equals( |
| intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY)); |
| if (mDataDisconnectedDueToRoaming != disconnectedDueToRoaming) { |
| mDataDisconnectedDueToRoaming = disconnectedDueToRoaming; |
| mHandler.sendEmptyMessage(disconnectedDueToRoaming |
| ? EVENT_DATA_ROAMING_DISCONNECTED : EVENT_DATA_ROAMING_OK); |
| } |
| } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) && |
| (mPUKEntryActivity != null)) { |
| // if an attempt to un-PUK-lock the device was made, while we're |
| // receiving this state change notification, notify the handler. |
| // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has |
| // been attempted. |
| mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED, |
| intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))); |
| } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) { |
| String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY); |
| int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, |
| SubscriptionManager.INVALID_PHONE_INDEX); |
| Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " (" + phoneId |
| + ") is active."); |
| initForNewRadioTechnology(phoneId); |
| } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) { |
| handleServiceStateChanged(intent); |
| } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { |
| if (TelephonyCapabilities.supportsEcm(mCM.getFgPhone())) { |
| Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp."); |
| // Start Emergency Callback Mode service |
| if (intent.getBooleanExtra("phoneinECMState", false)) { |
| context.startService(new Intent(context, |
| EmergencyCallbackModeService.class)); |
| } |
| } else { |
| // It doesn't make sense to get ACTION_EMERGENCY_CALLBACK_MODE_CHANGED |
| // on a device that doesn't support ECM in the first place. |
| Log.e(LOG_TAG, "Got ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, " |
| + "but ECM isn't supported for phone: " |
| + mCM.getFgPhone().getPhoneName()); |
| } |
| } else if (action.equals(Intent.ACTION_DOCK_EVENT)) { |
| mDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, |
| Intent.EXTRA_DOCK_STATE_UNDOCKED); |
| if (VDBG) Log.d(LOG_TAG, "ACTION_DOCK_EVENT -> mDockState = " + mDockState); |
| mHandler.sendMessage(mHandler.obtainMessage(EVENT_DOCK_STATE_CHANGED, 0)); |
| } |
| } |
| } |
| |
| /** |
| * Accepts broadcast Intents which will be prepared by {@link NotificationMgr} and thus |
| * sent from framework's notification mechanism (which is outside Phone context). |
| * This should be visible from outside, but shouldn't be in "exported" state. |
| * |
| * TODO: If possible merge this into PhoneAppBroadcastReceiver. |
| */ |
| public static class NotificationBroadcastReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| // TODO: use "if (VDBG)" here. |
| Log.d(LOG_TAG, "Broadcast from Notification: " + action); |
| |
| if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) { |
| PhoneUtils.hangup(PhoneGlobals.getInstance().mCM); |
| } else { |
| Log.w(LOG_TAG, "Received hang-up request from notification," |
| + " but there's no call the system can hang up."); |
| } |
| } |
| } |
| |
| private void handleServiceStateChanged(Intent intent) { |
| /** |
| * This used to handle updating EriTextWidgetProvider this routine |
| * and and listening for ACTION_SERVICE_STATE_CHANGED intents could |
| * be removed. But leaving just in case it might be needed in the near |
| * future. |
| */ |
| |
| // If service just returned, start sending out the queued messages |
| ServiceState ss = ServiceState.newFromBundle(intent.getExtras()); |
| |
| if (ss != null) { |
| int state = ss.getState(); |
| notificationMgr.updateNetworkSelection(state); |
| } |
| } |
| |
| public boolean isOtaCallInActiveState() { |
| boolean otaCallActive = false; |
| if (VDBG) Log.d(LOG_TAG, "- isOtaCallInActiveState " + otaCallActive); |
| return otaCallActive; |
| } |
| |
| public boolean isOtaCallInEndState() { |
| boolean otaCallEnded = false; |
| if (VDBG) Log.d(LOG_TAG, "- isOtaCallInEndState " + otaCallEnded); |
| return otaCallEnded; |
| } |
| |
| // it is safe to call clearOtaState() even if the InCallScreen isn't active |
| public void clearOtaState() { |
| if (DBG) Log.d(LOG_TAG, "- clearOtaState ..."); |
| if (otaUtils != null) { |
| otaUtils.cleanOtaScreen(true); |
| if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen"); |
| } |
| } |
| |
| // it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active |
| public void dismissOtaDialogs() { |
| if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ..."); |
| if (otaUtils != null) { |
| otaUtils.dismissAllOtaDialogs(); |
| if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs"); |
| } |
| } |
| |
| /** |
| * Triggers a refresh of the message waiting (voicemail) indicator. |
| * |
| * @param subId the subscription id we should refresh the notification for. |
| */ |
| public void refreshMwiIndicator(int subId) { |
| notificationMgr.refreshMwi(subId); |
| } |
| |
| /** |
| * "Call origin" may be used by Contacts app to specify where the phone call comes from. |
| * Currently, the only permitted value for this extra is {@link #ALLOWED_EXTRA_CALL_ORIGIN}. |
| * Any other value will be ignored, to make sure that malicious apps can't trick the in-call |
| * UI into launching some random other app after a call ends. |
| * |
| * TODO: make this more generic. Note that we should let the "origin" specify its package |
| * while we are now assuming it is "com.android.contacts" |
| */ |
| public static final String EXTRA_CALL_ORIGIN = "com.android.phone.CALL_ORIGIN"; |
| private static final String DEFAULT_CALL_ORIGIN_PACKAGE = "com.android.dialer"; |
| private static final String ALLOWED_EXTRA_CALL_ORIGIN = |
| "com.android.dialer.DialtactsActivity"; |
| /** |
| * Used to determine if the preserved call origin is fresh enough. |
| */ |
| private static final long CALL_ORIGIN_EXPIRATION_MILLIS = 30 * 1000; |
| } |