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