Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd41ef6d authored by Shuo Qian's avatar Shuo Qian Committed by Gerrit Code Review
Browse files

Merge "Use Call Redirection service in Telecom"

parents 42f5f46b 896224fd
Loading
Loading
Loading
Loading
+63 −1
Original line number Diff line number Diff line
@@ -299,6 +299,7 @@ public class CallsManager extends Call.ListenerBase
            new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
    private final HeadsetMediaButton mHeadsetMediaButton;
    private final WiredHeadsetManager mWiredHeadsetManager;
    private final SystemStateHelper mSystemStateHelper;
    private final BluetoothRouteManager mBluetoothRouteManager;
    private final DockManager mDockManager;
    private final TtyManager mTtyManager;
@@ -436,6 +437,7 @@ public class CallsManager extends Call.ListenerBase
        mMissedCallNotifier = missedCallNotifier;
        StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
        mWiredHeadsetManager = wiredHeadsetManager;
        mSystemStateHelper = systemStateHelper;
        mDefaultDialerCache = defaultDialerCache;
        mBluetoothRouteManager = bluetoothManager;
        mDockManager = new DockManager(context);
@@ -1606,6 +1608,57 @@ public class CallsManager extends Call.ListenerBase
        return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();
    }

    public void onCallRedirectionComplete(Call call, Uri handle,
                                          PhoneAccountHandle phoneAccountHandle,
                                          GatewayInfo gatewayInfo, boolean speakerphoneOn,
                                          int videoState, boolean shouldCancelCall,
                                          String uiAction) {
        Log.i(this, "onCallRedirectionComplete for Call %s with handle %s" +
                        " and phoneAccountHandle %s", call, handle, phoneAccountHandle);

        boolean endEarly = false;
        String disconnectReason = "";

        if (shouldCancelCall) {
            Log.w(this, "onCallRedirectionComplete: call is canceled");
            endEarly = true;
            disconnectReason = "Canceled from Call Redirection Service";
            // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_TIMEOUT
        } else if (handle == null) {
            Log.w(this, "onCallRedirectionComplete: handle is null");
            endEarly = true;
            disconnectReason = "Null handle from Call Redirection Service";
        } else if (phoneAccountHandle == null) {
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
            endEarly = true;
            disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
        } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext,
                handle.getSchemeSpecificPart())) {
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
                    + " Redirection Service", handle.getSchemeSpecificPart());
            endEarly = true;
            disconnectReason = "Emergency number is redirected from Call Redirection Service";
        }
        if (endEarly) {
            if (call != null) {
                call.disconnect(disconnectReason);
            }
            return;
        }

        // If this call is already disconnected then we have nothing more to do.
        if (call.isDisconnected()) {
            Log.w(this, "onCallRedirectionComplete: Call has already been disconnected,"
                    + " ignore the call redirection %s", call);
            return;
        }

        // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM

        call.setTargetPhoneAccount(phoneAccountHandle);
        placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
    }

    /**
     * Attempts to issue/connect the specified call.
     *
@@ -2590,7 +2643,8 @@ public class CallsManager extends Call.ListenerBase
     *
     * @return The {@link PhoneAccountRegistrar}.
     */
    PhoneAccountRegistrar getPhoneAccountRegistrar() {
    @VisibleForTesting
    public PhoneAccountRegistrar getPhoneAccountRegistrar() {
        return mPhoneAccountRegistrar;
    }

@@ -3387,6 +3441,14 @@ public class CallsManager extends Call.ListenerBase
        return mLock;
    }

    public Timeouts.Adapter getTimeoutsAdapter() {
        return mTimeoutsAdapter;
    }

    public SystemStateHelper getSystemStateHelper() {
        return mSystemStateHelper;
    }

    private void reloadMissedCallsOfUser(UserHandle userHandle) {
        mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper,
                new MissedCallNotifier.CallInfoFactory(), userHandle);
+28 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.telephony.DisconnectCause;
import android.text.TextUtils;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.callredirection.CallRedirectionProcessor;

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

@@ -240,6 +241,8 @@ public class NewOutgoingCallIntentBroadcaster {
        boolean callImmediately = false;
        // True for all managed calls, false for self-managed calls.
        boolean sendNewOutgoingCallBroadcast = true;
        // True for requesting call redirection, false for not requesting it.
        boolean requestCallRedirection = true;
        Uri callingAddress = handle;

        if (!isSelfManaged) {
@@ -294,6 +297,7 @@ public class NewOutgoingCallIntentBroadcaster {
            // Self-managed call.
            callImmediately = true;
            sendNewOutgoingCallBroadcast = false;
            requestCallRedirection = false;
            Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
        }

@@ -312,10 +316,33 @@ public class NewOutgoingCallIntentBroadcaster {
            // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
        }

        boolean callRedirectionWithService = false;
        if (requestCallRedirection) {
            CallRedirectionProcessor callRedirectionProcessor = new CallRedirectionProcessor(
                    mContext, mCallsManager, mCall, callingAddress,
                    mCallsManager.getPhoneAccountRegistrar(),
                    getGateWayInfoFromIntent(intent, handle),
                    intent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
                            false),
                    intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                            VideoProfile.STATE_AUDIO_ONLY));
            /**
             * If there is an available {@link android.telecom.CallRedirectionService}, use the
             * {@link CallRedirectionProcessor} to perform call redirection instead of using
             * broadcasting.
             */
            callRedirectionWithService = callRedirectionProcessor
                    .canMakeCallRedirectionWithService();
            if (callRedirectionWithService) {
                callRedirectionProcessor.performCallRedirection();
            }
        }

        if (sendNewOutgoingCallBroadcast) {
            UserHandle targetUser = mCall.getInitiatingUser();
            Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
            broadcastIntent(intent, number, !callImmediately, targetUser);
            broadcastIntent(intent, number,
                    !callImmediately && !callRedirectionWithService, targetUser);
        }
        return DisconnectCause.NOT_DISCONNECTED;
    }
+2 −2
Original line number Diff line number Diff line
@@ -187,7 +187,7 @@ public final class Timeouts {
     */
    public static long getUserDefinedCallRedirectionTimeoutMillis(ContentResolver contentResolver) {
        return get(contentResolver, "user_defined_call_redirection_timeout",
            3000L /* 3 seconds */);
            5000L /* 5 seconds */);
    }

    /**
@@ -196,6 +196,6 @@ public final class Timeouts {
     * @param contentResolver The content resolved.
     */
    public static long getCarrierCallRedirectionTimeoutMillis(ContentResolver contentResolver) {
        return get(contentResolver, "carrier_call_redirection_timeout", 3000L /* 3 seconds */);
        return get(contentResolver, "carrier_call_redirection_timeout", 5000L /* 5 seconds */);
    }
}
+99 −86
Original line number Diff line number Diff line
@@ -16,26 +16,22 @@

package com.android.server.telecom.callredirection;

import android.Manifest;
import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telecom.CallRedirectionService;
import android.telecom.GatewayInfo;
import android.telecom.Log;
import android.telecom.Logging.Runnable;
import android.telecom.PhoneAccountHandle;
import android.telephony.CarrierConfigManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.ICallRedirectionAdapter;
import com.android.internal.telecom.ICallRedirectionService;
@@ -46,8 +42,6 @@ import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;

import java.util.List;

/**
 * A single instance of call redirection processor that handles the call redirection with
 * user-defined {@link CallRedirectionService} and carrier {@link CallRedirectionService} for a
@@ -98,10 +92,14 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
        private void onServiceBound(ICallRedirectionService service) {
            mService = service;
            try {
                mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle);
                mHandle = mCallRedirectionProcessorHelper.formatNumberForRedirection(mHandle);
                // Telecom does not perform user interactions for carrier call redirection.
                mService.placeCall(new CallRedirectionAdapter(), mHandle, mPhoneAccountHandle,
                        mAllowInteractiveResponse
                                && mServiceType.equals(SERVICE_TYPE_USER_DEFINED));
                Log.addEvent(mCall, mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
                        ? LogUtils.Events.REDIRECTION_SENT_USER
                        : LogUtils.Events.REDIRECTION_SENT_CARRIER);
                        : LogUtils.Events.REDIRECTION_SENT_CARRIER, mComponentName);
                Log.d(this, "Requested placeCall with [handle]" + Log.pii(mHandle)
                        + " [phoneAccountHandle]" + mPhoneAccountHandle);
            } catch (RemoteException e) {
@@ -189,16 +187,20 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
            }

            @Override
            public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
            public void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount,
                                     boolean confirmFirst) {
                Log.startSession("CRA.rC");
                long token = Binder.clearCallingIdentity();
                try {
                    synchronized (mTelecomLock) {
                        mHandle = handle;
                        mPhoneAccountHandle = targetPhoneAccount;
                        mUiAction = (confirmFirst && mServiceType.equals(SERVICE_TYPE_USER_DEFINED)
                                && mAllowInteractiveResponse)
                                ? UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM : mUiAction;
                        Log.d(this, "Received redirectCall with [handle]" + Log.pii(mHandle)
                                + " [phoneAccountHandle]" + mPhoneAccountHandle + " from "
                                + mServiceType + " call" + " redirection service");
                                + mServiceType + " call redirection service");
                        finishCallRedirection();
                    }
                } finally {
@@ -212,14 +214,23 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
    private final Context mContext;
    private final CallsManager mCallsManager;
    private final Call mCall;
    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    private final boolean mAllowInteractiveResponse;
    private final GatewayInfo mGatewayInfo;
    private final boolean mSpeakerphoneOn;
    private final int mVideoState;
    private final Timeouts.Adapter mTimeoutsAdapter;
    private final TelecomSystem.SyncRoot mTelecomLock;
    private final Handler mHandler = new Handler(Looper.getMainLooper());

    private CallRedirectionAttempt mAttempt;
    private CallRedirectionProcessorHelper mCallRedirectionProcessorHelper;

    public static final String SERVICE_TYPE_CARRIER = "carrier";
    public static final String SERVICE_TYPE_USER_DEFINED = "user_defined";
    public static final String UI_TYPE_NO_ACTION = "no_action";
    public static final String UI_TYPE_USER_DEFINED_TIMEOUT = "user_defined_timeout";
    public static final String UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM
            = "user_defined_ask_for_confirm";

    private PhoneAccountHandle mPhoneAccountHandle;
    private Uri mHandle;
@@ -228,6 +239,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
     * Indicates if Telecom should cancel the call when the whole call redirection finishes.
     */
    private boolean mShouldCancelCall = false;
    /**
     * Indicates Telecom should handle different types of UI if need.
     */
    private String mUiAction = UI_TYPE_NO_ACTION;
    /**
     * Indicates if Telecom is waiting for a callback from a user-defined
     * {@link CallRedirectionService}.
@@ -243,19 +258,28 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
            Context context,
            CallsManager callsManager,
            Call call,
            PhoneAccountRegistrar phoneAccountRegistrar,
            Uri handle,
            PhoneAccountHandle phoneAccountHandle,
            Timeouts.Adapter timeoutsAdapter,
            TelecomSystem.SyncRoot lock) {
            PhoneAccountRegistrar phoneAccountRegistrar,
            GatewayInfo gatewayInfo,
            boolean speakerphoneOn,
            int videoState) {
        mContext = context;
        mCallsManager = callsManager;
        mCall = call;
        mPhoneAccountRegistrar = phoneAccountRegistrar;
        mHandle = handle;
        mPhoneAccountHandle = phoneAccountHandle;
        mTimeoutsAdapter = timeoutsAdapter;
        mTelecomLock = lock;
        mPhoneAccountHandle = call.getTargetPhoneAccount();
        mGatewayInfo = gatewayInfo;
        mSpeakerphoneOn = speakerphoneOn;
        mVideoState = videoState;
        mTimeoutsAdapter = callsManager.getTimeoutsAdapter();
        mTelecomLock = callsManager.getLock();
        /**
         * The current rule to decide whether the implemented {@link CallRedirectionService} should
         * allow interactive responses with users is only based on whether it is in car mode.
         */
        mAllowInteractiveResponse = !callsManager.getSystemStateHelper().isCarMode();
        mCallRedirectionProcessorHelper = new CallRedirectionProcessorHelper(
                context, callsManager, phoneAccountRegistrar);
    }

    @Override
@@ -264,11 +288,15 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
        mHandler.post(new Runnable("CRP.oCRC", mTelecomLock) {
            @Override
            public void loggedRun() {
                mHandle = mCallRedirectionProcessorHelper.processNumberWhenRedirectionComplete(
                        mHandle);
                if (mIsUserDefinedRedirectionPending) {
                    Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_USER);
                    mIsUserDefinedRedirectionPending = false;
                    if (mShouldCancelCall) {
                        // TODO mCallsManager.onCallRedirectionComplete
                        mCallsManager.onCallRedirectionComplete(mCall, mHandle,
                                mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState,
                                mShouldCancelCall, mUiAction);
                    } else {
                        performCarrierCallRedirection();
                    }
@@ -276,23 +304,33 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
                if (mIsCarrierRedirectionPending) {
                    Log.addEvent(mCall, LogUtils.Events.REDIRECTION_COMPLETED_CARRIER);
                    mIsCarrierRedirectionPending = false;
                    // TODO mCallsManager.onCallRedirectionComplete
                    mCallsManager.onCallRedirectionComplete(mCall, mHandle,
                            mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState,
                            mShouldCancelCall, mUiAction);
                }
            }
        }.prepare());
    }

    /*
    /**
     * The entry to perform call redirection of the call from (@link CallsManager)
     */
    public void performCallRedirection() {
        // If the Gateway Info is set with intent, do not perform call redirection.
        if (mGatewayInfo != null) {
            mCallsManager.onCallRedirectionComplete(mCall, mHandle, mPhoneAccountHandle,
                    mGatewayInfo, mSpeakerphoneOn, mVideoState, mShouldCancelCall, mUiAction);
        } else {
            mCallRedirectionProcessorHelper.storePostDialDigits(mHandle);
            performUserDefinedCallRedirection();
        }
    }

    private void performUserDefinedCallRedirection() {
        Log.d(this, "performUserDefinedCallRedirection");
        ComponentName componentName = getUserDefinedCallRedirectionService(mContext);
        if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) {
        ComponentName componentName =
                mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService();
        if (componentName != null) {
            mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_USER_DEFINED);
            mAttempt.process();
            mIsUserDefinedRedirectionPending = true;
@@ -306,9 +344,10 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {

    private void performCarrierCallRedirection() {
        Log.d(this, "performCarrierCallRedirection");
        ComponentName componentName = getCarrierCallRedirectionService(
            mContext, mPhoneAccountHandle);
        if (componentName != null && canBindToCallRedirectionService(mContext, componentName)) {
        ComponentName componentName =
                mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
                        mPhoneAccountHandle);
        if (componentName != null) {
            mAttempt = new CallRedirectionAttempt(componentName, SERVICE_TYPE_CARRIER);
            mAttempt.process();
            mIsCarrierRedirectionPending = true;
@@ -316,7 +355,9 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
        } else {
            Log.i(this, "There are no carrier call redirection services installed on this"
                    + " device.");
            // TODO return to CallsManager.onCallRedirectionComplete
            mCallsManager.onCallRedirectionComplete(mCall, mHandle,
                    mPhoneAccountHandle, mGatewayInfo, mSpeakerphoneOn, mVideoState,
                    mShouldCancelCall, mUiAction);
        }
    }

@@ -333,77 +374,49 @@ public class CallRedirectionProcessor implements CallRedirectionCallback {
                        serviceType.equals(SERVICE_TYPE_USER_DEFINED) ?
                                mIsUserDefinedRedirectionPending : mIsCarrierRedirectionPending;
                if (isCurrentRedirectionPending) {
                    Log.i(CallRedirectionProcessor.this,
                            serviceType + "call redirection has timed out.");
                    Log.i(this, serviceType + " call redirection has timed out.");
                    Log.addEvent(mCall, serviceType.equals(SERVICE_TYPE_USER_DEFINED)
                            ? LogUtils.Events.REDIRECTION_TIMED_OUT_USER
                            : LogUtils.Events.REDIRECTION_TIMED_OUT_CARRIER);
                    if (serviceType.equals(SERVICE_TYPE_USER_DEFINED)) {
                        mUiAction = UI_TYPE_USER_DEFINED_TIMEOUT;
                        mShouldCancelCall = true;
                    }
                    onCallRedirectionComplete(mCall);
                }
            }
        }.prepare(), timeout);
    }

    private ComponentName getUserDefinedCallRedirectionService(Context context) {
        // TODO get service component name from settings default value:
        // android.provider.Settings#CALL_REDIRECTION_DEFAULT_APPLICATION
        return null;
    }

    private ComponentName getCarrierCallRedirectionService(Context context, PhoneAccountHandle
            targetPhoneAccountHandle) {
        CarrierConfigManager configManager = (CarrierConfigManager)
                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        if (configManager == null) {
            Log.i(this, "Cannot get CarrierConfigManager.");
            return null;
        }
        PersistableBundle pb = configManager.getConfigForSubId(mPhoneAccountRegistrar
                .getSubscriptionIdForPhoneAccount(targetPhoneAccountHandle));
        if (pb == null) {
            Log.i(this, "Cannot get PersistableBundle.");
            return null;
        }
        String componentNameString = pb.getString(
            CarrierConfigManager.KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING);
        return new ComponentName(context, componentNameString);
    }

    private boolean canBindToCallRedirectionService(Context context, ComponentName componentName) {
        Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE);
        intent.setComponent(componentName);
        List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
                intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
        if (entries.isEmpty()) {
            Log.i(this, "There are no call redirection services installed on this device.");
            return false;
        } else if (entries.size() != 1) {
            Log.i(this, "There are multiple call redirection services installed on this device.");
            return false;
        } else {
            ResolveInfo entry = entries.get(0);
            if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
                    Manifest.permission.BIND_CALL_REDIRECTION_SERVICE)) {
                Log.w(this, "CallRedirectionService must require BIND_CALL_REDIRECTION_SERVICE"
                        + " permission: " + entry.serviceInfo.packageName);
                return false;
            }
            AppOpsManager appOps = (AppOpsManager) context.getSystemService(
                Context.APP_OPS_SERVICE);
            if (appOps.noteOp(AppOpsManager.OP_PROCESS_OUTGOING_CALLS, Binder.getCallingUid(),
                    entry.serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
                Log.w(this, "App Ops does not allow " + entry.serviceInfo.packageName);
                return false;
            }
        }
        return true;
    /**
     * Checks if Telecom can make call redirection with any available call redirection service.
     *
     * @return {@code true} if it can; {@code false} otherwise.
     */
    public boolean canMakeCallRedirectionWithService() {
        boolean canMakeCallRedirectionWithService =
                mCallRedirectionProcessorHelper.getUserDefinedCallRedirectionService() != null
                        || mCallRedirectionProcessorHelper.getCarrierCallRedirectionService(
                                mPhoneAccountHandle) != null;
        Log.w(this, "Can make call redirection with any available service: "
                + canMakeCallRedirectionWithService);
        return canMakeCallRedirectionWithService;
    }

    /**
     * Returns the handler for testing purposes.
     * Returns the handler, for testing purposes.
     */
    @VisibleForTesting
    public Handler getHandler() {
        return mHandler;
    }

    /**
     * Set CallRedirectionProcessorHelper for testing purposes.
     */
    @VisibleForTesting
    public void setCallRedirectionServiceHelper(
            CallRedirectionProcessorHelper callRedirectionProcessorHelper) {
        mCallRedirectionProcessorHelper = callRedirectionProcessorHelper;
    }
}
+199 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading