Home | History | Annotate | Download | only in telecom
      1 package com.android.server.telecom;
      2 
      3 import android.content.BroadcastReceiver;
      4 import android.content.Context;
      5 import android.content.Intent;
      6 import android.net.Uri;
      7 import android.os.Bundle;
      8 import android.os.UserHandle;
      9 import android.telecom.PhoneAccount;
     10 import android.telecom.PhoneAccountHandle;
     11 import android.telecom.TelecomManager;
     12 import android.telephony.DisconnectCause;
     13 import android.telephony.PhoneNumberUtils;
     14 
     15 /**
     16  * Single point of entry for all outgoing and incoming calls. {@link CallActivity} serves as a
     17  * trampoline activity that captures call intents for individual users and forwards it to
     18  * the {@link CallReceiver} which interacts with the rest of Telecom, both of which run only as
     19  * the primary user.
     20  */
     21 public class CallReceiver extends BroadcastReceiver {
     22     private static final String TAG = CallReceiver.class.getName();
     23 
     24     static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
     25     static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
     26     static final String KEY_IS_DEFAULT_DIALER =
     27             "is_default_dialer";
     28 
     29     @Override
     30     public void onReceive(Context context, Intent intent) {
     31         final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
     32         final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);
     33         Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,
     34                 isUnknownCall);
     35 
     36         if (isUnknownCall) {
     37             processUnknownCallIntent(intent);
     38         } else if (isIncomingCall) {
     39             processIncomingCallIntent(intent);
     40         } else {
     41             processOutgoingCallIntent(context, intent);
     42         }
     43     }
     44 
     45     /**
     46      * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
     47      *
     48      * @param intent Call intent containing data about the handle to call.
     49      */
     50     static void processOutgoingCallIntent(Context context, Intent intent) {
     51         Uri handle = intent.getData();
     52         String scheme = handle.getScheme();
     53         String uriString = handle.getSchemeSpecificPart();
     54 
     55         if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
     56             handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
     57                     PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
     58         }
     59 
     60         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
     61                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
     62 
     63         Bundle clientExtras = null;
     64         if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
     65             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
     66         }
     67         if (clientExtras == null) {
     68             clientExtras = Bundle.EMPTY;
     69         }
     70 
     71         final boolean isDefaultDialer = intent.getBooleanExtra(KEY_IS_DEFAULT_DIALER, false);
     72 
     73         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
     74         Call call = getCallsManager().startOutgoingCall(handle, phoneAccountHandle, clientExtras);
     75 
     76         if (call != null) {
     77             // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
     78             // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
     79             // killed if memory is scarce. However, this is OK here because the entire Telecom
     80             // process will be running throughout the duration of the phone call and should never
     81             // be killed.
     82             NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
     83                     context, getCallsManager(), call, intent, isDefaultDialer);
     84             final int result = broadcaster.processIntent();
     85             final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
     86 
     87             if (!success && call != null) {
     88                 disconnectCallAndShowErrorDialog(context, call, result);
     89             }
     90         }
     91     }
     92 
     93     static void processIncomingCallIntent(Intent intent) {
     94         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
     95                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
     96 
     97         if (phoneAccountHandle == null) {
     98             Log.w(TAG, "Rejecting incoming call due to null phone account");
     99             return;
    100         }
    101         if (phoneAccountHandle.getComponentName() == null) {
    102             Log.w(TAG, "Rejecting incoming call due to null component name");
    103             return;
    104         }
    105 
    106         Bundle clientExtras = null;
    107         if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
    108             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
    109         }
    110         if (clientExtras == null) {
    111             clientExtras = Bundle.EMPTY;
    112         }
    113 
    114         Log.d(TAG, "Processing incoming call from connection service [%s]",
    115                 phoneAccountHandle.getComponentName());
    116         getCallsManager().processIncomingCallIntent(phoneAccountHandle, clientExtras);
    117     }
    118 
    119     private void processUnknownCallIntent(Intent intent) {
    120         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
    121                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
    122 
    123         if (phoneAccountHandle == null) {
    124             Log.w(this, "Rejecting unknown call due to null phone account");
    125             return;
    126         }
    127         if (phoneAccountHandle.getComponentName() == null) {
    128             Log.w(this, "Rejecting unknown call due to null component name");
    129             return;
    130         }
    131 
    132         getCallsManager().addNewUnknownCall(phoneAccountHandle, intent.getExtras());
    133     }
    134 
    135     static CallsManager getCallsManager() {
    136         return CallsManager.getInstance();
    137     }
    138 
    139     private static void disconnectCallAndShowErrorDialog(
    140             Context context, Call call, int errorCode) {
    141         call.disconnect();
    142         final Intent errorIntent = new Intent(context, ErrorDialogActivity.class);
    143         int errorMessageId = -1;
    144         switch (errorCode) {
    145             case DisconnectCause.INVALID_NUMBER:
    146             case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
    147                 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
    148                 break;
    149         }
    150         if (errorMessageId != -1) {
    151             errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
    152         }
    153         errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    154         context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
    155     }
    156 }
    157