• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onReceive(Context context, Intent intent)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      */
processOutgoingCallIntent(Context context, Intent intent)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 
processIncomingCallIntent(Intent intent)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 
processUnknownCallIntent(Intent intent)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 
getCallsManager()135     static CallsManager getCallsManager() {
136         return CallsManager.getInstance();
137     }
138 
disconnectCallAndShowErrorDialog( Context context, Call call, int errorCode)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