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

Commit 2e1e43ca authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Add callback registration in ConnectivityService." am: e534c452 am:...

Merge "Add callback registration in ConnectivityService." am: e534c452 am: 52309b98 am: dcc5a26a

Change-Id: I596d4788f6d35f1adf110d8edd390f5632b4dc06
parents aac44351 dcc5a26a
Loading
Loading
Loading
Loading
+31 −9
Original line number Diff line number Diff line
@@ -25,13 +25,16 @@ import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
@@ -57,6 +60,11 @@ import java.util.concurrent.Executor;
 * </ul>
 */
public class ConnectivityDiagnosticsManager {
    /** @hide */
    @VisibleForTesting
    public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
            sCallbacks = new ConcurrentHashMap<>();

    private final Context mContext;
    private final IConnectivityManager mService;

@@ -646,9 +654,9 @@ public class ConnectivityDiagnosticsManager {
     * <p>If a registering app loses its relevant permissions, any callbacks it registered will
     * silently stop receiving callbacks.
     *
     * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If
     * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException
     * will be thrown.
     * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
     * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
     * multiple NetworkRequests, an IllegalArgumentException will be thrown.
     *
     * @param request The NetworkRequest that will be used to match with Networks for which
     *     callbacks will be fired
@@ -657,15 +665,21 @@ public class ConnectivityDiagnosticsManager {
     *     System
     * @throws IllegalArgumentException if the same callback instance is registered with multiple
     *     NetworkRequests
     * @throws SecurityException if the caller does not have appropriate permissions to register a
     *     callback
     */
    public void registerConnectivityDiagnosticsCallback(
            @NonNull NetworkRequest request,
            @NonNull Executor e,
            @NonNull ConnectivityDiagnosticsCallback callback) {
        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
        throw new UnsupportedOperationException("registerCallback() not supported yet");
        final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
        if (sCallbacks.putIfAbsent(callback, binder) != null) {
            throw new IllegalArgumentException("Callback is currently registered");
        }

        try {
            mService.registerConnectivityDiagnosticsCallback(binder, request);
        } catch (RemoteException exception) {
            exception.rethrowFromSystemServer();
        }
    }

    /**
@@ -678,7 +692,15 @@ public class ConnectivityDiagnosticsManager {
     */
    public void unregisterConnectivityDiagnosticsCallback(
            @NonNull ConnectivityDiagnosticsCallback callback) {
        // TODO(b/143187964): implement ConnectivityDiagnostics functionality
        throw new UnsupportedOperationException("registerCallback() not supported yet");
        // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
        // atomic.
        final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
        if (binder == null) return;

        try {
            mService.unregisterConnectivityDiagnosticsCallback(binder);
        } catch (RemoteException exception) {
            exception.rethrowFromSystemServer();
        }
    }
}
+181 −20
Original line number Diff line number Diff line
@@ -557,13 +557,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
                .asInterface(ServiceManager.getService("dnsresolver"));
    }

    /** Handler thread used for both of the handlers below. */
    /** Handler thread used for all of the handlers below. */
    @VisibleForTesting
    protected final HandlerThread mHandlerThread;
    /** Handler used for internal events. */
    final private InternalHandler mHandler;
    /** Handler used for incoming {@link NetworkStateTracker} events. */
    final private NetworkStateTrackerHandler mTrackerHandler;
    /** Handler used for processing {@link android.net.ConnectivityDiagnosticsManager} events */
    @VisibleForTesting
    final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;

    private final DnsManager mDnsManager;

    private boolean mSystemReady;
@@ -630,6 +634,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
    @VisibleForTesting
    final MultipathPolicyTracker mMultipathPolicyTracker;

    @VisibleForTesting
    final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
            mConnectivityDiagnosticsCallbacks = new HashMap<>();

    /**
     * Implements support for the legacy "one network per network type" model.
     *
@@ -962,6 +970,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mHandlerThread.start();
        mHandler = new InternalHandler(mHandlerThread.getLooper());
        mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
        mConnectivityDiagnosticsHandler =
                new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());

        mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -3391,18 +3401,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        nri.unlinkDeathRecipient();
        mNetworkRequests.remove(nri.request);

        synchronized (mUidToNetworkRequestCount) {
            int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
            if (requests < 1) {
                Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
                        nri.mUid);
            } else if (requests == 1) {
                mUidToNetworkRequestCount.removeAt(
                        mUidToNetworkRequestCount.indexOfKey(nri.mUid));
            } else {
                mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
            }
        }
        decrementNetworkRequestPerUidCount(nri);

        mNetworkRequestInfoLogs.log("RELEASE " + nri);
        if (nri.request.isRequest()) {
@@ -3473,6 +3472,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
        synchronized (mUidToNetworkRequestCount) {
            final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
            if (requests < 1) {
                Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
            } else if (requests == 1) {
                mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
            } else {
                mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
            }
        }
    }

    @Override
    public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
        enforceNetworkStackSettingsOrSetup();
@@ -5091,6 +5103,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
            }
        }

        NetworkRequestInfo(NetworkRequest r) {
            this(r, null);
        }

        private void enforceRequestCountLimit() {
            synchronized (mUidToNetworkRequestCount) {
                int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
@@ -6165,7 +6181,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
    private void callCallbackForRequest(NetworkRequestInfo nri,
            NetworkAgentInfo networkAgent, int notificationType, int arg1) {
        if (nri.messenger == null) {
            return;  // Default request has no msgr
            // Default request has no msgr. Also prevents callbacks from being invoked for
            // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
            // are Type.LISTEN, but should not have NetworkCallbacks invoked.
            return;
        }
        Bundle bundle = new Bundle();
        // TODO: check if defensive copies of data is needed.
@@ -7321,19 +7340,161 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    /**
     * Handler used for managing all Connectivity Diagnostics related functions.
     *
     * @see android.net.ConnectivityDiagnosticsManager
     *
     * TODO(b/147816404): Explore moving ConnectivityDiagnosticsHandler to a separate file
     */
    @VisibleForTesting
    class ConnectivityDiagnosticsHandler extends Handler {
        /**
         * Used to handle ConnectivityDiagnosticsCallback registration events from {@link
         * android.net.ConnectivityDiagnosticsManager}.
         * obj = ConnectivityDiagnosticsCallbackInfo with IConnectivityDiagnosticsCallback and
         * NetworkRequestInfo to be registered
         */
        private static final int EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 1;

        /**
         * Used to handle ConnectivityDiagnosticsCallback unregister events from {@link
         * android.net.ConnectivityDiagnosticsManager}.
         * obj = the IConnectivityDiagnosticsCallback to be unregistered
         * arg1 = the uid of the caller
         */
        private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;

        private ConnectivityDiagnosticsHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
                    handleRegisterConnectivityDiagnosticsCallback(
                            (ConnectivityDiagnosticsCallbackInfo) msg.obj);
                    break;
                }
                case EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
                    handleUnregisterConnectivityDiagnosticsCallback(
                            (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
                    break;
                }
            }
        }
    }

    /** Class used for cleaning up IConnectivityDiagnosticsCallback instances after their death. */
    @VisibleForTesting
    class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
        @NonNull private final IConnectivityDiagnosticsCallback mCb;
        @NonNull private final NetworkRequestInfo mRequestInfo;

        @VisibleForTesting
        ConnectivityDiagnosticsCallbackInfo(
                @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
            mCb = cb;
            mRequestInfo = nri;
        }

        @Override
        public void binderDied() {
            log("ConnectivityDiagnosticsCallback IBinder died.");
            unregisterConnectivityDiagnosticsCallback(mCb);
        }
    }

    private void handleRegisterConnectivityDiagnosticsCallback(
            @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
        ensureRunningOnConnectivityServiceThread();

        final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
        final NetworkRequestInfo nri = cbInfo.mRequestInfo;

        // This means that the client registered the same callback multiple times. Do
        // not override the previous entry, and exit silently.
        if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
            if (VDBG) log("Diagnostics callback is already registered");

            // Decrement the reference count for this NetworkRequestInfo. The reference count is
            // incremented when the NetworkRequestInfo is created as part of
            // enforceRequestCountLimit().
            decrementNetworkRequestPerUidCount(nri);
            return;
        }

        mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);

        try {
            cb.asBinder().linkToDeath(cbInfo, 0);
        } catch (RemoteException e) {
            cbInfo.binderDied();
        }
    }

    private void handleUnregisterConnectivityDiagnosticsCallback(
            @NonNull IConnectivityDiagnosticsCallback cb, int uid) {
        ensureRunningOnConnectivityServiceThread();

        if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
            if (VDBG) log("Removing diagnostics callback that is not currently registered");
            return;
        }

        final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;

        if (uid != nri.mUid) {
            if (VDBG) loge("Different uid than registrant attempting to unregister cb");
            return;
        }

        cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
    }

    @Override
    public void registerConnectivityDiagnosticsCallback(
            @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
        // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
        throw new UnsupportedOperationException(
                "registerConnectivityDiagnosticsCallback not yet implemented");
        if (request.legacyType != TYPE_NONE) {
            throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                    + " Please use NetworkCapabilities instead.");
        }

        // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
        // and administrator uids to be safe.
        final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
        restrictRequestUidsForCaller(nc);

        final NetworkRequest requestWithId =
                new NetworkRequest(
                        nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN);

        // NetworkRequestInfos created here count towards MAX_NETWORK_REQUESTS_PER_UID limit.
        //
        // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in
        // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the
        // callback's binder death.
        final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
        final ConnectivityDiagnosticsCallbackInfo cbInfo =
                new ConnectivityDiagnosticsCallbackInfo(callback, nri);

        mConnectivityDiagnosticsHandler.sendMessage(
                mConnectivityDiagnosticsHandler.obtainMessage(
                        ConnectivityDiagnosticsHandler
                                .EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
                        cbInfo));
    }

    @Override
    public void unregisterConnectivityDiagnosticsCallback(
            @NonNull IConnectivityDiagnosticsCallback callback) {
        // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
        throw new UnsupportedOperationException(
                "unregisterConnectivityDiagnosticsCallback not yet implemented");
        mConnectivityDiagnosticsHandler.sendMessage(
                mConnectivityDiagnosticsHandler.obtainMessage(
                        ConnectivityDiagnosticsHandler
                                .EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
                        Binder.getCallingUid(),
                        0,
                        callback));
    }
}
+67 −0
Original line number Diff line number Diff line
@@ -27,12 +27,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.content.Context;
import android.os.PersistableBundle;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,15 +58,27 @@ public class ConnectivityDiagnosticsManagerTest {

    private static final Executor INLINE_EXECUTOR = x -> x.run();

    @Mock private Context mContext;
    @Mock private IConnectivityManager mService;
    @Mock private ConnectivityDiagnosticsCallback mCb;

    private ConnectivityDiagnosticsBinder mBinder;
    private ConnectivityDiagnosticsManager mManager;

    @Before
    public void setUp() {
        mContext = mock(Context.class);
        mService = mock(IConnectivityManager.class);
        mCb = mock(ConnectivityDiagnosticsCallback.class);

        mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
        mManager = new ConnectivityDiagnosticsManager(mContext, mService);
    }

    @After
    public void tearDown() {
        // clear ConnectivityDiagnosticsManager callbacks map
        ConnectivityDiagnosticsManager.sCallbacks.clear();
    }

    private ConnectivityReport createSampleConnectivityReport() {
@@ -245,4 +263,53 @@ public class ConnectivityDiagnosticsManagerTest {
        // latch without waiting.
        verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity));
    }

    @Test
    public void testRegisterConnectivityDiagnosticsCallback() throws Exception {
        final NetworkRequest request = new NetworkRequest.Builder().build();

        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);

        verify(mService).registerConnectivityDiagnosticsCallback(
                any(ConnectivityDiagnosticsBinder.class), eq(request));
        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
    }

    @Test
    public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
        final NetworkRequest request = new NetworkRequest.Builder().build();

        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);

        try {
            mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
            fail("Duplicate callback registration should fail");
        } catch (IllegalArgumentException expected) {
        }
    }

    @Test
    public void testUnregisterConnectivityDiagnosticsCallback() throws Exception {
        final NetworkRequest request = new NetworkRequest.Builder().build();
        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);

        mManager.unregisterConnectivityDiagnosticsCallback(mCb);

        verify(mService).unregisterConnectivityDiagnosticsCallback(
                any(ConnectivityDiagnosticsBinder.class));
        assertFalse(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));

        // verify that re-registering is successful
        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
        verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
                any(ConnectivityDiagnosticsBinder.class), eq(request));
        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
    }

    @Test
    public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception {
        mManager.unregisterConnectivityDiagnosticsCallback(mCb);

        verifyNoMoreInteractions(mService);
    }
}
+71 −0
Original line number Diff line number Diff line
@@ -139,6 +139,7 @@ import android.net.ConnectivityManager.PacketKeepalive;
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -180,6 +181,7 @@ import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Parcel;
@@ -210,6 +212,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
@@ -322,6 +325,8 @@ public class ConnectivityServiceTest {
    @Mock UserManager mUserManager;
    @Mock NotificationManager mNotificationManager;
    @Mock AlarmManager mAlarmManager;
    @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
    @Mock IBinder mIBinder;

    private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
            ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -6355,4 +6360,70 @@ public class ConnectivityServiceTest {
                UserHandle.getAppId(uid));
        return packageInfo;
    }

    @Test
    public void testRegisterConnectivityDiagnosticsCallbackInvalidRequest() throws Exception {
        final NetworkRequest request =
                new NetworkRequest(
                        new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
        try {
            mService.registerConnectivityDiagnosticsCallback(
                    mConnectivityDiagnosticsCallback, request);
            fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
        } catch (IllegalArgumentException expected) {
        }
    }

    @Test
    public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
        final NetworkRequest wifiRequest =
                new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();

        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);

        mService.registerConnectivityDiagnosticsCallback(
                mConnectivityDiagnosticsCallback, wifiRequest);

        verify(mIBinder, timeout(TIMEOUT_MS))
                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
        assertTrue(
                mService.mConnectivityDiagnosticsCallbacks.containsKey(
                        mConnectivityDiagnosticsCallback));

        mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
        verify(mIBinder, timeout(TIMEOUT_MS))
                .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
        assertFalse(
                mService.mConnectivityDiagnosticsCallbacks.containsKey(
                        mConnectivityDiagnosticsCallback));
        verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
    }

    @Test
    public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
        final NetworkRequest wifiRequest =
                new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);

        mService.registerConnectivityDiagnosticsCallback(
                mConnectivityDiagnosticsCallback, wifiRequest);

        verify(mIBinder, timeout(TIMEOUT_MS))
                .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
        verify(mConnectivityDiagnosticsCallback).asBinder();
        assertTrue(
                mService.mConnectivityDiagnosticsCallbacks.containsKey(
                        mConnectivityDiagnosticsCallback));

        // Register the same callback again
        mService.registerConnectivityDiagnosticsCallback(
                mConnectivityDiagnosticsCallback, wifiRequest);

        // Block until all other events are done processing.
        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);

        assertTrue(
                mService.mConnectivityDiagnosticsCallbacks.containsKey(
                        mConnectivityDiagnosticsCallback));
    }
}