• 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         @Override
processOutgoingCallIntent(Context context, CallsManager callsManager, Intent intent, String callingPackage)42         public void processOutgoingCallIntent(Context context, CallsManager callsManager,
43                 Intent intent, String callingPackage) {
44             CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent,
45                     callingPackage);
46         }
47 
48         @Override
processIncomingCallIntent(CallsManager callsManager, Intent intent)49         public void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
50             CallIntentProcessor.processIncomingCallIntent(callsManager, intent);
51         }
52 
53         @Override
processUnknownCallIntent(CallsManager callsManager, Intent intent)54         public void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
55             CallIntentProcessor.processUnknownCallIntent(callsManager, intent);
56         }
57     }
58 
59     public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
60     public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
61     /*
62      *  Whether or not the dialer initiating this outgoing call is the default dialer, or system
63      *  dialer and thus allowed to make emergency calls.
64      */
65     public static final String KEY_IS_PRIVILEGED_DIALER = "is_privileged_dialer";
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 
CallIntentProcessor(Context context, CallsManager callsManager)76     public CallIntentProcessor(Context context, CallsManager callsManager) {
77         this.mContext = context;
78         this.mCallsManager = callsManager;
79     }
80 
processIntent(Intent intent, String callingPackage)81     public void processIntent(Intent intent, String callingPackage) {
82         final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
83         Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
84 
85         Trace.beginSection("processNewCallCallIntent");
86         if (isUnknownCall) {
87             processUnknownCallIntent(mCallsManager, intent);
88         } else {
89             processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage);
90         }
91         Trace.endSection();
92     }
93 
94 
95     /**
96      * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
97      *
98      * @param intent Call intent containing data about the handle to call.
99      * @param callingPackage The package which initiated the outgoing call (if known).
100      */
processOutgoingCallIntent( Context context, CallsManager callsManager, Intent intent, String callingPackage)101     static void processOutgoingCallIntent(
102             Context context,
103             CallsManager callsManager,
104             Intent intent,
105             String callingPackage) {
106 
107         Uri handle = intent.getData();
108         String scheme = handle.getScheme();
109         String uriString = handle.getSchemeSpecificPart();
110 
111         // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
112         if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
113             handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
114         }
115 
116         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
117                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
118 
119         Bundle clientExtras = null;
120         if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
121             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
122         }
123         if (clientExtras == null) {
124             clientExtras = new Bundle();
125         }
126 
127         if (intent.hasExtra(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)) {
128             clientExtras.putBoolean(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL,
129                     intent.getBooleanExtra(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL,
130                             false));
131         }
132 
133         // Ensure call subject is passed on to the connection service.
134         if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
135             String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
136             clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
137         }
138 
139         final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
140                 VideoProfile.STATE_AUDIO_ONLY);
141         clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
142 
143         if (!callsManager.isSelfManaged(phoneAccountHandle,
144                 (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
145             boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
146             // Show the toast to warn user that it is a personal call though initiated in work
147             // profile.
148             if (fixedInitiatingUser) {
149                 Toast.makeText(context, Looper.getMainLooper(),
150                         context.getString(R.string.toast_personal_call_msg),
151                         Toast.LENGTH_LONG).show();
152             }
153         } else {
154             Log.i(CallIntentProcessor.class,
155                     "processOutgoingCallIntent: skip initiating user check");
156         }
157 
158         UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
159 
160         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
161         CompletableFuture<Call> callFuture = callsManager
162                 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
163                         intent, callingPackage);
164 
165         final Session logSubsession = Log.createSubsession();
166         callFuture.thenAccept((call) -> {
167             if (call != null) {
168                 Log.continueSession(logSubsession, "CIP.sNOCI");
169                 try {
170                     sendNewOutgoingCallIntent(context, call, callsManager, intent);
171                 } finally {
172                     Log.endSession();
173                 }
174             }
175         });
176     }
177 
sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager, Intent intent)178     static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
179             Intent intent) {
180         // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
181         // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
182         // killed if memory is scarce. However, this is OK here because the entire Telecom
183         // process will be running throughout the duration of the phone call and should never
184         // be killed.
185         final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
186 
187         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
188                 context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
189                 isPrivilegedDialer);
190 
191         // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
192         NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
193         if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
194             disconnectCallAndShowErrorDialog(context, call, disposition.disconnectCause);
195             return;
196         }
197 
198         broadcaster.processCall(disposition);
199     }
200 
201     /**
202      * If the call is initiated from managed profile but there is no work dialer installed, treat
203      * the call is initiated from its parent user.
204      *
205      * @return whether the initiating user is fixed.
206      */
fixInitiatingUserIfNecessary(Context context, Intent intent)207     static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) {
208         final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
209         if (UserUtil.isManagedProfile(context, initiatingUser)) {
210             boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context,
211                     initiatingUser.getIdentifier()).size() == 0;
212             if (noDialerInstalled) {
213                 final UserManager userManager = UserManager.get(context);
214                 UserHandle parentUserHandle =
215                         userManager.getProfileParent(
216                                 initiatingUser.getIdentifier()).getUserHandle();
217                 intent.putExtra(KEY_INITIATING_USER, parentUserHandle);
218 
219                 Log.i(CallIntentProcessor.class, "fixInitiatingUserIfNecessary: no dialer installed"
220                         + " for current user; setting initiator to parent %s" + parentUserHandle);
221                 return true;
222             }
223         }
224         return false;
225     }
226 
processIncomingCallIntent(CallsManager callsManager, Intent intent)227     static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
228         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
229                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
230 
231         if (phoneAccountHandle == null) {
232             Log.w(CallIntentProcessor.class,
233                     "Rejecting incoming call due to null phone account");
234             return;
235         }
236         if (phoneAccountHandle.getComponentName() == null) {
237             Log.w(CallIntentProcessor.class,
238                     "Rejecting incoming call due to null component name");
239             return;
240         }
241 
242         Bundle clientExtras = null;
243         if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
244             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
245         }
246         if (clientExtras == null) {
247             clientExtras = new Bundle();
248         }
249 
250         Log.d(CallIntentProcessor.class,
251                 "Processing incoming call from connection service [%s]",
252                 phoneAccountHandle.getComponentName());
253         callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
254     }
255 
processUnknownCallIntent(CallsManager callsManager, Intent intent)256     static void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
257         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
258                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
259 
260         if (phoneAccountHandle == null) {
261             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account");
262             return;
263         }
264         if (phoneAccountHandle.getComponentName() == null) {
265             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name");
266             return;
267         }
268 
269         callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras());
270     }
271 
disconnectCallAndShowErrorDialog( Context context, Call call, int errorCode)272     private static void disconnectCallAndShowErrorDialog(
273             Context context, Call call, int errorCode) {
274         call.disconnect();
275         final Intent errorIntent = new Intent(context, ErrorDialogActivity.class);
276         int errorMessageId = -1;
277         switch (errorCode) {
278             case DisconnectCause.INVALID_NUMBER:
279             case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
280                 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
281                 break;
282         }
283         if (errorMessageId != -1) {
284             errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
285             errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
286             context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
287         }
288     }
289 }
290