Loading common/networkstackclient/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,9 @@ aidl_interface { "3", "4", ], visibility: [ "//system/tools/aidl/build" ], } aidl_interface { Loading Loading @@ -84,6 +87,11 @@ aidl_interface { "3", "4", ], // TODO: have tethering depend on networkstack-client and set visibility to private visibility: [ "//system/tools/aidl/build", "//frameworks/base/packages/Tethering" ], } java_library { Loading @@ -93,9 +101,16 @@ java_library { ":framework-annotations", "src/android/net/IpMemoryStoreClient.java", "src/android/net/ipmemorystore/**/*.java", "src/android/net/networkstack/**/*.java", ], static_libs: [ "ipmemorystore-aidl-interfaces-V3-java", "networkstack-aidl-interfaces-unstable-java", ], visibility: [ "//frameworks/base/packages/Tethering", "//frameworks/base/services/net", "//frameworks/opt/net/wifi/service", "//packages/modules/NetworkStack", ] } common/networkstackclient/src/android/net/networkstack/ModuleNetworkStackClient.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack; import static android.content.Context.NETWORK_STACK_SERVICE; import static android.os.Build.VERSION.SDK_INT; import android.annotation.NonNull; import android.content.Context; import android.net.INetworkStackConnector; import android.os.Build; import android.os.IBinder; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; /** * A {@link NetworkStackClientBase} implementation for use within modules (not the system server). */ public class ModuleNetworkStackClient extends NetworkStackClientBase { private static final String TAG = ModuleNetworkStackClient.class.getSimpleName(); private ModuleNetworkStackClient() {} private static ModuleNetworkStackClient sInstance; /** * Get an instance of the ModuleNetworkStackClient. * @param packageContext Context to use to obtain the network stack connector. */ @NonNull public static synchronized ModuleNetworkStackClient getInstance(Context packageContext) { // TODO(b/149676685): change this check to "< R" once R is defined if (SDK_INT < Build.VERSION_CODES.Q || (SDK_INT == Build.VERSION_CODES.Q && "REL".equals(Build.VERSION.CODENAME))) { // NETWORK_STACK_SERVICE is not available through getSystemService before R throw new UnsupportedOperationException( "ModuleNetworkStackClient is not supported on API " + SDK_INT); } if (sInstance == null) { sInstance = new ModuleNetworkStackClient(); sInstance.startPolling(packageContext); } return sInstance; } @VisibleForTesting protected static synchronized void resetInstanceForTest() { sInstance = null; } private void startPolling(Context context) { // If the service is already registered (as it will be most of the time), do not poll and // fulfill requests immediately. final IBinder nss = (IBinder) context.getSystemService(NETWORK_STACK_SERVICE); if (nss != null) { // Calling onNetworkStackConnected here means that pending oneway Binder calls to the // NetworkStack get sent from the current thread and not a worker thread; this is fine // considering that those are only non-blocking, oneway Binder calls. onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(nss)); return; } new Thread(new PollingRunner(context)).start(); } private class PollingRunner implements Runnable { private final Context mContext; private PollingRunner(Context context) { mContext = context; } @Override public void run() { // Block until the NetworkStack connector is registered in ServiceManager. IBinder nss; while ((nss = (IBinder) mContext.getSystemService(NETWORK_STACK_SERVICE)) == null) { try { Thread.sleep(200); } catch (InterruptedException e) { Log.e(TAG, "Interrupted while waiting for NetworkStack connector", e); // Keep trying, the system would just crash without a connector } } onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(nss)); } } } common/networkstackclient/src/android/net/networkstack/NetworkStackClientBase.java 0 → 100644 +161 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.IIpMemoryStoreCallbacks; import android.net.INetworkMonitorCallbacks; import android.net.INetworkStackConnector; import android.net.Network; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; import android.os.RemoteException; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.function.Consumer; /** * Utility class to obtain and communicate with the NetworkStack module. */ public abstract class NetworkStackClientBase { @NonNull @GuardedBy("mPendingNetStackRequests") private final ArrayList<Consumer<INetworkStackConnector>> mPendingNetStackRequests = new ArrayList<>(); @Nullable @GuardedBy("mPendingNetStackRequests") private INetworkStackConnector mConnector; /** * Create a DHCP server according to the specified parameters. * * <p>The server will be returned asynchronously through the provided callbacks. */ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params, final IDhcpServerCallbacks cb) { requestConnector(connector -> { try { connector.makeDhcpServer(ifName, params, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create DhcpServer", e); } }); } /** * Create an IpClient on the specified interface. * * <p>The IpClient will be returned asynchronously through the provided callbacks. */ public void makeIpClient(String ifName, IIpClientCallbacks cb) { requestConnector(connector -> { try { connector.makeIpClient(ifName, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create IpClient", e); } }); } /** * Create a NetworkMonitor. * * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. */ public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) { requestConnector(connector -> { try { connector.makeNetworkMonitor(network, name, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create NetworkMonitor", e); } }); } /** * Get an instance of the IpMemoryStore. * * <p>The IpMemoryStore will be returned asynchronously through the provided callbacks. */ public void fetchIpMemoryStore(IIpMemoryStoreCallbacks cb) { requestConnector(connector -> { try { connector.fetchIpMemoryStore(cb); } catch (RemoteException e) { throw new IllegalStateException("Could not fetch IpMemoryStore", e); } }); } protected void requestConnector(@NonNull Consumer<INetworkStackConnector> request) { final INetworkStackConnector connector; synchronized (mPendingNetStackRequests) { connector = mConnector; if (connector == null) { mPendingNetStackRequests.add(request); return; } } request.accept(connector); } /** * Call this method once the NetworkStack is connected. * * <p>This method will cause pending oneway Binder calls for the NetworkStack to be processed on * the calling thread. */ protected void onNetworkStackConnected(@NonNull INetworkStackConnector connector) { // Process the connector wait queue in order, including any items that are added // while processing. while (true) { final ArrayList<Consumer<INetworkStackConnector>> requests; synchronized (mPendingNetStackRequests) { requests = new ArrayList<>(mPendingNetStackRequests); mPendingNetStackRequests.clear(); } for (Consumer<INetworkStackConnector> consumer : requests) { consumer.accept(connector); } synchronized (mPendingNetStackRequests) { if (mPendingNetStackRequests.size() == 0) { // Once mConnector is non-null, no more tasks will be queued. mConnector = connector; return; } } } } /** * Used in subclasses for diagnostics (dumpsys) purposes. * @return How many requests for the network stack are currently pending. */ protected int getQueueLength() { synchronized (mPendingNetStackRequests) { return mPendingNetStackRequests.size(); } } } tests/unit/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ java_defaults { srcs: ["src/**/*.java", "src/**/*.kt"], resource_dirs: ["res"], static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", "kotlin-reflect", "mockito-target-extended-minus-junit4", Loading tests/unit/src/android/net/networkstack/ModuleNetworkStackClientTest.kt 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack import android.content.Context import android.net.IIpMemoryStoreCallbacks import android.net.INetworkMonitorCallbacks import android.net.INetworkStackConnector import android.net.Network import android.net.dhcp.DhcpServingParamsParcel import android.net.dhcp.IDhcpServerCallbacks import android.net.ip.IIpClientCallbacks import android.os.Build import android.os.IBinder import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.networkstack.apishim.ShimUtils import org.junit.After import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.Mock import org.mockito.Mockito.doReturn import org.mockito.Mockito.never import org.mockito.Mockito.timeout import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) @SmallTest class ModuleNetworkStackClientTest { private val TEST_IFNAME = "testiface" private val TEST_NETWORK = Network(43) private val TEST_TIMEOUT_MS = 500L @Mock private lateinit var mContext: Context @Mock private lateinit var mConnectorBinder: IBinder @Mock private lateinit var mConnector: INetworkStackConnector @Mock private lateinit var mIpClientCb: IIpClientCallbacks @Mock private lateinit var mDhcpServerCb: IDhcpServerCallbacks @Mock private lateinit var mNetworkMonitorCb: INetworkMonitorCallbacks @Mock private lateinit var mIpMemoryStoreCb: IIpMemoryStoreCallbacks @Before fun setUp() { // ModuleNetworkStackClient is only available after Q assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) MockitoAnnotations.initMocks(this) doReturn(mConnector).`when`(mConnectorBinder).queryLocalInterface( INetworkStackConnector::class.qualifiedName!!) } @After fun tearDown() { ModuleNetworkStackClient.resetInstanceForTest() } @Test fun testIpClientServiceAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) ModuleNetworkStackClient.getInstance(mContext).makeIpClient(TEST_IFNAME, mIpClientCb) verify(mConnector).makeIpClient(TEST_IFNAME, mIpClientCb) } @Test fun testIpClientServiceAvailableAfterPolling() { ModuleNetworkStackClient.getInstance(mContext).makeIpClient(TEST_IFNAME, mIpClientCb) Thread.sleep(TEST_TIMEOUT_MS) verify(mConnector, never()).makeIpClient(any(), any()) doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) // Use a longer timeout as polling can cause larger delays verify(mConnector, timeout(TEST_TIMEOUT_MS * 4)).makeIpClient(TEST_IFNAME, mIpClientCb) } @Test fun testDhcpServerAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) val testParams = DhcpServingParamsParcel() ModuleNetworkStackClient.getInstance(mContext).makeDhcpServer(TEST_IFNAME, testParams, mDhcpServerCb) verify(mConnector).makeDhcpServer(TEST_IFNAME, testParams, mDhcpServerCb) } @Test fun testNetworkMonitorAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) val testName = "NetworkMonitorName" ModuleNetworkStackClient.getInstance(mContext).makeNetworkMonitor(TEST_NETWORK, testName, mNetworkMonitorCb) verify(mConnector).makeNetworkMonitor(TEST_NETWORK, testName, mNetworkMonitorCb) } @Test fun testIpMemoryStoreAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) ModuleNetworkStackClient.getInstance(mContext).fetchIpMemoryStore(mIpMemoryStoreCb) verify(mConnector).fetchIpMemoryStore(mIpMemoryStoreCb) } } No newline at end of file Loading
common/networkstackclient/Android.bp +15 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,9 @@ aidl_interface { "3", "4", ], visibility: [ "//system/tools/aidl/build" ], } aidl_interface { Loading Loading @@ -84,6 +87,11 @@ aidl_interface { "3", "4", ], // TODO: have tethering depend on networkstack-client and set visibility to private visibility: [ "//system/tools/aidl/build", "//frameworks/base/packages/Tethering" ], } java_library { Loading @@ -93,9 +101,16 @@ java_library { ":framework-annotations", "src/android/net/IpMemoryStoreClient.java", "src/android/net/ipmemorystore/**/*.java", "src/android/net/networkstack/**/*.java", ], static_libs: [ "ipmemorystore-aidl-interfaces-V3-java", "networkstack-aidl-interfaces-unstable-java", ], visibility: [ "//frameworks/base/packages/Tethering", "//frameworks/base/services/net", "//frameworks/opt/net/wifi/service", "//packages/modules/NetworkStack", ] }
common/networkstackclient/src/android/net/networkstack/ModuleNetworkStackClient.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack; import static android.content.Context.NETWORK_STACK_SERVICE; import static android.os.Build.VERSION.SDK_INT; import android.annotation.NonNull; import android.content.Context; import android.net.INetworkStackConnector; import android.os.Build; import android.os.IBinder; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; /** * A {@link NetworkStackClientBase} implementation for use within modules (not the system server). */ public class ModuleNetworkStackClient extends NetworkStackClientBase { private static final String TAG = ModuleNetworkStackClient.class.getSimpleName(); private ModuleNetworkStackClient() {} private static ModuleNetworkStackClient sInstance; /** * Get an instance of the ModuleNetworkStackClient. * @param packageContext Context to use to obtain the network stack connector. */ @NonNull public static synchronized ModuleNetworkStackClient getInstance(Context packageContext) { // TODO(b/149676685): change this check to "< R" once R is defined if (SDK_INT < Build.VERSION_CODES.Q || (SDK_INT == Build.VERSION_CODES.Q && "REL".equals(Build.VERSION.CODENAME))) { // NETWORK_STACK_SERVICE is not available through getSystemService before R throw new UnsupportedOperationException( "ModuleNetworkStackClient is not supported on API " + SDK_INT); } if (sInstance == null) { sInstance = new ModuleNetworkStackClient(); sInstance.startPolling(packageContext); } return sInstance; } @VisibleForTesting protected static synchronized void resetInstanceForTest() { sInstance = null; } private void startPolling(Context context) { // If the service is already registered (as it will be most of the time), do not poll and // fulfill requests immediately. final IBinder nss = (IBinder) context.getSystemService(NETWORK_STACK_SERVICE); if (nss != null) { // Calling onNetworkStackConnected here means that pending oneway Binder calls to the // NetworkStack get sent from the current thread and not a worker thread; this is fine // considering that those are only non-blocking, oneway Binder calls. onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(nss)); return; } new Thread(new PollingRunner(context)).start(); } private class PollingRunner implements Runnable { private final Context mContext; private PollingRunner(Context context) { mContext = context; } @Override public void run() { // Block until the NetworkStack connector is registered in ServiceManager. IBinder nss; while ((nss = (IBinder) mContext.getSystemService(NETWORK_STACK_SERVICE)) == null) { try { Thread.sleep(200); } catch (InterruptedException e) { Log.e(TAG, "Interrupted while waiting for NetworkStack connector", e); // Keep trying, the system would just crash without a connector } } onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(nss)); } } }
common/networkstackclient/src/android/net/networkstack/NetworkStackClientBase.java 0 → 100644 +161 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.IIpMemoryStoreCallbacks; import android.net.INetworkMonitorCallbacks; import android.net.INetworkStackConnector; import android.net.Network; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; import android.os.RemoteException; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.function.Consumer; /** * Utility class to obtain and communicate with the NetworkStack module. */ public abstract class NetworkStackClientBase { @NonNull @GuardedBy("mPendingNetStackRequests") private final ArrayList<Consumer<INetworkStackConnector>> mPendingNetStackRequests = new ArrayList<>(); @Nullable @GuardedBy("mPendingNetStackRequests") private INetworkStackConnector mConnector; /** * Create a DHCP server according to the specified parameters. * * <p>The server will be returned asynchronously through the provided callbacks. */ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params, final IDhcpServerCallbacks cb) { requestConnector(connector -> { try { connector.makeDhcpServer(ifName, params, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create DhcpServer", e); } }); } /** * Create an IpClient on the specified interface. * * <p>The IpClient will be returned asynchronously through the provided callbacks. */ public void makeIpClient(String ifName, IIpClientCallbacks cb) { requestConnector(connector -> { try { connector.makeIpClient(ifName, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create IpClient", e); } }); } /** * Create a NetworkMonitor. * * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. */ public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) { requestConnector(connector -> { try { connector.makeNetworkMonitor(network, name, cb); } catch (RemoteException e) { throw new IllegalStateException("Could not create NetworkMonitor", e); } }); } /** * Get an instance of the IpMemoryStore. * * <p>The IpMemoryStore will be returned asynchronously through the provided callbacks. */ public void fetchIpMemoryStore(IIpMemoryStoreCallbacks cb) { requestConnector(connector -> { try { connector.fetchIpMemoryStore(cb); } catch (RemoteException e) { throw new IllegalStateException("Could not fetch IpMemoryStore", e); } }); } protected void requestConnector(@NonNull Consumer<INetworkStackConnector> request) { final INetworkStackConnector connector; synchronized (mPendingNetStackRequests) { connector = mConnector; if (connector == null) { mPendingNetStackRequests.add(request); return; } } request.accept(connector); } /** * Call this method once the NetworkStack is connected. * * <p>This method will cause pending oneway Binder calls for the NetworkStack to be processed on * the calling thread. */ protected void onNetworkStackConnected(@NonNull INetworkStackConnector connector) { // Process the connector wait queue in order, including any items that are added // while processing. while (true) { final ArrayList<Consumer<INetworkStackConnector>> requests; synchronized (mPendingNetStackRequests) { requests = new ArrayList<>(mPendingNetStackRequests); mPendingNetStackRequests.clear(); } for (Consumer<INetworkStackConnector> consumer : requests) { consumer.accept(connector); } synchronized (mPendingNetStackRequests) { if (mPendingNetStackRequests.size() == 0) { // Once mConnector is non-null, no more tasks will be queued. mConnector = connector; return; } } } } /** * Used in subclasses for diagnostics (dumpsys) purposes. * @return How many requests for the network stack are currently pending. */ protected int getQueueLength() { synchronized (mPendingNetStackRequests) { return mPendingNetStackRequests.size(); } } }
tests/unit/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ java_defaults { srcs: ["src/**/*.java", "src/**/*.kt"], resource_dirs: ["res"], static_libs: [ "androidx.test.ext.junit", "androidx.test.rules", "kotlin-reflect", "mockito-target-extended-minus-junit4", Loading
tests/unit/src/android/net/networkstack/ModuleNetworkStackClientTest.kt 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.networkstack import android.content.Context import android.net.IIpMemoryStoreCallbacks import android.net.INetworkMonitorCallbacks import android.net.INetworkStackConnector import android.net.Network import android.net.dhcp.DhcpServingParamsParcel import android.net.dhcp.IDhcpServerCallbacks import android.net.ip.IIpClientCallbacks import android.os.Build import android.os.IBinder import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.networkstack.apishim.ShimUtils import org.junit.After import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.Mock import org.mockito.Mockito.doReturn import org.mockito.Mockito.never import org.mockito.Mockito.timeout import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidJUnit4::class) @SmallTest class ModuleNetworkStackClientTest { private val TEST_IFNAME = "testiface" private val TEST_NETWORK = Network(43) private val TEST_TIMEOUT_MS = 500L @Mock private lateinit var mContext: Context @Mock private lateinit var mConnectorBinder: IBinder @Mock private lateinit var mConnector: INetworkStackConnector @Mock private lateinit var mIpClientCb: IIpClientCallbacks @Mock private lateinit var mDhcpServerCb: IDhcpServerCallbacks @Mock private lateinit var mNetworkMonitorCb: INetworkMonitorCallbacks @Mock private lateinit var mIpMemoryStoreCb: IIpMemoryStoreCallbacks @Before fun setUp() { // ModuleNetworkStackClient is only available after Q assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) MockitoAnnotations.initMocks(this) doReturn(mConnector).`when`(mConnectorBinder).queryLocalInterface( INetworkStackConnector::class.qualifiedName!!) } @After fun tearDown() { ModuleNetworkStackClient.resetInstanceForTest() } @Test fun testIpClientServiceAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) ModuleNetworkStackClient.getInstance(mContext).makeIpClient(TEST_IFNAME, mIpClientCb) verify(mConnector).makeIpClient(TEST_IFNAME, mIpClientCb) } @Test fun testIpClientServiceAvailableAfterPolling() { ModuleNetworkStackClient.getInstance(mContext).makeIpClient(TEST_IFNAME, mIpClientCb) Thread.sleep(TEST_TIMEOUT_MS) verify(mConnector, never()).makeIpClient(any(), any()) doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) // Use a longer timeout as polling can cause larger delays verify(mConnector, timeout(TEST_TIMEOUT_MS * 4)).makeIpClient(TEST_IFNAME, mIpClientCb) } @Test fun testDhcpServerAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) val testParams = DhcpServingParamsParcel() ModuleNetworkStackClient.getInstance(mContext).makeDhcpServer(TEST_IFNAME, testParams, mDhcpServerCb) verify(mConnector).makeDhcpServer(TEST_IFNAME, testParams, mDhcpServerCb) } @Test fun testNetworkMonitorAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) val testName = "NetworkMonitorName" ModuleNetworkStackClient.getInstance(mContext).makeNetworkMonitor(TEST_NETWORK, testName, mNetworkMonitorCb) verify(mConnector).makeNetworkMonitor(TEST_NETWORK, testName, mNetworkMonitorCb) } @Test fun testIpMemoryStoreAvailableImmediately() { doReturn(mConnectorBinder).`when`(mContext).getSystemService(Context.NETWORK_STACK_SERVICE) ModuleNetworkStackClient.getInstance(mContext).fetchIpMemoryStore(mIpMemoryStoreCb) verify(mConnector).fetchIpMemoryStore(mIpMemoryStoreCb) } } No newline at end of file