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