• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Looper;
10 import android.os.Trace;
11 import android.os.UserHandle;
12 import android.os.UserManager;
13 import android.telecom.DefaultDialerManager;
14 import android.telecom.Log;
15 import android.telecom.Logging.Session;
16 import android.telecom.PhoneAccount;
17 import android.telecom.PhoneAccountHandle;
18 import android.telecom.TelecomManager;
19 import android.telecom.VideoProfile;
20 import android.telephony.DisconnectCause;
21 import android.telephony.PhoneNumberUtils;
22 import android.widget.Toast;
23 
24 import java.util.concurrent.CompletableFuture;
25 
26 /**
27  * Single point of entry for all outgoing and incoming calls.
28  * {@link com.android.server.telecom.components.UserCallIntentProcessor} serves as a trampoline that
29  * captures call intents for individual users and forwards it to the {@link CallIntentProcessor}
30  * which interacts with the rest of Telecom, both of which run only as the primary user.
31  */
32 public class CallIntentProcessor {
33     public interface Adapter {
processOutgoingCallIntent(Context context, CallsManager callsManager, Intent intent, String callingPackage)34         void processOutgoingCallIntent(Context context, CallsManager callsManager,
35                 Intent intent, String callingPackage);
processIncomingCallIntent(CallsManager callsManager, Intent intent)36         void processIncomingCallIntent(CallsManager callsManager, Intent intent);
processUnknownCallIntent(CallsManager callsManager, Intent intent)37         void processUnknownCallIntent(CallsManager callsManager, Intent intent);
38     }
39 
40     public static class AdapterImpl implements Adapter {
41         private final DefaultDialerCache mDefaultDialerCache;
AdapterImpl(DefaultDialerCache cache)42         public AdapterImpl(DefaultDialerCache cache) {
43             mDefaultDialerCache = cache;
44         }
45 
46         @Override
processOutgoingCallIntent(Context context, CallsManager callsManager, Intent intent, String callingPackage)47         public void processOutgoingCallIntent(Context context, CallsManager callsManager,
48                 Intent intent, String callingPackage) {
49             CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent,
50                     callingPackage, mDefaultDialerCache);
51         }
52 
53         @Override
processIncomingCallIntent(CallsManager callsManager, Intent intent)54         public void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
55             CallIntentProcessor.processIncomingCallIntent(callsManager, intent);
56         }
57 
58         @Override
processUnknownCallIntent(CallsManager callsManager, Intent intent)59         public void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
60             CallIntentProcessor.processUnknownCallIntent(callsManager, intent);
61         }
62     }
63 
64     public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
65     public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
66 
67     /**
68      * The user initiating the outgoing call.
69      */
70     public static final String KEY_INITIATING_USER = "initiating_user";
71 
72 
73     private final Context mContext;
74     private final CallsManager mCallsManager;
75     private final DefaultDialerCache mDefaultDialerCache;
76 
CallIntentProcessor(Context context, CallsManager callsManager, DefaultDialerCache defaultDialerCache)77     public CallIntentProcessor(Context context, CallsManager callsManager,
78             DefaultDialerCache defaultDialerCache) {
79         this.mContext = context;
80         this.mCallsManager = callsManager;
81         this.mDefaultDialerCache = defaultDialerCache;
82     }
83 
processIntent(Intent intent, String callingPackage)84     public void processIntent(Intent intent, String callingPackage) {
85         final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
86         Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
87 
88         Trace.beginSection("processNewCallCallIntent");
89         if (isUnknownCall) {
90             processUnknownCallIntent(mCallsManager, intent);
91         } else {
92             processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage,
93                     mDefaultDialerCache);
94         }
95         Trace.endSection();
96     }
97 
98 
99     /**
100      * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
101      *
102      * @param intent Call intent containing data about the handle to call.
103      * @param callingPackage The package which initiated the outgoing call (if known).
104      */
processOutgoingCallIntent( Context context, CallsManager callsManager, Intent intent, String callingPackage, DefaultDialerCache defaultDialerCache)105     static void processOutgoingCallIntent(
106             Context context,
107             CallsManager callsManager,
108             Intent intent,
109             String callingPackage,
110             DefaultDialerCache defaultDialerCache) {
111 
112         Uri handle = intent.getData();
113         String scheme = handle.getScheme();
114         String uriString = handle.getSchemeSpecificPart();
115 
116         // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
117         if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
118             handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
119         }
120 
121         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
122                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
123 
124         Bundle clientExtras = null;
125         if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
126             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
127         }
128         if (clientExtras == null) {
129             clientExtras = new Bundle();
130         }
131 
132         if (intent.hasExtra(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)) {
133             clientExtras.putBoolean(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL,
134                     intent.getBooleanExtra(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL,
135                             false));
136         }
137 
138         // Ensure call subject is passed on to the connection service.
139         if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
140             String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
141             clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
142         }
143 
144         if (intent.hasExtra(android.telecom.TelecomManager.EXTRA_PRIORITY)) {
145             clientExtras.putInt(android.telecom.TelecomManager.EXTRA_PRIORITY, intent.getIntExtra(
146                     android.telecom.TelecomManager.EXTRA_PRIORITY,
147                             android.telecom.TelecomManager.PRIORITY_NORMAL));
148         }
149 
150         if (intent.hasExtra(android.telecom.TelecomManager.EXTRA_LOCATION)) {
151             clientExtras.putParcelable(android.telecom.TelecomManager.EXTRA_LOCATION,
152                     intent.getParcelableExtra(android.telecom.TelecomManager.EXTRA_LOCATION));
153         }
154 
155         if (intent.hasExtra(android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE)) {
156             clientExtras.putParcelable(android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE,
157                     intent.getParcelableExtra(
158                             android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE));
159         }
160 
161         final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
162                 VideoProfile.STATE_AUDIO_ONLY);
163         clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
164 
165         if (!callsManager.isSelfManaged(phoneAccountHandle,
166                 (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
167             boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
168             // Show the toast to warn user that it is a personal call though initiated in work
169             // profile.
170             if (fixedInitiatingUser) {
171                 Toast.makeText(context, Looper.getMainLooper(),
172                         context.getString(R.string.toast_personal_call_msg),
173                         Toast.LENGTH_LONG).show();
174             }
175         } else {
176             Log.i(CallIntentProcessor.class,
177                     "processOutgoingCallIntent: skip initiating user check");
178         }
179 
180         UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
181 
182         boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
183                 initiatingUser.getIdentifier());
184 
185 
186         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
187                 context, callsManager, intent, callsManager.getPhoneNumberUtilsAdapter(),
188                 isPrivilegedDialer, defaultDialerCache, new MmiUtils());
189 
190         // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
191         NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
192         if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
193             showErrorDialog(context, disposition.disconnectCause);
194             return;
195         }
196 
197         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
198         CompletableFuture<Call> callFuture = callsManager
199                 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
200                         intent, callingPackage);
201 
202         final Session logSubsession = Log.createSubsession();
203         callFuture.thenAccept((call) -> {
204             if (call != null) {
205                 Log.continueSession(logSubsession, "CIP.sNOCI");
206                 try {
207                     broadcaster.processCall(call, disposition);
208                 } finally {
209                     Log.endSession();
210                 }
211             }
212         });
213     }
214 
215     /**
216      * If the call is initiated from managed profile but there is no work dialer installed, treat
217      * the call is initiated from its parent user.
218      *
219      * @return whether the initiating user is fixed.
220      */
fixInitiatingUserIfNecessary(Context context, Intent intent)221     static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) {
222         final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
223         if (UserUtil.isManagedProfile(context, initiatingUser)) {
224             boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context,
225                     initiatingUser.getIdentifier()).size() == 0;
226             if (noDialerInstalled) {
227                 final UserManager userManager = UserManager.get(context);
228                 UserHandle parentUserHandle =
229                         userManager.getProfileParent(
230                                 initiatingUser.getIdentifier()).getUserHandle();
231                 intent.putExtra(KEY_INITIATING_USER, parentUserHandle);
232 
233                 Log.i(CallIntentProcessor.class, "fixInitiatingUserIfNecessary: no dialer installed"
234                         + " for current user; setting initiator to parent %s" + parentUserHandle);
235                 return true;
236             }
237         }
238         return false;
239     }
240 
processIncomingCallIntent(CallsManager callsManager, Intent intent)241     static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
242         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
243                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
244 
245         if (phoneAccountHandle == null) {
246             Log.w(CallIntentProcessor.class,
247                     "Rejecting incoming call due to null phone account");
248             return;
249         }
250         if (phoneAccountHandle.getComponentName() == null) {
251             Log.w(CallIntentProcessor.class,
252                     "Rejecting incoming call due to null component name");
253             return;
254         }
255 
256         Bundle clientExtras = null;
257         if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
258             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
259         }
260         if (clientExtras == null) {
261             clientExtras = new Bundle();
262         }
263 
264         Log.d(CallIntentProcessor.class,
265                 "Processing incoming call from connection service [%s]",
266                 phoneAccountHandle.getComponentName());
267         callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
268     }
269 
processUnknownCallIntent(CallsManager callsManager, Intent intent)270     static void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
271         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
272                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
273 
274         if (phoneAccountHandle == null) {
275             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account");
276             return;
277         }
278         if (phoneAccountHandle.getComponentName() == null) {
279             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name");
280             return;
281         }
282 
283         callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras());
284     }
285 
showErrorDialog(Context context, int errorCode)286     private static void showErrorDialog(Context context, int errorCode) {
287         final Intent errorIntent = new Intent(context, ErrorDialogActivity.class);
288         int errorMessageId = -1;
289         switch (errorCode) {
290             case DisconnectCause.INVALID_NUMBER:
291             case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
292                 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
293                 break;
294         }
295         if (errorMessageId != -1) {
296             errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
297             errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
298             context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
299         }
300     }
301 }
302