Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 1 | package com.android.server.telecom; |
| 2 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 3 | import com.android.server.telecom.components.ErrorDialogActivity; |
| 4 | |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 5 | import android.content.Context; |
| 6 | import android.content.Intent; |
| 7 | import android.net.Uri; |
| 8 | import android.os.Bundle; |
Yorke Lee | 9b71ea8 | 2014-11-26 11:15:13 -0800 | [diff] [blame] | 9 | import android.os.Trace; |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 10 | import android.os.UserHandle; |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 11 | import android.os.UserManager; |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 12 | import android.telecom.DefaultDialerManager; |
Brad Ebinger | a3eccfe | 2016-10-05 15:45:22 -0700 | [diff] [blame] | 13 | import android.telecom.Log; |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 14 | import android.telecom.PhoneAccount; |
| 15 | import android.telecom.PhoneAccountHandle; |
| 16 | import android.telecom.TelecomManager; |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 17 | import android.telecom.VideoProfile; |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 18 | import android.telephony.DisconnectCause; |
| 19 | import android.telephony.PhoneNumberUtils; |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 20 | import android.widget.Toast; |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 21 | |
| 22 | /** |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 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. |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 27 | */ |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 28 | public class CallIntentProcessor { |
Hall Liu | ecda554 | 2015-12-04 11:31:31 -0800 | [diff] [blame] | 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 | } |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 53 | |
Ihab Awad | 8d5d9dd | 2015-03-12 11:11:06 -0700 | [diff] [blame] | 54 | public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call"; |
| 55 | public static final String KEY_IS_INCOMING_CALL = "is_incoming_call"; |
Yorke Lee | 8105072 | 2015-04-23 19:57:10 -0700 | [diff] [blame] | 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"; |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 61 | |
Tony Mak | 578a4e6 | 2015-11-23 11:18:51 +0000 | [diff] [blame] | 62 | /** |
| 63 | * The user initiating the outgoing call. |
| 64 | */ |
| 65 | public static final String KEY_INITIATING_USER = "initiating_user"; |
| 66 | |
| 67 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 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) { |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 77 | final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false); |
Yorke Lee | e732ef4 | 2014-10-14 11:53:16 -0700 | [diff] [blame] | 78 | Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 79 | |
Yorke Lee | 9b71ea8 | 2014-11-26 11:15:13 -0800 | [diff] [blame] | 80 | Trace.beginSection("processNewCallCallIntent"); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 81 | if (isUnknownCall) { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 82 | processUnknownCallIntent(mCallsManager, intent); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 83 | } else { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 84 | processOutgoingCallIntent(mContext, mCallsManager, intent); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 85 | } |
Yorke Lee | 9b71ea8 | 2014-11-26 11:15:13 -0800 | [diff] [blame] | 86 | Trace.endSection(); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 87 | } |
| 88 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 89 | |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 90 | /** |
| 91 | * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents. |
| 92 | * |
| 93 | * @param intent Call intent containing data about the handle to call. |
| 94 | */ |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 95 | static void processOutgoingCallIntent( |
| 96 | Context context, |
| 97 | CallsManager callsManager, |
| 98 | Intent intent) { |
Andrew Lee | 4550623 | 2014-10-22 17:54:33 -0700 | [diff] [blame] | 99 | |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 100 | Uri handle = intent.getData(); |
| 101 | String scheme = handle.getScheme(); |
| 102 | String uriString = handle.getSchemeSpecificPart(); |
| 103 | |
| 104 | if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) { |
| 105 | handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ? |
| 106 | PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, 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) { |
Yorke Lee | a49ede1 | 2015-03-23 11:15:35 -0700 | [diff] [blame] | 117 | clientExtras = new Bundle(); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 118 | } |
| 119 | |
Tyler Gunn | 24b6d77 | 2015-08-03 08:45:40 -0700 | [diff] [blame] | 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 | |
Omkar Kolangade | 005612a | 2015-08-25 11:08:18 -0700 | [diff] [blame] | 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 | |
Tony Mak | d7aaa1c | 2016-02-17 17:37:22 +0000 | [diff] [blame] | 130 | boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent); |
| 131 | // Show the toast to warn user that it is a personal call though initiated in work profile. |
| 132 | if (fixedInitiatingUser) { |
| 133 | Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show(); |
| 134 | } |
| 135 | |
Tony Mak | 578a4e6 | 2015-11-23 11:18:51 +0000 | [diff] [blame] | 136 | UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER); |
| 137 | |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 138 | // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns |
Tony Mak | 578a4e6 | 2015-11-23 11:18:51 +0000 | [diff] [blame] | 139 | Call call = callsManager |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 140 | .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser, |
| 141 | intent); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 142 | |
| 143 | if (call != null) { |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 144 | sendNewOutgoingCallIntent(context, call, callsManager, intent); |
| 145 | } |
| 146 | } |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 147 | |
Tyler Gunn | bbd78a7 | 2017-04-30 14:16:07 -0700 | [diff] [blame] | 148 | static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager, |
| 149 | Intent intent) { |
| 150 | // Asynchronous calls should not usually be made inside a BroadcastReceiver because once |
| 151 | // onReceive is complete, the BroadcastReceiver's process runs the risk of getting |
| 152 | // killed if memory is scarce. However, this is OK here because the entire Telecom |
| 153 | // process will be running throughout the duration of the phone call and should never |
| 154 | // be killed. |
| 155 | final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false); |
| 156 | |
| 157 | NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster( |
| 158 | context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(), |
| 159 | isPrivilegedDialer); |
| 160 | final int result = broadcaster.processIntent(); |
| 161 | final boolean success = result == DisconnectCause.NOT_DISCONNECTED; |
| 162 | |
| 163 | if (!success && call != null) { |
| 164 | disconnectCallAndShowErrorDialog(context, call, result); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 165 | } |
| 166 | } |
| 167 | |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 168 | /** |
| 169 | * If the call is initiated from managed profile but there is no work dialer installed, treat |
| 170 | * the call is initiated from its parent user. |
Tony Mak | d7aaa1c | 2016-02-17 17:37:22 +0000 | [diff] [blame] | 171 | * |
| 172 | * @return whether the initiating user is fixed. |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 173 | */ |
Tony Mak | d7aaa1c | 2016-02-17 17:37:22 +0000 | [diff] [blame] | 174 | static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) { |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 175 | final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER); |
| 176 | if (UserUtil.isManagedProfile(context, initiatingUser)) { |
| 177 | boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context, |
| 178 | initiatingUser.getIdentifier()).size() == 0; |
| 179 | if (noDialerInstalled) { |
| 180 | final UserManager userManager = UserManager.get(context); |
| 181 | UserHandle parentUserHandle = |
| 182 | userManager.getProfileParent( |
| 183 | initiatingUser.getIdentifier()).getUserHandle(); |
| 184 | intent.putExtra(KEY_INITIATING_USER, parentUserHandle); |
Tony Mak | d7aaa1c | 2016-02-17 17:37:22 +0000 | [diff] [blame] | 185 | return true; |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 186 | } |
| 187 | } |
Tony Mak | d7aaa1c | 2016-02-17 17:37:22 +0000 | [diff] [blame] | 188 | return false; |
Tony Mak | 2221405 | 2016-01-19 18:50:44 +0000 | [diff] [blame] | 189 | } |
| 190 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 191 | static void processIncomingCallIntent(CallsManager callsManager, Intent intent) { |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 192 | PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( |
| 193 | TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); |
| 194 | |
| 195 | if (phoneAccountHandle == null) { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 196 | Log.w(CallIntentProcessor.class, |
| 197 | "Rejecting incoming call due to null phone account"); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 198 | return; |
| 199 | } |
| 200 | if (phoneAccountHandle.getComponentName() == null) { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 201 | Log.w(CallIntentProcessor.class, |
| 202 | "Rejecting incoming call due to null component name"); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 203 | return; |
| 204 | } |
| 205 | |
| 206 | Bundle clientExtras = null; |
| 207 | if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) { |
| 208 | clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS); |
| 209 | } |
| 210 | if (clientExtras == null) { |
Yorke Lee | a49ede1 | 2015-03-23 11:15:35 -0700 | [diff] [blame] | 211 | clientExtras = new Bundle(); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 212 | } |
| 213 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 214 | Log.d(CallIntentProcessor.class, |
| 215 | "Processing incoming call from connection service [%s]", |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 216 | phoneAccountHandle.getComponentName()); |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 217 | callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 218 | } |
| 219 | |
David Stevens | 0785274 | 2015-04-10 18:22:47 -0700 | [diff] [blame] | 220 | static void processUnknownCallIntent(CallsManager callsManager, Intent intent) { |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 221 | PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra( |
| 222 | TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); |
| 223 | |
| 224 | if (phoneAccountHandle == null) { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 225 | Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account"); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 226 | return; |
| 227 | } |
| 228 | if (phoneAccountHandle.getComponentName() == null) { |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 229 | Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name"); |
Yorke Lee | 9250e5f | 2014-10-01 13:39:09 -0700 | [diff] [blame] | 230 | return; |
| 231 | } |
| 232 | |
Ihab Awad | 78a5e6b | 2015-02-06 10:13:05 -0800 | [diff] [blame] | 233 | callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras()); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 234 | } |
| 235 | |
Andrew Lee | 72d271c | 2014-10-03 11:58:06 -0700 | [diff] [blame] | 236 | private static void disconnectCallAndShowErrorDialog( |
| 237 | Context context, Call call, int errorCode) { |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 238 | call.disconnect(); |
| 239 | final Intent errorIntent = new Intent(context, ErrorDialogActivity.class); |
| 240 | int errorMessageId = -1; |
| 241 | switch (errorCode) { |
| 242 | case DisconnectCause.INVALID_NUMBER: |
| 243 | case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED: |
| 244 | errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied; |
| 245 | break; |
| 246 | } |
| 247 | if (errorMessageId != -1) { |
| 248 | errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId); |
Yorke Lee | 94c7ab2 | 2015-04-30 14:59:12 -0700 | [diff] [blame] | 249 | errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| 250 | context.startActivityAsUser(errorIntent, UserHandle.CURRENT); |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 251 | } |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 252 | } |
Yorke Lee | 6dc1c75 | 2014-09-23 18:50:35 -0700 | [diff] [blame] | 253 | } |