• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.telecom;
18 
19 import android.app.Activity;
20 import android.app.AppOpsManager;
21 import android.app.BroadcastOptions;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.Trace;
28 import android.os.UserHandle;
29 import android.telecom.GatewayInfo;
30 import android.telecom.Log;
31 import android.telecom.PhoneAccount;
32 import android.telecom.PhoneAccountHandle;
33 import android.telecom.TelecomManager;
34 import android.telecom.VideoProfile;
35 import android.telephony.DisconnectCause;
36 import android.telephony.TelephonyManager;
37 import android.text.TextUtils;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.server.telecom.callredirection.CallRedirectionProcessor;
41 
42 // TODO: Needed for move to system service: import com.android.internal.R;
43 
44 /**
45  * OutgoingCallIntentBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the
46  * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which
47  * contains the phone number being dialed. Applications can use this intent to (1) see which numbers
48  * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call
49  * from being placed.
50  *
51  * After the other applications have had a chance to see the ACTION_NEW_OUTGOING_CALL intent, it
52  * finally reaches the {@link NewOutgoingCallBroadcastIntentReceiver}.
53  *
54  * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail
55  * number) are exempt from being broadcast.
56  *
57  * Calls to emergency numbers are still broadcast for informative purposes. The call is placed
58  * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
59  */
60 @VisibleForTesting
61 public class NewOutgoingCallIntentBroadcaster {
62     /**
63      * Legacy string constants used to retrieve gateway provider extras from intents. These still
64      * need to be copied from the source call intent to the destination intent in order to
65      * support third party gateway providers that are still using old string constants in
66      * Telephony.
67      */
68     public static final String EXTRA_GATEWAY_PROVIDER_PACKAGE =
69             "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
70     public static final String EXTRA_GATEWAY_URI = "com.android.phone.extra.GATEWAY_URI";
71 
72     private final CallsManager mCallsManager;
73     private Call mCall;
74     private final Intent mIntent;
75     private final Context mContext;
76     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
77     private final TelecomSystem.SyncRoot mLock;
78     private final DefaultDialerCache mDefaultDialerCache;
79     private final MmiUtils mMmiUtils;
80 
81     /*
82      * Whether or not the outgoing call intent originated from the default phone application. If
83      * so, it will be allowed to make emergency calls, even with the ACTION_CALL intent.
84      */
85     private final boolean mIsDefaultOrSystemPhoneApp;
86 
87     public static class CallDisposition {
88         // True for certain types of numbers that are not intended to be intercepted or modified
89         // by third parties (e.g. emergency numbers).
90         public boolean callImmediately = false;
91         // True for all managed calls, false for self-managed calls.
92         public boolean sendBroadcast = true;
93         // True for requesting call redirection, false for not requesting it.
94         public boolean requestRedirection = true;
95         public int disconnectCause = DisconnectCause.NOT_DISCONNECTED;
96         String number;
97         Uri callingAddress;
98     }
99 
100     @VisibleForTesting
NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache, MmiUtils mmiUtils)101     public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager,
102             Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
103             boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache, MmiUtils mmiUtils) {
104         mContext = context;
105         mCallsManager = callsManager;
106         mIntent = intent;
107         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
108         mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp;
109         mLock = mCallsManager.getLock();
110         mDefaultDialerCache = defaultDialerCache;
111         mMmiUtils = mmiUtils;
112     }
113 
114     /**
115      * Processes the result of the outgoing call broadcast intent, and performs callbacks to
116      * the OutgoingCallIntentBroadcasterListener as necessary.
117      */
118     public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
119 
120         @Override
onReceive(Context context, Intent intent)121         public void onReceive(Context context, Intent intent) {
122             try {
123                 Log.startSession("NOCBIR.oR");
124                 Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
125                 synchronized (mLock) {
126                     Log.v(this, "onReceive: %s", intent);
127 
128                     // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is
129                     // used as the actual number to call. (If null, no call will be placed.)
130                     String resultNumber = getResultData();
131                     Log.i(this, "Received new-outgoing-call-broadcast for %s with data %s", mCall,
132                             Log.pii(resultNumber));
133 
134                     boolean endEarly = false;
135                     long disconnectTimeout =
136                             Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver());
137                     if (resultNumber == null) {
138                         Log.v(this, "Call cancelled (null number), returning...");
139                         disconnectTimeout = getDisconnectTimeoutFromApp(
140                                 getResultExtras(false), disconnectTimeout);
141                         endEarly = true;
142                     } else if (isEmergencyNumber(resultNumber)) {
143                         Log.w(this, "Cannot modify outgoing call to emergency number %s.",
144                                 resultNumber);
145                         disconnectTimeout = 0;
146                         endEarly = true;
147                     }
148 
149                     if (endEarly) {
150                         if (mCall != null) {
151                             mCall.disconnect(disconnectTimeout);
152                         }
153                         return;
154                     }
155 
156                     // If this call is already disconnected then we have nothing more to do.
157                     if (mCall.isDisconnected()) {
158                         Log.w(this, "Call has already been disconnected," +
159                                         " ignore the broadcast Call %s", mCall);
160                         return;
161                     }
162 
163                     // TODO: Remove the assumption that phone numbers are either SIP or TEL.
164                     // This does not impact self-managed ConnectionServices as they do not use the
165                     // NewOutgoingCallIntentBroadcaster.
166                     Uri resultHandleUri = Uri.fromParts(
167                             mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
168                                     PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
169                             resultNumber, null);
170 
171                     Uri originalUri = mIntent.getData();
172 
173                     if (originalUri.getSchemeSpecificPart().equals(resultNumber)) {
174                         Log.v(this, "Call number unmodified after" +
175                                 " new outgoing call intent broadcast.");
176                     } else {
177                         Log.v(this, "Retrieved modified handle after outgoing call intent" +
178                                 " broadcast: Original: %s, Modified: %s",
179                                 Log.pii(originalUri),
180                                 Log.pii(resultHandleUri));
181                     }
182 
183                     GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
184                     placeOutgoingCallImmediately(mCall, resultHandleUri, gatewayInfo,
185                             mIntent.getBooleanExtra(
186                                     TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false),
187                             mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
188                                     VideoProfile.STATE_AUDIO_ONLY));
189                 }
190             } finally {
191                 Trace.endSection();
192                 Log.endSession();
193             }
194         }
195     }
196 
197     /**
198      * Processes the supplied intent and starts the outgoing call broadcast process relevant to the
199      * intent.
200      *
201      * This method will handle three kinds of actions:
202      *
203      * - CALL (intent launched by all third party dialers)
204      * - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer)
205      * - CALL_EMERGENCY (intent launched by lock screen emergency dialer)
206      *
207      * @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate
208      *         {@link DisconnectCause} if the call did not, describing why it failed.
209      */
210     @VisibleForTesting
evaluateCall()211     public CallDisposition evaluateCall() {
212         CallDisposition result = new CallDisposition();
213 
214         Intent intent = mIntent;
215         String action = intent.getAction();
216         final Uri handle = intent.getData();
217 
218         if (handle == null) {
219             Log.w(this, "Empty handle obtained from the call intent.");
220             result.disconnectCause = DisconnectCause.INVALID_NUMBER;
221             return result;
222         }
223 
224         boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());
225         if (isVoicemailNumber) {
226             if (Intent.ACTION_CALL.equals(action)
227                     || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
228                 // Voicemail calls will be handled directly by the telephony connection manager
229                 Log.i(this, "Voicemail number dialed. Skipping redirection and broadcast", intent);
230                 mIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
231                         VideoProfile.STATE_AUDIO_ONLY);
232                 result.callImmediately = true;
233                 result.requestRedirection = false;
234                 result.sendBroadcast = false;
235                 result.callingAddress = handle;
236                 return result;
237             } else {
238                 Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
239                 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
240                 return result;
241             }
242         }
243 
244         PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra(
245                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
246         boolean isSelfManaged = false;
247         if (targetPhoneAccount != null) {
248             PhoneAccount phoneAccount =
249                     mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked(
250                             targetPhoneAccount);
251             if (phoneAccount != null) {
252                 isSelfManaged = phoneAccount.isSelfManaged();
253             }
254         }
255 
256         result.number = "";
257         result.callingAddress = handle;
258 
259         if (isSelfManaged) {
260             // Self-managed call.
261             result.callImmediately = true;
262             result.sendBroadcast = false;
263             result.requestRedirection = false;
264             Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
265             return result;
266         }
267 
268         // Placing a managed call
269         String number = getNumberFromCallIntent(intent);
270         result.number = number;
271         if (number == null) {
272             result.disconnectCause = DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
273             return result;
274         }
275 
276         final boolean isEmergencyNumber = isEmergencyNumber(number);
277         Log.v(this, "isEmergencyNumber = %s", isEmergencyNumber);
278 
279         action = calculateCallIntentAction(intent, isEmergencyNumber);
280         intent.setAction(action);
281 
282         if (Intent.ACTION_CALL.equals(action)) {
283             if (isEmergencyNumber) {
284                 if (!mIsDefaultOrSystemPhoneApp) {
285                     Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
286                             + "unless caller is system or default dialer.", number, intent);
287                     launchSystemDialer(intent.getData());
288                     result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
289                     return result;
290                 } else {
291                     result.callImmediately = true;
292                     result.requestRedirection = false;
293                 }
294             } else if (mMmiUtils.isDangerousMmiOrVerticalCode(intent.getData())) {
295                 if (!mIsDefaultOrSystemPhoneApp) {
296                     Log.w(this,
297                             "Potentially dangerous MMI code %s with CALL Intent %s can only be "
298                                     + "sent if caller is the system or default dialer",
299                             number, intent);
300                     launchSystemDialer(intent.getData());
301                     result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
302                     return result;
303                 }
304             }
305         } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
306             if (!isEmergencyNumber) {
307                 Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
308                         + "Intent %s.", number, intent);
309                 result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
310                 return result;
311             }
312             result.callImmediately = true;
313             result.requestRedirection = false;
314         } else {
315             Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
316             result.disconnectCause = DisconnectCause.INVALID_NUMBER;
317             return result;
318         }
319 
320         String scheme = mPhoneNumberUtilsAdapter.isUriNumber(number)
321                 ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
322         result.callingAddress = Uri.fromParts(scheme, number, null);
323         return result;
324     }
325 
getNumberFromCallIntent(Intent intent)326     private String getNumberFromCallIntent(Intent intent) {
327         String number = null;
328 
329         Uri uri = intent.getData();
330         if (uri != null) {
331             String scheme = uri.getScheme();
332             if (scheme != null) {
333                 if (scheme.equals("tel") || scheme.equals("sip")) {
334                     number = uri.getSchemeSpecificPart();
335                 }
336             }
337         }
338 
339         if (TextUtils.isEmpty(number)) {
340             Log.w(this, "Empty number obtained from the call intent.");
341             return null;
342         }
343 
344         boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
345         if (!isUriNumber) {
346             number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
347             number = mPhoneNumberUtilsAdapter.stripSeparators(number);
348         }
349         return number;
350     }
351 
processCall(Call call, CallDisposition disposition)352     public void processCall(Call call, CallDisposition disposition) {
353         mCall = call;
354         if (disposition.callImmediately) {
355             boolean speakerphoneOn = mIntent.getBooleanExtra(
356                     TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
357             int videoState = mIntent.getIntExtra(
358                     TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
359                     VideoProfile.STATE_AUDIO_ONLY);
360             placeOutgoingCallImmediately(mCall, disposition.callingAddress, null,
361                     speakerphoneOn, videoState);
362 
363             // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
364             // so that third parties can still inspect (but not intercept) the outgoing call. When
365             // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
366             // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
367         }
368 
369         boolean callRedirectionWithService = false;
370         if (disposition.requestRedirection) {
371             CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor(
372                     mContext, mCallsManager, mCall, disposition.callingAddress,
373                     mCallsManager.getPhoneAccountRegistrar(),
374                     getGateWayInfoFromIntent(mIntent, mIntent.getData()),
375                     mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
376                             false),
377                     mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
378                             VideoProfile.STATE_AUDIO_ONLY));
379             /**
380              * If there is an available {@link android.telecom.CallRedirectionService}, use the
381              * {@link CallRedirectionProcessor} to perform call redirection instead of using
382              * broadcasting.
383              */
384             callRedirectionWithService = callRedirectionProcessor
385                     .canMakeCallRedirectionWithServiceAsUser(mCall.getAssociatedUser());
386             if (callRedirectionWithService) {
387                 callRedirectionProcessor.performCallRedirection(mCall.getAssociatedUser());
388             }
389         }
390 
391         if (disposition.sendBroadcast) {
392             UserHandle targetUser = mCall.getAssociatedUser();
393             Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
394             broadcastIntent(mIntent, disposition.number,
395                     !disposition.callImmediately && !callRedirectionWithService, targetUser);
396         }
397     }
398 
399     /**
400      * Sends a new outgoing call ordered broadcast so that third party apps can cancel the
401      * placement of the call or redirect it to a different number.
402      *
403      * @param originalCallIntent The original call intent.
404      * @param number Call number that was stored in the original call intent.
405      * @param receiverRequired Whether or not the result from the ordered broadcast should be
406      *                         processed using a {@link NewOutgoingCallIntentBroadcaster}.
407      * @param targetUser User that the broadcast sent to.
408      */
broadcastIntent( Intent originalCallIntent, String number, boolean receiverRequired, UserHandle targetUser)409     private void broadcastIntent(
410             Intent originalCallIntent,
411             String number,
412             boolean receiverRequired,
413             UserHandle targetUser) {
414         Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
415         if (number != null) {
416             broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
417         }
418 
419         // Force receivers of this broadcast intent to run at foreground priority because we
420         // want to finish processing the broadcast intent as soon as possible.
421         broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
422                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
423         Log.v(this, "Broadcasting intent: %s.", broadcastIntent);
424 
425         checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);
426 
427         final BroadcastOptions options = BroadcastOptions.makeBasic();
428         options.setBackgroundActivityStartsAllowed(true);
429         mContext.sendOrderedBroadcastAsUser(
430                 broadcastIntent,
431                 targetUser,
432                 android.Manifest.permission.PROCESS_OUTGOING_CALLS,
433                 AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
434                 options.toBundle(),
435                 receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,
436                 null,  // scheduler
437                 Activity.RESULT_OK,  // initialCode
438                 number,  // initialData: initial value for the result data (number to be modified)
439                 null);  // initialExtras
440     }
441 
442     /**
443      * Copy all the expected extras set when a 3rd party gateway provider is to be used, from the
444      * source intent to the destination one.
445      *
446      * @param src Intent which may contain the provider's extras.
447      * @param dst Intent where a copy of the extras will be added if applicable.
448      */
checkAndCopyProviderExtras(Intent src, Intent dst)449     public void checkAndCopyProviderExtras(Intent src, Intent dst) {
450         if (src == null) {
451             return;
452         }
453         if (hasGatewayProviderExtras(src)) {
454             dst.putExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE,
455                     src.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE));
456             dst.putExtra(EXTRA_GATEWAY_URI,
457                     src.getStringExtra(EXTRA_GATEWAY_URI));
458             Log.d(this, "Found and copied gateway provider extras to broadcast intent.");
459             return;
460         }
461 
462         Log.d(this, "No provider extras found in call intent.");
463     }
464 
465     /**
466      * Check if valid gateway provider information is stored as extras in the intent
467      *
468      * @param intent to check for
469      * @return true if the intent has all the gateway information extras needed.
470      */
hasGatewayProviderExtras(Intent intent)471     private boolean hasGatewayProviderExtras(Intent intent) {
472         final String name = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE);
473         final String uriString = intent.getStringExtra(EXTRA_GATEWAY_URI);
474 
475         return !TextUtils.isEmpty(name) && !TextUtils.isEmpty(uriString);
476     }
477 
getGatewayUriFromString(String gatewayUriString)478     private static Uri getGatewayUriFromString(String gatewayUriString) {
479         return TextUtils.isEmpty(gatewayUriString) ? null : Uri.parse(gatewayUriString);
480     }
481 
482     /**
483      * Extracts gateway provider information from a provided intent..
484      *
485      * @param intent to extract gateway provider information from.
486      * @param trueHandle The actual call handle that the user is trying to dial
487      * @return GatewayInfo object containing extracted gateway provider information as well as
488      *     the actual handle the user is trying to dial.
489      */
getGateWayInfoFromIntent(Intent intent, Uri trueHandle)490     public static GatewayInfo getGateWayInfoFromIntent(Intent intent, Uri trueHandle) {
491         if (intent == null) {
492             return null;
493         }
494 
495         // Check if gateway extras are present.
496         String gatewayPackageName = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE);
497         Uri gatewayUri = getGatewayUriFromString(intent.getStringExtra(EXTRA_GATEWAY_URI));
498         if (!TextUtils.isEmpty(gatewayPackageName) && gatewayUri != null) {
499             return new GatewayInfo(gatewayPackageName, gatewayUri, trueHandle);
500         }
501 
502         return null;
503     }
504 
placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)505     private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,
506             boolean speakerphoneOn, int videoState) {
507         Log.i(this,
508                 "Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
509         // Since we are not going to go through "Outgoing call broadcast", make sure
510         // we mark it as ready.
511         mCall.setNewOutgoingCallIntentBroadcastIsDone();
512         mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
513     }
514 
launchSystemDialer(Uri handle)515     private void launchSystemDialer(Uri handle) {
516         Intent systemDialerIntent = new Intent();
517         systemDialerIntent.setComponent(mDefaultDialerCache.getDialtactsSystemDialerComponent());
518         systemDialerIntent.setAction(Intent.ACTION_DIAL);
519         systemDialerIntent.setData(handle);
520         systemDialerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
521         Log.v(this, "calling startActivity for default dialer: %s", systemDialerIntent);
522         mContext.startActivityAsUser(systemDialerIntent, UserHandle.CURRENT);
523     }
524 
525     /**
526      * Check whether or not this is an emergency number, in order to enforce the restriction
527      * that only the CALL_PRIVILEGED and CALL_EMERGENCY intents are allowed to make emergency
528      * calls.
529      *
530      * @param number number to inspect in order to determine whether or not an emergency number
531      * is being dialed
532      * @return True if the handle is an emergency number.
533      */
isEmergencyNumber(String number)534     private boolean isEmergencyNumber(String number) {
535         Log.v(this, "Checking restrictions for number : %s", Log.pii(number));
536         if (number == null) return false;
537         try {
538             return mContext.getSystemService(TelephonyManager.class).isEmergencyNumber(
539                     number);
540         } catch (Exception e) {
541             Log.e(this, e, "isEmergencyNumber: Telephony threw an exception.");
542             return false;
543         }
544     }
545 
546     /**
547      * Given a call intent and whether or not the number to dial is an emergency number, determine
548      * the appropriate call intent action.
549      *
550      * @param intent Intent to evaluate
551      * @param isEmergencyNumber Whether or not the number is an emergency
552      * number.
553      * @return The appropriate action.
554      */
calculateCallIntentAction(Intent intent, boolean isEmergencyNumber)555     private String calculateCallIntentAction(Intent intent, boolean isEmergencyNumber) {
556         String action = intent.getAction();
557 
558         /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
559         if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
560             if (isEmergencyNumber) {
561                 Log.i(this, "ACTION_CALL_PRIVILEGED is used while the number is a"
562                         + " emergency number. Using ACTION_CALL_EMERGENCY as an action instead.");
563                 action = Intent.ACTION_CALL_EMERGENCY;
564             } else {
565                 action = Intent.ACTION_CALL;
566             }
567             Log.v(this, " - updating action from CALL_PRIVILEGED to %s", action);
568         }
569         return action;
570     }
571 
getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout)572     private long getDisconnectTimeoutFromApp(Bundle resultExtras, long defaultTimeout) {
573         if (resultExtras != null) {
574             long disconnectTimeout = resultExtras.getLong(
575                     TelecomManager.EXTRA_NEW_OUTGOING_CALL_CANCEL_TIMEOUT, defaultTimeout);
576             if (disconnectTimeout < 0) {
577                 disconnectTimeout = 0;
578             }
579             return Math.min(disconnectTimeout,
580                     Timeouts.getMaxNewOutgoingCallCancelMillis(mContext.getContentResolver()));
581         } else {
582             return defaultTimeout;
583         }
584     }
585 }
586