blob: 3a327c8c8e8a98753e1d268d382b9cd4f0ede6ef [file] [log] [blame]
Yorke Lee6dc1c752014-09-23 18:50:35 -07001package com.android.server.telecom;
2
Ihab Awad78a5e6b2015-02-06 10:13:05 -08003import com.android.server.telecom.components.ErrorDialogActivity;
4
Yorke Lee6dc1c752014-09-23 18:50:35 -07005import android.content.Context;
6import android.content.Intent;
7import android.net.Uri;
8import android.os.Bundle;
Yorke Lee9b71ea82014-11-26 11:15:13 -08009import android.os.Trace;
Yorke Lee6dc1c752014-09-23 18:50:35 -070010import android.os.UserHandle;
Tony Mak22214052016-01-19 18:50:44 +000011import android.os.UserManager;
Tyler Gunn24b6d772015-08-03 08:45:40 -070012import android.telecom.Connection;
Tony Mak22214052016-01-19 18:50:44 +000013import android.telecom.DefaultDialerManager;
Yorke Lee6dc1c752014-09-23 18:50:35 -070014import android.telecom.PhoneAccount;
15import android.telecom.PhoneAccountHandle;
16import android.telecom.TelecomManager;
Andrew Lee45506232014-10-22 17:54:33 -070017import android.telecom.VideoProfile;
Yorke Lee6dc1c752014-09-23 18:50:35 -070018import android.telephony.DisconnectCause;
19import android.telephony.PhoneNumberUtils;
Andrew Lee45506232014-10-22 17:54:33 -070020import android.widget.Toast;
Yorke Lee6dc1c752014-09-23 18:50:35 -070021
22/**
Ihab Awad8d5d9dd2015-03-12 11:11:06 -070023 * 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 Lee6dc1c752014-09-23 18:50:35 -070027 */
Ihab Awad78a5e6b2015-02-06 10:13:05 -080028public class CallIntentProcessor {
Hall Liuecda5542015-12-04 11:31:31 -080029 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 Lee6dc1c752014-09-23 18:50:35 -070053
Ihab Awad8d5d9dd2015-03-12 11:11:06 -070054 public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
55 public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
Yorke Lee81050722015-04-23 19:57:10 -070056 /*
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 Lee6dc1c752014-09-23 18:50:35 -070061
Tony Mak578a4e62015-11-23 11:18:51 +000062 /**
63 * The user initiating the outgoing call.
64 */
65 public static final String KEY_INITIATING_USER = "initiating_user";
66
67
Ihab Awad78a5e6b2015-02-06 10:13:05 -080068 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 Lee9250e5f2014-10-01 13:39:09 -070077 final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
Yorke Leee732ef42014-10-14 11:53:16 -070078 Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
Yorke Lee6dc1c752014-09-23 18:50:35 -070079
Yorke Lee9b71ea82014-11-26 11:15:13 -080080 Trace.beginSection("processNewCallCallIntent");
Yorke Lee9250e5f2014-10-01 13:39:09 -070081 if (isUnknownCall) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -080082 processUnknownCallIntent(mCallsManager, intent);
Yorke Lee6dc1c752014-09-23 18:50:35 -070083 } else {
Ihab Awad78a5e6b2015-02-06 10:13:05 -080084 processOutgoingCallIntent(mContext, mCallsManager, intent);
Yorke Lee6dc1c752014-09-23 18:50:35 -070085 }
Yorke Lee9b71ea82014-11-26 11:15:13 -080086 Trace.endSection();
Yorke Lee6dc1c752014-09-23 18:50:35 -070087 }
88
Ihab Awad78a5e6b2015-02-06 10:13:05 -080089
Yorke Lee6dc1c752014-09-23 18:50:35 -070090 /**
91 * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
92 *
93 * @param intent Call intent containing data about the handle to call.
94 */
Ihab Awad78a5e6b2015-02-06 10:13:05 -080095 static void processOutgoingCallIntent(
96 Context context,
97 CallsManager callsManager,
98 Intent intent) {
Andrew Lee45506232014-10-22 17:54:33 -070099
Yorke Lee6dc1c752014-09-23 18:50:35 -0700100 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 Leea49ede12015-03-23 11:15:35 -0700117 clientExtras = new Bundle();
Yorke Lee6dc1c752014-09-23 18:50:35 -0700118 }
119
Tyler Gunn24b6d772015-08-03 08:45:40 -0700120 // 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
Yorke Lee81050722015-04-23 19:57:10 -0700126 final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
Yorke Lee6dc1c752014-09-23 18:50:35 -0700127
Tony Makd7aaa1c2016-02-17 17:37:22 +0000128 boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
129 // Show the toast to warn user that it is a personal call though initiated in work profile.
130 if (fixedInitiatingUser) {
131 Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
132 }
133
Tony Mak578a4e62015-11-23 11:18:51 +0000134 UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
135
Yorke Lee6dc1c752014-09-23 18:50:35 -0700136 // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Tony Mak578a4e62015-11-23 11:18:51 +0000137 Call call = callsManager
138 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
Yorke Lee6dc1c752014-09-23 18:50:35 -0700139
140 if (call != null) {
141 // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
142 // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
143 // killed if memory is scarce. However, this is OK here because the entire Telecom
144 // process will be running throughout the duration of the phone call and should never
145 // be killed.
146 NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
Hall Liu220b4192015-12-11 11:33:08 -0800147 context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(),
148 isPrivilegedDialer);
Yorke Lee6dc1c752014-09-23 18:50:35 -0700149 final int result = broadcaster.processIntent();
150 final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
151
152 if (!success && call != null) {
153 disconnectCallAndShowErrorDialog(context, call, result);
154 }
155 }
156 }
157
Tony Mak22214052016-01-19 18:50:44 +0000158 /**
159 * If the call is initiated from managed profile but there is no work dialer installed, treat
160 * the call is initiated from its parent user.
Tony Makd7aaa1c2016-02-17 17:37:22 +0000161 *
162 * @return whether the initiating user is fixed.
Tony Mak22214052016-01-19 18:50:44 +0000163 */
Tony Makd7aaa1c2016-02-17 17:37:22 +0000164 static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) {
Tony Mak22214052016-01-19 18:50:44 +0000165 final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
166 if (UserUtil.isManagedProfile(context, initiatingUser)) {
167 boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context,
168 initiatingUser.getIdentifier()).size() == 0;
169 if (noDialerInstalled) {
170 final UserManager userManager = UserManager.get(context);
171 UserHandle parentUserHandle =
172 userManager.getProfileParent(
173 initiatingUser.getIdentifier()).getUserHandle();
174 intent.putExtra(KEY_INITIATING_USER, parentUserHandle);
Tony Makd7aaa1c2016-02-17 17:37:22 +0000175 return true;
Tony Mak22214052016-01-19 18:50:44 +0000176 }
177 }
Tony Makd7aaa1c2016-02-17 17:37:22 +0000178 return false;
Tony Mak22214052016-01-19 18:50:44 +0000179 }
180
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800181 static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
Yorke Lee6dc1c752014-09-23 18:50:35 -0700182 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
183 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
184
185 if (phoneAccountHandle == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800186 Log.w(CallIntentProcessor.class,
187 "Rejecting incoming call due to null phone account");
Yorke Lee6dc1c752014-09-23 18:50:35 -0700188 return;
189 }
190 if (phoneAccountHandle.getComponentName() == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800191 Log.w(CallIntentProcessor.class,
192 "Rejecting incoming call due to null component name");
Yorke Lee6dc1c752014-09-23 18:50:35 -0700193 return;
194 }
195
196 Bundle clientExtras = null;
197 if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
198 clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
199 }
200 if (clientExtras == null) {
Yorke Leea49ede12015-03-23 11:15:35 -0700201 clientExtras = new Bundle();
Yorke Lee6dc1c752014-09-23 18:50:35 -0700202 }
203
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800204 Log.d(CallIntentProcessor.class,
205 "Processing incoming call from connection service [%s]",
Yorke Lee6dc1c752014-09-23 18:50:35 -0700206 phoneAccountHandle.getComponentName());
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800207 callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
Yorke Lee6dc1c752014-09-23 18:50:35 -0700208 }
209
David Stevens07852742015-04-10 18:22:47 -0700210 static void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
Yorke Lee9250e5f2014-10-01 13:39:09 -0700211 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
212 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
213
214 if (phoneAccountHandle == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800215 Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account");
Yorke Lee9250e5f2014-10-01 13:39:09 -0700216 return;
217 }
218 if (phoneAccountHandle.getComponentName() == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800219 Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name");
Yorke Lee9250e5f2014-10-01 13:39:09 -0700220 return;
221 }
222
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800223 callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras());
Yorke Lee6dc1c752014-09-23 18:50:35 -0700224 }
225
Andrew Lee72d271c2014-10-03 11:58:06 -0700226 private static void disconnectCallAndShowErrorDialog(
227 Context context, Call call, int errorCode) {
Yorke Lee6dc1c752014-09-23 18:50:35 -0700228 call.disconnect();
229 final Intent errorIntent = new Intent(context, ErrorDialogActivity.class);
230 int errorMessageId = -1;
231 switch (errorCode) {
232 case DisconnectCause.INVALID_NUMBER:
233 case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
234 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
235 break;
236 }
237 if (errorMessageId != -1) {
238 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
Yorke Lee94c7ab22015-04-30 14:59:12 -0700239 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
240 context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
Yorke Lee6dc1c752014-09-23 18:50:35 -0700241 }
Yorke Lee6dc1c752014-09-23 18:50:35 -0700242 }
Yorke Lee6dc1c752014-09-23 18:50:35 -0700243}