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

Commit c74b3e25 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Enhance PhoneAccount selection to take video capability into account.

Enhance PhoneAccount selection logic to take into account which phone
accounts can place video calls when placing a video call.
As part of this work, moved that selection logic in startOutgoingCall into
a new method and set up a CallsManager unit test class which tests the
phone account selection logic.

Test: Added unit tests, performed manual tests using test app.
Bug: 63939933
Change-Id: If79131b311ca19ae6f6cc2878892398633315c63
parent 8d1780fd
Loading
Loading
Loading
Loading
+109 −54
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.IncomingCallNotifier;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -288,7 +289,8 @@ public class CallsManager extends Call.ListenerBase
    /**
     * Initializes the required Telecom components.
     */
    CallsManager(
    @VisibleForTesting
    public CallsManager(
            Context context,
            TelecomSystem.SyncRoot lock,
            ContactsAsyncHelper contactsAsyncHelper,
@@ -308,7 +310,8 @@ public class CallsManager extends Call.ListenerBase
            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
            EmergencyCallHelper emergencyCallHelper,
            InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
            ClockProxy clockProxy) {
            ClockProxy clockProxy,
            InCallControllerFactory inCallControllerFactory) {
        mContext = context;
        mLock = lock;
        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -353,8 +356,8 @@ public class CallsManager extends Call.ListenerBase
        SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
        RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
        SystemVibrator systemVibrator = new SystemVibrator(context);
        mInCallController = new InCallController(
                context, mLock, this, systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
        mInCallController = inCallControllerFactory.create(context, mLock, this,
                systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
                emergencyCallHelper);
        mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
                ringtoneFactory, systemVibrator, mInCallController);
@@ -1033,6 +1036,7 @@ public class CallsManager extends Call.ListenerBase

        PhoneAccount account =
                mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
        boolean isSelfManaged = account != null && account.isSelfManaged();

        // Create a call with original handle. The handle may be changed when the call is attached
        // to a connection service, but in most cases will remain the same.
@@ -1058,22 +1062,20 @@ public class CallsManager extends Call.ListenerBase
            // will be overridden when the actual connection is returned in startCreateConnection,
            // however doing this now ensures the logs and any other logic will treat this call as
            // self-managed from the moment it is created.
            if (account != null) {
                call.setIsSelfManaged(account.isSelfManaged());
                if (call.isSelfManaged()) {
            call.setIsSelfManaged(isSelfManaged);
            if (isSelfManaged) {
                // Self-managed calls will ALWAYS use voip audio mode.
                call.setIsVoipAudioMode(true);
            }
            }

            call.setInitiatingUser(initiatingUser);
            isReusedCall = false;
        }

        int videoState = VideoProfile.STATE_AUDIO_ONLY;
        if (extras != null) {
            // Set the video state on the call early so that when it is added to the InCall UI the
            // UI knows to configure itself as a video call immediately.
            int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
            videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);

            // If this is an emergency video call, we need to check if the phone account supports
@@ -1101,44 +1103,13 @@ public class CallsManager extends Call.ListenerBase
            call.setVideoState(videoState);
        }

        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
                phoneAccountHandle, initiatingUser);
        boolean isSelfManaged = targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();

        List<PhoneAccountHandle> accounts;
        if (!isSelfManaged) {
            accounts = constructPossiblePhoneAccounts(handle, initiatingUser);
            Log.v(this, "startOutgoingCall found accounts = " + accounts);

            // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this
            // call as if a phoneAccount was not specified (does the default behavior instead).
            // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
            if (phoneAccountHandle != null) {
                if (!accounts.contains(phoneAccountHandle)) {
                    phoneAccountHandle = null;
                }
            }

            if (phoneAccountHandle == null && accounts.size() > 0) {
                // No preset account, check if default exists that supports the URI scheme for the
                // handle and verify it can be used.
                if (accounts.size() > 1) {
                    PhoneAccountHandle defaultPhoneAccountHandle =
                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
                                    handle.getScheme(), initiatingUser);
                    if (defaultPhoneAccountHandle != null &&
                            accounts.contains(defaultPhoneAccountHandle)) {
                        phoneAccountHandle = defaultPhoneAccountHandle;
                    }
                } else {
                    // Use the only PhoneAccount that is available
                    phoneAccountHandle = accounts.get(0);
                }
            }
        List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
                phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser);
        if (potentialPhoneAccounts.size() == 1) {
            phoneAccountHandle = potentialPhoneAccounts.get(0);
        } else {
            accounts = Collections.EMPTY_LIST;
            phoneAccountHandle = null;
        }

        call.setTargetPhoneAccount(phoneAccountHandle);

        boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;
@@ -1159,15 +1130,17 @@ public class CallsManager extends Call.ListenerBase
            return null;
        }

        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
                !call.isEmergencyCall() && !isSelfManaged;
        boolean needsAccountSelection =
                phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
                        && !call.isEmergencyCall() && !isSelfManaged;

        if (needsAccountSelection) {
            // This is the state where the user is expected to select an account
            call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
            // Create our own instance to modify (since extras may be Bundle.EMPTY)
            extras = new Bundle(extras);
            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
                    potentialPhoneAccounts);
        } else {
            PhoneAccount accountToUse =
                    mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
@@ -1211,6 +1184,82 @@ public class CallsManager extends Call.ListenerBase
        return call;
    }

    /**
     * Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing
     * call.  Takes into account the following:
     * 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the
     * {@link Intent#ACTION_CALL} intent.  If one was chosen it will be used if possible.
     * 2. Whether the call is a video call.  If the call being placed is a video call, an attempt is
     * first made to consider video capable phone accounts.  If no video capable phone accounts are
     * found, the usual non-video capable phone accounts will be considered.
     * 3. Whether there is a user-chosen default phone account; that one will be used if possible.
     *
     * @param targetPhoneAccountHandle The pre-chosen {@link PhoneAccountHandle} passed in when the
     *                                 call was placed.  Will be {@code null} if the
     *                                 {@link Intent#ACTION_CALL} intent did not specify a target
     *                                 phone account.
     * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching
     *               phone accounts.
     * @param isVideo {@code true} if the call is a video call, {@code false} otherwise.
     * @param initiatingUser The {@link UserHandle} the call is placed on.
     * @return
     */
    @VisibleForTesting
    public List<PhoneAccountHandle> findOutgoingCallPhoneAccount(
            PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,
            UserHandle initiatingUser) {
        boolean isSelfManaged = isSelfManaged(targetPhoneAccountHandle, initiatingUser);

        List<PhoneAccountHandle> accounts;
        if (!isSelfManaged) {
            // Try to find a potential phone account, taking into account whether this is a video
            // call.
            accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo);
            if (isVideo && accounts.size() == 0) {
                // Placing a video call but no video capable accounts were found, so consider any
                // call capable accounts (we can fallback to audio).
                accounts = constructPossiblePhoneAccounts(handle, initiatingUser,
                        false /* isVideo */);
            }
            Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts);

            // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this
            // call as if a phoneAccount was not specified (does the default behavior instead).
            // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
            if (targetPhoneAccountHandle != null) {
                if (!accounts.contains(targetPhoneAccountHandle)) {
                    targetPhoneAccountHandle = null;
                }
            }

            if (targetPhoneAccountHandle == null && accounts.size() > 0) {
                // No preset account, check if default exists that supports the URI scheme for the
                // handle and verify it can be used.
                if (accounts.size() > 1) {
                    PhoneAccountHandle defaultPhoneAccountHandle =
                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
                                    handle.getScheme(), initiatingUser);
                    if (defaultPhoneAccountHandle != null &&
                            accounts.contains(defaultPhoneAccountHandle)) {
                        accounts.clear();
                        accounts.add(defaultPhoneAccountHandle);
                    }
                }
            }
        } else {
            // Self-managed ConnectionServices can only have a single potential account.
            accounts = Arrays.asList(targetPhoneAccountHandle);
        }
        return accounts;
    }

    private boolean isSelfManaged(PhoneAccountHandle targetPhoneAccountHandle,
            UserHandle initiatingUser) {
        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
                targetPhoneAccountHandle, initiatingUser);
        return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();
    }

    /**
     * Attempts to issue/connect the specified call.
     *
@@ -1566,12 +1615,17 @@ public class CallsManager extends Call.ListenerBase
    // Construct the list of possible PhoneAccounts that the outgoing call can use based on the
    // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
    // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
    private List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user) {
    @VisibleForTesting
    public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
            boolean isVideo) {
        if (handle == null) {
            return Collections.emptyList();
        }
        // If we're specifically looking for video capable accounts, then include that capability,
        // otherwise specify no additional capability constraints.
        List<PhoneAccountHandle> allAccounts =
                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user);
                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
                        isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */);
        // First check the Radio SIM Technology
        if(mRadioSimVariants == null) {
            TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
@@ -2131,7 +2185,8 @@ public class CallsManager extends Call.ListenerBase
     *
     * @param call The call to add.
     */
    private void addCall(Call call) {
    @VisibleForTesting
    public void addCall(Call call) {
        Trace.beginSection("addCall");
        Log.v(this, "addCall(%s)", call);
        call.addListener(this);
+28 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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;

import android.content.Context;

/**
 * Abstracts out creation of InCallController for unit test purposes.
 */
public interface InCallControllerFactory {
    InCallController create(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
            SystemStateProvider systemStateProvider, DefaultDialerCache defaultDialerCache,
            Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper);
}
+23 −1
Original line number Diff line number Diff line
@@ -571,12 +571,34 @@ public class PhoneAccountRegistrar {
     * specified URI scheme.
     *
     * @param uriScheme The URI scheme.
     * @param includeDisabledAccounts {@code} if disabled {@link PhoneAccount}s should be included
     *      in the results.
     * @param userHandle The {@link UserHandle} to retrieve the {@link PhoneAccount}s for.
     * @return The phone account handles.
     */
    public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
            String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle) {
        return getCallCapablePhoneAccounts(uriScheme, includeDisabledAccounts, userHandle,
                0 /* capabilities */);
    }

    /**
     * Retrieves a list of all phone account call provider phone accounts supporting the
     * specified URI scheme.
     *
     * @param uriScheme The URI scheme.
     * @param includeDisabledAccounts {@code} if disabled {@link PhoneAccount}s should be included
     *      in the results.
     * @param userHandle The {@link UserHandle} to retrieve the {@link PhoneAccount}s for.
     * @param capabilities Extra {@link PhoneAccount} capabilities which matching
     *      {@link PhoneAccount}s must have.
     * @return The phone account handles.
     */
    public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
            String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle,
            int capabilities) {
        return getPhoneAccountHandles(
                PhoneAccount.CAPABILITY_CALL_PROVIDER,
                PhoneAccount.CAPABILITY_CALL_PROVIDER | capabilities,
                PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY /*excludedCapabilities*/,
                uriScheme, null, includeDisabledAccounts, userHandle);
    }
+13 −1
Original line number Diff line number Diff line
@@ -236,6 +236,17 @@ public class TelecomSystem {
        EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
                mContext.getResources().getString(R.string.ui_default_package), timeoutsAdapter);

        InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
            @Override
            public InCallController create(Context context, SyncRoot lock,
                    CallsManager callsManager, SystemStateProvider systemStateProvider,
                    DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
                    EmergencyCallHelper emergencyCallHelper) {
                return new InCallController(context, lock, callsManager, systemStateProvider,
                        defaultDialerCache, timeoutsAdapter, emergencyCallHelper);
            }
        };

        mCallsManager = new CallsManager(
                mContext,
                mLock,
@@ -256,7 +267,8 @@ public class TelecomSystem {
                phoneNumberUtilsAdapter,
                emergencyCallHelper,
                toneGeneratorFactory,
                clockProxy);
                clockProxy,
                inCallControllerFactory);

        mIncomingCallNotifier = incomingCallNotifier;
        incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
+328 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading