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

Commit 0663a612 authored by Pranav Madapurmath's avatar Pranav Madapurmath
Browse files

DSDA: Supporting ECC for call sequencing

Addresses a bug where when we have an active + held call on two
different subs, that historically Telecom always opts to disconnect the
live call. This suffices for the single sim case but for dual sim
behavior, we should always try to disconnect the held call and hold the
active call.

This CL also performs bulk disconnect of transactional, self-managed,
and non-holdable calls (only for DSDA). The phone account selection
logic has been modified to sort accounts based on current call state,
prioritizing accounts with ACTIVE > HOLDING > DIALING > RINGING > other
states first.

Bug: 392910450
Bug: 393240400
Flag: com.android.server.telecom.flags.enable_call_sequencing
Test: atest CtsTelecomCujTestCases
Test: atest CallSequencingTests
Change-Id: I7d1a7ce79559016dade505a9acd3af1bc3adc59b
parent b76dd023
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2651,7 +2651,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            return;
        }
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext, mFlags, new Timeouts.Adapter());
                phoneAccountRegistrar, mCallsManager, mContext, mFlags, new Timeouts.Adapter());
        mCreateConnectionProcessor.process();
    }

+57 −2
Original line number Diff line number Diff line
@@ -37,12 +37,13 @@ import com.android.server.telecom.flags.FeatureFlags;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
@@ -127,6 +128,21 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        }
    };

    /**
     * Call states which should be prioritized when sorting phone accounts. The ordering is
     * intentional and should NOT be modified. Other call states will not have any priority.
     */
    private static final int[] PRIORITY_CALL_STATES = new int []
            {CallState.ACTIVE, CallState.ON_HOLD, CallState.DIALING, CallState.RINGING};
    private static final int DEFAULT_CALL_STATE_PRIORITY = PRIORITY_CALL_STATES.length;
    private static final Map<Integer, Integer> mCallStatePriorityMap = new HashMap<>();
    static {
        for (int i = 0; i < PRIORITY_CALL_STATES.length; i++) {
            mCallStatePriorityMap.put(PRIORITY_CALL_STATES[i], i);
        }
    }


    private ITelephonyManagerAdapter mTelephonyAdapter = new ITelephonyManagerAdapterImpl();

    private final Call mCall;
@@ -136,6 +152,7 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
    private CreateConnectionResponse mCallResponse;
    private DisconnectCause mLastErrorDisconnectCause;
    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    private final CallsManager mCallsManager;
    private final Context mContext;
    private final FeatureFlags mFlags;
    private final Timeouts.Adapter mTimeoutsAdapter;
@@ -148,6 +165,7 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
            ConnectionServiceRepository repository,
            CreateConnectionResponse response,
            PhoneAccountRegistrar phoneAccountRegistrar,
            CallsManager callsManager,
            Context context,
            FeatureFlags featureFlags,
            Timeouts.Adapter timeoutsAdapter) {
@@ -156,6 +174,7 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        mRepository = repository;
        mCallResponse = response;
        mPhoneAccountRegistrar = phoneAccountRegistrar;
        mCallsManager = callsManager;
        mContext = context;
        mConnectionAttempt = 0;
        mFlags = featureFlags;
@@ -693,6 +712,23 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
                return retval;
            }

            // Sort accounts by ongoing call states
            Set<Integer> callStatesAccount1 = mCallsManager.getCalls().stream()
                    .filter(c -> Objects.equals(account1.getAccountHandle(),
                            c.getTargetPhoneAccount()))
                    .map(Call::getState).collect(Collectors.toSet());
            Set<Integer> callStatesAccount2 = mCallsManager.getCalls().stream()
                    .filter(c -> Objects.equals(account2.getAccountHandle(),
                            c.getTargetPhoneAccount()))
                    .map(Call::getState).collect(Collectors.toSet());
            int account1Priority = computeCallStatePriority(callStatesAccount1);
            int account2Priority = computeCallStatePriority(callStatesAccount2);
            Log.d(this, "account1: %s, call state priority: %s", account1, account1Priority);
            Log.d(this, "account2: %s, call state priority: %s", account2, account2Priority);
            if (account1Priority != account2Priority) {
                return account1Priority < account2Priority ? -1 : 1;
            }

            // Prefer the user's choice if all PhoneAccounts are associated with valid logical
            // slots.
            if (userPreferredAccount != null) {
@@ -731,6 +767,25 @@ public class CreateConnectionProcessor implements CreateConnectionResponse {
        });
    }

    /**
     * Computes the call state priority based on the passed in call states associated with the
     * calls present on the phone account. The lower the value, the higher the priority (i.e.
     * ACTIVE (0) < HOLDING (1) < DIALING (2) < RINGING (3) equates to ACTIVE holding the highest
     * priority).
     */
    private int computeCallStatePriority(Set<Integer> callStates) {
        int priority = DEFAULT_CALL_STATE_PRIORITY;
        for (int state: callStates) {
            if (priority == mCallStatePriorityMap.get(CallState.ACTIVE)) {
                return priority;
            } else if (mCallStatePriorityMap.containsKey(state)
                    && priority > mCallStatePriorityMap.get(state)) {
                priority = mCallStatePriorityMap.get(state);
            }
        }
        return priority;
    }

    private static String nullToEmpty(String str) {
        return str == null ? "" : str;
    }
+20 −9
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ public class PhoneAccountRegistrar {
    private final TelecomSystem.SyncRoot mLock;
    private State mState;
    private UserHandle mCurrentUserHandle;
    private String mTestPhoneAccountPackageNameFilter;
    private final Set<String> mTestPhoneAccountPackageNameFilters;
    private interface PhoneAccountRegistrarWriteLock {}
    private final PhoneAccountRegistrarWriteLock mWriteLock =
            new PhoneAccountRegistrarWriteLock() {};
@@ -215,6 +215,7 @@ public class PhoneAccountRegistrar {
        mAppLabelProxy = appLabelProxy;
        mCurrentUserHandle = Process.myUserHandle();
        mTelecomFeatureFlags = telecomFeatureFlags;
        mTestPhoneAccountPackageNameFilters = new HashSet<>();

        if (telephonyFeatureFlags != null) {
            mTelephonyFeatureFlags = telephonyFeatureFlags;
@@ -607,23 +608,33 @@ public class PhoneAccountRegistrar {
     * {@link PhoneAccount}s with the same package name.
     */
    public void setTestPhoneAccountPackageNameFilter(String packageNameFilter) {
        mTestPhoneAccountPackageNameFilter = packageNameFilter;
        Log.i(this, "filter set for PhoneAccounts, packageName=" + packageNameFilter);
        mTestPhoneAccountPackageNameFilters.clear();
        if (packageNameFilter == null) {
            return;
        }
        String [] pkgNamesFilter = packageNameFilter.split(",");
        mTestPhoneAccountPackageNameFilters.addAll(Arrays.asList(pkgNamesFilter));
        StringBuilder pkgNames = new StringBuilder();
        for (int i = 0; i < pkgNamesFilter.length; i++) {
            pkgNames.append(pkgNamesFilter[i])
                    .append(i != pkgNamesFilter.length - 1 ? ", " : ".");
        }
        Log.i(this, "filter set for PhoneAccounts, packageNames: %s", pkgNames.toString());
    }

    /**
     * Filter the given {@link List<PhoneAccount>} and keep only {@link PhoneAccount}s that have the
     * #mTestPhoneAccountPackageNameFilter.
     * #mTestPhoneAccountPackageNameFilters.
     * @param accounts List of {@link PhoneAccount}s to filter.
     * @return new list of filtered {@link PhoneAccount}s.
     */
    public List<PhoneAccount> filterRestrictedPhoneAccounts(List<PhoneAccount> accounts) {
        if (TextUtils.isEmpty(mTestPhoneAccountPackageNameFilter)) {
        if (mTestPhoneAccountPackageNameFilters.isEmpty()) {
            return new ArrayList<>(accounts);
        }
        // Remove all PhoneAccounts that do not have the same package name as the filter.
        return accounts.stream().filter(account -> mTestPhoneAccountPackageNameFilter.equals(
                account.getAccountHandle().getComponentName().getPackageName()))
        // Remove all PhoneAccounts that do not have the same package name (prefix) as the filter.
        return accounts.stream().filter(account -> mTestPhoneAccountPackageNameFilters
                .contains(account.getAccountHandle().getComponentName().getPackageName()))
                .collect(Collectors.toList());
    }

@@ -1977,7 +1988,7 @@ public class PhoneAccountRegistrar {
            }
            pw.decreaseIndent();
            pw.increaseIndent();
            pw.println("test emergency PhoneAccount filter: " + mTestPhoneAccountPackageNameFilter);
            pw.println("test emergency PhoneAccount filter: " + mTestPhoneAccountPackageNameFilters);
            pw.decreaseIndent();
        }
    }
+226 −112

File changed.

Preview size limit exceeded, changes collapsed.

+19 −0
Original line number Diff line number Diff line
@@ -516,6 +516,25 @@ public class CallSequencingTests extends TelecomTestCase {
        assertTrue(waitForFutureResult(future, false));
    }

    @SmallTest
    @Test
    public void testMakeRoomForOutgoingEmergencyCall_DoesNotSupportHoldingEmergency() {
        setupMakeRoomForOutgoingEmergencyCallMocks();
        when(mCallsManager.getCalls()).thenReturn(List.of(mActiveCall, mRingingCall));
        when(mActiveCall.getTargetPhoneAccount()).thenReturn(mHandle1);
        // Set the KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL carrier config to false for the active
        // call's phone account.
        PersistableBundle bundle = new PersistableBundle();
        bundle.putBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, false);
        when(mCallsManager.getCarrierConfigForPhoneAccount(eq(mHandle1))).thenReturn(bundle);
        when(mNewCall.getTargetPhoneAccount()).thenReturn(mHandle2);
        when(mRingingCall.getTargetPhoneAccount()).thenReturn(mHandle2);

        mController.makeRoomForOutgoingCall(true, mNewCall);
        // Verify that the active call got disconnected as it doesn't support holding for emergency.
        verify(mActiveCall, timeout(SEQUENCING_TIMEOUT_MS)).disconnect(anyString());
    }

    @Test
    @SmallTest
    public void testMakeRoomForOutgoingCall() {
Loading