1 /*
2  * Copyright (C) 2013 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.components;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.telecom.Log;
26 import android.telecom.PhoneAccount;
27 import android.telecom.TelecomManager;
28 import android.telecom.VideoProfile;
29 import android.telephony.PhoneNumberUtils;
30 import android.telephony.TelephonyManager;
31 
32 import com.android.server.telecom.CallIntentProcessor;
33 import com.android.server.telecom.R;
34 import com.android.server.telecom.TelecomSystem;
35 import com.android.server.telecom.TelephonyUtil;
36 import com.android.server.telecom.UserUtil;
37 
38 // TODO: Needed for move to system service: import com.android.internal.R;
39 
40 /**
41  * Handles system CALL actions and forwards them to {@link CallIntentProcessor}.
42  * Handles all three CALL action types: CALL, CALL_PRIVILEGED, and CALL_EMERGENCY.
43  *
44  * Pre-L, the only way apps were were allowed to make outgoing emergency calls was the
45  * ACTION_CALL_PRIVILEGED action (which requires the system only CALL_PRIVILEGED permission).
46  *
47  * In L, any app that has the CALL_PRIVILEGED permission can continue to make outgoing emergency
48  * calls via ACTION_CALL_PRIVILEGED.
49  *
50  * In addition, the default dialer (identified via
51  * {@link android.telecom.TelecomManager#getDefaultDialerPackage()} will also be granted the
52  * ability to make emergency outgoing calls using the CALL action. In order to do this, it must
53  * use the {@link TelecomManager#placeCall(Uri, android.os.Bundle)} method to allow its package
54  * name to be passed to {@link UserCallIntentProcessor}. Calling startActivity will continue to
55  * work on all non-emergency numbers just like it did pre-L.
56  */
57 public class UserCallIntentProcessor {
58 
59     private final Context mContext;
60     private final UserHandle mUserHandle;
61 
62     public UserCallIntentProcessor(Context context, UserHandle userHandle) {
63         mContext = context;
64         mUserHandle = userHandle;
65     }
66 
67     /**
68      * Processes intents sent to the activity.
69      *
70      * @param intent The intent.
71      * @param callingPackageName The package name of the calling app.
72      * @param canCallNonEmergency {@code true} if the caller is permitted to call non-emergency
73      *                            numbers.
74      * @param isLocalInvocation {@code true} if the caller is within the system service (i.e. the
75      *                            caller is {@link com.android.server.telecom.TelecomServiceImpl})
76      *                            and we can skip the re-broadcast of the intent to Telecom.
77      *                            When {@code false}, we need to re-broadcast the intent to Telcom
78      *                            to trampoline it to the system service where the Telecom
79      *                            service resides.
80      */
81     public void processIntent(Intent intent, String callingPackageName,
82             boolean canCallNonEmergency, boolean isLocalInvocation) {
83         // Ensure call intents are not processed on devices that are not capable of calling.
84         if (!isVoiceCapable()) {
85             return;
86         }
87 
88         String action = intent.getAction();
89 
90         if (Intent.ACTION_CALL.equals(action) ||
91                 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
92                 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
93             processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
94                     isLocalInvocation);
95         }
96     }
97 
98     private void processOutgoingCallIntent(Intent intent, String callingPackageName,
99             boolean canCallNonEmergency, boolean isLocalInvocation) {
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         // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this check in a managed
110         // profile user because this check can always be bypassed by copying and pasting the phone
111         // number into the personal dialer.
112         if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
113             // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
114             // restriction.
115             if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
116                 final UserManager userManager = (UserManager) mContext.getSystemService(
117                         Context.USER_SERVICE);
118                 if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
119                         mUserHandle)) {
120                     showErrorDialogForRestrictedOutgoingCall(mContext,
121                             R.string.outgoing_call_not_allowed_user_restriction);
122                     Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
123                             + "restriction");
124                     return;
125                 } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
126                         mUserHandle)) {
127                     final DevicePolicyManager dpm =
128                             mContext.getSystemService(DevicePolicyManager.class);
129                     if (dpm == null) {
130                         return;
131                     }
132                     final Intent adminSupportIntent = dpm.createAdminSupportIntent(
133                             UserManager.DISALLOW_OUTGOING_CALLS);
134                     if (adminSupportIntent != null) {
135                         mContext.startActivity(adminSupportIntent);
136                     }
137                     return;
138                 }
139             }
140         }
141 
142         if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
143             showErrorDialogForRestrictedOutgoingCall(mContext,
144                     R.string.outgoing_call_not_allowed_no_permission);
145             Log.w(this, "Rejecting non-emergency phone call because "
146                     + android.Manifest.permission.CALL_PHONE + " permission is not granted.");
147             return;
148         }
149 
150         int videoState = intent.getIntExtra(
151                 TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
152                 VideoProfile.STATE_AUDIO_ONLY);
153         Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
154 
155         // Save the user handle of current user before forwarding the intent to primary user.
156         intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
157 
158         sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
159     }
160 
161     /**
162      * Returns whether the device is voice-capable (e.g. a phone vs a tablet).
163      *
164      * @return {@code True} if the device is voice-capable.
165      */
166     private boolean isVoiceCapable() {
167         return ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
168                 .isVoiceCapable();
169     }
170 
171     /**
172      * Potentially trampolines the intent to Telecom via TelecomServiceImpl.
173      * If the caller is local to the Telecom service, we send the intent to Telecom without
174      * sending it through TelecomServiceImpl.
175      */
176     private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,
177             String callingPackage) {
178         intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
179         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
180         if (isLocalInvocation) {
181             // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
182             // bother trampolining the intent, just sent it directly to the call intent processor.
183             // TODO: We should not be using an intent here; this whole flows needs cleanup.
184             Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
185             synchronized (TelecomSystem.getInstance().getLock()) {
186                 TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,
187                         callingPackage);
188             }
189         } else {
190             // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
191             // process; we need to trampoline to TelecomSystem in the system server process.
192             Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
193             TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
194             tm.handleCallIntent(intent, callingPackage);
195         }
196         return true;
197     }
198 
199     private static void showErrorDialogForRestrictedOutgoingCall(Context context, int stringId) {
200         final Intent intent = new Intent(context, ErrorDialogActivity.class);
201         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
202         intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, stringId);
203         context.startActivityAsUser(intent, UserHandle.CURRENT);
204     }
205 }
206