1 package com.android.server.telecom; 2 3 import com.android.server.telecom.components.ErrorDialogActivity; 4 5 import android.content.Context; 6 import android.content.Intent; 7 import android.net.Uri; 8 import android.os.Bundle; 9 import android.os.Trace; 10 import android.os.UserHandle; 11 import android.os.UserManager; 12 import android.telecom.DefaultDialerManager; 13 import android.telecom.Log; 14 import android.telecom.PhoneAccount; 15 import android.telecom.PhoneAccountHandle; 16 import android.telecom.TelecomManager; 17 import android.telecom.VideoProfile; 18 import android.telephony.DisconnectCause; 19 import android.telephony.PhoneNumberUtils; 20 import android.widget.Toast; 21 22 /** 23 * Single point of entry for all outgoing and incoming calls. 24 * {@link com.android.server.telecom.components.UserCallIntentProcessor} serves as a trampoline that 25 * captures call intents for individual users and forwards it to the {@link CallIntentProcessor} 26 * which interacts with the rest of Telecom, both of which run only as the primary user. 27 */ 28 public class CallIntentProcessor { 29 public interface Adapter { 30 void processOutgoingCallIntent(Context context, CallsManager callsManager, 31 Intent intent); 32 void processIncomingCallIntent(CallsManager callsManager, Intent intent); 33 void processUnknownCallIntent(CallsManager callsManager, Intent intent); 34 } 35 36 public static class AdapterImpl implements Adapter { 37 @Override 38 public void processOutgoingCallIntent(Context context, CallsManager callsManager, 39 Intent intent) { 40 CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent); 41 } 42 43 @Override 44 public void processIncomingCallIntent(CallsManager callsManager, Intent intent) { 45 CallIntentProcessor.processIncomingCallIntent(callsManager, intent); 46 } 47 48 @Override 49 public void processUnknownCallIntent(CallsManager callsManager, Intent intent) { 50 CallIntentProcessor.processUnknownCallIntent(callsManager, intent); 51 } 52 } 53 54 public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call"; 55 public static final String KEY_IS_INCOMING_CALL = "is_incoming_call"; 56 /* 57 * Whether or not the dialer initiating this outgoing call is the default dialer, or system 58 * dialer and thus allowed to make emergency calls. 59 */ 60 public static final String KEY_IS_PRIVILEGED_DIALER = "is_privileged_dialer"; 61 62 /** 63 * The user initiating the outgoing call. 64 */ 65 public static final String KEY_INITIATING_USER = "initiating_user"; 66 67 68 private final Context mContext; 69 private final CallsManager mCallsManager; 70 71 public CallIntentProcessor(Context context, CallsManager callsManager) { 72 this.mContext = context; 73 this.mCallsManager = callsManager; 74 } 75 76 public void processIntent(Intent intent) { 77 final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false); 78 Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall); 79 80 Trace.beginSection("processNewCallCallIntent"); 81 if (isUnknownCall) { 82 processUnknownCallIntent(mCallsManager, intent); 83 } else { 84 processOutgoingCallIntent(mContext, mCallsManager, intent); 85 } 86 Trace.endSection(); 87 } 88 89 90 /** 91 * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents. 92 * 93 * @param intent Call intent containing data about the handle to call. 94 */ 95 static void processOutgoingCallIntent( 96 Context context, 97 CallsManager callsManager, 98 Intent intent) { 99 100 Uri handle = intent.getData(); 101 String scheme = handle.getScheme(); 102 String uriString = handle.getSchemeSpecificPart(); 103 104 // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme. 105 if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) { 106 handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null); 107 } 108 109 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( 110 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); 111 112 Bundle clientExtras = null; 113 if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) { 114 clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS); 115 } 116 if (clientExtras == null) { 117 clientExtras = new Bundle(); 118 } 119 120 // Ensure call subject is passed on to the connection service. 121 if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) { 122 String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT); 123 clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject); 124 } 125 126 final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 127 VideoProfile.STATE_AUDIO_ONLY); 128 clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); 129 130 if (!callsManager.isSelfManaged(phoneAccountHandle, 131 (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) { 132 boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent); 133 // Show the toast to warn user that it is a personal call though initiated in work 134 // profile. 135 if (fixedInitiatingUser) { 136 Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show(); 137 } 138 } else { 139 Log.i(CallIntentProcessor.class, 140 "processOutgoingCallIntent: skip initiating user check"); 141 } 142 143 UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER); 144 145 // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns 146 Call call = callsManager 147 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser, 148 intent); 149 150 if (call != null) { 151 sendNewOutgoingCallIntent(context, call, callsManager, intent); 152 } 153 } 154 155 static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager, 156 Intent intent) { 157 // Asynchronous calls should not usually be made inside a BroadcastReceiver because once 158 // onReceive is complete, the BroadcastReceiver's process runs the risk of getting 159 // killed if memory is scarce. However, this is OK here because the entire Telecom 160 // process will be running throughout the duration of the phone call and should never 161 // be killed. 162 final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false); 163 164 NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster( 165 context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(), 166 isPrivilegedDialer); 167 final int result = broadcaster.processIntent(); 168 final boolean success = result == DisconnectCause.NOT_DISCONNECTED; 169 170 if (!success && call != null) { 171 disconnectCallAndShowErrorDialog(context, call, result); 172 } 173 } 174 175 /** 176 * If the call is initiated from managed profile but there is no work dialer installed, treat 177 * the call is initiated from its parent user. 178 * 179 * @return whether the initiating user is fixed. 180 */ 181 static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) { 182 final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER); 183 if (UserUtil.isManagedProfile(context, initiatingUser)) { 184 boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context, 185 initiatingUser.getIdentifier()).size() == 0; 186 if (noDialerInstalled) { 187 final UserManager userManager = UserManager.get(context); 188 UserHandle parentUserHandle = 189 userManager.getProfileParent( 190 initiatingUser.getIdentifier()).getUserHandle(); 191 intent.putExtra(KEY_INITIATING_USER, parentUserHandle); 192 193 Log.i(CallIntentProcessor.class, "fixInitiatingUserIfNecessary: no dialer installed" 194 + " for current user; setting initiator to parent %s" + parentUserHandle); 195 return true; 196 } 197 } 198 return false; 199 } 200 201 static void processIncomingCallIntent(CallsManager callsManager, Intent intent) { 202 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( 203 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); 204 205 if (phoneAccountHandle == null) { 206 Log.w(CallIntentProcessor.class, 207 "Rejecting incoming call due to null phone account"); 208 return; 209 } 210 if (phoneAccountHandle.getComponentName() == null) { 211 Log.w(CallIntentProcessor.class, 212 "Rejecting incoming call due to null component name"); 213 return; 214 } 215 216 Bundle clientExtras = null; 217 if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) { 218 clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); 219 } 220 if (clientExtras == null) { 221 clientExtras = new Bundle(); 222 } 223 224 Log.d(CallIntentProcessor.class, 225 "Processing incoming call from connection service [%s]", 226 phoneAccountHandle.getComponentName()); 227 callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras); 228 } 229 230 static void processUnknownCallIntent(CallsManager callsManager, Intent intent) { 231 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( 232 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); 233 234 if (phoneAccountHandle == null) { 235 Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account"); 236 return; 237 } 238 if (phoneAccountHandle.getComponentName() == null) { 239 Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name"); 240 return; 241 } 242 243 callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras()); 244 } 245 246 private static void disconnectCallAndShowErrorDialog( 247 Context context, Call call, int errorCode) { 248 call.disconnect(); 249 final Intent errorIntent = new Intent(context, ErrorDialogActivity.class); 250 int errorMessageId = -1; 251 switch (errorCode) { 252 case DisconnectCause.INVALID_NUMBER: 253 case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: 254 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied; 255 break; 256 } 257 if (errorMessageId != -1) { 258 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId); 259 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 260 context.startActivityAsUser(errorIntent, UserHandle.CURRENT); 261 } 262 } 263 } 264