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

Commit ba248894 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Ims: Add support for Adhoc Conference calls"

parents fe8fa5f6 ff90e05f
Loading
Loading
Loading
Loading
+100 −6
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.Log;
import android.telecom.Logging.EventManager;
import android.telecom.ParcelableConference;
import android.telecom.ParcelableConnection;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -326,6 +327,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    /** The handle with which to establish this call. */
    private Uri mHandle;

    /** The participants with which to establish adhoc conference call */
    private List<Uri> mParticipants;
    /**
     * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
     */
@@ -624,15 +627,43 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            boolean isConference,
            ClockProxy clockProxy,
            ToastFactory toastFactory) {
        this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter,
               handle, null, gatewayInfo, connectionManagerPhoneAccountHandle,
               targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection,
               isConference, clockProxy, toastFactory);

    }

    public Call(
            String callId,
            Context context,
            CallsManager callsManager,
            TelecomSystem.SyncRoot lock,
            ConnectionServiceRepository repository,
            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
            Uri handle,
            List<Uri> participants,
            GatewayInfo gatewayInfo,
            PhoneAccountHandle connectionManagerPhoneAccountHandle,
            PhoneAccountHandle targetPhoneAccountHandle,
            int callDirection,
            boolean shouldAttachToExistingConnection,
            boolean isConference,
            ClockProxy clockProxy,
            ToastFactory toastFactory) {

        mId = callId;
        mConnectionId = callId;
        mState = isConference ? CallState.ACTIVE : CallState.NEW;
        mState = (isConference && callDirection != CALL_DIRECTION_INCOMING &&
                callDirection != CALL_DIRECTION_OUTGOING) ?
                CallState.ACTIVE : CallState.NEW;
        mContext = context;
        mCallsManager = callsManager;
        mLock = lock;
        mRepository = repository;
        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
        setHandle(handle);
        mParticipants = participants;
        mPostDialDigits = handle != null
                ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
        mGatewayInfo = gatewayInfo;
@@ -1080,6 +1111,16 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return mHandle;
    }

    public List<Uri> getParticipants() {
        return mParticipants;
    }

    public boolean isAdhocConferenceCall() {
        return mIsConference &&
                (mCallDirection == CALL_DIRECTION_OUTGOING ||
                mCallDirection == CALL_DIRECTION_INCOMING);
    }

    public String getPostDialDigits() {
        return mPostDialDigits;
    }
@@ -1827,6 +1868,39 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        mCreateConnectionProcessor.process();
    }

    @Override
    public void handleCreateConferenceSuccess(
            CallIdMapper idMapper,
            ParcelableConference conference) {
        Log.v(this, "handleCreateConferenceSuccessful %s", conference);
        setTargetPhoneAccount(conference.getPhoneAccount());
        setHandle(conference.getHandle(), conference.getHandlePresentation());

        setConnectionCapabilities(conference.getConnectionCapabilities());
        setConnectionProperties(conference.getConnectionProperties());
        setVideoProvider(conference.getVideoProvider());
        setVideoState(conference.getVideoState());
        setRingbackRequested(conference.isRingbackRequested());
        setStatusHints(conference.getStatusHints());
        putExtras(SOURCE_CONNECTION_SERVICE, conference.getExtras());

        switch (mCallDirection) {
            case CALL_DIRECTION_INCOMING:
                // Listeners (just CallsManager for now) will be responsible for checking whether
                // the call should be blocked.
                for (Listener l : mListeners) {
                    l.onSuccessfulIncomingCall(this);
                }
                break;
            case CALL_DIRECTION_OUTGOING:
                for (Listener l : mListeners) {
                    l.onSuccessfulOutgoingCall(this,
                            getStateFromConnectionState(conference.getState()));
                }
                break;
        }
    }

    @Override
    public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
@@ -1877,6 +1951,26 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
    }

    @Override
    public void handleCreateConferenceFailure(DisconnectCause disconnectCause) {
        clearConnectionService();
        setDisconnectCause(disconnectCause);
        mCallsManager.markCallAsDisconnected(this, disconnectCause);

        switch (mCallDirection) {
            case CALL_DIRECTION_INCOMING:
                for (Listener listener : mListeners) {
                    listener.onFailedIncomingCall(this);
                }
                break;
            case CALL_DIRECTION_OUTGOING:
                for (Listener listener : mListeners) {
                    listener.onFailedOutgoingCall(this, disconnectCause);
                }
                break;
        }
    }

    @Override
    public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
        clearConnectionService();
+124 −11
Original line number Diff line number Diff line
@@ -109,6 +109,7 @@ import com.android.server.telecom.ui.IncomingCallNotifier;
import com.android.server.telecom.ui.ToastFactory;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -1153,6 +1154,11 @@ public class CallsManager extends Call.ListenerBase
        mListeners.remove(listener);
    }

    void processIncomingConference(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
        Log.d(this, "processIncomingCallConference");
        processIncomingCallIntent(phoneAccountHandle, extras, true);
    }

    /**
     * Starts the process to attach the call to a connection service.
     *
@@ -1161,6 +1167,11 @@ public class CallsManager extends Call.ListenerBase
     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
     */
    void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
        processIncomingCallIntent(phoneAccountHandle, extras, false);
    }

    void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras,
        boolean isConference) {
        Log.d(this, "processIncomingCallIntent");
        boolean isHandover = extras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER);
        Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
@@ -1181,7 +1192,7 @@ public class CallsManager extends Call.ListenerBase
                phoneAccountHandle,
                Call.CALL_DIRECTION_INCOMING /* callDirection */,
                false /* forceAttachToExistingConnection */,
                false, /* isConference */
                isConference, /* isConference */
                mClockProxy,
                mToastFactory);

@@ -1295,14 +1306,22 @@ public class CallsManager extends Call.ListenerBase

        if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call,
                call.getTargetPhoneAccount()))) {
            if (isConference) {
                notifyCreateConferenceFailed(phoneAccountHandle, call);
            } else {
                notifyCreateConnectionFailed(phoneAccountHandle, call);
            }
        } else if (isInEmergencyCall()) {
            // The incoming call is implicitly being rejected so the user does not get any incoming
            // call UI during an emergency call. In this case, log the call as missed instead of
            // rejected since the user did not explicitly reject.
            mCallLogManager.logCall(call, Calls.MISSED_TYPE,
                    true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/);
            if (isConference) {
                notifyCreateConferenceFailed(phoneAccountHandle, call);
            } else {
                notifyCreateConnectionFailed(phoneAccountHandle, call);
            }
        } else {
            call.startCreateConnection(mPhoneAccountRegistrar);
        }
@@ -1390,7 +1409,18 @@ public class CallsManager extends Call.ListenerBase
            PhoneAccountHandle requestedAccountHandle,
            Bundle extras, UserHandle initiatingUser, Intent originalIntent,
            String callingPackage) {
        final List<Uri> callee = new ArrayList<>();
        callee.add(handle);
        return startOutgoingCall(callee, requestedAccountHandle, extras, initiatingUser,
                originalIntent, callingPackage, false);
    }

    private CompletableFuture<Call> startOutgoingCall(List<Uri> participants,
            PhoneAccountHandle requestedAccountHandle,
            Bundle extras, UserHandle initiatingUser, Intent originalIntent,
            String callingPackage, boolean isConference) {
        boolean isReusedCall;
        Uri handle = isConference ? Uri.parse("tel:conf-factory") : participants.get(0);
        Call call = reuseOutgoingCall(handle);

        PhoneAccount account =
@@ -1406,12 +1436,13 @@ public class CallsManager extends Call.ListenerBase
                    mConnectionServiceRepository,
                    mPhoneNumberUtilsAdapter,
                    handle,
                    isConference ? participants : null,
                    null /* gatewayInfo */,
                    null /* connectionManagerPhoneAccount */,
                    null /* requestedAccountHandle */,
                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                    false /* forceAttachToExistingConnection */,
                    false, /* isConference */
                    isConference, /* isConference */
                    mClockProxy,
                    mToastFactory);
            call.initAnalytics(callingPackage);
@@ -1475,7 +1506,8 @@ public class CallsManager extends Call.ListenerBase
                CompletableFuture.completedFuture((Void) null).thenComposeAsync((x) ->
                                findOutgoingCallPhoneAccount(requestedAccountHandle, handle,
                                        VideoProfile.isVideo(finalVideoState),
                                        finalCall.isEmergencyCall(), initiatingUser),
                                        finalCall.isEmergencyCall(), initiatingUser,
                                        isConference),
                        new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP", mLock));

        // This is a block of code that executes after the list of potential phone accts has been
@@ -1543,9 +1575,14 @@ public class CallsManager extends Call.ListenerBase
                        } else {
                            // If the ongoing call is a managed call, we will prevent the outgoing
                            // call from dialing.
                            if (isConference) {
                                notifyCreateConferenceFailed(finalCall.getTargetPhoneAccount(),
                                    finalCall);
                            } else {
                                notifyCreateConnectionFailed(
                                        finalCall.getTargetPhoneAccount(), finalCall);
                            }
                        }
                        Log.i(CallsManager.this, "Aborting call since there's no room");
                        return CompletableFuture.completedFuture(null);
                    }
@@ -1706,6 +1743,38 @@ public class CallsManager extends Call.ListenerBase
        return mLatestPostSelectionProcessingFuture;
    }

    public void startConference(List<Uri> participants, Bundle clientExtras, String callingPackage,
            UserHandle initiatingUser) {

         if (clientExtras == null) {
             clientExtras = new Bundle();
         }

         PhoneAccountHandle phoneAccountHandle = clientExtras.getParcelable(
                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
         CompletableFuture<Call> callFuture = startOutgoingCall(participants, phoneAccountHandle,
                 clientExtras, initiatingUser, null/* originalIntent */, callingPackage,
                 true/* isconference*/);

         final boolean speakerphoneOn = clientExtras.getBoolean(
                 TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE);
         final int videoState = clientExtras.getInt(
                 TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE);

         final Session logSubsession = Log.createSubsession();
         callFuture.thenAccept((call) -> {
             if (call != null) {
                 Log.continueSession(logSubsession, "CM.pOGC");
                 try {
                     placeOutgoingCall(call, call.getHandle(), null/* gatewayInfo */,
                             speakerphoneOn, videoState);
                 } finally {
                     Log.endSession();
                 }
             }
         });
    }

    /**
     * Performs call identification for an outgoing phone call.
     * @param theCall The outgoing call to perform identification.
@@ -1768,6 +1837,14 @@ public class CallsManager extends Call.ListenerBase
    public CompletableFuture<List<PhoneAccountHandle>> findOutgoingCallPhoneAccount(
            PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,
            boolean isEmergency, UserHandle initiatingUser) {
       return findOutgoingCallPhoneAccount(targetPhoneAccountHandle, handle, isVideo,
               isEmergency, initiatingUser, false/* isConference */);
    }

    public CompletableFuture<List<PhoneAccountHandle>> findOutgoingCallPhoneAccount(
            PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,
            boolean isEmergency, UserHandle initiatingUser, boolean isConference) {

        if (isSelfManaged(targetPhoneAccountHandle, initiatingUser)) {
            return CompletableFuture.completedFuture(Arrays.asList(targetPhoneAccountHandle));
        }
@@ -1775,12 +1852,13 @@ public class CallsManager extends Call.ListenerBase
        List<PhoneAccountHandle> accounts;
        // Try to find a potential phone account, taking into account whether this is a video
        // call.
        accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo, isEmergency);
        accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo, isEmergency,
                isConference);
        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 */, isEmergency /* isEmergency */);
                    false /* isVideo */, isEmergency /* isEmergency */, isConference);
        }
        Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts);

@@ -2057,7 +2135,11 @@ public class CallsManager extends Call.ListenerBase
            // If the account has been set, proceed to place the outgoing call.
            // Otherwise the connection will be initiated when the account is set by the user.
            if (call.isSelfManaged() && !isOutgoingCallPermitted) {
                if (call.isAdhocConferenceCall()) {
                    notifyCreateConferenceFailed(call.getTargetPhoneAccount(), call);
                } else {
                    notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
                }
            } else {
                if (call.isEmergencyCall()) {
                    // Drop any ongoing self-managed calls to make way for an emergency call.
@@ -2458,15 +2540,23 @@ public class CallsManager extends Call.ListenerBase
    @VisibleForTesting
    public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
            boolean isVideo, boolean isEmergency) {
        return constructPossiblePhoneAccounts(handle, user, isVideo, isEmergency, false);
    }

    public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
            boolean isVideo, boolean isEmergency, boolean isConference) {

        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. When handling the emergency call,
        // it also needs to find the phone accounts excluded by CAPABILITY_EMERGENCY_CALLS_ONLY.
        int capabilities = isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0;
        capabilities |= isConference ? PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING : 0;
        List<PhoneAccountHandle> allAccounts =
                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
                        isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */,
                        capabilities,
                        isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY);
        if (mMaxNumberOfSimultaneouslyActiveSims < 0) {
            mMaxNumberOfSimultaneouslyActiveSims =
@@ -4362,6 +4452,29 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    /**
     * Notifies the {@link android.telecom.ConnectionService} associated with a
     * {@link PhoneAccountHandle} that the attempt to create a new connection has failed.
     *
     * @param phoneAccountHandle The {@link PhoneAccountHandle}.
     * @param call The {@link Call} which could not be added.
     */
    private void notifyCreateConferenceFailed(PhoneAccountHandle phoneAccountHandle, Call call) {
        if (phoneAccountHandle == null) {
            return;
        }
        ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
                phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle());
        if (service == null) {
            Log.i(this, "Found no connection service.");
            return;
        } else {
            call.setConnectionService(service);
            service.createConferenceFailed(call);
        }
    }


    /**
     * Notifies the {@link android.telecom.ConnectionService} associated with a
     * {@link PhoneAccountHandle} that the attempt to handover a call has failed.
+157 −0

File changed.

Preview size limit exceeded, changes collapsed.

+62 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.telecom;
import android.content.Context;
import android.telecom.DisconnectCause;
import android.telecom.Log;
import android.telecom.ParcelableConference;
import android.telecom.ParcelableConnection;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -245,7 +246,11 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);
                if (mCall.isIncoming()) {
                    if (mCall.isAdhocConferenceCall()) {
                        mService.createConference(mCall, CreateConnectionProcessor.this);
                    } else {
                        mService.createConnection(mCall, CreateConnectionProcessor.this);
                    }
                } else {
                    // Start to create the connection for outgoing call after the ConnectionService
                    // of the call has gained the focus.
@@ -254,11 +259,17 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
                            new CallsManager.RequestCallback(new CallsManager.PendingAction() {
                                @Override
                                public void performAction() {
                                    if (mCall.isAdhocConferenceCall()) {
                                        Log.d(this, "perform create conference");
                                        mService.createConference(mCall,
                                                CreateConnectionProcessor.this);
                                    } else {
                                        Log.d(this, "perform create connection");
                                        mService.createConnection(
                                                mCall,
                                                CreateConnectionProcessor.this);
                                    }
                                }
                            }));

                }
@@ -267,9 +278,13 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            if (mCall.isAdhocConferenceCall()) {
                notifyConferenceCallFailure(disconnectCause);
            } else {
                notifyCallConnectionFailure(disconnectCause);
            }
        }
    }

    private void setTimeoutIfNeeded(ConnectionServiceWrapper service, CallAttemptRecord attempt) {
        clearTimeout();
@@ -457,6 +472,16 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        }
    }

    private void notifyConferenceCallFailure(DisconnectCause errorDisconnectCause) {
        if (mCallResponse != null) {
            clearTimeout();
            mCallResponse.handleCreateConferenceFailure(errorDisconnectCause);
            mCallResponse = null;
            mCall.clearConnectionService();
        }
    }


    @Override
    public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
@@ -475,6 +500,25 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        }
    }

    @Override
    public void handleCreateConferenceSuccess(
            CallIdMapper idMapper,
            ParcelableConference conference) {
        if (mCallResponse == null) {
            // Nobody is listening for this conference attempt any longer; ask the responsible
            // ConnectionService to tear down any resources associated with the call
            mService.abort(mCall);
        } else {
            // Success -- share the good news and remember that we are no longer interested
            // in hearing about any more attempts
            mCallResponse.handleCreateConferenceSuccess(idMapper, conference);
            mCallResponse = null;
            // If there's a timeout running then don't clear it. The timeout can be triggered
            // after the call has successfully been created but before it has become active.
        }
    }


    private boolean shouldFailCallIfConnectionManagerFails(DisconnectCause cause) {
        // Connection Manager does not exist or does not match registered Connection Manager
        // Since Connection manager is a proxy for SIM, fall back to SIM
@@ -522,6 +566,18 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        attemptNextPhoneAccount();
    }

    @Override
    public void handleCreateConferenceFailure(DisconnectCause errorDisconnectCause) {
        // Failure of some sort; record the reasons for failure and try again if possible
        Log.d(CreateConnectionProcessor.this, "Conference failed: (%s)", errorDisconnectCause);
        if (shouldFailCallIfConnectionManagerFails(errorDisconnectCause)) {
            notifyConferenceCallFailure(errorDisconnectCause);
            return;
        }
        mLastErrorDisconnectCause = errorDisconnectCause;
        attemptNextPhoneAccount();
    }

    public void sortSimPhoneAccountsForEmergency(List<PhoneAccount> accounts,
            PhoneAccount userPreferredAccount) {
        // Sort the accounts according to how we want to display them (ascending order).
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom;

import android.telecom.DisconnectCause;
import android.telecom.ParcelableConference;
import android.telecom.ParcelableConnection;

import com.android.internal.annotations.VisibleForTesting;
@@ -28,4 +29,7 @@ import com.android.internal.annotations.VisibleForTesting;
public interface CreateConnectionResponse {
    void handleCreateConnectionSuccess(CallIdMapper idMapper, ParcelableConnection connection);
    void handleCreateConnectionFailure(DisconnectCause disconnectCaused);

    void handleCreateConferenceSuccess(CallIdMapper idMapper, ParcelableConference conference);
    void handleCreateConferenceFailure(DisconnectCause disconnectCaused);
}
Loading