summaryrefslogtreecommitdiff
path: root/src/com/android/server/telecom/components/UserCallIntentProcessor.java
blob: a4602c1cae364dd2197526ccb13c2bba18f93ce9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.telecom.components;

import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
import android.telecom.Log;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;

import com.android.server.telecom.CallIntentProcessor;
import com.android.server.telecom.R;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.TelephonyUtil;
import com.android.server.telecom.UserUtil;

// TODO: Needed for move to system service: import com.android.internal.R;

/**
 * Handles system CALL actions and forwards them to {@link CallIntentProcessor}.
 * Handles all three CALL action types: CALL, CALL_PRIVILEGED, and CALL_EMERGENCY.
 *
 * Pre-L, the only way apps were were allowed to make outgoing emergency calls was the
 * ACTION_CALL_PRIVILEGED action (which requires the system only CALL_PRIVILEGED permission).
 *
 * In L, any app that has the CALL_PRIVILEGED permission can continue to make outgoing emergency
 * calls via ACTION_CALL_PRIVILEGED.
 *
 * In addition, the default dialer (identified via
 * {@link android.telecom.TelecomManager#getDefaultDialerPackage()} will also be granted the
 * ability to make emergency outgoing calls using the CALL action. In order to do this, it must
 * use the {@link TelecomManager#placeCall(Uri, android.os.Bundle)} method to allow its package
 * name to be passed to {@link UserCallIntentProcessor}. Calling startActivity will continue to
 * work on all non-emergency numbers just like it did pre-L.
 */
public class UserCallIntentProcessor {

    private final Context mContext;
    private final UserHandle mUserHandle;

    public UserCallIntentProcessor(Context context, UserHandle userHandle) {
        mContext = context;
        mUserHandle = userHandle;
    }

    /**
     * Processes intents sent to the activity.
     *
     * @param intent The intent.
     * @param callingPackageName The package name of the calling app.
     * @param isSelfManaged      {@code true} if SelfManaged profile enabled.
     * @param canCallNonEmergency {@code true} if the caller is permitted to call non-emergency
     *                            numbers.
     * @param isLocalInvocation {@code true} if the caller is within the system service (i.e. the
     *                            caller is {@link com.android.server.telecom.TelecomServiceImpl})
     *                            and we can skip the re-broadcast of the intent to Telecom.
     *                            When {@code false}, we need to re-broadcast the intent to Telcom
     *                            to trampoline it to the system service where the Telecom
     *                            service resides.
     */
    public void processIntent(Intent intent, String callingPackageName,
            boolean isSelfManaged, boolean canCallNonEmergency,
            boolean isLocalInvocation) {
        String action = intent.getAction();

        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            processOutgoingCallIntent(intent, callingPackageName, isSelfManaged,
                    canCallNonEmergency, isLocalInvocation);
        }
    }

    private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean isSelfManaged, boolean canCallNonEmergency,
            boolean isLocalInvocation) {
        Uri handle = intent.getData();
        if (handle == null) return;
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
        }

       if(!isSelfManaged) {
            // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this
            // check in a managed profile user because this check can always be bypassed
            // by copying and pasting the phone number into the personal dialer.
            if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
                // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
                // restriction.
                if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
                    final UserManager userManager =
                            (UserManager) mContext.getSystemService(Context.USER_SERVICE);
                    if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
                            mUserHandle)) {
                        showErrorDialogForRestrictedOutgoingCall(mContext,
                                R.string.outgoing_call_not_allowed_user_restriction);
                        Log.w(this, "Rejecting non-emergency phone call "
                                + "due to DISALLOW_OUTGOING_CALLS restriction");
                        return;
                    } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
                            mUserHandle)) {
                        final DevicePolicyManager dpm =
                                mContext.getSystemService(DevicePolicyManager.class);
                        if (dpm == null) {
                            return;
                        }
                        final Intent adminSupportIntent = dpm.createAdminSupportIntent(
                                UserManager.DISALLOW_OUTGOING_CALLS);
                        if (adminSupportIntent != null) {
                            mContext.startActivity(adminSupportIntent);
                        }
                        return;
                    }
                }
            }
        }

        if (!isSelfManaged && !canCallNonEmergency &&
                !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
            showErrorDialogForRestrictedOutgoingCall(mContext,
                    R.string.outgoing_call_not_allowed_no_permission);
            Log.w(this, "Rejecting non-emergency phone call because "
                    + android.Manifest.permission.CALL_PHONE + " permission is not granted.");
            return;
        }

        int videoState = intent.getIntExtra(
                TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        Log.d(this, "processOutgoingCallIntent videoState = " + videoState);

        // Save the user handle of current user before forwarding the intent to primary user.
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
    }

    /**
     * Potentially trampolines the intent to Telecom via TelecomServiceImpl.
     * If the caller is local to the Telecom service, we send the intent to Telecom without
     * sending it through TelecomServiceImpl.
     */
    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,
            String callingPackage) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        if (isLocalInvocation) {
            // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
            // bother trampolining the intent, just sent it directly to the call intent processor.
            // TODO: We should not be using an intent here; this whole flows needs cleanup.
            Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
            synchronized (TelecomSystem.getInstance().getLock()) {
                TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,
                        callingPackage);
            }
        } else {
            // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
            // process; we need to trampoline to TelecomSystem in the system server process.
            Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
            tm.handleCallIntent(intent, callingPackage);
        }
        return true;
    }

    private static void showErrorDialogForRestrictedOutgoingCall(Context context, int stringId) {
        final Intent intent = new Intent(context, ErrorDialogActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, stringId);
        context.startActivityAsUser(intent, UserHandle.CURRENT);
    }
}