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