/* * 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 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 canCallNonEmergency, boolean isLocalInvocation) { // Ensure call intents are not processed on devices that are not capable of calling. if (!isVoiceCapable()) { return; } 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, canCallNonEmergency, isLocalInvocation); } } private void processOutgoingCallIntent(Intent intent, String callingPackageName, boolean canCallNonEmergency, boolean isLocalInvocation) { Uri handle = intent.getData(); 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); } // 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 (!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); } /** * Returns whether the device is voice-capable (e.g. a phone vs a tablet). * * @return {@code True} if the device is voice-capable. */ private boolean isVoiceCapable() { return ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) .isVoiceCapable(); } /** * 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); } }