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

Commit f98037ab authored by Junyu Lai's avatar Junyu Lai Committed by Gerrit Code Review
Browse files

Merge "[VCN13] Implement tracking best matching network"

parents 0ebddf5f dbf52a28
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -18,8 +18,8 @@ package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_BEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
import static android.net.QosCallback.QosCallbackRegistrationException;
@@ -4249,7 +4249,7 @@ public class ConnectivityManager {
            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
        final NetworkCapabilities nc = request.networkCapabilities;
        final CallbackHandler cbHandler = new CallbackHandler(handler);
        sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler);
        sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler);
    }

    /**
+10 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ public class NetworkRequest implements Parcelable {
        REQUEST,
        BACKGROUND_REQUEST,
        TRACK_SYSTEM_DEFAULT,
        TRACK_BEST,
        LISTEN_FOR_BEST,
    };

    /**
@@ -513,6 +513,15 @@ public class NetworkRequest implements Parcelable {
        return type == Type.LISTEN;
    }

    /**
     * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST.
     *
     * @hide
     */
    public boolean isListenForBest() {
        return type == Type.LISTEN_FOR_BEST;
    }

    /**
     * Returns true iff. the contained NetworkRequest is one that:
     *
+18 −7
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
@@ -3663,6 +3664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
            mNetworkRequestInfoLogs.log("REGISTER " + nri);
            for (final NetworkRequest req : nri.mRequests) {
                mNetworkRequests.put(req, nri);
                // TODO: Consider update signal strength for other types.
                if (req.isListen()) {
                    for (final NetworkAgentInfo network : mNetworkAgentInfos) {
                        if (req.networkCapabilities.hasSignalStrength()
@@ -3755,18 +3757,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
        // listen requests won't keep up a network satisfying it. If this is not a multilayer
        // request, return immediately. For multilayer requests, check to see if any of the
        // multilayer requests may have a potential satisfier.
        if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
        if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
                || nri.mRequests.get(0).isListenForBest())) {
            return false;
        }
        for (final NetworkRequest req : nri.mRequests) {
            // This multilayer listen request is satisfied therefore no further requests need to be
            // evaluated deeming this network not a potential satisfier.
            if (req.isListen() && nri.getActiveRequest() == req) {
            if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) {
                return false;
            }
            // As non-multilayer listen requests have already returned, the below would only happen
            // for a multilayer request therefore continue to the next request if available.
            if (req.isListen()) {
            if (req.isListen() || req.isListenForBest()) {
                continue;
            }
            // If this Network is already the highest scoring Network for a request, or if
@@ -5549,8 +5552,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
                //  request if the app changes network state. http://b/29964605
                enforceMeteredApnPolicy(networkCapabilities);
                break;
            case TRACK_BEST:
                throw new UnsupportedOperationException("Not implemented yet");
            case LISTEN_FOR_BEST:
                enforceAccessPermission();
                networkCapabilities = new NetworkCapabilities(networkCapabilities);
                break;
            default:
                throw new IllegalArgumentException("Unsupported request type " + reqType);
        }
@@ -5558,11 +5563,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
        ensureSufficientPermissionsForRequest(networkCapabilities,
                Binder.getCallingPid(), callingUid, callingPackageName);

        // Set the UID range for this request to the single UID of the requester, or to an empty
        // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
        // Enforce FOREGROUND if the caller does not have permission to use background network.
        if (reqType == LISTEN_FOR_BEST) {
            restrictBackgroundRequestForCaller(networkCapabilities);
        }

        // Set the UID range for this request to the single UID of the requester, unless the
        // requester has the permission to specify other UIDs.
        // This will overwrite any allowed UIDs in the requested capabilities. Though there
        // are no visible methods to set the UIDs, an app could use reflection to try and get
        // networks for other apps so it's essential that the UIDs are overwritten.
        // Also set the requester UID and package name in the request.
        restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
                callingUid, callingPackageName);

+1 −0
Original line number Diff line number Diff line
@@ -719,6 +719,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
                break;

            case LISTEN:
            case LISTEN_FOR_BEST:
            case TRACK_DEFAULT:
            case TRACK_SYSTEM_DEFAULT:
                break;
+97 −5
Original line number Diff line number Diff line
@@ -1079,6 +1079,10 @@ public class ConnectivityServiceTest {
        public void triggerUnfulfillable(NetworkRequest r) {
            super.releaseRequestAsUnfulfillableByAnyFactory(r);
        }
        public void assertNoRequestChanged() {
            assertNull(mRequestHistory.poll(0, r -> true));
        }
    }
    private Set<UidRange> uidRangesForUids(int... uids) {
@@ -11121,11 +11125,99 @@ public class ConnectivityServiceTest {
        mCm.unregisterNetworkCallback(cellCb);
    }
    // Cannot be part of MockNetworkFactory since it requires method of the test.
    private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) {
        waitForIdle();
        factory.assertNoRequestChanged();
    }
    @Test
    public void testRegisterBestMatchingNetworkCallback() throws Exception {
        final NetworkRequest request = new NetworkRequest.Builder().build();
        assertThrows(UnsupportedOperationException.class,
                () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(),
                        mCsHandlerThread.getThreadHandler()));
    public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception {
        // Prepare mock mms factory.
        final HandlerThread handlerThread = new HandlerThread("MockCellularFactory");
        handlerThread.start();
        NetworkCapabilities filter = new NetworkCapabilities()
                .addTransportType(TRANSPORT_CELLULAR)
                .addCapability(NET_CAPABILITY_MMS);
        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                mServiceContext, "testFactory", filter, mCsHandlerThread);
        testFactory.setScoreFilter(40);
        try {
            // Register the factory and expect it will see default request, because all requests
            // are sent to all factories.
            testFactory.register();
            testFactory.expectRequestAdd();
            testFactory.assertRequestCountEquals(1);
            // The factory won't try to start the network since the default request doesn't
            // match the filter (no INTERNET capability).
            assertFalse(testFactory.getMyStartRequested());
            // Register callback for listening best matching network. Verify that the request won't
            // be sent to factory.
            final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
            mCm.registerBestMatchingNetworkCallback(
                    new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
                    bestMatchingCb, mCsHandlerThread.getThreadHandler());
            bestMatchingCb.assertNoCallback();
            expectNoRequestChanged(testFactory);
            testFactory.assertRequestCountEquals(1);
            assertFalse(testFactory.getMyStartRequested());
            // Fire a normal mms request, verify the factory will only see the request.
            final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback();
            final NetworkRequest mmsRequest = new NetworkRequest.Builder()
                    .addCapability(NET_CAPABILITY_MMS).build();
            mCm.requestNetwork(mmsRequest, mmsNetworkCallback);
            testFactory.expectRequestAdd();
            testFactory.assertRequestCountEquals(2);
            assertTrue(testFactory.getMyStartRequested());
            // Unregister best matching callback, verify factory see no change.
            mCm.unregisterNetworkCallback(bestMatchingCb);
            expectNoRequestChanged(testFactory);
            testFactory.assertRequestCountEquals(2);
            assertTrue(testFactory.getMyStartRequested());
        } finally {
            testFactory.terminate();
        }
    }
    @Test
    public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception {
        final TestNetworkCallback bestMatchingCb = new TestNetworkCallback();
        mCm.registerBestMatchingNetworkCallback(
                new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(),
                bestMatchingCb, mCsHandlerThread.getThreadHandler());
        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
        mCellNetworkAgent.connect(true);
        bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
        mWiFiNetworkAgent.connect(true);
        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
        // Change something on cellular to trigger capabilities changed, since the callback
        // only cares about the best network, verify it received nothing from cellular.
        mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
        bestMatchingCb.assertNoCallback();
        // Make cellular the best network again, verify the callback now tracks cellular.
        mWiFiNetworkAgent.adjustScore(-50);
        bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent);
        // Make cellular temporary non-trusted, which will not satisfying the request.
        // Verify the callback switch from/to the other network accordingly.
        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
        bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
        mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED);
        bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent);
        // Verify the callback doesn't care about wifi disconnect.
        mWiFiNetworkAgent.disconnect();
        bestMatchingCb.assertNoCallback();
        mCellNetworkAgent.disconnect();
        bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
    }
}