/* * 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; }