1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import android.app.Activity;
20import android.app.ActivityManagerNative;
21import android.app.AlertDialog;
22import android.app.AppOpsManager;
23import android.app.Dialog;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.res.Configuration;
29import android.content.res.Resources;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.RemoteException;
35import android.os.SystemProperties;
36import android.os.UserHandle;
37import android.telecom.PhoneAccount;
38import android.telephony.PhoneNumberUtils;
39import android.text.TextUtils;
40import android.util.Log;
41import android.view.View;
42import android.widget.ProgressBar;
43
44import com.android.internal.telephony.Phone;
45import com.android.internal.telephony.PhoneConstants;
46import com.android.internal.telephony.TelephonyCapabilities;
47
48/**
49 * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the
50 * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which
51 * contains the phone number being dialed. Applications can use this intent to (1) see which numbers
52 * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call
53 * from being placed.
54 *
55 * After the other applications have had a chance to see the
56 * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the
57 * {@link OutgoingCallReceiver}, which passes the (possibly modified)
58 * intent on to the {@link SipCallOptionHandler}, which will
59 * ultimately start the call using the CallController.placeCall() API.
60 *
61 * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail
62 * number) are exempt from being broadcast.
63 * Calls to emergency numbers are still broadcast for informative purposes. The call is placed
64 * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
65 */
66public class OutgoingCallBroadcaster extends Activity
67        implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
68
69    private static final String TAG = "OutgoingCallBroadcaster";
70    private static final boolean DBG =
71            (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
72    // Do not check in with VDBG = true, since that may write PII to the system log.
73    private static final boolean VDBG = false;
74
75    /** Required permission for any app that wants to consume ACTION_NEW_OUTGOING_CALL. */
76    private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS;
77
78    public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE";
79    public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED";
80    public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI";
81    public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT";
82    public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI";
83    public static final String EXTRA_ACTUAL_NUMBER_TO_DIAL =
84            "android.phone.extra.ACTUAL_NUMBER_TO_DIAL";
85    public static final String EXTRA_THIRD_PARTY_CALL_COMPONENT =
86            "android.phone.extra.THIRD_PARTY_CALL_COMPONENT";
87
88    /**
89     * Identifier for intent extra for sending an empty Flash message for
90     * CDMA networks. This message is used by the network to simulate a
91     * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
92     *
93     * TODO: Receiving an intent extra to tell the phone to send this flash is a
94     * temporary measure. To be replaced with an external ITelephony call in the future.
95     * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app
96     * until this is replaced with the ITelephony API.
97     */
98    public static final String EXTRA_SEND_EMPTY_FLASH =
99            "com.android.phone.extra.SEND_EMPTY_FLASH";
100
101    // Dialog IDs
102    private static final int DIALOG_NOT_VOICE_CAPABLE = 1;
103
104    /** Note message codes < 100 are reserved for the PhoneApp. */
105    private static final int EVENT_OUTGOING_CALL_TIMEOUT = 101;
106    private static final int EVENT_DELAYED_FINISH = 102;
107
108    private static final int OUTGOING_CALL_TIMEOUT_THRESHOLD = 2000; // msec
109    private static final int DELAYED_FINISH_TIME = 2000; // msec
110
111    /**
112     * ProgressBar object with "spinner" style, which will be shown if we take more than
113     * {@link #EVENT_OUTGOING_CALL_TIMEOUT} msec to handle the incoming Intent.
114     */
115    private ProgressBar mWaitingSpinner;
116    private final Handler mHandler = new Handler() {
117        @Override
118        public void handleMessage(Message msg) {
119            if (msg.what == EVENT_OUTGOING_CALL_TIMEOUT) {
120                Log.i(TAG, "Outgoing call takes too long. Showing the spinner.");
121                mWaitingSpinner.setVisibility(View.VISIBLE);
122            } else if (msg.what == EVENT_DELAYED_FINISH) {
123                finish();
124            } else {
125                Log.wtf(TAG, "Unknown message id: " + msg.what);
126            }
127        }
128    };
129
130    /**
131     * Starts the delayed finish() of OutgoingCallBroadcaster in order to give the UI
132     * some time to start up.
133     */
134    private void startDelayedFinish() {
135        mHandler.sendEmptyMessageDelayed(EVENT_DELAYED_FINISH, DELAYED_FINISH_TIME);
136    }
137
138    /**
139     * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting
140     * the InCallScreen if the broadcast has not been canceled, possibly with
141     * a modified phone number and optional provider info (uri + package name + remote views.)
142     */
143    public class OutgoingCallReceiver extends BroadcastReceiver {
144        private static final String TAG = "OutgoingCallReceiver";
145
146        @Override
147        public void onReceive(Context context, Intent intent) {
148            mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
149            final boolean isAttemptingCall = doReceive(context, intent);
150            if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself.");
151
152            // We cannot finish the activity immediately here because it would cause the temporary
153            // black screen of OutgoingBroadcaster to go away and we need it to stay up until the
154            // UI (in a different process) has time to come up.
155            // However, if we know we are not attemping a call, we need to finish the activity
156            // immediately so that subsequent CALL intents will retrigger a new
157            // OutgoingCallReceiver. see b/10857203
158            if (isAttemptingCall) {
159                startDelayedFinish();
160            } else {
161                finish();
162            }
163        }
164
165
166        /**
167         * Handes receipt of ordered new_outgoing_call intent. Verifies that the return from the
168         * ordered intent is valid.
169         * @return true if the call is being attempted; false if we are canceling the call.
170         */
171        public boolean doReceive(Context context, Intent intent) {
172            if (DBG) Log.v(TAG, "doReceive: " + intent);
173
174            boolean alreadyCalled;
175            String number;
176            String originalUri;
177
178            alreadyCalled = intent.getBooleanExtra(
179                    OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);
180            if (alreadyCalled) {
181                if (DBG) Log.v(TAG, "CALL already placed -- returning.");
182                return false;
183            }
184
185            // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData
186            // is used as the actual number to call. (If null, no call will be
187            // placed.)
188
189            number = getResultData();
190            if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'");
191
192            final PhoneGlobals app = PhoneGlobals.getInstance();
193            final Phone phone = PhoneGlobals.getPhone();
194
195            // OTASP-specific checks.
196            // TODO: This should probably all happen in
197            // OutgoingCallBroadcaster.onCreate(), since there's no reason to
198            // even bother with the NEW_OUTGOING_CALL broadcast if we're going
199            // to disallow the outgoing call anyway...
200            if (TelephonyCapabilities.supportsOtasp(phone)) {
201                boolean activateState = (app.cdmaOtaScreenState.otaScreenState
202                        == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
203                boolean dialogState = (app.cdmaOtaScreenState.otaScreenState
204                        == OtaUtils.CdmaOtaScreenState.OtaScreenState
205                        .OTA_STATUS_SUCCESS_FAILURE_DLG);
206                boolean isOtaCallActive = false;
207
208                // TODO: Need cleaner way to check if OTA is active.
209                // Also, this check seems to be broken in one obscure case: if
210                // you interrupt an OTASP call by pressing Back then Skip,
211                // otaScreenState somehow gets left in either PROGRESS or
212                // LISTENING.
213                if ((app.cdmaOtaScreenState.otaScreenState
214                        == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS)
215                        || (app.cdmaOtaScreenState.otaScreenState
216                        == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)) {
217                    isOtaCallActive = true;
218                }
219
220                if (activateState || dialogState) {
221                    // The OTASP sequence is active, but either (1) the call
222                    // hasn't started yet, or (2) the call has ended and we're
223                    // showing the success/failure screen.  In either of these
224                    // cases it's OK to make a new outgoing call, but we need
225                    // to take down any OTASP-related UI first.
226                    if (dialogState) app.dismissOtaDialogs();
227                    app.clearOtaState();
228                } else if (isOtaCallActive) {
229                    // The actual OTASP call is active.  Don't allow new
230                    // outgoing calls at all from this state.
231                    Log.w(TAG, "OTASP call is active: disallowing a new outgoing call.");
232                    return false;
233                }
234            }
235
236            if (number == null) {
237                if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");
238                return false;
239            } else if (TelephonyCapabilities.supportsOtasp(phone)
240                    && (phone.getState() != PhoneConstants.State.IDLE)
241                    && (phone.isOtaSpNumber(number))) {
242                if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning.");
243                return false;
244            } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number)) {
245                // Just like 3rd-party apps aren't allowed to place emergency
246                // calls via the ACTION_CALL intent, we also don't allow 3rd
247                // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite
248                // an outgoing call into an emergency number.
249                Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + ".");
250                return false;
251            }
252
253            originalUri = intent.getStringExtra(
254                    OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
255            if (originalUri == null) {
256                Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning.");
257                return false;
258            }
259
260            Uri uri = Uri.parse(originalUri);
261
262            // We already called convertKeypadLettersToDigits() and
263            // stripSeparators() way back in onCreate(), before we sent out the
264            // NEW_OUTGOING_CALL broadcast.  But we need to do it again here
265            // too, since the number might have been modified/rewritten during
266            // the broadcast (and may now contain letters or separators again.)
267            number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
268            number = PhoneNumberUtils.stripSeparators(number);
269
270            if (DBG) Log.v(TAG, "doReceive: proceeding with call...");
271            if (VDBG) Log.v(TAG, "- uri: " + uri);
272            if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'");
273
274            startSipCallOptionHandler(context, intent, uri, number);
275
276            return true;
277        }
278    }
279
280    /**
281     * Launch the SipCallOptionHandler, which is the next step(*) in the
282     * outgoing-call sequence after the outgoing call broadcast is
283     * complete.
284     *
285     * (*) We now know exactly what phone number we need to dial, so the next
286     *     step is for the SipCallOptionHandler to decide which Phone type (SIP
287     *     or PSTN) should be used.  (Depending on the user's preferences, this
288     *     decision may also involve popping up a dialog to ask the user to
289     *     choose what type of call this should be.)
290     *
291     * @param context used for the startActivity() call
292     *
293     * @param intent the intent from the previous step of the outgoing-call
294     *   sequence.  Normally this will be the NEW_OUTGOING_CALL broadcast intent
295     *   that came in to the OutgoingCallReceiver, although it can also be the
296     *   original ACTION_CALL intent that started the whole sequence (in cases
297     *   where we don't do the NEW_OUTGOING_CALL broadcast at all, like for
298     *   emergency numbers or SIP addresses).
299     *
300     * @param uri the data URI from the original CALL intent, presumably either
301     *   a tel: or sip: URI.  For tel: URIs, note that the scheme-specific part
302     *   does *not* necessarily have separators and keypad letters stripped (so
303     *   we might see URIs like "tel:(650)%20555-1234" or "tel:1-800-GOOG-411"
304     *   here.)
305     *
306     * @param number the actual number (or SIP address) to dial.  This is
307     *   guaranteed to be either a PSTN phone number with separators stripped
308     *   out and keypad letters converted to digits (like "16505551234"), or a
309     *   raw SIP address (like "user@example.com").
310     */
311    private void startSipCallOptionHandler(Context context, Intent intent,
312            Uri uri, String number) {
313        // TODO: Remove this code.
314    }
315
316    /**
317     * This method is the single point of entry for the CALL intent, which is used (by built-in
318     * apps like Contacts / Dialer, as well as 3rd-party apps) to initiate an outgoing voice call.
319     *
320     *
321     */
322    @Override
323    protected void onCreate(Bundle icicle) {
324        super.onCreate(icicle);
325        setContentView(R.layout.outgoing_call_broadcaster);
326        mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner);
327
328        Intent intent = getIntent();
329        if (DBG) {
330            final Configuration configuration = getResources().getConfiguration();
331            Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle);
332            Log.v(TAG, " - getIntent() = " + intent);
333            Log.v(TAG, " - configuration = " + configuration);
334        }
335
336        if (icicle != null) {
337            // A non-null icicle means that this activity is being
338            // re-initialized after previously being shut down.
339            //
340            // In practice this happens very rarely (because the lifetime
341            // of this activity is so short!), but it *can* happen if the
342            // framework detects a configuration change at exactly the
343            // right moment; see bug 2202413.
344            //
345            // In this case, do nothing.  Our onCreate() method has already
346            // run once (with icicle==null the first time), which means
347            // that the NEW_OUTGOING_CALL broadcast for this new call has
348            // already been sent.
349            Log.i(TAG, "onCreate: non-null icicle!  "
350                  + "Bailing out, not sending NEW_OUTGOING_CALL broadcast...");
351
352            // No need to finish() here, since the OutgoingCallReceiver from
353            // our original instance will do that.  (It'll actually call
354            // finish() on our original instance, which apparently works fine
355            // even though the ActivityManager has already shut that instance
356            // down.  And note that if we *do* call finish() here, that just
357            // results in an "ActivityManager: Duplicate finish request"
358            // warning when the OutgoingCallReceiver runs.)
359
360            return;
361        }
362
363        processIntent(intent);
364
365        // isFinishing() return false when 1. broadcast is still ongoing, or 2. dialog is being
366        // shown. Otherwise finish() is called inside processIntent(), is isFinishing() here will
367        // return true.
368        if (DBG) Log.v(TAG, "At the end of onCreate(). isFinishing(): " + isFinishing());
369    }
370
371    /**
372     * Interprets a given Intent and starts something relevant to the Intent.
373     *
374     * This method will handle three kinds of actions:
375     *
376     * - CALL (action for usual outgoing voice calls)
377     * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth)
378     * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.)
379     *
380     * The exact behavior depends on the intent's data:
381     *
382     * - The most typical is a tel: URI, which we handle by starting the
383     *   NEW_OUTGOING_CALL broadcast.  That broadcast eventually triggers
384     *   the sequence OutgoingCallReceiver -> SipCallOptionHandler ->
385     *   InCallScreen.
386     *
387     * - Or, with a sip: URI we skip the NEW_OUTGOING_CALL broadcast and
388     *   go directly to SipCallOptionHandler, which then leads to the
389     *   InCallScreen.
390     *
391     * - voicemail: URIs take the same path as regular tel: URIs.
392     *
393     * Other special cases:
394     *
395     * - Outgoing calls are totally disallowed on non-voice-capable
396     *   devices (see handleNonVoiceCapable()).
397     *
398     * - A CALL intent with the EXTRA_SEND_EMPTY_FLASH extra (and
399     *   presumably no data at all) means "send an empty flash" (which
400     *   is only meaningful on CDMA devices while a call is already
401     *   active.)
402     *
403     */
404    private void processIntent(Intent intent) {
405        if (DBG) {
406            Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread());
407        }
408        final Configuration configuration = getResources().getConfiguration();
409
410        // Outgoing phone calls are only allowed on "voice-capable" devices.
411        if (!PhoneGlobals.sVoiceCapable) {
412            Log.i(TAG, "This device is detected as non-voice-capable device.");
413            handleNonVoiceCapable(intent);
414            return;
415        }
416
417        String action = intent.getAction();
418        String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
419        // Check the number, don't convert for sip uri
420        // TODO put uriNumber under PhoneNumberUtils
421        if (number != null) {
422            if (!PhoneNumberUtils.isUriNumber(number)) {
423                number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
424                number = PhoneNumberUtils.stripSeparators(number);
425            }
426        } else {
427            Log.w(TAG, "The number obtained from Intent is null.");
428        }
429
430        AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
431        int launchedFromUid;
432        String launchedFromPackage;
433        try {
434            launchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
435                    getActivityToken());
436            launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
437                    getActivityToken());
438        } catch (RemoteException e) {
439            launchedFromUid = -1;
440            launchedFromPackage = null;
441        }
442        if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
443                != AppOpsManager.MODE_ALLOWED) {
444            Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package "
445                    + launchedFromPackage);
446            finish();
447            return;
448        }
449
450        // If true, this flag will indicate that the current call is a special kind
451        // of call (most likely an emergency number) that 3rd parties aren't allowed
452        // to intercept or affect in any way.  (In that case, we start the call
453        // immediately rather than going through the NEW_OUTGOING_CALL sequence.)
454        boolean callNow;
455
456        if (getClass().getName().equals(intent.getComponent().getClassName())) {
457            // If we were launched directly from the OutgoingCallBroadcaster,
458            // not one of its more privileged aliases, then make sure that
459            // only the non-privileged actions are allowed.
460            if (!Intent.ACTION_CALL.equals(intent.getAction())) {
461                Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL");
462                intent.setAction(Intent.ACTION_CALL);
463            }
464        }
465
466        // Check whether or not this is an emergency number, in order to
467        // enforce the restriction that only the CALL_PRIVILEGED and
468        // CALL_EMERGENCY intents are allowed to make emergency calls.
469        //
470        // (Note that the ACTION_CALL check below depends on the result of
471        // isPotentialLocalEmergencyNumber() rather than just plain
472        // isLocalEmergencyNumber(), to be 100% certain that we *don't*
473        // allow 3rd party apps to make emergency calls by passing in an
474        // "invalid" number like "9111234" that isn't technically an
475        // emergency number but might still result in an emergency call
476        // with some networks.)
477        final boolean isExactEmergencyNumber =
478                (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(this, number);
479        final boolean isPotentialEmergencyNumber =
480                (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(this, number);
481        if (VDBG) {
482            Log.v(TAG, " - Checking restrictions for number '" + number + "':");
483            Log.v(TAG, "     isExactEmergencyNumber     = " + isExactEmergencyNumber);
484            Log.v(TAG, "     isPotentialEmergencyNumber = " + isPotentialEmergencyNumber);
485        }
486
487        /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
488        // TODO: This code is redundant with some code in InCallScreen: refactor.
489        if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
490            // We're handling a CALL_PRIVILEGED intent, so we know this request came
491            // from a trusted source (like the built-in dialer.)  So even a number
492            // that's *potentially* an emergency number can safely be promoted to
493            // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from
494            // the dialer if you really want to.)
495            if (isPotentialEmergencyNumber) {
496                Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential"
497                        + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead.");
498                action = Intent.ACTION_CALL_EMERGENCY;
499            } else {
500                action = Intent.ACTION_CALL;
501            }
502            if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action);
503            intent.setAction(action);
504        }
505
506        if (Intent.ACTION_CALL.equals(action)) {
507            if (isPotentialEmergencyNumber) {
508                Log.w(TAG, "Cannot call potential emergency number '" + number
509                        + "' with CALL Intent " + intent + ".");
510                Log.i(TAG, "Launching default dialer instead...");
511
512                Intent invokeFrameworkDialer = new Intent();
513
514                // TwelveKeyDialer is in a tab so we really want
515                // DialtactsActivity.  Build the intent 'manually' to
516                // use the java resolver to find the dialer class (as
517                // opposed to a Context which look up known android
518                // packages only)
519                final Resources resources = getResources();
520                invokeFrameworkDialer.setClassName(
521                        resources.getString(R.string.ui_default_package),
522                        resources.getString(R.string.dialer_default_class));
523                invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
524                invokeFrameworkDialer.setData(intent.getData());
525                if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: "
526                               + invokeFrameworkDialer);
527                startActivity(invokeFrameworkDialer);
528                finish();
529                return;
530            }
531            callNow = false;
532        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
533            // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED
534            // intent that we just turned into a CALL_EMERGENCY intent (see
535            // above), or else it really is an CALL_EMERGENCY intent that
536            // came directly from some other app (e.g. the EmergencyDialer
537            // activity built in to the Phone app.)
538            // Make sure it's at least *possible* that this is really an
539            // emergency number.
540            if (!isPotentialEmergencyNumber) {
541                Log.w(TAG, "Cannot call non-potential-emergency number " + number
542                        + " with EMERGENCY_CALL Intent " + intent + "."
543                        + " Finish the Activity immediately.");
544                finish();
545                return;
546            }
547            callNow = true;
548        } else {
549            Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately.");
550            finish();
551            return;
552        }
553
554        // Make sure the screen is turned on.  This is probably the right
555        // thing to do, and more importantly it works around an issue in the
556        // activity manager where we will not launch activities consistently
557        // when the screen is off (since it is trying to keep them paused
558        // and has...  issues).
559        //
560        // Also, this ensures the device stays awake while doing the following
561        // broadcast; technically we should be holding a wake lock here
562        // as well.
563        PhoneGlobals.getInstance().wakeUpScreen();
564
565        // If number is null, we're probably trying to call a non-existent voicemail number,
566        // send an empty flash or something else is fishy.  Whatever the problem, there's no
567        // number, so there's no point in allowing apps to modify the number.
568        if (TextUtils.isEmpty(number)) {
569            if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
570                Log.i(TAG, "onCreate: SEND_EMPTY_FLASH...");
571                PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
572                finish();
573                return;
574            } else {
575                Log.i(TAG, "onCreate: null or empty number, setting callNow=true...");
576                callNow = true;
577            }
578        }
579
580        if (callNow) {
581            // This is a special kind of call (most likely an emergency number)
582            // that 3rd parties aren't allowed to intercept or affect in any way.
583            // So initiate the outgoing call immediately.
584
585            Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent);
586
587            // Initiate the outgoing call, and simultaneously launch the
588            // InCallScreen to display the in-call UI:
589            PhoneGlobals.getInstance().callController.placeCall(intent);
590
591            // Note we do *not* "return" here, but instead continue and
592            // send the ACTION_NEW_OUTGOING_CALL broadcast like for any
593            // other outgoing call.  (But when the broadcast finally
594            // reaches the OutgoingCallReceiver, we'll know not to
595            // initiate the call again because of the presence of the
596            // EXTRA_ALREADY_CALLED extra.)
597        }
598
599        // For now, SIP calls will be processed directly without a
600        // NEW_OUTGOING_CALL broadcast.
601        //
602        // TODO: In the future, though, 3rd party apps *should* be allowed to
603        // intercept outgoing calls to SIP addresses as well.  To do this, we should
604        // (1) update the NEW_OUTGOING_CALL intent documentation to explain this
605        // case, and (2) pass the outgoing SIP address by *not* overloading the
606        // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold
607        // the outgoing SIP address.  (Be sure to document whether it's a URI or just
608        // a plain address, whether it could be a tel: URI, etc.)
609        Uri uri = intent.getData();
610        String scheme = uri.getScheme();
611        if (PhoneAccount.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) {
612            Log.i(TAG, "The requested number was detected as SIP call.");
613            startSipCallOptionHandler(this, intent, uri, number);
614            finish();
615            return;
616
617            // TODO: if there's ever a way for SIP calls to trigger a
618            // "callNow=true" case (see above), we'll need to handle that
619            // case here too (most likely by just doing nothing at all.)
620        }
621
622        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
623        if (number != null) {
624            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
625        }
626        CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
627        broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
628        broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString());
629        // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app
630        // to intercept the outgoing call.
631        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
632        if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + ".");
633
634        // Set a timer so that we can prepare for unexpected delay introduced by the broadcast.
635        // If it takes too much time, the timer will show "waiting" spinner.
636        // This message will be removed when OutgoingCallReceiver#onReceive() is called before the
637        // timeout.
638        mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
639                OUTGOING_CALL_TIMEOUT_THRESHOLD);
640        sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,
641                PERMISSION, new OutgoingCallReceiver(),
642                null,  // scheduler
643                Activity.RESULT_OK,  // initialCode
644                number,  // initialData: initial value for the result data
645                null);  // initialExtras
646    }
647
648    @Override
649    protected void onStop() {
650        // Clean up (and dismiss if necessary) any managed dialogs.
651        //
652        // We don't do this in onPause() since we can be paused/resumed
653        // due to orientation changes (in which case we don't want to
654        // disturb the dialog), but we *do* need it here in onStop() to be
655        // sure we clean up if the user hits HOME while the dialog is up.
656        //
657        // Note it's safe to call removeDialog() even if there's no dialog
658        // associated with that ID.
659        removeDialog(DIALOG_NOT_VOICE_CAPABLE);
660
661        super.onStop();
662    }
663
664    /**
665     * Handle the specified CALL or CALL_* intent on a non-voice-capable
666     * device.
667     *
668     * This method may launch a different intent (if there's some useful
669     * alternative action to take), or otherwise display an error dialog,
670     * and in either case will finish() the current activity when done.
671     */
672    private void handleNonVoiceCapable(Intent intent) {
673        if (DBG) Log.v(TAG, "handleNonVoiceCapable: handling " + intent
674                       + " on non-voice-capable device...");
675
676        // Just show a generic "voice calling not supported" dialog.
677        showDialog(DIALOG_NOT_VOICE_CAPABLE);
678        // ...and we'll eventually finish() when the user dismisses
679        // or cancels the dialog.
680    }
681
682    @Override
683    protected Dialog onCreateDialog(int id) {
684        Dialog dialog;
685        switch(id) {
686            case DIALOG_NOT_VOICE_CAPABLE:
687                dialog = new AlertDialog.Builder(this)
688                        .setTitle(R.string.not_voice_capable)
689                        .setIconAttribute(android.R.attr.alertDialogIcon)
690                        .setPositiveButton(android.R.string.ok, this)
691                        .setOnCancelListener(this)
692                        .create();
693                break;
694            default:
695                Log.w(TAG, "onCreateDialog: unexpected ID " + id);
696                dialog = null;
697                break;
698        }
699        return dialog;
700    }
701
702    /** DialogInterface.OnClickListener implementation */
703    @Override
704    public void onClick(DialogInterface dialog, int id) {
705        // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
706        // at least), and its only button is "OK".
707        finish();
708    }
709
710    /** DialogInterface.OnCancelListener implementation */
711    @Override
712    public void onCancel(DialogInterface dialog) {
713        // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
714        // at least), and canceling it is just like hitting "OK".
715        finish();
716    }
717
718    /**
719     * Implement onConfigurationChanged() purely for debugging purposes,
720     * to make sure that the android:configChanges element in our manifest
721     * is working properly.
722     */
723    @Override
724    public void onConfigurationChanged(Configuration newConfig) {
725        super.onConfigurationChanged(newConfig);
726        if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig);
727    }
728}
729