Home | History | Annotate | Download | only in telecom
      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.Trace;
     10 import android.os.UserHandle;
     11 import android.os.UserManager;
     12 import android.telecom.DefaultDialerManager;
     13 import android.telecom.Log;
     14 import android.telecom.PhoneAccount;
     15 import android.telecom.PhoneAccountHandle;
     16 import android.telecom.TelecomManager;
     17 import android.telecom.VideoProfile;
     18 import android.telephony.DisconnectCause;
     19 import android.telephony.PhoneNumberUtils;
     20 import android.widget.Toast;
     21 
     22 /**
     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.
     27  */
     28 public class CallIntentProcessor {
     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     }
     53 
     54     public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
     55     public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
     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";
     61 
     62     /**
     63      * The user initiating the outgoing call.
     64      */
     65     public static final String KEY_INITIATING_USER = "initiating_user";
     66 
     67 
     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) {
     77         final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
     78         Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
     79 
     80         Trace.beginSection("processNewCallCallIntent");
     81         if (isUnknownCall) {
     82             processUnknownCallIntent(mCallsManager, intent);
     83         } else {
     84             processOutgoingCallIntent(mContext, mCallsManager, intent);
     85         }
     86         Trace.endSection();
     87     }
     88 
     89 
     90     /**
     91      * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
     92      *
     93      * @param intent Call intent containing data about the handle to call.
     94      */
     95     static void processOutgoingCallIntent(
     96             Context context,
     97             CallsManager callsManager,
     98             Intent intent) {
     99 
    100         Uri handle = intent.getData();
    101         String scheme = handle.getScheme();
    102         String uriString = handle.getSchemeSpecificPart();
    103 
    104         // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
    105         if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
    106             handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, 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) {
    117             clientExtras = new Bundle();
    118         }
    119 
    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 
    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 
    130         if (!callsManager.isSelfManaged(phoneAccountHandle,
    131                 (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
    132             boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
    133             // Show the toast to warn user that it is a personal call though initiated in work
    134             // profile.
    135             if (fixedInitiatingUser) {
    136                 Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
    137             }
    138         } else {
    139             Log.i(CallIntentProcessor.class,
    140                     "processOutgoingCallIntent: skip initiating user check");
    141         }
    142 
    143         UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
    144 
    145         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
    146         Call call = callsManager
    147                 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
    148                         intent);
    149 
    150         if (call != null) {
    151             sendNewOutgoingCallIntent(context, call, callsManager, intent);
    152         }
    153     }
    154 
    155     static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
    156             Intent intent) {
    157         // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
    158         // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
    159         // killed if memory is scarce. However, this is OK here because the entire Telecom
    160         // process will be running throughout the duration of the phone call and should never
    161         // be killed.
    162         final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
    163 
    164         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
    165                 context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
    166                 isPrivilegedDialer);
    167         final int result = broadcaster.processIntent();
    168         final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
    169 
    170         if (!success && call != null) {
    171             disconnectCallAndShowErrorDialog(context, call, result);
    172         }
    173     }
    174 
    175     /**
    176      * If the call is initiated from managed profile but there is no work dialer installed, treat
    177      * the call is initiated from its parent user.
    178      *
    179      * @return whether the initiating user is fixed.
    180      */
    181     static boolean fixInitiatingUserIfNecessary(Context context, Intent intent) {
    182         final UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
    183         if (UserUtil.isManagedProfile(context, initiatingUser)) {
    184             boolean noDialerInstalled = DefaultDialerManager.getInstalledDialerApplications(context,
    185                     initiatingUser.getIdentifier()).size() == 0;
    186             if (noDialerInstalled) {
    187                 final UserManager userManager = UserManager.get(context);
    188                 UserHandle parentUserHandle =
    189                         userManager.getProfileParent(
    190                                 initiatingUser.getIdentifier()).getUserHandle();
    191                 intent.putExtra(KEY_INITIATING_USER, parentUserHandle);
    192 
    193                 Log.i(CallIntentProcessor.class, "fixInitiatingUserIfNecessary: no dialer installed"
    194                         + " for current user; setting initiator to parent %s" + parentUserHandle);
    195                 return true;
    196             }
    197         }
    198         return false;
    199     }
    200 
    201     static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
    202         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
    203                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
    204 
    205         if (phoneAccountHandle == null) {
    206             Log.w(CallIntentProcessor.class,
    207                     "Rejecting incoming call due to null phone account");
    208             return;
    209         }
    210         if (phoneAccountHandle.getComponentName() == null) {
    211             Log.w(CallIntentProcessor.class,
    212                     "Rejecting incoming call due to null component name");
    213             return;
    214         }
    215 
    216         Bundle clientExtras = null;
    217         if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
    218             clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
    219         }
    220         if (clientExtras == null) {
    221             clientExtras = new Bundle();
    222         }
    223 
    224         Log.d(CallIntentProcessor.class,
    225                 "Processing incoming call from connection service [%s]",
    226                 phoneAccountHandle.getComponentName());
    227         callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
    228     }
    229 
    230     static void processUnknownCallIntent(CallsManager callsManager, Intent intent) {
    231         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
    232                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
    233 
    234         if (phoneAccountHandle == null) {
    235             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null phone account");
    236             return;
    237         }
    238         if (phoneAccountHandle.getComponentName() == null) {
    239             Log.w(CallIntentProcessor.class, "Rejecting unknown call due to null component name");
    240             return;
    241         }
    242 
    243         callsManager.addNewUnknownCall(phoneAccountHandle, intent.getExtras());
    244     }
    245 
    246     private static void disconnectCallAndShowErrorDialog(
    247             Context context, Call call, int errorCode) {
    248         call.disconnect();
    249         final Intent errorIntent = new Intent(context, ErrorDialogActivity.class);
    250         int errorMessageId = -1;
    251         switch (errorCode) {
    252             case DisconnectCause.INVALID_NUMBER:
    253             case DisconnectCause.NO_PHONE_NUMBER_SUPPLIED:
    254                 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
    255                 break;
    256         }
    257         if (errorMessageId != -1) {
    258             errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
    259             errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    260             context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
    261         }
    262     }
    263 }
    264