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

Commit f59e841c authored by Xiangyu/Malcolm Chen's avatar Xiangyu/Malcolm Chen Committed by Gerrit Code Review
Browse files

Merge changes I48eb4966,I32f311a6,Ib82d26ee,Iec9fe858,I43e34237

* changes:
  Fix bug that phone crashes when switching data.
  Fix CBRS DataSwitch sending multiple START events.
  For back to back setPreferredDataCall, send failure on first callback.
  Fix PhoneSwitcherTest flakiness by adding a short wait.
  Switch to SIM with voice call on hold.
parents c8d6062a e05ede20
Loading
Loading
Loading
Loading
+67 −51
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -255,20 +254,26 @@ public class PhoneSwitcher extends Handler {

    private ConnectivityManager mConnectivityManager;

    private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback =
            new NetworkCallback() {
    private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
        public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
        @Override
                public void onAvailable(Network network) {
                    if (mConnectivityManager.getNetworkCapabilities(network)
                            .hasTransport(TRANSPORT_CELLULAR)) {
        public void onCapabilitiesChanged(Network network,
                NetworkCapabilities networkCapabilities) {
            if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)
                    && SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
                    && mExpectedSubId == getSubIdFromNetworkSpecifier(
                            networkCapabilities.getNetworkSpecifier())) {
                logDataSwitchEvent(
                                mOpptDataSubId,
                        mExpectedSubId,
                        TelephonyEvent.EventState.EVENT_STATE_END,
                                TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN);
                    }
                        mSwitchReason);
                removeDefaultNetworkChangeCallback();
            }
            };
        }
    }

    private final DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback();

    /**
     * Method to get singleton instance.
@@ -321,7 +326,7 @@ public class PhoneSwitcher extends Handler {
        // subscription.
        mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX;
        for (Phone phone : mPhones) {
            if (isCallActive(phone) || isCallActive(phone.getImsPhone())) {
            if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) {
                mPhoneIdInVoiceCall = phone.getPhoneId();
                break;
            }
@@ -448,10 +453,11 @@ public class PhoneSwitcher extends Handler {
            }
            case EVENT_PRIMARY_DATA_SUB_CHANGED: {
                if (onEvaluate(REQUESTS_UNCHANGED, "primary data subId changed")) {
                    logDataSwitchEvent(mOpptDataSubId,
                    logDataSwitchEvent(mPreferredDataSubId,
                            TelephonyEvent.EventState.EVENT_STATE_START,
                            DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
                    registerDefaultNetworkChangeCallback();
                    registerDefaultNetworkChangeCallback(mPreferredDataSubId,
                            DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
                }
                break;
            }
@@ -521,10 +527,11 @@ public class PhoneSwitcher extends Handler {
            // fall through
            case EVENT_DATA_ENABLED_CHANGED:
                if (onEvaluate(REQUESTS_UNCHANGED, "EVENT_PRECISE_CALL_STATE_CHANGED")) {
                    logDataSwitchEvent(mOpptDataSubId,
                    logDataSwitchEvent(mPreferredDataSubId,
                            TelephonyEvent.EventState.EVENT_STATE_START,
                            DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
                    registerDefaultNetworkChangeCallback();
                    registerDefaultNetworkChangeCallback(mPreferredDataSubId,
                            DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
                }
                break;
            case EVENT_NETWORK_VALIDATION_DONE: {
@@ -669,26 +676,21 @@ public class PhoneSwitcher extends Handler {
    }

    private void removeDefaultNetworkChangeCallback() {
        synchronized (mHasRegisteredDefaultNetworkChangeCallback) {
            if (mHasRegisteredDefaultNetworkChangeCallback) {
                mHasRegisteredDefaultNetworkChangeCallback = false;
        removeMessages(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK);
        mDefaultNetworkCallback.mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
        mDefaultNetworkCallback.mSwitchReason =
                TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
        mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
    }
        }
    }

    private void registerDefaultNetworkChangeCallback() {
        removeDefaultNetworkChangeCallback();

        synchronized (mHasRegisteredDefaultNetworkChangeCallback) {
            mHasRegisteredDefaultNetworkChangeCallback = true;
            mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback);
    private void registerDefaultNetworkChangeCallback(int expectedSubId, int reason) {
        mDefaultNetworkCallback.mExpectedSubId = expectedSubId;
        mDefaultNetworkCallback.mSwitchReason = reason;
        mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
        sendMessageDelayed(
                obtainMessage(EVENT_REMOVE_DEFAULT_NETWORK_CHANGE_CALLBACK),
                DEFAULT_NETWORK_CHANGE_TIMEOUT_MS);
    }
    }

    private void collectRequestNetworkMetrics(NetworkRequest networkRequest) {
        // Request network for MMS will temporary disable the network on default data subscription,
@@ -943,7 +945,8 @@ public class PhoneSwitcher extends Handler {
    }

    private int phoneIdForRequest(NetworkRequest netRequest) {
        int subId = getSubIdFromNetworkRequest(netRequest);
        int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities
                .getNetworkSpecifier());

        if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId;
        if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX;
@@ -975,8 +978,7 @@ public class PhoneSwitcher extends Handler {
        return phoneId;
    }

    private int getSubIdFromNetworkRequest(NetworkRequest networkRequest) {
        NetworkSpecifier specifier = networkRequest.networkCapabilities.getNetworkSpecifier();
    private int getSubIdFromNetworkSpecifier(NetworkSpecifier specifier) {
        if (specifier == null) {
            return DEFAULT_SUBSCRIPTION_ID;
        }
@@ -1131,12 +1133,17 @@ public class PhoneSwitcher extends Handler {
            return;
        }

        // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId
        // if queued.
        removeMessages(EVENT_NETWORK_VALIDATION_DONE);

        int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
                ? mPrimaryDataSubId : subId;

        if (mValidator.isValidating()
                && (!needValidation || subIdToValidate != mValidator.getSubIdInValidation())) {
        if (mValidator.isValidating()) {
            mValidator.stopValidation();
            sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
            mSetOpptSubCallback = null;
        }

        if (subId == mOpptDataSubId) {
@@ -1144,6 +1151,13 @@ public class PhoneSwitcher extends Handler {
            return;
        }

        logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
                TelephonyEvent.EventState.EVENT_STATE_START,
                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
        registerDefaultNetworkChangeCallback(
                subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);

        // If validation feature is not supported, set it directly. Otherwise,
        // start validation on the subscription first.
        if (mValidator.isValidationFeatureSupported() && needValidation) {
@@ -1173,12 +1187,7 @@ public class PhoneSwitcher extends Handler {
    private void setOpportunisticSubscriptionInternal(int subId) {
        if (mOpptDataSubId != subId) {
            mOpptDataSubId = subId;
            if (onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed")) {
                logDataSwitchEvent(mOpptDataSubId,
                        TelephonyEvent.EventState.EVENT_STATE_START,
                        DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
                registerDefaultNetworkChangeCallback();
            }
            onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed");
        }
    }

@@ -1231,13 +1240,20 @@ public class PhoneSwitcher extends Handler {
                subId, needValidation ? 1 : 0, callback).sendToTarget();
    }

    private boolean isCallActive(Phone phone) {
    private boolean isPhoneInVoiceCall(Phone phone) {
        if (phone == null) {
            return false;
        }

        // A phone in voice call might trigger data being switched to it.
        // We only report true if its precise call state is ACTIVE, ALERTING or HOLDING.
        // The reason is data switching is interrupting, so we only switch when necessary and
        // acknowledged by the users. For incoming call, we don't switch until answered
        // (RINGING -> ACTIVE), for outgoing call we don't switch until call is connected
        // in network (DIALING -> ALERTING).
        return (phone.getForegroundCall().getState() == Call.State.ACTIVE
                || phone.getForegroundCall().getState() == Call.State.ALERTING);
                || phone.getForegroundCall().getState() == Call.State.ALERTING
                || phone.getBackgroundCall().getState() == Call.State.HOLDING);
    }

    private void updateHalCommandToUse() {
@@ -1260,7 +1276,7 @@ public class PhoneSwitcher extends Handler {
    }

    private void logDataSwitchEvent(int subId, int state, int reason) {
        subId = subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId;
        log("logDataSwitchEvent subId " + subId + " state " + state + " reason " + reason);
        DataSwitch dataSwitch = new DataSwitch();
        dataSwitch.state = state;
        dataSwitch.reason = reason;
+129 −3
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.internal.telephony;

import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;

import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;

@@ -91,7 +95,13 @@ public class PhoneSwitcherTest extends TelephonyTest {
    @Mock
    private GsmCdmaCall mActiveCall;
    @Mock
    private GsmCdmaCall mHoldingCall;
    @Mock
    private GsmCdmaCall mInactiveCall;
    @Mock
    private ISetOpportunisticDataCallback mSetOpptDataCallback1;
    @Mock
    private ISetOpportunisticDataCallback mSetOpptDataCallback2;

    // The thread that mPhoneSwitcher will handle events in.
    private HandlerThread mHandlerThread;
@@ -116,6 +126,7 @@ public class PhoneSwitcherTest extends TelephonyTest {

        doReturn(Call.State.ACTIVE).when(mActiveCall).getState();
        doReturn(Call.State.IDLE).when(mInactiveCall).getState();
        doReturn(Call.State.HOLDING).when(mHoldingCall).getState();
    }

    @After
@@ -153,6 +164,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
        clearInvocations(mActivePhoneSwitchHandler);

        setDefaultDataSubId(0);
        waitABit();

        verify(mActivePhoneSwitchHandler, never()).sendMessageAtTime(any(), anyLong());

@@ -504,14 +516,15 @@ public class PhoneSwitcherTest extends TelephonyTest {
        doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
        initialize(numPhones, maxActivePhones);

        // Mark sub 2 as opportunistic.
        doReturn(true).when(mSubscriptionController).isOpportunistic(2);
        // Phone 0 has sub 1, phone 1 has sub 2.
        // Sub 1 is default data sub.
        // Both are active subscriptions are active sub, as they are in both active slots.
        setSlotIndexToSubId(0, 1);
        setSlotIndexToSubId(1, 2);
        setDefaultDataSubId(1);
        // Mark sub 2 as opportunistic.
        doReturn(true).when(mSubscriptionController).isOpportunistic(2);
        waitABit();

        // Phone 0 (sub 1) should be activated as it has default data sub.
        assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
@@ -572,7 +585,6 @@ public class PhoneSwitcherTest extends TelephonyTest {
        notifyDataEnabled(false);
        notifyPhoneAsInCall(mPhone2);
        verify(mMockRadioConfig, never()).setPreferredDataModem(anyInt(), any());
        waitABit();
        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));

@@ -588,6 +600,13 @@ public class PhoneSwitcherTest extends TelephonyTest {
        verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
        clearInvocations(mMockRadioConfig);

        // Phone2 has holding call, but data is turned off. So no data switching should happen.
        notifyPhoneAsInHoldingCall(mPhone2);
        verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 1));
        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));

        mHandlerThread.quit();
    }
@@ -861,6 +880,107 @@ public class PhoneSwitcherTest extends TelephonyTest {
        mHandlerThread.quit();
    }

    @Test
    @SmallTest
    public void testSetPreferredDataCallback() throws Exception {
        final int numPhones = 2;
        final int maxActivePhones = 1;
        doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
        initialize(numPhones, maxActivePhones);

        // Mark sub 2 as opportunistic.
        doReturn(true).when(mSubscriptionController).isOpportunistic(2);
        // Phone 0 has sub 1, phone 1 has sub 2.
        // Sub 1 is default data sub.
        // Both are active subscriptions are active sub, as they are in both active slots.
        setSlotIndexToSubId(0, 1);
        setSlotIndexToSubId(1, 2);
        setDefaultDataSubId(1);
        waitABit();

        // Validating on sub 10 which is inactive.
        mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
        waitABit();
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);

        // Switch to active subId without validating. Should always succeed.
        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, mSetOpptDataCallback1);
        waitABit();
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);

        // Validating on sub 1 and fails.
        clearInvocations(mSetOpptDataCallback1);
        mPhoneSwitcher.trySetOpportunisticDataSubscription(1, true, mSetOpptDataCallback1);
        waitABit();
        mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1);
        waitABit();
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);

        // Validating on sub 2 and succeeds.
        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
        waitABit();
        mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2);
        waitABit();
        verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);

        // Switching data back to primary and validation fails.
        clearInvocations(mSetOpptDataCallback2);
        mPhoneSwitcher.trySetOpportunisticDataSubscription(
                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
        waitABit();
        mPhoneSwitcher.mValidationCallback.onValidationResult(false, 1);
        waitABit();
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);

        // Switching data back to primary and succeeds.
        clearInvocations(mSetOpptDataCallback2);
        mPhoneSwitcher.trySetOpportunisticDataSubscription(
                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
        waitABit();
        mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1);
        waitABit();
        verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);

        // Back to back call on same subId.
        clearInvocations(mSetOpptDataCallback1);
        clearInvocations(mSetOpptDataCallback2);
        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
        waitABit();
        verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
                eq(mPhoneSwitcher.mValidationCallback));
        doReturn(true).when(mCellularNetworkValidator).isValidating();
        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
        waitABit();
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
        verify(mSetOpptDataCallback2, never()).onComplete(anyInt());
        // Validation succeeds.
        doReturn(false).when(mCellularNetworkValidator).isValidating();
        mPhoneSwitcher.mValidationCallback.onValidationResult(true, 2);
        waitABit();
        verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);

        mPhoneSwitcher.trySetOpportunisticDataSubscription(
                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, null);
        waitABit();
        clearInvocations(mSetOpptDataCallback1);
        clearInvocations(mSetOpptDataCallback2);
        clearInvocations(mCellularNetworkValidator);
        // Back to back call, call 1 to switch to subId 2, call 2 to switch back.
        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
        waitABit();
        verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
                eq(mPhoneSwitcher.mValidationCallback));
        doReturn(true).when(mCellularNetworkValidator).isValidating();
        mPhoneSwitcher.trySetOpportunisticDataSubscription(
                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, mSetOpptDataCallback2);
        waitABit();
        // Call 1 should be cancelled and failed. Call 2 return success immediately as there's no
        // change.
        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
        verify(mSetOpptDataCallback2).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
        mHandlerThread.quit();
    }

    /* Private utility methods start here */

    private void setAllPhonesInactive() {
@@ -878,6 +998,12 @@ public class PhoneSwitcherTest extends TelephonyTest {
        waitABit();
    }

    private void notifyPhoneAsInHoldingCall(Phone phone) {
        doReturn(mHoldingCall).when(phone).getBackgroundCall();
        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
        waitABit();
    }

    private void notifyPhoneAsInactive(Phone phone) {
        doReturn(mInactiveCall).when(phone).getForegroundCall();
        mPhoneSwitcher.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);