17d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon/* 27d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Copyright (C) 2008 The Android Open Source Project 37d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 47d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License"); 57d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * you may not use this file except in compliance with the License. 67d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * You may obtain a copy of the License at 77d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 87d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * http://www.apache.org/licenses/LICENSE-2.0 97d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Unless required by applicable law or agreed to in writing, software 117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS, 127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * See the License for the specific language governing permissions and 147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * limitations under the License. 157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonpackage com.android.phone; 187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.app.Activity; 207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.app.ActivityManagerNative; 217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.app.AlertDialog; 227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.app.AppOpsManager; 237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.app.Dialog; 247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.BroadcastReceiver; 257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.Context; 267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.DialogInterface; 277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.Intent; 287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.res.Configuration; 29d3105fe2f9f544fccd2cddbb1ab977dfe157b56eYorke Leeimport android.content.res.Resources; 307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.net.Uri; 317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.Bundle; 327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.Handler; 337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.Message; 347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.RemoteException; 357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.SystemProperties; 367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.UserHandle; 374d45d1cf58a2003378fd35912d6d73a00001bf06Tyler Gunnimport android.telecom.PhoneAccount; 387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.telephony.PhoneNumberUtils; 397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.text.TextUtils; 407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.util.Log; 417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.view.View; 427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.widget.ProgressBar; 437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.PhoneConstants; 457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.TelephonyCapabilities; 467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon/** 48c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the 49c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which 50c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * contains the phone number being dialed. Applications can use this intent to (1) see which numbers 51c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call 52c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * from being placed. 53c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * 547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * After the other applications have had a chance to see the 557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the 567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * {@link OutgoingCallReceiver}, which passes the (possibly modified) 577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * intent on to the {@link SipCallOptionHandler}, which will 587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * ultimately start the call using the CallController.placeCall() API. 597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 60c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail 61c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * number) are exempt from being broadcast. 62c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * Calls to emergency numbers are still broadcast for informative purposes. The call is placed 63c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented. 647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonpublic class OutgoingCallBroadcaster extends Activity 667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { 677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final String TAG = "OutgoingCallBroadcaster"; 697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final boolean DBG = 707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Do not check in with VDBG = true, since that may write PII to the system log. 727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final boolean VDBG = false; 737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 74c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon /** Required permission for any app that wants to consume ACTION_NEW_OUTGOING_CALL. */ 75c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS; 76c65769e202f771137686574bb097ee3f7aaccdfaSantos Cordon 777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE"; 787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED"; 797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI"; 807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT"; 817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI"; 827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_ACTUAL_NUMBER_TO_DIAL = 837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon "android.phone.extra.ACTUAL_NUMBER_TO_DIAL"; 84bfb6832c4bcc535aef12b07ac5bcf4a973c65d00Sailesh Nepal public static final String EXTRA_THIRD_PARTY_CALL_COMPONENT = 85bfb6832c4bcc535aef12b07ac5bcf4a973c65d00Sailesh Nepal "android.phone.extra.THIRD_PARTY_CALL_COMPONENT"; 867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Identifier for intent extra for sending an empty Flash message for 897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * CDMA networks. This message is used by the network to simulate a 907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * press/depress of the "hookswitch" of a landline phone. Aka "empty flash". 917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * TODO: Receiving an intent extra to tell the phone to send this flash is a 937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * temporary measure. To be replaced with an external ITelephony call in the future. 947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app 957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * until this is replaced with the ITelephony API. 967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final String EXTRA_SEND_EMPTY_FLASH = 987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon "com.android.phone.extra.SEND_EMPTY_FLASH"; 997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Dialog IDs 1017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int DIALOG_NOT_VOICE_CAPABLE = 1; 1027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** Note message codes < 100 are reserved for the PhoneApp. */ 1047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int EVENT_OUTGOING_CALL_TIMEOUT = 101; 1057d86bec92eaa062ae56ddb906199839da7805022Santos Cordon private static final int EVENT_DELAYED_FINISH = 102; 1067d86bec92eaa062ae56ddb906199839da7805022Santos Cordon 1077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int OUTGOING_CALL_TIMEOUT_THRESHOLD = 2000; // msec 1087d86bec92eaa062ae56ddb906199839da7805022Santos Cordon private static final int DELAYED_FINISH_TIME = 2000; // msec 1097d86bec92eaa062ae56ddb906199839da7805022Santos Cordon 1107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 1117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * ProgressBar object with "spinner" style, which will be shown if we take more than 1127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * {@link #EVENT_OUTGOING_CALL_TIMEOUT} msec to handle the incoming Intent. 1137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 1147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private ProgressBar mWaitingSpinner; 1157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private final Handler mHandler = new Handler() { 1167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 1177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void handleMessage(Message msg) { 1187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (msg.what == EVENT_OUTGOING_CALL_TIMEOUT) { 1197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "Outgoing call takes too long. Showing the spinner."); 1207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mWaitingSpinner.setVisibility(View.VISIBLE); 1217d86bec92eaa062ae56ddb906199839da7805022Santos Cordon } else if (msg.what == EVENT_DELAYED_FINISH) { 1227d86bec92eaa062ae56ddb906199839da7805022Santos Cordon finish(); 1237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 1247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.wtf(TAG, "Unknown message id: " + msg.what); 1257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon }; 1287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 1307d86bec92eaa062ae56ddb906199839da7805022Santos Cordon * Starts the delayed finish() of OutgoingCallBroadcaster in order to give the UI 1317d86bec92eaa062ae56ddb906199839da7805022Santos Cordon * some time to start up. 1327d86bec92eaa062ae56ddb906199839da7805022Santos Cordon */ 1337d86bec92eaa062ae56ddb906199839da7805022Santos Cordon private void startDelayedFinish() { 1347d86bec92eaa062ae56ddb906199839da7805022Santos Cordon mHandler.sendEmptyMessageDelayed(EVENT_DELAYED_FINISH, DELAYED_FINISH_TIME); 1357d86bec92eaa062ae56ddb906199839da7805022Santos Cordon } 1367d86bec92eaa062ae56ddb906199839da7805022Santos Cordon 1377d86bec92eaa062ae56ddb906199839da7805022Santos Cordon /** 1387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting 1397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * the InCallScreen if the broadcast has not been canceled, possibly with 1407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * a modified phone number and optional provider info (uri + package name + remote views.) 1417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 1427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public class OutgoingCallReceiver extends BroadcastReceiver { 1437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final String TAG = "OutgoingCallReceiver"; 1447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 1467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void onReceive(Context context, Intent intent) { 1477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT); 1484ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon final boolean isAttemptingCall = doReceive(context, intent); 1497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself."); 1507d86bec92eaa062ae56ddb906199839da7805022Santos Cordon 1517d86bec92eaa062ae56ddb906199839da7805022Santos Cordon // We cannot finish the activity immediately here because it would cause the temporary 1527d86bec92eaa062ae56ddb906199839da7805022Santos Cordon // black screen of OutgoingBroadcaster to go away and we need it to stay up until the 1537d86bec92eaa062ae56ddb906199839da7805022Santos Cordon // UI (in a different process) has time to come up. 1544ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon // However, if we know we are not attemping a call, we need to finish the activity 1554ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon // immediately so that subsequent CALL intents will retrigger a new 1564ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon // OutgoingCallReceiver. see b/10857203 1574ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon if (isAttemptingCall) { 1584ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon startDelayedFinish(); 1594ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon } else { 1604ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon finish(); 1614ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon } 1627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1644ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon 1654ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon /** 1664ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon * Handes receipt of ordered new_outgoing_call intent. Verifies that the return from the 1674ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon * ordered intent is valid. 1684ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon * @return true if the call is being attempted; false if we are canceling the call. 1694ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon */ 1704ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon public boolean doReceive(Context context, Intent intent) { 1717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "doReceive: " + intent); 1727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean alreadyCalled; 1747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String number; 1757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String originalUri; 1767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon alreadyCalled = intent.getBooleanExtra( 1787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false); 1797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (alreadyCalled) { 1807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "CALL already placed -- returning."); 1814ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 1827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData 1857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // is used as the actual number to call. (If null, no call will be 1867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // placed.) 1877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number = getResultData(); 1897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'"); 1907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon final PhoneGlobals app = PhoneGlobals.getInstance(); 1927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // OTASP-specific checks. 1947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO: This should probably all happen in 1957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // OutgoingCallBroadcaster.onCreate(), since there's no reason to 1967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // even bother with the NEW_OUTGOING_CALL broadcast if we're going 1977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // to disallow the outgoing call anyway... 1987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (TelephonyCapabilities.supportsOtasp(app.phone)) { 1997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean activateState = (app.cdmaOtaScreenState.otaScreenState 2007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION); 2017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean dialogState = (app.cdmaOtaScreenState.otaScreenState 2027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon == OtaUtils.CdmaOtaScreenState.OtaScreenState 2037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .OTA_STATUS_SUCCESS_FAILURE_DLG); 2047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean isOtaCallActive = false; 2057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO: Need cleaner way to check if OTA is active. 2077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Also, this check seems to be broken in one obscure case: if 2087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // you interrupt an OTASP call by pressing Back then Skip, 2097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // otaScreenState somehow gets left in either PROGRESS or 2107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // LISTENING. 2117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if ((app.cdmaOtaScreenState.otaScreenState 2127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) 2137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon || (app.cdmaOtaScreenState.otaScreenState 2147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)) { 2157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon isOtaCallActive = true; 2167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (activateState || dialogState) { 2197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // The OTASP sequence is active, but either (1) the call 2207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // hasn't started yet, or (2) the call has ended and we're 2217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // showing the success/failure screen. In either of these 2227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // cases it's OK to make a new outgoing call, but we need 2237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // to take down any OTASP-related UI first. 2247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (dialogState) app.dismissOtaDialogs(); 2257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon app.clearOtaState(); 2267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else if (isOtaCallActive) { 2277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // The actual OTASP call is active. Don't allow new 2287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // outgoing calls at all from this state. 2297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "OTASP call is active: disallowing a new outgoing call."); 2304ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 2317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (number == null) { 2357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "CALL cancelled (null number), returning..."); 2364ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 2377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else if (TelephonyCapabilities.supportsOtasp(app.phone) 2387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon && (app.phone.getState() != PhoneConstants.State.IDLE) 2397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon && (app.phone.isOtaSpNumber(number))) { 2407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning."); 2414ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 24236bb2546cbc86a4e6d338c3c31bbd02c4602b5e2Yorke Lee } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number)) { 2437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Just like 3rd-party apps aren't allowed to place emergency 2447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // calls via the ACTION_CALL intent, we also don't allow 3rd 2457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite 2467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // an outgoing call into an emergency number. 2477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + "."); 2484ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 2497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon originalUri = intent.getStringExtra( 2527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI); 2537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (originalUri == null) { 2547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning."); 2554ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return false; 2567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Uri uri = Uri.parse(originalUri); 2597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // We already called convertKeypadLettersToDigits() and 2617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // stripSeparators() way back in onCreate(), before we sent out the 2627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // NEW_OUTGOING_CALL broadcast. But we need to do it again here 2637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // too, since the number might have been modified/rewritten during 2647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // the broadcast (and may now contain letters or separators again.) 2657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number = PhoneNumberUtils.convertKeypadLettersToDigits(number); 2667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number = PhoneNumberUtils.stripSeparators(number); 2677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "doReceive: proceeding with call..."); 2697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (VDBG) Log.v(TAG, "- uri: " + uri); 2707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'"); 2717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startSipCallOptionHandler(context, intent, uri, number); 2734ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon 2744ebe45dd5aea839774bfb5adefd635356ffe2154Santos Cordon return true; 2757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 2797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Launch the SipCallOptionHandler, which is the next step(*) in the 2807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * outgoing-call sequence after the outgoing call broadcast is 2817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * complete. 2827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 2837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * (*) We now know exactly what phone number we need to dial, so the next 2847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * step is for the SipCallOptionHandler to decide which Phone type (SIP 2857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * or PSTN) should be used. (Depending on the user's preferences, this 2867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * decision may also involve popping up a dialog to ask the user to 2877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * choose what type of call this should be.) 2887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 2897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * @param context used for the startActivity() call 2907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 2917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * @param intent the intent from the previous step of the outgoing-call 2927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * sequence. Normally this will be the NEW_OUTGOING_CALL broadcast intent 2937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * that came in to the OutgoingCallReceiver, although it can also be the 2947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * original ACTION_CALL intent that started the whole sequence (in cases 2957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * where we don't do the NEW_OUTGOING_CALL broadcast at all, like for 2967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * emergency numbers or SIP addresses). 2977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 2987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * @param uri the data URI from the original CALL intent, presumably either 2997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * a tel: or sip: URI. For tel: URIs, note that the scheme-specific part 3007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * does *not* necessarily have separators and keypad letters stripped (so 3017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * we might see URIs like "tel:(650)%20555-1234" or "tel:1-800-GOOG-411" 3027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * here.) 3037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * @param number the actual number (or SIP address) to dial. This is 3057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * guaranteed to be either a PSTN phone number with separators stripped 3067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * out and keypad letters converted to digits (like "16505551234"), or a 3077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * raw SIP address (like "user@example.com"). 3087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 3097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void startSipCallOptionHandler(Context context, Intent intent, 3107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Uri uri, String number) { 311da120f4e3d32ca97c5b4c21d6c505d834a29ab8dSantos Cordon // TODO: Remove this code. 3127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 3157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method is the single point of entry for the CALL intent, which is used (by built-in 3167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * apps like Contacts / Dialer, as well as 3rd-party apps) to initiate an outgoing voice call. 3177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 3207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 3217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon protected void onCreate(Bundle icicle) { 3227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon super.onCreate(icicle); 3237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon setContentView(R.layout.outgoing_call_broadcaster); 3247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner); 3257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Intent intent = getIntent(); 3277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) { 3287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon final Configuration configuration = getResources().getConfiguration(); 3297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle); 3307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, " - getIntent() = " + intent); 3317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, " - configuration = " + configuration); 3327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (icicle != null) { 3357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // A non-null icicle means that this activity is being 3367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // re-initialized after previously being shut down. 3377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 3387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // In practice this happens very rarely (because the lifetime 3397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // of this activity is so short!), but it *can* happen if the 3407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // framework detects a configuration change at exactly the 3417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // right moment; see bug 2202413. 3427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 3437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // In this case, do nothing. Our onCreate() method has already 3447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // run once (with icicle==null the first time), which means 3457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // that the NEW_OUTGOING_CALL broadcast for this new call has 3467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // already been sent. 3477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "onCreate: non-null icicle! " 3487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + "Bailing out, not sending NEW_OUTGOING_CALL broadcast..."); 3497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // No need to finish() here, since the OutgoingCallReceiver from 3517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // our original instance will do that. (It'll actually call 3527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // finish() on our original instance, which apparently works fine 3537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // even though the ActivityManager has already shut that instance 3547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // down. And note that if we *do* call finish() here, that just 3557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // results in an "ActivityManager: Duplicate finish request" 3567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // warning when the OutgoingCallReceiver runs.) 3577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 3597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon processIntent(intent); 3627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // isFinishing() return false when 1. broadcast is still ongoing, or 2. dialog is being 3647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // shown. Otherwise finish() is called inside processIntent(), is isFinishing() here will 3657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // return true. 3667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "At the end of onCreate(). isFinishing(): " + isFinishing()); 3677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 3707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Interprets a given Intent and starts something relevant to the Intent. 3717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method will handle three kinds of actions: 3737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - CALL (action for usual outgoing voice calls) 3757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth) 3767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.) 3777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * The exact behavior depends on the intent's data: 3797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - The most typical is a tel: URI, which we handle by starting the 3817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * NEW_OUTGOING_CALL broadcast. That broadcast eventually triggers 3827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * the sequence OutgoingCallReceiver -> SipCallOptionHandler -> 3837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * InCallScreen. 3847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Or, with a sip: URI we skip the NEW_OUTGOING_CALL broadcast and 3867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * go directly to SipCallOptionHandler, which then leads to the 3877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * InCallScreen. 3887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - voicemail: URIs take the same path as regular tel: URIs. 3907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Other special cases: 3927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Outgoing calls are totally disallowed on non-voice-capable 3947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * devices (see handleNonVoiceCapable()). 3957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - A CALL intent with the EXTRA_SEND_EMPTY_FLASH extra (and 3977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * presumably no data at all) means "send an empty flash" (which 3987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * is only meaningful on CDMA devices while a call is already 3997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * active.) 4007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 4017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 4027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void processIntent(Intent intent) { 4037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) { 4047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread()); 4057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon final Configuration configuration = getResources().getConfiguration(); 4077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Outgoing phone calls are only allowed on "voice-capable" devices. 4097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (!PhoneGlobals.sVoiceCapable) { 4107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "This device is detected as non-voice-capable device."); 4117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon handleNonVoiceCapable(intent); 4127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 4137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String action = intent.getAction(); 4167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String number = PhoneNumberUtils.getNumberFromIntent(intent, this); 4177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Check the number, don't convert for sip uri 4187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO put uriNumber under PhoneNumberUtils 4197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (number != null) { 4207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (!PhoneNumberUtils.isUriNumber(number)) { 4217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number = PhoneNumberUtils.convertKeypadLettersToDigits(number); 4227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number = PhoneNumberUtils.stripSeparators(number); 4237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 4257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "The number obtained from Intent is null."); 4267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); 4297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon int launchedFromUid; 4307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String launchedFromPackage; 4317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon try { 4327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid( 4337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon getActivityToken()); 4347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage( 4357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon getActivityToken()); 4367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } catch (RemoteException e) { 4377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon launchedFromUid = -1; 4387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon launchedFromPackage = null; 4397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 440829b26ee6d940957aa48f964e94db3ce6d9ccbe7Dianne Hackborn if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage) 4417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon != AppOpsManager.MODE_ALLOWED) { 4427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package " 4437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + launchedFromPackage); 4447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 4457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 4467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // If true, this flag will indicate that the current call is a special kind 4497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // of call (most likely an emergency number) that 3rd parties aren't allowed 4507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // to intercept or affect in any way. (In that case, we start the call 4517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // immediately rather than going through the NEW_OUTGOING_CALL sequence.) 4527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean callNow; 4537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (getClass().getName().equals(intent.getComponent().getClassName())) { 4557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // If we were launched directly from the OutgoingCallBroadcaster, 4567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // not one of its more privileged aliases, then make sure that 4577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // only the non-privileged actions are allowed. 4587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (!Intent.ACTION_CALL.equals(intent.getAction())) { 4597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL"); 4607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon intent.setAction(Intent.ACTION_CALL); 4617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Check whether or not this is an emergency number, in order to 4657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // enforce the restriction that only the CALL_PRIVILEGED and 4667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // CALL_EMERGENCY intents are allowed to make emergency calls. 4677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 4687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (Note that the ACTION_CALL check below depends on the result of 4697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // isPotentialLocalEmergencyNumber() rather than just plain 4707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // isLocalEmergencyNumber(), to be 100% certain that we *don't* 4717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // allow 3rd party apps to make emergency calls by passing in an 4727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // "invalid" number like "9111234" that isn't technically an 4737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // emergency number but might still result in an emergency call 4747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // with some networks.) 4757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon final boolean isExactEmergencyNumber = 47636bb2546cbc86a4e6d338c3c31bbd02c4602b5e2Yorke Lee (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(this, number); 4777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon final boolean isPotentialEmergencyNumber = 47836bb2546cbc86a4e6d338c3c31bbd02c4602b5e2Yorke Lee (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(this, number); 4797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (VDBG) { 4807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, " - Checking restrictions for number '" + number + "':"); 4817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, " isExactEmergencyNumber = " + isExactEmergencyNumber); 4827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.v(TAG, " isPotentialEmergencyNumber = " + isPotentialEmergencyNumber); 4837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */ 4867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO: This code is redundant with some code in InCallScreen: refactor. 4877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) { 4887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // We're handling a CALL_PRIVILEGED intent, so we know this request came 4897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // from a trusted source (like the built-in dialer.) So even a number 4907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // that's *potentially* an emergency number can safely be promoted to 4917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from 4927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // the dialer if you really want to.) 4937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (isPotentialEmergencyNumber) { 4947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential" 4957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead."); 4967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon action = Intent.ACTION_CALL_EMERGENCY; 4977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 4987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon action = Intent.ACTION_CALL; 4997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action); 5017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon intent.setAction(action); 5027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (Intent.ACTION_CALL.equals(action)) { 5057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (isPotentialEmergencyNumber) { 5067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "Cannot call potential emergency number '" + number 5077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + "' with CALL Intent " + intent + "."); 5087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "Launching default dialer instead..."); 5097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Intent invokeFrameworkDialer = new Intent(); 5117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TwelveKeyDialer is in a tab so we really want 5137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // DialtactsActivity. Build the intent 'manually' to 5147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // use the java resolver to find the dialer class (as 5157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // opposed to a Context which look up known android 5167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // packages only) 517d3105fe2f9f544fccd2cddbb1ab977dfe157b56eYorke Lee final Resources resources = getResources(); 518d3105fe2f9f544fccd2cddbb1ab977dfe157b56eYorke Lee invokeFrameworkDialer.setClassName( 519d3105fe2f9f544fccd2cddbb1ab977dfe157b56eYorke Lee resources.getString(R.string.ui_default_package), 520d3105fe2f9f544fccd2cddbb1ab977dfe157b56eYorke Lee resources.getString(R.string.dialer_default_class)); 5217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon invokeFrameworkDialer.setAction(Intent.ACTION_DIAL); 5227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon invokeFrameworkDialer.setData(intent.getData()); 5237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " 5247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + invokeFrameworkDialer); 5257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startActivity(invokeFrameworkDialer); 5267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 5277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 5287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon callNow = false; 5307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) { 5317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED 5327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // intent that we just turned into a CALL_EMERGENCY intent (see 5337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // above), or else it really is an CALL_EMERGENCY intent that 5347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // came directly from some other app (e.g. the EmergencyDialer 5357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // activity built in to the Phone app.) 5367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Make sure it's at least *possible* that this is really an 5377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // emergency number. 5387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (!isPotentialEmergencyNumber) { 5397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "Cannot call non-potential-emergency number " + number 5407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + " with EMERGENCY_CALL Intent " + intent + "." 5417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + " Finish the Activity immediately."); 5427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 5437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 5447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon callNow = true; 5467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 5477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately."); 5487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 5497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 5507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Make sure the screen is turned on. This is probably the right 5537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // thing to do, and more importantly it works around an issue in the 5547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // activity manager where we will not launch activities consistently 5557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // when the screen is off (since it is trying to keep them paused 5567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // and has... issues). 5577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 5587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Also, this ensures the device stays awake while doing the following 5597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // broadcast; technically we should be holding a wake lock here 5607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // as well. 5617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PhoneGlobals.getInstance().wakeUpScreen(); 5627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // If number is null, we're probably trying to call a non-existent voicemail number, 5647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // send an empty flash or something else is fishy. Whatever the problem, there's no 5657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // number, so there's no point in allowing apps to modify the number. 5667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (TextUtils.isEmpty(number)) { 5677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) { 5687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "onCreate: SEND_EMPTY_FLASH..."); 5697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone()); 5707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 5717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 5727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 5737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "onCreate: null or empty number, setting callNow=true..."); 5747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon callNow = true; 5757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (callNow) { 5797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // This is a special kind of call (most likely an emergency number) 5807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // that 3rd parties aren't allowed to intercept or affect in any way. 5817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // So initiate the outgoing call immediately. 5827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent); 5847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Initiate the outgoing call, and simultaneously launch the 5867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // InCallScreen to display the in-call UI: 5877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PhoneGlobals.getInstance().callController.placeCall(intent); 5887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Note we do *not* "return" here, but instead continue and 5907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // send the ACTION_NEW_OUTGOING_CALL broadcast like for any 5917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // other outgoing call. (But when the broadcast finally 5927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // reaches the OutgoingCallReceiver, we'll know not to 5937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // initiate the call again because of the presence of the 5947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // EXTRA_ALREADY_CALLED extra.) 5957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // For now, SIP calls will be processed directly without a 5987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // NEW_OUTGOING_CALL broadcast. 5997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 6007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO: In the future, though, 3rd party apps *should* be allowed to 6017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // intercept outgoing calls to SIP addresses as well. To do this, we should 6027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (1) update the NEW_OUTGOING_CALL intent documentation to explain this 6037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // case, and (2) pass the outgoing SIP address by *not* overloading the 6047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold 6057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // the outgoing SIP address. (Be sure to document whether it's a URI or just 6067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // a plain address, whether it could be a tel: URI, etc.) 6077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Uri uri = intent.getData(); 6087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon String scheme = uri.getScheme(); 609137458b4bf3516941483e59c123c22cbee27ed43Jay Shrauner if (PhoneAccount.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) { 6107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.i(TAG, "The requested number was detected as SIP call."); 6117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startSipCallOptionHandler(this, intent, uri, number); 6127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 6137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 6147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // TODO: if there's ever a way for SIP calls to trigger a 6167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // "callNow=true" case (see above), we'll need to handle that 6177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // case here too (most likely by just doing nothing at all.) 6187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); 6217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (number != null) { 6227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 6237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 62469a691914e9b013a7ff52c129d8466c152ed7239Santos Cordon CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent); 6257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow); 6267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString()); 6277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app 6287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // to intercept the outgoing call. 6297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 6307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + "."); 6317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Set a timer so that we can prepare for unexpected delay introduced by the broadcast. 6337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // If it takes too much time, the timer will show "waiting" spinner. 6347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // This message will be removed when OutgoingCallReceiver#onReceive() is called before the 6357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // timeout. 6367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT, 6377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon OUTGOING_CALL_TIMEOUT_THRESHOLD); 6387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER, 6397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PERMISSION, new OutgoingCallReceiver(), 6407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon null, // scheduler 6417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Activity.RESULT_OK, // initialCode 6427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon number, // initialData: initial value for the result data 6437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon null); // initialExtras 6447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 6477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon protected void onStop() { 6487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Clean up (and dismiss if necessary) any managed dialogs. 6497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 6507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // We don't do this in onPause() since we can be paused/resumed 6517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // due to orientation changes (in which case we don't want to 6527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // disturb the dialog), but we *do* need it here in onStop() to be 6537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // sure we clean up if the user hits HOME while the dialog is up. 6547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 6557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Note it's safe to call removeDialog() even if there's no dialog 6567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // associated with that ID. 6577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon removeDialog(DIALOG_NOT_VOICE_CAPABLE); 6587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon super.onStop(); 6607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 6637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Handle the specified CALL or CALL_* intent on a non-voice-capable 6647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * device. 6657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 6667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method may launch a different intent (if there's some useful 6677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * alternative action to take), or otherwise display an error dialog, 6687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * and in either case will finish() the current activity when done. 6697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 6707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void handleNonVoiceCapable(Intent intent) { 6717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "handleNonVoiceCapable: handling " + intent 6727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + " on non-voice-capable device..."); 6737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 67488653e75f7dc4494e54728ce13b15f296c3e6f82Chiao Cheng // Just show a generic "voice calling not supported" dialog. 6757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon showDialog(DIALOG_NOT_VOICE_CAPABLE); 6767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // ...and we'll eventually finish() when the user dismisses 6777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // or cancels the dialog. 6787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 6807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 6817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon protected Dialog onCreateDialog(int id) { 6827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Dialog dialog; 6837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon switch(id) { 6847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case DIALOG_NOT_VOICE_CAPABLE: 6857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon dialog = new AlertDialog.Builder(this) 6867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .setTitle(R.string.not_voice_capable) 6877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .setIconAttribute(android.R.attr.alertDialogIcon) 6887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .setPositiveButton(android.R.string.ok, this) 6897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .setOnCancelListener(this) 6907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon .create(); 6917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 6927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon default: 6937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "onCreateDialog: unexpected ID " + id); 6947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon dialog = null; 6957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 6967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return dialog; 6987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 6997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 7007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** DialogInterface.OnClickListener implementation */ 7017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 7027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void onClick(DialogInterface dialog, int id) { 7037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far 7047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // at least), and its only button is "OK". 7057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 7067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 7077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 7087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** DialogInterface.OnCancelListener implementation */ 7097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 7107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void onCancel(DialogInterface dialog) { 7117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far 7127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // at least), and canceling it is just like hitting "OK". 7137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon finish(); 7147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 7157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 7167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 7177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Implement onConfigurationChanged() purely for debugging purposes, 7187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * to make sure that the android:configChanges element in our manifest 7197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * is working properly. 7207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 7217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 7227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void onConfigurationChanged(Configuration newConfig) { 7237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon super.onConfigurationChanged(newConfig); 7247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig); 7257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 7267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon} 727