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

Commit 43514d27 authored by Ling Ma's avatar Ling Ma
Browse files

Unregister listeners when settings disabled or connect to wifi

Without this change, even when user disabled the mobile data/auto data switch feature, or when connect to wifi, we're still listening and processing the signal strength/service state/display info events, which unnecessary comsumes power.

Fix: 389591869
Test: QA verified all auto data switch test cases b/409423653
Flag: EXEMPT bugfix
Change-Id: I392c059727c425d17b0a51808db894553ce580c3
parent 8e14097a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
package: "com.android.internal.telephony.flags"
container: "system"

# OWNER=linggm TARGET=25Q3
flag {
  name: "auto_data_prune_listener"
  namespace: "telephony"
  description: "Unregister listeners when user disable settings or connect to wifi."
  bug:"389591869"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

# OWNER=sarahchin TARGET=24Q3
flag {
  name: "slicing_additional_error_codes"
+103 −17
Original line number Diff line number Diff line
@@ -422,6 +422,10 @@ public class AutoDataSwitchController extends Handler {
     * sub to reduce unnecessary tracking.
     */
    private void onSubscriptionsChanged() {
        if (sFeatureFlags.autoDataPruneListener()) {
            boolean changed = updateListenerRegistrations();
            if (changed) logl("onSubscriptionChanged: " + Arrays.toString(mPhonesSignalStatus));
        } else {
            Set<Integer> activePhoneIds = Arrays.stream(mSubscriptionManagerService
                            .getActiveSubIdList(true /*visibleOnly*/))
                    .map(mSubscriptionManagerService::getPhoneId)
@@ -443,6 +447,80 @@ public class AutoDataSwitchController extends Handler {
            }
            if (changed) logl("onSubscriptionChanged: " + Arrays.toString(mPhonesSignalStatus));
        }
    }

    /**
     * Updates network event listener registrations based on conditions.
     *
     * Unregisters listeners for *all* phones if:
     * 1. <2 active subscriptions (auto-switching requires 2+).
     * 2. Non-cellular default network (auto-switching not relevant).
     * 3. Default data disabled (no need to monitor for switching).
     * 4. No eligible auto-switch candidates (all other phones' data
     *    disabled, preventing switching).
     *
     * Registers listeners if none of the above apply and a phone's
     * listeners are currently unregistered.
     *
     * @return `true` if any registration changed; `false` otherwise.
     */
    private boolean updateListenerRegistrations() {
        if (!sFeatureFlags.autoDataPruneListener()) {
            return false;
        }
        boolean shouldUnregister = false;
        String reason = "";

        if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) {
            shouldUnregister = true;
            reason = "only have one active subscription";
        } else if (mDefaultNetworkIsOnNonCellular) {
            shouldUnregister = true;
            reason = "default network is on non-cellular network";
        } else {
            int defaultDataPhoneId = mSubscriptionManagerService.getPhoneId(
                    mSubscriptionManagerService.getDefaultDataSubId());
            Phone defaultDataPhone = PhoneFactory.getPhone(defaultDataPhoneId);

            if (defaultDataPhone != null && !defaultDataPhone.isUserDataEnabled()) {
                shouldUnregister = true;
                reason = "default data is disabled";
            } else {
                boolean anyCandidateEnabled = false;
                for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
                    if (phoneId != defaultDataPhoneId) {
                        Phone phone = PhoneFactory.getPhone(phoneId);
                        if (phone != null && phone.getDataSettingsManager().isDataEnabled()) {
                            anyCandidateEnabled = true;
                            break;
                        }
                    }
                }

                if (!anyCandidateEnabled) {
                    shouldUnregister = true;
                    reason = "no candidate enabled auto data switch feature";
                }
            }
        }

        if (shouldUnregister) {
            log("updateListenerRegistrations: " + reason);
        }

        // Register or unregister as needed
        boolean changed = false;
        for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
            if (shouldUnregister && mPhonesSignalStatus[phoneId].mListeningForEvents) {
                unregisterAllEventsForPhone(phoneId);
                changed = true;
            } else if (!shouldUnregister && !mPhonesSignalStatus[phoneId].mListeningForEvents) {
                registerAllEventsForPhone(phoneId);
                changed = true;
            }
        }
        return changed;
    }

    /**
     * Register all tracking events for a phone.
@@ -459,6 +537,7 @@ public class AutoDataSwitchController extends Handler {
            phone.getServiceStateTracker().registerForServiceStateChanged(this,
                    EVENT_SERVICE_STATE_CHANGED, phoneId);
            mPhonesSignalStatus[phoneId].mListeningForEvents = true;
            log("registerAllEventsForPhone: registered listeners for phone " + phoneId);
        } else {
            loge("Unexpected null phone " + phoneId + " when register all events");
        }
@@ -475,6 +554,7 @@ public class AutoDataSwitchController extends Handler {
            phone.getSignalStrengthController().unregisterForSignalStrengthChanged(this);
            phone.getServiceStateTracker().unregisterForServiceStateChanged(this);
            mPhonesSignalStatus[phoneId].mListeningForEvents = false;
            log("unregisterAllEventsForPhone: unregistered listeners for phone " + phoneId);
        } else {
            loge("Unexpected out of bound phone " + phoneId + " when unregister all events");
        }
@@ -497,8 +577,7 @@ public class AutoDataSwitchController extends Handler {
                dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold() >= 0
                        ? dataConfig.getAutoDataSwitchAvailabilitySwitchbackStabilityTimeThreshold()
                        : dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold());
        mAutoDataSwitchValidationMaxRetry =
                dataConfig.getAutoDataSwitchValidationMaxRetry();
        mAutoDataSwitchValidationMaxRetry = dataConfig.getAutoDataSwitchValidationMaxRetry();
    }

    @Override
@@ -664,7 +743,14 @@ public class AutoDataSwitchController extends Handler {
                ? STABILITY_CHECK_TIMER_MAP.get(STABILITY_CHECK_AVAILABILITY_SWITCH)
                << mAutoSwitchValidationFailedCount
                : 0;
        if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
        if (reason == EVALUATION_REASON_DATA_SETTINGS_CHANGED
                || reason == EVALUATION_REASON_DEFAULT_NETWORK_CHANGED) {
            // In some conditions, listeners are paused to reduce unnecessary tracking.
            updateListenerRegistrations();
            // Always reevaluate with those critical condition change.
            scheduleEventWithTimer(EVENT_EVALUATE_AUTO_SWITCH, new EvaluateEventExtra(reason),
                    delayMs);
        } else if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
            scheduleEventWithTimer(EVENT_EVALUATE_AUTO_SWITCH, new EvaluateEventExtra(reason),
                    delayMs);
        }
+184 −0
Original line number Diff line number Diff line
@@ -138,6 +138,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
            doReturn(mSignalStrength).when(phone).getSignalStrength();
            doReturn(mDataNetworkController).when(phone).getDataNetworkController();
            doReturn(mDataConfigManager).when(mDataNetworkController).getDataConfigManager();
            doReturn(mDataSettingsManager).when(phone).getDataSettingsManager();
            doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
                    .when(phone).isUserDataEnabled();
        }
@@ -195,6 +196,10 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
                "mEventsToAlarmListener", Map.class);
        mScheduledEventsToExtras = getPrivateField(mAutoDataSwitchControllerUT,
                "mScheduledEventsToExtras", Map.class);

        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);

        doReturn(true).when(mFeatureFlags).autoDataPruneListener();
    }

    @After
@@ -721,6 +726,184 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {
        }
    }

    @Test
    public void testDataSettingsChangedUpdateListener() {
        setDefaultDataSubId(SUB_1); // Phone 1 is default
        int modemCount = mPhones.length; // Should be 2

        // Pre-condition: Assume listeners are registered initially (cleared invocations in setUp)

        // --- Scenario 1: Disable Default Phone User Data ---
        logd("Scenario 1: Disable Default Phone User Data");
        doReturn(false).when(mPhone).isUserDataEnabled();

        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
        processAllMessages();

        // Verify unregister calls for *both* phones
        verify(mDisplayInfoController, times(modemCount))
                .unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, times(modemCount)).unregisterForSignalStrengthChanged(
                any());
        verify(mSST, times(modemCount)).unregisterForServiceStateChanged(any());
        // Verify register calls were NOT made
        verify(mDisplayInfoController, never()).registerForTelephonyDisplayInfoChanged(
                any(), anyInt(), any());
        verify(mSignalStrengthController, never()).registerForSignalStrengthChanged(
                any(), anyInt(), any());
        verify(mSST, never()).registerForServiceStateChanged(any(), anyInt(), any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);

        // --- Scenario 2: Re-enable Default Phone User Data ---
        logd("Scenario 2: Re-enable Default Phone User Data");
        doReturn(true).when(mPhone).isUserDataEnabled();

        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
        processAllMessages();

        // Verify register calls for *both* phones
        verify(mDisplayInfoController, times(modemCount)).registerForTelephonyDisplayInfoChanged(
                any(), eq(EVENT_DISPLAY_INFO_CHANGED), any());
        verify(mSignalStrengthController, times(modemCount)).registerForSignalStrengthChanged(
                any(), eq(EVENT_SIGNAL_STRENGTH_CHANGED), any());
        verify(mSST, times(modemCount)).registerForServiceStateChanged(
                any(), eq(EVENT_SERVICE_STATE_CHANGED), any());
        // Verify unregister calls were NOT made
        verify(mDisplayInfoController, never()).unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, never()).unregisterForSignalStrengthChanged(any());
        verify(mSST, never()).unregisterForServiceStateChanged(any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST); // Reset

        // --- Scenario 3: Disable *Only* Candidate Phone Data Setting ---
        logd("Scenario 3: Disable *Only* Candidate Phone Data Setting");
        doReturn(true).when(mPhone).isUserDataEnabled(); // Ensure default is enabled
        doReturn(false).when(mDataSettingsManager).isDataEnabled(); // Disable candidate

        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
        processAllMessages();

        // Verify unregister calls for *both* phones (as no candidates left)
        verify(mDisplayInfoController, times(modemCount))
                .unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, times(modemCount))
                .unregisterForSignalStrengthChanged(any());
        verify(mSST, times(modemCount)).unregisterForServiceStateChanged(any());
        // Verify register calls were NOT made
        verify(mDisplayInfoController, never())
                .registerForTelephonyDisplayInfoChanged(any(), anyInt(), any());
        verify(mSignalStrengthController, never())
                .registerForSignalStrengthChanged(any(), anyInt(), any());
        verify(mSST, never()).registerForServiceStateChanged(any(), anyInt(), any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST); // Reset

        // --- Scenario 4: Re-enable Candidate Phone Data Setting ---
        logd("Scenario 4: Re-enable Candidate Phone Data Setting");
        doReturn(true).when(mDataSettingsManager).isDataEnabled();

        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
        processAllMessages();

        // Verify register calls for *both* phones
        verify(mDisplayInfoController, times(modemCount)).registerForTelephonyDisplayInfoChanged(
                any(), eq(EVENT_DISPLAY_INFO_CHANGED), any());
        verify(mSignalStrengthController, times(modemCount)).registerForSignalStrengthChanged(
                any(), eq(EVENT_SIGNAL_STRENGTH_CHANGED), any());
        verify(mSST, times(modemCount)).registerForServiceStateChanged(
                any(), eq(EVENT_SERVICE_STATE_CHANGED), any());
        // Verify unregister calls were NOT made
        verify(mDisplayInfoController, never()).unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, never()).unregisterForSignalStrengthChanged(any());
        verify(mSST, never()).unregisterForServiceStateChanged(any());
    }

    @Test
    public void testDefaultNetworkChangedUpdateListener() {
        setDefaultDataSubId(SUB_1); // Phone 1 is default
        int modemCount = mPhones.length; // Should be 2

        // Pre-condition: Assume listeners are registered initially (cleared invocations in setUp)

        // --- Scenario 1: Default network becomes non-cellular (WIFI) ---
        logd("Scenario 1: Default network becomes WIFI");
        NetworkCapabilities wifiCapabilities = new NetworkCapabilities();
        wifiCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(wifiCapabilities);
        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(AutoDataSwitchController
                .EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
        processAllMessages();

        // Verify unregister calls for *both* phones
        verify(mDisplayInfoController, times(modemCount))
                .unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, times(modemCount))
                .unregisterForSignalStrengthChanged(any());
        verify(mSST, times(modemCount)).unregisterForServiceStateChanged(any());
        // Verify register calls were NOT made
        verify(mDisplayInfoController, never()).registerForTelephonyDisplayInfoChanged(
                any(), anyInt(), any());
        verify(mSignalStrengthController, never()).registerForSignalStrengthChanged(
                any(), anyInt(), any());
        verify(mSST, never()).registerForServiceStateChanged(any(), anyInt(), any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST); // Reset

        // --- Scenario 2: Default network becomes cellular ---
        logd("Scenario 2: Default network becomes CELLULAR");
        NetworkCapabilities cellularCapabilities = new NetworkCapabilities();
        cellularCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
        mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(cellularCapabilities);
        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(AutoDataSwitchController
                .EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
        processAllMessages();

        // Verify register calls for *both* phones
        verify(mDisplayInfoController, times(modemCount)).registerForTelephonyDisplayInfoChanged(
                any(), eq(EVENT_DISPLAY_INFO_CHANGED), any());
        verify(mSignalStrengthController, times(modemCount)).registerForSignalStrengthChanged(any(),
                eq(EVENT_SIGNAL_STRENGTH_CHANGED), any());
        verify(mSST, times(modemCount)).registerForServiceStateChanged(any(),
                eq(EVENT_SERVICE_STATE_CHANGED), any());
        // Verify unregister calls were NOT made
        verify(mDisplayInfoController, never()).unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, never()).unregisterForSignalStrengthChanged(any());
        verify(mSST, never()).unregisterForServiceStateChanged(any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST); // Reset

        // --- Scenario 3: Default network lost (null) ---
        logd("Scenario 3: Default network lost (null)");
        // First switch to non-cellular to ensure listeners are off
        mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(wifiCapabilities);
        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(AutoDataSwitchController
                .EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
        processAllMessages();

        verify(mDisplayInfoController, times(modemCount))
                .unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, times(modemCount))
                .unregisterForSignalStrengthChanged(any());
        verify(mSST, times(modemCount))
                .unregisterForServiceStateChanged(any());
        clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);

        // Now lose the network
        mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(null);
        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(AutoDataSwitchController
                .EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
        processAllMessages();

        // Verify register calls for *both* phones (null network means cellular is possible)
        verify(mDisplayInfoController, times(modemCount)).registerForTelephonyDisplayInfoChanged(
                any(), eq(EVENT_DISPLAY_INFO_CHANGED), any());
        verify(mSignalStrengthController, times(modemCount)).registerForSignalStrengthChanged(any(),
                eq(EVENT_SIGNAL_STRENGTH_CHANGED), any());
        verify(mSST, times(modemCount)).registerForServiceStateChanged(any(),
                eq(EVENT_SERVICE_STATE_CHANGED), any());

        verify(mDisplayInfoController, never())
                .unregisterForTelephonyDisplayInfoChanged(any());
        verify(mSignalStrengthController, never()).unregisterForSignalStrengthChanged(any());
        verify(mSST, never()).unregisterForServiceStateChanged(any());
    }

    @Test
    public void testRatSignalStrengthSkipEvaluation() {
        // Verify the secondary phone is OOS and its score(0) is too low to justify the evaluation
@@ -764,6 +947,7 @@ public class AutoDataSwitchControllerTest extends TelephonyTest {

        // 4.2 Auto switch feature is enabled
        doReturn(true).when(mPhone2).getDataRoamingEnabled();
        doReturn(true).when(mDataSettingsManager).isDataEnabled();
        mDataEvaluation.addDataAllowedReason(DataEvaluation.DataAllowedReason.NORMAL);

        // 5. No default network