Home | History | Annotate | Download | only in phone
      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 
     17 package com.android.phone;
     18 
     19 import android.app.Activity;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.res.Configuration;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.SystemProperties;
     27 import android.telephony.PhoneNumberUtils;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 
     31 import com.android.internal.telephony.Phone;
     32 
     33 /**
     34  * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and
     35  * broadcasts the ACTION_NEW_OUTGOING_CALL intent which allows other
     36  * applications to monitor, redirect, or prevent the outgoing call.
     37 
     38  * After the other applications have had a chance to see the
     39  * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the
     40  * {@link OutgoingCallReceiver}, which passes the (possibly modified)
     41  * intent on to the {@link InCallScreen}.
     42  *
     43  * Emergency calls and calls where no number is present (like for a CDMA
     44  * "empty flash" or a nonexistent voicemail number) are exempt from being
     45  * broadcast.
     46  */
     47 public class OutgoingCallBroadcaster extends Activity {
     48 
     49     private static final String PERMISSION = android.Manifest.permission.PROCESS_OUTGOING_CALLS;
     50     private static final String TAG = "OutgoingCallBroadcaster";
     51     private static final boolean DBG =
     52             (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
     53 
     54     public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED";
     55     public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI";
     56     public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT";
     57     public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI";
     58 
     59     /**
     60      * Identifier for intent extra for sending an empty Flash message for
     61      * CDMA networks. This message is used by the network to simulate a
     62      * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
     63      *
     64      * TODO: Receiving an intent extra to tell the phone to send this flash is a
     65      * temporary measure. To be replaced with an external ITelephony call in the future.
     66      * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app
     67      * until this is replaced with the ITelephony API.
     68      */
     69     public static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH";
     70 
     71     /**
     72      * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting
     73      * the InCallScreen if the broadcast has not been canceled, possibly with
     74      * a modified phone number and optional provider info (uri + package name + remote views.)
     75      */
     76     public class OutgoingCallReceiver extends BroadcastReceiver {
     77         private static final String TAG = "OutgoingCallReceiver";
     78 
     79         public void onReceive(Context context, Intent intent) {
     80             doReceive(context, intent);
     81             finish();
     82         }
     83 
     84         public void doReceive(Context context, Intent intent) {
     85             if (DBG) Log.v(TAG, "doReceive: " + intent);
     86 
     87             boolean alreadyCalled;
     88             String number;
     89             String originalUri;
     90 
     91             alreadyCalled = intent.getBooleanExtra(
     92                     OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);
     93             if (alreadyCalled) {
     94                 if (DBG) Log.v(TAG, "CALL already placed -- returning.");
     95                 return;
     96             }
     97 
     98             number = getResultData();
     99             final PhoneApp app = PhoneApp.getInstance();
    100 
    101             if (TelephonyCapabilities.supportsOtasp(app.phone)) {
    102                 boolean activateState = (app.cdmaOtaScreenState.otaScreenState
    103                         == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
    104                 boolean dialogState = (app.cdmaOtaScreenState.otaScreenState
    105                         == OtaUtils.CdmaOtaScreenState.OtaScreenState
    106                         .OTA_STATUS_SUCCESS_FAILURE_DLG);
    107                 boolean isOtaCallActive = false;
    108 
    109                 if ((app.cdmaOtaScreenState.otaScreenState
    110                         == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS)
    111                         || (app.cdmaOtaScreenState.otaScreenState
    112                         == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)) {
    113                     isOtaCallActive = true;
    114                 }
    115 
    116                 if (activateState || dialogState) {
    117                     if (dialogState) app.dismissOtaDialogs();
    118                     app.clearOtaState();
    119                     app.clearInCallScreenMode();
    120                 } else if (isOtaCallActive) {
    121                     if (DBG) Log.v(TAG, "OTA call is active, a 2nd CALL cancelled -- returning.");
    122                     return;
    123                 }
    124             }
    125 
    126             if (number == null) {
    127                 if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");
    128                 return;
    129             } else if (TelephonyCapabilities.supportsOtasp(app.phone)
    130                     && (app.phone.getState() != Phone.State.IDLE)
    131                     && (app.phone.isOtaSpNumber(number))) {
    132                 if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning.");
    133                 return;
    134             } else if (PhoneNumberUtils.isEmergencyNumber(number)) {
    135                 Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + ".");
    136                 return;
    137             }
    138 
    139             originalUri = intent.getStringExtra(
    140                     OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
    141             if (originalUri == null) {
    142                 Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning.");
    143                 return;
    144             }
    145 
    146             Uri uri = Uri.parse(originalUri);
    147 
    148             if (DBG) Log.v(TAG, "CALL to " + /*number*/ "xxxxxxx" + " proceeding.");
    149 
    150             startSipCallOptionsHandler(context, intent, uri, number);
    151         }
    152     }
    153 
    154     private void startSipCallOptionsHandler(Context context, Intent intent,
    155             Uri uri, String number) {
    156         Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
    157         newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
    158 
    159         PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent);
    160 
    161         newIntent.setClass(context, InCallScreen.class);
    162         newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    163 
    164         Intent selectPhoneIntent = new Intent(EXTRA_NEW_CALL_INTENT, uri);
    165         selectPhoneIntent.setClass(context, SipCallOptionHandler.class);
    166         selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT, newIntent);
    167         selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    168         if (DBG) Log.v(TAG, "startSipCallOptionsHandler(): " +
    169                 "calling startActivity: " + selectPhoneIntent);
    170         context.startActivity(selectPhoneIntent);
    171     }
    172 
    173     @Override
    174     protected void onCreate(Bundle icicle) {
    175         super.onCreate(icicle);
    176 
    177         Intent intent = getIntent();
    178         final Configuration configuration = getResources().getConfiguration();
    179 
    180         if (DBG) Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle);
    181         if (DBG) Log.v(TAG, " - getIntent() = " + intent);
    182         if (DBG) Log.v(TAG, " - configuration = " + configuration);
    183 
    184         if (icicle != null) {
    185             // A non-null icicle means that this activity is being
    186             // re-initialized after previously being shut down.
    187             //
    188             // In practice this happens very rarely (because the lifetime
    189             // of this activity is so short!), but it *can* happen if the
    190             // framework detects a configuration change at exactly the
    191             // right moment; see bug 2202413.
    192             //
    193             // In this case, do nothing.  Our onCreate() method has already
    194             // run once (with icicle==null the first time), which means
    195             // that the NEW_OUTGOING_CALL broadcast for this new call has
    196             // already been sent.
    197             Log.i(TAG, "onCreate: non-null icicle!  "
    198                   + "Bailing out, not sending NEW_OUTGOING_CALL broadcast...");
    199 
    200             // No need to finish() here, since the OutgoingCallReceiver from
    201             // our original instance will do that.  (It'll actually call
    202             // finish() on our original instance, which apparently works fine
    203             // even though the ActivityManager has already shut that instance
    204             // down.  And note that if we *do* call finish() here, that just
    205             // results in an "ActivityManager: Duplicate finish request"
    206             // warning when the OutgoingCallReceiver runs.)
    207 
    208             return;
    209         }
    210 
    211         String action = intent.getAction();
    212         String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
    213         // Check the number, don't convert for sip uri
    214         // TODO put uriNumber under PhoneNumberUtils
    215         if (number != null) {
    216             if (!PhoneNumberUtils.isUriNumber(number)) {
    217                 number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
    218                 number = PhoneNumberUtils.stripSeparators(number);
    219             }
    220         }
    221         final boolean emergencyNumber =
    222                 (number != null) && PhoneNumberUtils.isEmergencyNumber(number);
    223 
    224         boolean callNow;
    225 
    226         if (getClass().getName().equals(intent.getComponent().getClassName())) {
    227             // If we were launched directly from the OutgoingCallBroadcaster,
    228             // not one of its more privileged aliases, then make sure that
    229             // only the non-privileged actions are allowed.
    230             if (!Intent.ACTION_CALL.equals(intent.getAction())) {
    231                 Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL");
    232                 intent.setAction(Intent.ACTION_CALL);
    233             }
    234         }
    235 
    236         /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
    237         // TODO: This code is redundant with some code in InCallScreen: refactor.
    238         if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
    239             action = emergencyNumber
    240                     ? Intent.ACTION_CALL_EMERGENCY
    241                     : Intent.ACTION_CALL;
    242             if (DBG) Log.v(TAG, "- updating action from CALL_PRIVILEGED to " + action);
    243             intent.setAction(action);
    244         }
    245 
    246         if (Intent.ACTION_CALL.equals(action)) {
    247             if (emergencyNumber) {
    248                 Log.w(TAG, "Cannot call emergency number " + number
    249                         + " with CALL Intent " + intent + ".");
    250 
    251                 Intent invokeFrameworkDialer = new Intent();
    252 
    253                 // TwelveKeyDialer is in a tab so we really want
    254                 // DialtactsActivity.  Build the intent 'manually' to
    255                 // use the java resolver to find the dialer class (as
    256                 // opposed to a Context which look up known android
    257                 // packages only)
    258                 invokeFrameworkDialer.setClassName("com.android.contacts",
    259                                                    "com.android.contacts.DialtactsActivity");
    260                 invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
    261                 invokeFrameworkDialer.setData(intent.getData());
    262 
    263                 if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: "
    264                                + invokeFrameworkDialer);
    265                 startActivity(invokeFrameworkDialer);
    266                 finish();
    267                 return;
    268             }
    269             callNow = false;
    270         } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
    271             // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED
    272             // intent that we just turned into a CALL_EMERGENCY intent (see
    273             // above), or else it really is an CALL_EMERGENCY intent that
    274             // came directly from some other app (e.g. the EmergencyDialer
    275             // activity built in to the Phone app.)
    276             if (!emergencyNumber) {
    277                 Log.w(TAG, "Cannot call non-emergency number " + number
    278                         + " with EMERGENCY_CALL Intent " + intent + ".");
    279                 finish();
    280                 return;
    281             }
    282             callNow = true;
    283         } else {
    284             Log.e(TAG, "Unhandled Intent " + intent + ".");
    285             finish();
    286             return;
    287         }
    288 
    289         // Make sure the screen is turned on.  This is probably the right
    290         // thing to do, and more importantly it works around an issue in the
    291         // activity manager where we will not launch activities consistently
    292         // when the screen is off (since it is trying to keep them paused
    293         // and has...  issues).
    294         //
    295         // Also, this ensures the device stays awake while doing the following
    296         // broadcast; technically we should be holding a wake lock here
    297         // as well.
    298         PhoneApp.getInstance().wakeUpScreen();
    299 
    300         /* If number is null, we're probably trying to call a non-existent voicemail number,
    301          * send an empty flash or something else is fishy.  Whatever the problem, there's no
    302          * number, so there's no point in allowing apps to modify the number. */
    303         if (number == null || TextUtils.isEmpty(number)) {
    304             if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
    305                 Log.i(TAG, "onCreate: SEND_EMPTY_FLASH...");
    306                 PhoneUtils.sendEmptyFlash(PhoneApp.getPhone());
    307                 finish();
    308                 return;
    309             } else {
    310                 Log.i(TAG, "onCreate: null or empty number, setting callNow=true...");
    311                 callNow = true;
    312             }
    313         }
    314 
    315         if (callNow) {
    316             intent.setClass(this, InCallScreen.class);
    317             if (DBG) Log.v(TAG, "onCreate(): callNow case, calling startActivity: " + intent);
    318             startActivity(intent);
    319         }
    320 
    321         // For now, SIP calls will be processed directly without a
    322         // NEW_OUTGOING_CALL broadcast.
    323         //
    324         // TODO: In the future, though, 3rd party apps *should* be allowed to
    325         // intercept outgoing calls to SIP addresses as well.  To do this, we should
    326         // (1) update the NEW_OUTGOING_CALL intent documentation to explain this
    327         // case, and (2) pass the outgoing SIP address by *not* overloading the
    328         // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold
    329         // the outgoing SIP address.  (Be sure to document whether it's a URI or just
    330         // a plain address, whether it could be a tel: URI, etc.)
    331         Uri uri = intent.getData();
    332         String scheme = uri.getScheme();
    333         if ("sip".equals(scheme) || PhoneNumberUtils.isUriNumber(number)) {
    334             startSipCallOptionsHandler(this, intent, uri, number);
    335             finish();
    336             return;
    337         }
    338 
    339         Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
    340         if (number != null) {
    341             broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
    342         }
    343         PhoneUtils.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
    344         broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
    345         broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString());
    346 
    347         if (DBG) Log.v(TAG, "Broadcasting intent: " + broadcastIntent + ".");
    348         sendOrderedBroadcast(broadcastIntent, PERMISSION, new OutgoingCallReceiver(),
    349                 null, Activity.RESULT_OK, number, null);
    350     }
    351 
    352     // Implement onConfigurationChanged() purely for debugging purposes,
    353     // to make sure that the android:configChanges element in our manifest
    354     // is working properly.
    355     @Override
    356     public void onConfigurationChanged(Configuration newConfig) {
    357         super.onConfigurationChanged(newConfig);
    358         if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig);
    359     }
    360 }
    361