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

Commit 75215f12 authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

Filter out uninteresting UidObserver callbacks in NPMS

NPMS depends on three different process state thresholds for evaluating
network access. But holding the common lock for evaluating the network
rules may cause a lot of contention on the handler as uid changes are
frequent.
Separating out a fast check to filter out uninteresting proc-state
callbacks before posting to the handler for full evaluation of the
rules. This is especially useful when we use no cutpoint for the uid
observer registration.

This also fixes an inconsistency where changes crossing
TOP_THRESHOLD_STATE may not be received if the app transitioned
from some process-states below BFGS.

Test: atest FrameworksServicesTests:NetworkPolicyManagerServiceTest
Test: atest CtsHostsideNetworkTests

Bug: 319728914
Change-Id: Ia7a5aef1ce9705a55912749985c7ee950c24ac84
parent 5a8e66fd
Loading
Loading
Loading
Loading
+58 −9
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ActivityManager.isProcStateConsideredInteraction;
import static android.app.ActivityManager.printCapabilitiesSummary;
@@ -85,8 +87,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_
import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -97,6 +101,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.allowedReasonsToString;
import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground;
@@ -468,7 +473,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
     */
    private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24;

    private static final int UID_MSG_STATE_CHANGED = 100;
    @VisibleForTesting
    static final int UID_MSG_STATE_CHANGED = 100;
    private static final int UID_MSG_GONE = 101;

    private static final String PROP_SUB_PLAN_OWNER = "persist.sys.sub_plan_owner";
@@ -1074,8 +1080,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

                final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN
                        : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
                // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint.

                mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes,
                        cutpoint, "android");
                mNetworkManager.registerObserver(mAlertObserver);
@@ -1184,6 +1188,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    }

    final private IUidObserver mUidObserver = new UidObserver() {

        /**
         * Returns whether the uid state change information is relevant for the service. If the
         * state information does not lead to any change in the network rules, it can safely be
         * ignored.
         */
        @GuardedBy("mUidStateCallbackInfos")
        private boolean isUidStateChangeRelevant(UidStateCallbackInfo previousInfo,
                int newProcState, long newProcStateSeq, int newCapability) {
            if (previousInfo.procStateSeq == -1) {
                // No previous record. Always process the first state change callback.
                return true;
            }
            if (newProcStateSeq <= previousInfo.procStateSeq) {
                // Stale callback. Ignore.
                return false;
            }
            final int previousProcState = previousInfo.procState;
            if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
                    != (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
                // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
                // BACKGROUND chain may change.
                return true;
            }
            if ((previousProcState <= TOP_THRESHOLD_STATE)
                    != (newProcState <= TOP_THRESHOLD_STATE)) {
                // Proc-state change crossed TOP_THRESHOLD_STATE: Network rules for the
                // LOW_POWER_STANDBY chain may change.
                return true;
            }
            if ((previousProcState <= FOREGROUND_THRESHOLD_STATE)
                    != (newProcState <= FOREGROUND_THRESHOLD_STATE)) {
                // Proc-state change crossed FOREGROUND_THRESHOLD_STATE: Network rules for many
                // different chains may change.
                return true;
            }
            final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
                    | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
            if ((previousInfo.capability & networkCapabilities)
                    != (newCapability & networkCapabilities)) {
                return true;
            }
            return false;
        }

        @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
                @ProcessCapability int capability) {
            synchronized (mUidStateCallbackInfos) {
@@ -1192,9 +1241,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                    callbackInfo = new UidStateCallbackInfo();
                    mUidStateCallbackInfos.put(uid, callbackInfo);
                }
                if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) {
                if (isUidStateChangeRelevant(callbackInfo, procState, procStateSeq, capability)) {
                    callbackInfo.update(uid, procState, procStateSeq, capability);
                }
                    if (!callbackInfo.isPending) {
                        mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
                                .sendToTarget();
@@ -1202,6 +1250,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                    }
                }
            }
        }

        @Override public void onUidGone(int uid, boolean disabled) {
            mUidEventHandler.obtainMessage(UID_MSG_GONE, uid, 0).sendToTarget();
+121 −15
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.net;

import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
@@ -58,9 +59,11 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.allowedReasonsToString;
import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
@@ -88,6 +91,7 @@ import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
import static com.android.server.net.NetworkPolicyManagerService.UID_MSG_STATE_CHANGED;
import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
import static com.android.server.net.NetworkPolicyManagerService.normalizeTemplate;

@@ -196,8 +200,6 @@ import com.android.server.LocalServices;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.usage.AppStandbyInternal;

import com.google.common.util.concurrent.AbstractFuture;

import libcore.io.Streams;

import org.junit.After;
@@ -241,10 +243,8 @@ import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@@ -2246,6 +2246,123 @@ public class NetworkPolicyManagerServiceTest {
        assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
    public void testUidObserverFiltersProcStateChanges() throws Exception {
        int testProcStateSeq = 0;
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // First callback for uid.
            callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Doesn't cross the background threshold.
            callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Crosses the background threshold.
            callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Doesn't cross the foreground threshold.
            callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE + 1, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Crosses the foreground threshold.
            callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Doesn't cross the top threshold.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE + 1, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Crosses the top threshold.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Doesn't cross any other threshold.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE - 1, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
    public void testUidObserverFiltersStaleChanges() throws Exception {
        final int testProcStateSeq = 51;
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // First callback for uid.
            callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE + 100, testProcStateSeq,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // Stale callback because the procStateSeq is smaller.
            callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE - 100, testProcStateSeq - 10,
                    PROCESS_CAPABILITY_NONE);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
    public void testUidObserverFiltersCapabilityChanges() throws Exception {
        int testProcStateSeq = 0;
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // First callback for uid.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_NONE);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // The same process-state with one network capability added.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // The same process-state with another network capability added.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
                            | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
            assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
        try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
            // The same process-state with all capabilities, but no change in network capabilities.
            callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
                    PROCESS_CAPABILITY_ALL);
            assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
        }
        waitForUidEventHandlerIdle();
    }

    @Test
    public void testLowPowerStandbyAllowlist() throws Exception {
        // Chain background is also enabled but these procstates are important enough to be exempt.
@@ -2559,17 +2676,6 @@ public class NetworkPolicyManagerServiceTest {
        verify(mStatsManager).setDefaultGlobalAlert(anyLong());
    }

    private static class TestAbstractFuture<T> extends AbstractFuture<T> {
        @Override
        public T get() throws InterruptedException, ExecutionException {
            try {
                return get(5, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static void assertTimeEquals(long expected, long actual) {
        if (expected != actual) {
            fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));