1b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/* 2b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 3b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * 4b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * you may not use this file except in compliance with the License. 6b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * You may obtain a copy of the License at 7b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * 8b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * 10b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * See the License for the specific language governing permissions and 14b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * limitations under the License. 15b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */ 16b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 17b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectpackage com.android.phone; 18b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 19b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.app.Activity; 20f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackbornimport android.app.ActivityManagerNative; 216c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brownimport android.app.AlertDialog; 22f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackbornimport android.app.AppOpsManager; 236c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brownimport android.app.Dialog; 24b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.BroadcastReceiver; 25b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Context; 266c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brownimport android.content.DialogInterface; 27b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.content.Intent; 287291a106eb69b365de60a9452c5fc31828e2db8eDavid Brownimport android.content.res.Configuration; 29b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.net.Uri; 30f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackbornimport android.os.Binder; 31b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.os.Bundle; 32ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawaimport android.os.Handler; 33ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawaimport android.os.Message; 34f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackbornimport android.os.RemoteException; 356b0efb615a5b1a65984192384c88965fa7dd06a0David Brownimport android.os.SystemProperties; 3691177a70c9024522c1d0d3ac570124998f09647aDianne Hackbornimport android.os.UserHandle; 37b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.telephony.PhoneNumberUtils; 38008791ca2a9a1406c2a9f7b39c794104d151b898Paul Bermanimport android.text.TextUtils; 39b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Projectimport android.util.Log; 40ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawaimport android.view.View; 41ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawaimport android.widget.ProgressBar; 42b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 436b0efb615a5b1a65984192384c88965fa7dd06a0David Brownimport com.android.internal.telephony.Phone; 44b0f85b4a78abead921c363f9c8e247d5bdd20c74Wink Savilleimport com.android.internal.telephony.PhoneConstants; 45f8a9fbe31969b4f97ea03d233684efc117888879Daisuke Miyakawaimport com.android.internal.telephony.TelephonyCapabilities; 461ca2b2b333a7c22b728d648d3592ee064762dd00Shaopeng Jia 47b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project/** 486b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and 496b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * broadcasts the ACTION_NEW_OUTGOING_CALL intent which allows other 506b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * applications to monitor, redirect, or prevent the outgoing call. 516b0efb615a5b1a65984192384c88965fa7dd06a0David Brown 526b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * After the other applications have had a chance to see the 536b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the 546b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * {@link OutgoingCallReceiver}, which passes the (possibly modified) 558ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * intent on to the {@link SipCallOptionHandler}, which will 568ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * ultimately start the call using the CallController.placeCall() API. 57b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project * 586b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * Emergency calls and calls where no number is present (like for a CDMA 596b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * "empty flash" or a nonexistent voicemail number) are exempt from being 606b0efb615a5b1a65984192384c88965fa7dd06a0David Brown * broadcast. 61b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project */ 626c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brownpublic class OutgoingCallBroadcaster extends Activity 636c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { 64b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 65b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS; 66b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project private static final String TAG = "OutgoingCallBroadcaster"; 676b0efb615a5b1a65984192384c88965fa7dd06a0David Brown private static final boolean DBG = 68a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 69a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown // Do not check in with VDBG = true, since that may write PII to the system log. 70a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown private static final boolean VDBG = false; 71b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 7275e3711d82d0c98444f6c438437cad41d862fca6David Brown public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE"; 73b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED"; 74b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI"; 752587d9c6437b680911e252efecc73788e876de76Chung-yih Wang public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT"; 762587d9c6437b680911e252efecc73788e876de76Chung-yih Wang public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI"; 778ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown public static final String EXTRA_ACTUAL_NUMBER_TO_DIAL = 788ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown "android.phone.extra.ACTUAL_NUMBER_TO_DIAL"; 79b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 80e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn /** 81008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * Identifier for intent extra for sending an empty Flash message for 82008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * CDMA networks. This message is used by the network to simulate a 83008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * press/depress of the "hookswitch" of a landline phone. Aka "empty flash". 84008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * 85008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * TODO: Receiving an intent extra to tell the phone to send this flash is a 86008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * temporary measure. To be replaced with an external ITelephony call in the future. 87008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app 88008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman * until this is replaced with the ITelephony API. 89008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman */ 90ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa public static final String EXTRA_SEND_EMPTY_FLASH = 91ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa "com.android.phone.extra.SEND_EMPTY_FLASH"; 92008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman 936c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Dialog IDs 946c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown private static final int DIALOG_NOT_VOICE_CAPABLE = 1; 956c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 96ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** Note message codes < 100 are reserved for the PhoneApp. */ 97ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa private static final int EVENT_OUTGOING_CALL_TIMEOUT = 101; 98ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa private static final int OUTGOING_CALL_TIMEOUT_THRESHOLD = 2000; // msec 99ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** 100ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * ProgressBar object with "spinner" style, which will be shown if we take more than 101ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * {@link #EVENT_OUTGOING_CALL_TIMEOUT} msec to handle the incoming Intent. 102ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa */ 103ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa private ProgressBar mWaitingSpinner; 104ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa private final Handler mHandler = new Handler() { 105ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa @Override 106ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa public void handleMessage(Message msg) { 107ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (msg.what == EVENT_OUTGOING_CALL_TIMEOUT) { 108ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.i(TAG, "Outgoing call takes too long. Showing the spinner."); 109ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa mWaitingSpinner.setVisibility(View.VISIBLE); 110ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } else { 111ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.wtf(TAG, "Unknown message id: " + msg.what); 112ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } 113ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } 114ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa }; 115ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa 116008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman /** 117e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting 118e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn * the InCallScreen if the broadcast has not been canceled, possibly with 119e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn * a modified phone number and optional provider info (uri + package name + remote views.) 120e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn */ 121e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn public class OutgoingCallReceiver extends BroadcastReceiver { 122e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn private static final String TAG = "OutgoingCallReceiver"; 123e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 124ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa @Override 125e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn public void onReceive(Context context, Intent intent) { 12608ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT); 127e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn doReceive(context, intent); 128ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself."); 129e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn finish(); 130e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 131008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman 132e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn public void doReceive(Context context, Intent intent) { 1336b0efb615a5b1a65984192384c88965fa7dd06a0David Brown if (DBG) Log.v(TAG, "doReceive: " + intent); 1346b0efb615a5b1a65984192384c88965fa7dd06a0David Brown 135e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn boolean alreadyCalled; 136e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn String number; 137e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn String originalUri; 138e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 139e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn alreadyCalled = intent.getBooleanExtra( 140e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false); 141e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if (alreadyCalled) { 1426b0efb615a5b1a65984192384c88965fa7dd06a0David Brown if (DBG) Log.v(TAG, "CALL already placed -- returning."); 143e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 144e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 145e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 1468ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData 1478ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // is used as the actual number to call. (If null, no call will be 1488ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // placed.) 1498ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown 150e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn number = getResultData(); 151a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'"); 1528ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown 153a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn final PhoneGlobals app = PhoneGlobals.getInstance(); 1548343169cc89621d46dce86449f5ee1ff5d3a4919John Wang 1557c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // OTASP-specific checks. 1567c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // TODO: This should probably all happen in 1577c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // OutgoingCallBroadcaster.onCreate(), since there's no reason to 1587c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // even bother with the NEW_OUTGOING_CALL broadcast if we're going 1597c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // to disallow the outgoing call anyway... 1608343169cc89621d46dce86449f5ee1ff5d3a4919John Wang if (TelephonyCapabilities.supportsOtasp(app.phone)) { 161e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn boolean activateState = (app.cdmaOtaScreenState.otaScreenState 162e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION); 163e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn boolean dialogState = (app.cdmaOtaScreenState.otaScreenState 164e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn == OtaUtils.CdmaOtaScreenState.OtaScreenState 165e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn .OTA_STATUS_SUCCESS_FAILURE_DLG); 166e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn boolean isOtaCallActive = false; 167e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 1687c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // TODO: Need cleaner way to check if OTA is active. 1697c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // Also, this check seems to be broken in one obscure case: if 1707c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // you interrupt an OTASP call by pressing Back then Skip, 1717c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // otaScreenState somehow gets left in either PROGRESS or 1727c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // LISTENING. 173e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if ((app.cdmaOtaScreenState.otaScreenState 174e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) 175e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn || (app.cdmaOtaScreenState.otaScreenState 176e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)) { 177e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn isOtaCallActive = true; 178e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 179e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 180e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if (activateState || dialogState) { 1817c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // The OTASP sequence is active, but either (1) the call 1827c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // hasn't started yet, or (2) the call has ended and we're 1837c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // showing the success/failure screen. In either of these 1847c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // cases it's OK to make a new outgoing call, but we need 1857c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // to take down any OTASP-related UI first. 186e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if (dialogState) app.dismissOtaDialogs(); 187e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn app.clearOtaState(); 188e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn app.clearInCallScreenMode(); 189e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } else if (isOtaCallActive) { 1907c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // The actual OTASP call is active. Don't allow new 1917c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown // outgoing calls at all from this state. 1927c8cb32060a685b2f20c1e0cf181fc392be840e0David Brown Log.w(TAG, "OTASP call is active: disallowing a new outgoing call."); 193e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 194e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 195e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 196e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 197e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if (number == null) { 1986b0efb615a5b1a65984192384c88965fa7dd06a0David Brown if (DBG) Log.v(TAG, "CALL cancelled (null number), returning..."); 199e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 2008343169cc89621d46dce86449f5ee1ff5d3a4919John Wang } else if (TelephonyCapabilities.supportsOtasp(app.phone) 201b0f85b4a78abead921c363f9c8e247d5bdd20c74Wink Saville && (app.phone.getState() != PhoneConstants.State.IDLE) 2028343169cc89621d46dce86449f5ee1ff5d3a4919John Wang && (app.phone.isOtaSpNumber(number))) { 2036b0efb615a5b1a65984192384c88965fa7dd06a0David Brown if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning."); 204e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 205a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, context)) { 206a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // Just like 3rd-party apps aren't allowed to place emergency 207a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // calls via the ACTION_CALL intent, we also don't allow 3rd 208a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite 209a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // an outgoing call into an emergency number. 210e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + "."); 211e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 212e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 213e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 214e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn originalUri = intent.getStringExtra( 215e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI); 216e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn if (originalUri == null) { 217e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning."); 218e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn return; 219e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 220e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 221e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn Uri uri = Uri.parse(originalUri); 222e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 2238ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // We already called convertKeypadLettersToDigits() and 2248ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // stripSeparators() way back in onCreate(), before we sent out the 2258ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // NEW_OUTGOING_CALL broadcast. But we need to do it again here 2268ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // too, since the number might have been modified/rewritten during 2278ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // the broadcast (and may now contain letters or separators again.) 2288ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown number = PhoneNumberUtils.convertKeypadLettersToDigits(number); 2298ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown number = PhoneNumberUtils.stripSeparators(number); 2305a57f046c7acae9867e7e4700ceec9fb07eb0a78Chung-yih Wang 2318ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown if (DBG) Log.v(TAG, "doReceive: proceeding with call..."); 232a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) Log.v(TAG, "- uri: " + uri); 233a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'"); 234e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 2358ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown startSipCallOptionHandler(context, intent, uri, number); 2361e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang } 2371e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang } 238e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 2398ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown /** 2408ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * Launch the SipCallOptionHandler, which is the next step(*) in the 2418ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * outgoing-call sequence after the outgoing call broadcast is 2428ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * complete. 2438ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * 2448ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * (*) We now know exactly what phone number we need to dial, so the next 2458ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * step is for the SipCallOptionHandler to decide which Phone type (SIP 2468ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * or PSTN) should be used. (Depending on the user's preferences, this 2478ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * decision may also involve popping up a dialog to ask the user to 2488ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * choose what type of call this should be.) 2498ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * 2508ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * @param context used for the startActivity() call 2518ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * 2528ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * @param intent the intent from the previous step of the outgoing-call 2538ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * sequence. Normally this will be the NEW_OUTGOING_CALL broadcast intent 2548ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * that came in to the OutgoingCallReceiver, although it can also be the 2558ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * original ACTION_CALL intent that started the whole sequence (in cases 2568ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * where we don't do the NEW_OUTGOING_CALL broadcast at all, like for 2578ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * emergency numbers or SIP addresses). 2588ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * 2598ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * @param uri the data URI from the original CALL intent, presumably either 2608ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * a tel: or sip: URI. For tel: URIs, note that the scheme-specific part 2618ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * does *not* necessarily have separators and keypad letters stripped (so 2628ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * we might see URIs like "tel:(650)%20555-1234" or "tel:1-800-GOOG-411" 2638ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * here.) 2648ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * 2658ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * @param number the actual number (or SIP address) to dial. This is 2668ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * guaranteed to be either a PSTN phone number with separators stripped 2678ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * out and keypad letters converted to digits (like "16505551234"), or a 2688ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown * raw SIP address (like "user@example.com"). 2698ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown */ 2708ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown private void startSipCallOptionHandler(Context context, Intent intent, 2711e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang Uri uri, String number) { 272a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) { 273a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown Log.i(TAG, "startSipCallOptionHandler..."); 274a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown Log.i(TAG, "- intent: " + intent); 275a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown Log.i(TAG, "- uri: " + uri); 276a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown Log.i(TAG, "- number: " + number); 277a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown } 27875e3711d82d0c98444f6c438437cad41d862fca6David Brown 27975e3711d82d0c98444f6c438437cad41d862fca6David Brown // Create a copy of the original CALL intent that started the whole 28075e3711d82d0c98444f6c438437cad41d862fca6David Brown // outgoing-call sequence. This intent will ultimately be passed to 28175e3711d82d0c98444f6c438437cad41d862fca6David Brown // CallController.placeCall() after the SipCallOptionHandler step. 28275e3711d82d0c98444f6c438437cad41d862fca6David Brown 2831e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang Intent newIntent = new Intent(Intent.ACTION_CALL, uri); 2848ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number); 2851e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent); 286e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn 28775e3711d82d0c98444f6c438437cad41d862fca6David Brown // Finally, launch the SipCallOptionHandler, with the copy of the 28875e3711d82d0c98444f6c438437cad41d862fca6David Brown // original CALL intent stashed away in the EXTRA_NEW_CALL_INTENT 28975e3711d82d0c98444f6c438437cad41d862fca6David Brown // extra. 2902587d9c6437b680911e252efecc73788e876de76Chung-yih Wang 29175e3711d82d0c98444f6c438437cad41d862fca6David Brown Intent selectPhoneIntent = new Intent(ACTION_SIP_SELECT_PHONE, uri); 2921e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang selectPhoneIntent.setClass(context, SipCallOptionHandler.class); 2931e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT, newIntent); 2941e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 295ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) { 296ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, "startSipCallOptionHandler(): " + 297ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa "calling startActivity: " + selectPhoneIntent); 298ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } 2991e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang context.startActivity(selectPhoneIntent); 3008ffe7a03a21441fa6d1f3c96a82c68f4ee8900ddDavid Brown // ...and see SipCallOptionHandler.onCreate() for the next step of the sequence. 301e118d10e688f3f98579253bc64de3c9422104350Dianne Hackborn } 302008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman 303ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** 304ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * This method is the single point of entry for the CALL intent, which is used (by built-in 305ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * apps like Contacts / Dialer, as well as 3rd-party apps) to initiate an outgoing voice call. 306ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 307ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 308ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa */ 309b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project @Override 310b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project protected void onCreate(Bundle icicle) { 311b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project super.onCreate(icicle); 312ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa setContentView(R.layout.outgoing_call_broadcaster); 313ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner); 3146c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 315b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project Intent intent = getIntent(); 316ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) { 317ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa final Configuration configuration = getResources().getConfiguration(); 318ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle); 319ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, " - getIntent() = " + intent); 320ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, " - configuration = " + configuration); 321ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } 3227291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown 3237291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown if (icicle != null) { 3247291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // A non-null icicle means that this activity is being 3257291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // re-initialized after previously being shut down. 3267291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // 3277291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // In practice this happens very rarely (because the lifetime 3287291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // of this activity is so short!), but it *can* happen if the 3297291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // framework detects a configuration change at exactly the 3307291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // right moment; see bug 2202413. 3317291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // 3327291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // In this case, do nothing. Our onCreate() method has already 3337291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // run once (with icicle==null the first time), which means 3347291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // that the NEW_OUTGOING_CALL broadcast for this new call has 3357291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // already been sent. 3367291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown Log.i(TAG, "onCreate: non-null icicle! " 3377291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown + "Bailing out, not sending NEW_OUTGOING_CALL broadcast..."); 3387291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown 3397291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // No need to finish() here, since the OutgoingCallReceiver from 3407291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // our original instance will do that. (It'll actually call 3417291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // finish() on our original instance, which apparently works fine 3427291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // even though the ActivityManager has already shut that instance 3437291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // down. And note that if we *do* call finish() here, that just 3447291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // results in an "ActivityManager: Duplicate finish request" 3457291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown // warning when the OutgoingCallReceiver runs.) 3467291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown 3477291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown return; 3487291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown } 349b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 3500ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa processIntent(intent); 3510ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa 3520ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa // isFinishing() return false when 1. broadcast is still ongoing, or 2. dialog is being 3530ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa // shown. Otherwise finish() is called inside processIntent(), is isFinishing() here will 3540ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa // return true. 3550ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa if (DBG) Log.v(TAG, "At the end of onCreate(). isFinishing(): " + isFinishing()); 356ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa } 357ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa 358ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** 3590ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa * Interprets a given Intent and starts something relevant to the Intent. 360ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 361ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * This method will handle three kinds of actions: 362ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 363ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - CALL (action for usual outgoing voice calls) 364ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth) 365ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.) 366ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 367ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * The exact behavior depends on the intent's data: 368ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 369ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - The most typical is a tel: URI, which we handle by starting the 370ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * NEW_OUTGOING_CALL broadcast. That broadcast eventually triggers 371ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * the sequence OutgoingCallReceiver -> SipCallOptionHandler -> 372ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * InCallScreen. 373ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 374ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - Or, with a sip: URI we skip the NEW_OUTGOING_CALL broadcast and 375ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * go directly to SipCallOptionHandler, which then leads to the 376ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * InCallScreen. 377ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 378ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - voicemail: URIs take the same path as regular tel: URIs. 379ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 380ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * Other special cases: 381ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 382ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - Outgoing calls are totally disallowed on non-voice-capable 383ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * devices (see handleNonVoiceCapable()). 384ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 385ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * - A CALL intent with the EXTRA_SEND_EMPTY_FLASH extra (and 386ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * presumably no data at all) means "send an empty flash" (which 387ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * is only meaningful on CDMA devices while a call is already 388ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * active.) 389ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * 390ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa */ 3910ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa private void processIntent(Intent intent) { 3920ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa if (DBG) { 3930ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread()); 3940ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa } 395ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa final Configuration configuration = getResources().getConfiguration(); 396ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa 3976c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Outgoing phone calls are only allowed on "voice-capable" devices. 398a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn if (!PhoneGlobals.sVoiceCapable) { 399ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.i(TAG, "This device is detected as non-voice-capable device."); 4000ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa handleNonVoiceCapable(intent); 4010ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 4026c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 4036c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 404b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project String action = intent.getAction(); 405b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project String number = PhoneNumberUtils.getNumberFromIntent(intent, this); 4068343169cc89621d46dce86449f5ee1ff5d3a4919John Wang // Check the number, don't convert for sip uri 4078343169cc89621d46dce86449f5ee1ff5d3a4919John Wang // TODO put uriNumber under PhoneNumberUtils 408b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (number != null) { 4098343169cc89621d46dce86449f5ee1ff5d3a4919John Wang if (!PhoneNumberUtils.isUriNumber(number)) { 4108343169cc89621d46dce86449f5ee1ff5d3a4919John Wang number = PhoneNumberUtils.convertKeypadLettersToDigits(number); 4118343169cc89621d46dce86449f5ee1ff5d3a4919John Wang number = PhoneNumberUtils.stripSeparators(number); 4128343169cc89621d46dce86449f5ee1ff5d3a4919John Wang } 413bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa } else { 414bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa Log.w(TAG, "The number obtained from Intent is null."); 415b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 416b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 417f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); 418f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn int launchedFromUid; 419f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn String launchedFromPackage; 420f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn try { 421f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid( 422f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn getActivityToken()); 423f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage( 424f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn getActivityToken()); 425f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn } catch (RemoteException e) { 426f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn launchedFromUid = -1; 427f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn launchedFromPackage = null; 428f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn } 429f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn if (appOps.noteOp(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage) 430f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn != AppOpsManager.MODE_ALLOWED) { 431f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package " 432f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn + launchedFromPackage); 433f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn finish(); 434f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn return; 435f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn } 436f88319b602881a0a67d80262b9842fbf42070ae3Dianne Hackborn 437a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // If true, this flag will indicate that the current call is a special kind 438a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // of call (most likely an emergency number) that 3rd parties aren't allowed 439a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // to intercept or affect in any way. (In that case, we start the call 440a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // immediately rather than going through the NEW_OUTGOING_CALL sequence.) 441b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project boolean callNow; 442b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 443b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (getClass().getName().equals(intent.getComponent().getClassName())) { 444b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // If we were launched directly from the OutgoingCallBroadcaster, 445b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // not one of its more privileged aliases, then make sure that 446b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // only the non-privileged actions are allowed. 447b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (!Intent.ACTION_CALL.equals(intent.getAction())) { 448b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL"); 449b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project intent.setAction(Intent.ACTION_CALL); 450b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 451b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 4525398ca30f57b928c2dd9bf175c5e083726279494Nicolas Catania 453a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // Check whether or not this is an emergency number, in order to 454a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // enforce the restriction that only the CALL_PRIVILEGED and 455a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // CALL_EMERGENCY intents are allowed to make emergency calls. 456a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // 457a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // (Note that the ACTION_CALL check below depends on the result of 458a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // isPotentialLocalEmergencyNumber() rather than just plain 459a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // isLocalEmergencyNumber(), to be 100% certain that we *don't* 460a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // allow 3rd party apps to make emergency calls by passing in an 461a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // "invalid" number like "9111234" that isn't technically an 462a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // emergency number but might still result in an emergency call 463a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // with some networks.) 464a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown final boolean isExactEmergencyNumber = 465a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(number, this); 466a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown final boolean isPotentialEmergencyNumber = 467a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, this); 468a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown if (VDBG) { 469ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, " - Checking restrictions for number '" + number + "':"); 470ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, " isExactEmergencyNumber = " + isExactEmergencyNumber); 471ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa Log.v(TAG, " isPotentialEmergencyNumber = " + isPotentialEmergencyNumber); 472a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown } 473a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown 474b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ 475e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // TODO: This code is redundant with some code in InCallScreen: refactor. 476b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) { 477a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // We're handling a CALL_PRIVILEGED intent, so we know this request came 478a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // from a trusted source (like the built-in dialer.) So even a number 479a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // that's *potentially* an emergency number can safely be promoted to 480a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from 481a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // the dialer if you really want to.) 482bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa if (isPotentialEmergencyNumber) { 483bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential" 484bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead."); 485bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa action = Intent.ACTION_CALL_EMERGENCY; 486bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa } else { 487bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa action = Intent.ACTION_CALL; 488bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa } 489ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action); 490b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project intent.setAction(action); 491b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 492b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 493b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (Intent.ACTION_CALL.equals(action)) { 494a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown if (isPotentialEmergencyNumber) { 495a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown Log.w(TAG, "Cannot call potential emergency number '" + number 496a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown + "' with CALL Intent " + intent + "."); 497a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown Log.i(TAG, "Launching default dialer instead..."); 498e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania 499e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania Intent invokeFrameworkDialer = new Intent(); 500e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania 501e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // TwelveKeyDialer is in a tab so we really want 502e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // DialtactsActivity. Build the intent 'manually' to 503e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // use the java resolver to find the dialer class (as 504e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // opposed to a Context which look up known android 505e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania // packages only) 506d404e2b0d04af7deaa8a15ae6c852995ec602fc2Chiao Cheng invokeFrameworkDialer.setClassName("com.android.dialer", 507d404e2b0d04af7deaa8a15ae6c852995ec602fc2Chiao Cheng "com.android.dialer.DialtactsActivity"); 508e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania invokeFrameworkDialer.setAction(Intent.ACTION_DIAL); 509e9d36b8a17aadd2271db030fc13d0fef01ded7c2Nicolas Catania invokeFrameworkDialer.setData(intent.getData()); 5106b0efb615a5b1a65984192384c88965fa7dd06a0David Brown 5116b0efb615a5b1a65984192384c88965fa7dd06a0David Brown if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " 5126b0efb615a5b1a65984192384c88965fa7dd06a0David Brown + invokeFrameworkDialer); 5130ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa startActivity(invokeFrameworkDialer); 5140ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa finish(); 5150ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 516b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 517b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project callNow = false; 518b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { 519325cc2ced6f1ff5fb1708abfcc5e9c73ac0cd962David Brown // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED 520325cc2ced6f1ff5fb1708abfcc5e9c73ac0cd962David Brown // intent that we just turned into a CALL_EMERGENCY intent (see 521325cc2ced6f1ff5fb1708abfcc5e9c73ac0cd962David Brown // above), or else it really is an CALL_EMERGENCY intent that 522325cc2ced6f1ff5fb1708abfcc5e9c73ac0cd962David Brown // came directly from some other app (e.g. the EmergencyDialer 523325cc2ced6f1ff5fb1708abfcc5e9c73ac0cd962David Brown // activity built in to the Phone app.) 524a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // Make sure it's at least *possible* that this is really an 525a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown // emergency number. 526a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown if (!isPotentialEmergencyNumber) { 527a42348fcc3a76ab0db9b710ee40042a73a0b9dacDavid Brown Log.w(TAG, "Cannot call non-potential-emergency number " + number 5280ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa + " with EMERGENCY_CALL Intent " + intent + "." 5290ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa + " Finish the Activity immediately."); 5300ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa finish(); 5310ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 532b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 533b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project callNow = true; 534b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } else { 5350ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately."); 5360ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa finish(); 5370ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 538b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 539b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 540b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // Make sure the screen is turned on. This is probably the right 541b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // thing to do, and more importantly it works around an issue in the 542b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // activity manager where we will not launch activities consistently 543b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // when the screen is off (since it is trying to keep them paused 544b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // and has... issues). 545b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // 546b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // Also, this ensures the device stays awake while doing the following 547b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // broadcast; technically we should be holding a wake lock here 548b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project // as well. 549a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn PhoneGlobals.getInstance().wakeUpScreen(); 5505398ca30f57b928c2dd9bf175c5e083726279494Nicolas Catania 551b1545c869ff7fa76ce3c3256b23e1b2b4411f483Daisuke Miyakawa // If number is null, we're probably trying to call a non-existent voicemail number, 552b1545c869ff7fa76ce3c3256b23e1b2b4411f483Daisuke Miyakawa // send an empty flash or something else is fishy. Whatever the problem, there's no 553b1545c869ff7fa76ce3c3256b23e1b2b4411f483Daisuke Miyakawa // number, so there's no point in allowing apps to modify the number. 554bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa if (TextUtils.isEmpty(number)) { 555008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) { 5567291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown Log.i(TAG, "onCreate: SEND_EMPTY_FLASH..."); 557a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone()); 5580ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa finish(); 5590ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 560008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman } else { 5617291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown Log.i(TAG, "onCreate: null or empty number, setting callNow=true..."); 562008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman callNow = true; 563008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman } 564008791ca2a9a1406c2a9f7b39c794104d151b898Paul Berman } 565b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 566b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project if (callNow) { 56758e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // This is a special kind of call (most likely an emergency number) 56858e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // that 3rd parties aren't allowed to intercept or affect in any way. 56958e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // So initiate the outgoing call immediately. 57058e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown 571bd3132083f51f3e056134372720d73118cc5467aDaisuke Miyakawa Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent); 57258e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown 57358e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // Initiate the outgoing call, and simultaneously launch the 57458e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // InCallScreen to display the in-call UI: 575a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn PhoneGlobals.getInstance().callController.placeCall(intent); 57658e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown 57758e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // Note we do *not* "return" here, but instead continue and 57858e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // send the ACTION_NEW_OUTGOING_CALL broadcast like for any 57958e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // other outgoing call. (But when the broadcast finally 58058e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // reaches the OutgoingCallReceiver, we'll know not to 58158e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // initiate the call again because of the presence of the 58258e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // EXTRA_ALREADY_CALLED extra.) 583b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 584b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project 585919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa // Remember the call origin so that users will be able to see an appropriate screen 586919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa // after the phone call. This should affect both phone calls and SIP calls. 587a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn final String callOrigin = intent.getStringExtra(PhoneGlobals.EXTRA_CALL_ORIGIN); 588919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa if (callOrigin != null) { 589ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) Log.v(TAG, " - Call origin is passed (" + callOrigin + ")"); 590a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn PhoneGlobals.getInstance().setLatestActiveCallOrigin(callOrigin); 591919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa } else { 592ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) Log.v(TAG, " - Call origin is not passed. Reset current one."); 593a1a9601840e50e18ff8ca4be9b888e592287577bDianne Hackborn PhoneGlobals.getInstance().resetLatestActiveCallOrigin(); 594919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa } 595919b11dcec9c7a80121e6cb4b9e624f59da43d55Daisuke Miyakawa 5961e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // For now, SIP calls will be processed directly without a 5971e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // NEW_OUTGOING_CALL broadcast. 5981e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // 5991e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // TODO: In the future, though, 3rd party apps *should* be allowed to 6001e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // intercept outgoing calls to SIP addresses as well. To do this, we should 6011e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // (1) update the NEW_OUTGOING_CALL intent documentation to explain this 6021e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // case, and (2) pass the outgoing SIP address by *not* overloading the 6031e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold 6041e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // the outgoing SIP address. (Be sure to document whether it's a URI or just 6051e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang // a plain address, whether it could be a tel: URI, etc.) 6061e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang Uri uri = intent.getData(); 6071e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang String scheme = uri.getScheme(); 608ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (Constants.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) { 6090ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa Log.i(TAG, "The requested number was detected as SIP call."); 6100ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa startSipCallOptionHandler(this, intent, uri, number); 6110ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa finish(); 6120ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa return; 61358e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown 61458e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // TODO: if there's ever a way for SIP calls to trigger a 61558e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // "callNow=true" case (see above), we'll need to handle that 61658e4707b6cc021a663deae3e614364545ec9ee6aDavid Brown // case here too (most likely by just doing nothing at all.) 6171e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang } 6181e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang 619b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); 6205398ca30f57b928c2dd9bf175c5e083726279494Nicolas Catania if (number != null) { 6215398ca30f57b928c2dd9bf175c5e083726279494Nicolas Catania broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 6225398ca30f57b928c2dd9bf175c5e083726279494Nicolas Catania } 623d94cc5e209bf871e4b6d5e84bcf6bd4e38d8ac65Nicolas Catania PhoneUtils.checkAndCopyPhoneProviderExtras(intent, broadcastIntent); 624b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow); 6251e5cdee4e88ac385e0049876d192d5659a71e4a6Chung-yih Wang broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString()); 626b967bf2d5ecbe57e3fe2d9d9389b203582b73fbfDaisuke Miyakawa // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app 627b967bf2d5ecbe57e3fe2d9d9389b203582b73fbfDaisuke Miyakawa // to intercept the outgoing call. 628b967bf2d5ecbe57e3fe2d9d9389b203582b73fbfDaisuke Miyakawa broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 629ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + "."); 63008ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa 63108ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa // Set a timer so that we can prepare for unexpected delay introduced by the broadcast. 63208ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa // If it takes too much time, the timer will show "waiting" spinner. 63308ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa // This message will be removed when OutgoingCallReceiver#onReceive() is called before the 63408ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa // timeout. 63508ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT, 63608ad9a53367c93ab76dbc1d2c7a58681a6533cf8Daisuke Miyakawa OUTGOING_CALL_TIMEOUT_THRESHOLD); 63791177a70c9024522c1d0d3ac570124998f09647aDianne Hackborn sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER, 63891177a70c9024522c1d0d3ac570124998f09647aDianne Hackborn PERMISSION, new OutgoingCallReceiver(), 6390ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa null, // scheduler 6400ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa Activity.RESULT_OK, // initialCode 6410ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa number, // initialData: initial value for the result data 6420ab033df74e18e0775929422db5f4fce204ec22aDaisuke Miyakawa null); // initialExtras 643b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project } 6447291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown 6456c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown @Override 6466c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown protected void onStop() { 6476c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Clean up (and dismiss if necessary) any managed dialogs. 6486c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // 6496c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // We don't do this in onPause() since we can be paused/resumed 6506c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // due to orientation changes (in which case we don't want to 6516c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // disturb the dialog), but we *do* need it here in onStop() to be 6526c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // sure we clean up if the user hits HOME while the dialog is up. 6536c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // 6546c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Note it's safe to call removeDialog() even if there's no dialog 6556c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // associated with that ID. 6566c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown removeDialog(DIALOG_NOT_VOICE_CAPABLE); 6576c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 6586c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown super.onStop(); 6596c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 6606c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 6616c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown /** 6626c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * Handle the specified CALL or CALL_* intent on a non-voice-capable 6636c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * device. 6646c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * 6656c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * This method may launch a different intent (if there's some useful 6666c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * alternative action to take), or otherwise display an error dialog, 6676c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown * and in either case will finish() the current activity when done. 6686c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown */ 6696c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown private void handleNonVoiceCapable(Intent intent) { 6706c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown if (DBG) Log.v(TAG, "handleNonVoiceCapable: handling " + intent 6716c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown + " on non-voice-capable device..."); 6726c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown String action = intent.getAction(); 6736c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown Uri uri = intent.getData(); 6746c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown String scheme = uri.getScheme(); 6756c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 6766c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Handle one special case: If this is a regular CALL to a tel: URI, 6776c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // bring up a UI letting you do something useful with the phone number 6786c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // (like "Add to contacts" if it isn't a contact yet.) 6796c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // 6806c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // This UI is provided by the contacts app in response to a DIAL 6816c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // intent, so we bring it up here by demoting this CALL to a DIAL and 6826c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // relaunching. 6836c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // 6846c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // TODO: it's strange and unintuitive to manually launch a DIAL intent 6856c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // to do this; it would be cleaner to have some shared UI component 6866c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // that we could bring up directly. (But for now at least, since both 6876c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // Contacts and Phone are built-in apps, this implementation is fine.) 6886c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 68965454c803eb305c4740885ad4995a871b034a58aDavid Brown if (Intent.ACTION_CALL.equals(action) && (Constants.SCHEME_TEL.equals(scheme))) { 6906c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown Intent newIntent = new Intent(Intent.ACTION_DIAL, uri); 6916c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown if (DBG) Log.v(TAG, "- relaunching as a DIAL intent: " + newIntent); 6926c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown startActivity(newIntent); 6936c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown finish(); 6946c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown return; 6956c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 6966c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 6976c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // In all other cases, just show a generic "voice calling not 6986c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // supported" dialog. 6996c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown showDialog(DIALOG_NOT_VOICE_CAPABLE); 7006c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // ...and we'll eventually finish() when the user dismisses 7016c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // or cancels the dialog. 7026c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 7036c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 7046c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown @Override 7056c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown protected Dialog onCreateDialog(int id) { 7066c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown Dialog dialog; 7076c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown switch(id) { 7086c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown case DIALOG_NOT_VOICE_CAPABLE: 7096c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown dialog = new AlertDialog.Builder(this) 7106c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown .setTitle(R.string.not_voice_capable) 71150480073bf2394d5b065084f46b2223ab90fb7dcDaisuke Miyakawa .setIconAttribute(android.R.attr.alertDialogIcon) 7126c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown .setPositiveButton(android.R.string.ok, this) 7136c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown .setOnCancelListener(this) 7146c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown .create(); 7156c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown break; 7166c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown default: 7176c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown Log.w(TAG, "onCreateDialog: unexpected ID " + id); 7186c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown dialog = null; 7196c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown break; 7206c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 7216c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown return dialog; 7226c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 7236c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 724ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** DialogInterface.OnClickListener implementation */ 725ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa @Override 7266c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown public void onClick(DialogInterface dialog, int id) { 7276c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far 7286c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // at least), and its only button is "OK". 7296c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown finish(); 7306c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 7316c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 732ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** DialogInterface.OnCancelListener implementation */ 733ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa @Override 7346c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown public void onCancel(DialogInterface dialog) { 7356c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far 7366c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown // at least), and canceling it is just like hitting "OK". 7376c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown finish(); 7386c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown } 7396c5cf46a2a31f0bffe9df36da8922971f7ee296bDavid Brown 740ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa /** 741ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * Implement onConfigurationChanged() purely for debugging purposes, 742ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * to make sure that the android:configChanges element in our manifest 743ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa * is working properly. 744ac9a757e713579e7ae5728e22e884459682cbd50Daisuke Miyakawa */ 7457291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown @Override 7467291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown public void onConfigurationChanged(Configuration newConfig) { 7477291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown super.onConfigurationChanged(newConfig); 7487291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig); 7497291a106eb69b365de60a9452c5fc31828e2db8eDavid Brown } 750b16363f5fc191b769e88c364243e34b92eb22688The Android Open Source Project} 751