summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/com/android/server/NetworkStackService.java93
-rw-r--r--src/com/android/server/util/PermissionUtil.java2
-rw-r--r--tests/unit/src/com/android/server/NetworkStackServiceTest.kt192
3 files changed, 263 insertions, 24 deletions
diff --git a/src/com/android/server/NetworkStackService.java b/src/com/android/server/NetworkStackService.java
index 5ab2744..3a702e5 100644
--- a/src/com/android/server/NetworkStackService.java
+++ b/src/com/android/server/NetworkStackService.java
@@ -46,6 +46,7 @@ import android.net.util.SharedLog;
import android.os.Build;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.ArraySet;
@@ -86,8 +87,7 @@ public class NetworkStackService extends Service {
*/
public static synchronized IBinder makeConnector(Context context) {
if (sConnector == null) {
- sConnector = new NetworkStackConnector(
- context, new NetworkStackConnector.PermissionChecker());
+ sConnector = new NetworkStackConnector(context);
}
return sConnector;
}
@@ -115,6 +115,58 @@ public class NetworkStackService extends Service {
}
/**
+ * Permission checking dependency of the connector, useful for testing.
+ */
+ public static class PermissionChecker {
+ /**
+ * @see PermissionUtil#enforceNetworkStackCallingPermission()
+ */
+ public void enforceNetworkStackCallingPermission() {
+ PermissionUtil.enforceNetworkStackCallingPermission();
+ }
+ }
+
+ /**
+ * Dependencies of {@link NetworkStackConnector}, useful for testing.
+ */
+ public static class Dependencies {
+ /** @see IpMemoryStoreService */
+ @NonNull
+ public IpMemoryStoreService makeIpMemoryStoreService(@NonNull Context context) {
+ return new IpMemoryStoreService(context);
+ }
+
+ /** @see NetworkStackNotifier */
+ @NonNull
+ public NetworkStackNotifier makeNotifier(@NonNull Context context, @NonNull Looper looper) {
+ return new NetworkStackNotifier(context, looper);
+ }
+
+ /** @see DhcpServer */
+ @NonNull
+ public DhcpServer makeDhcpServer(@NonNull Context context, @NonNull String ifName,
+ @NonNull DhcpServingParams params, @NonNull SharedLog log) {
+ return new DhcpServer(context, ifName, params, log);
+ }
+
+ /** @see NetworkMonitor */
+ @NonNull
+ public NetworkMonitor makeNetworkMonitor(@NonNull Context context,
+ @NonNull INetworkMonitorCallbacks cb, @NonNull Network network,
+ @NonNull SharedLog log, @NonNull NetworkStackServiceManager nsServiceManager) {
+ return new NetworkMonitor(context, cb, network, log, nsServiceManager);
+ }
+
+ /** @see IpClient */
+ @NonNull
+ public IpClient makeIpClient(@NonNull Context context, @NonNull String ifName,
+ @NonNull IIpClientCallbacks cb, @NonNull NetworkObserverRegistry observerRegistry,
+ @NonNull NetworkStackServiceManager nsServiceManager) {
+ return new IpClient(context, ifName, cb, observerRegistry, nsServiceManager);
+ }
+ }
+
+ /**
* Connector implementing INetworkStackConnector for clients.
*/
@VisibleForTesting
@@ -123,6 +175,7 @@ public class NetworkStackService extends Service {
private static final int NUM_VALIDATION_LOG_LINES = 20;
private final Context mContext;
private final PermissionChecker mPermChecker;
+ private final Dependencies mDeps;
private final INetd mNetd;
private final NetworkObserverRegistry mObserverRegistry;
@GuardedBy("mIpClients")
@@ -142,19 +195,6 @@ public class NetworkStackService extends Service {
private final ArraySet<Integer> mFrameworkAidlVersions = new ArraySet<>(1);
private final int mNetdAidlVersion;
- /**
- * Permission checking dependency of the connector, useful for testing.
- */
- @VisibleForTesting
- public static class PermissionChecker {
- /**
- * @see PermissionUtil#enforceNetworkStackCallingPermission()
- */
- public void enforceNetworkStackCallingPermission() {
- PermissionUtil.enforceNetworkStackCallingPermission();
- }
- }
-
private SharedLog addValidationLogs(Network network, String name) {
final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name);
synchronized (mValidationLogs) {
@@ -166,21 +206,27 @@ public class NetworkStackService extends Service {
return log;
}
+ NetworkStackConnector(@NonNull Context context) {
+ this(context, new PermissionChecker(), new Dependencies());
+ }
+
@VisibleForTesting
public NetworkStackConnector(
- @NonNull Context context, @NonNull PermissionChecker permChecker) {
+ @NonNull Context context, @NonNull PermissionChecker permChecker,
+ @NonNull Dependencies deps) {
mContext = context;
mPermChecker = permChecker;
+ mDeps = deps;
mNetd = INetd.Stub.asInterface(
(IBinder) context.getSystemService(Context.NETD_SERVICE));
mObserverRegistry = new NetworkObserverRegistry();
- mIpMemoryStoreService = new IpMemoryStoreService(context);
+ mIpMemoryStoreService = mDeps.makeIpMemoryStoreService(context);
// NetworkStackNotifier only shows notifications relevant for API level > Q
if (ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) {
final HandlerThread notifierThread = new HandlerThread(
NetworkStackNotifier.class.getSimpleName());
notifierThread.start();
- mNotifier = new NetworkStackNotifier(context, notifierThread.getLooper());
+ mNotifier = mDeps.makeNotifier(context, notifierThread.getLooper());
} else {
mNotifier = null;
}
@@ -217,7 +263,7 @@ public class NetworkStackService extends Service {
updateSystemAidlVersion(cb.getInterfaceVersion());
final DhcpServer server;
try {
- server = new DhcpServer(
+ server = mDeps.makeDhcpServer(
mContext,
ifName,
DhcpServingParams.fromParcelableObject(params),
@@ -240,7 +286,7 @@ public class NetworkStackService extends Service {
mPermChecker.enforceNetworkStackCallingPermission();
updateSystemAidlVersion(cb.getInterfaceVersion());
final SharedLog log = addValidationLogs(network, name);
- final NetworkMonitor nm = new NetworkMonitor(mContext, cb, network, log, this);
+ final NetworkMonitor nm = mDeps.makeNetworkMonitor(mContext, cb, network, log, this);
cb.onNetworkMonitorCreated(new NetworkMonitorConnector(nm, mPermChecker));
}
@@ -248,7 +294,8 @@ public class NetworkStackService extends Service {
public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException {
mPermChecker.enforceNetworkStackCallingPermission();
updateSystemAidlVersion(cb.getInterfaceVersion());
- final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry, this);
+ final IpClient ipClient = mDeps.makeIpClient(
+ mContext, ifName, cb, mObserverRegistry, this);
synchronized (mIpClients) {
final Iterator<WeakReference<IpClient>> it = mIpClients.iterator();
@@ -371,10 +418,10 @@ public class NetworkStackService extends Service {
@NonNull
private final NetworkMonitor mNm;
@NonNull
- private final NetworkStackConnector.PermissionChecker mPermChecker;
+ private final PermissionChecker mPermChecker;
public NetworkMonitorConnector(@NonNull NetworkMonitor nm,
- @NonNull NetworkStackConnector.PermissionChecker permChecker) {
+ @NonNull PermissionChecker permChecker) {
mNm = nm;
mPermChecker = permChecker;
}
diff --git a/src/com/android/server/util/PermissionUtil.java b/src/com/android/server/util/PermissionUtil.java
index 28dad25..3dff715 100644
--- a/src/com/android/server/util/PermissionUtil.java
+++ b/src/com/android/server/util/PermissionUtil.java
@@ -73,7 +73,7 @@ public final class PermissionUtil {
*/
public static void checkDumpPermission() {
final int caller = getCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID
+ if (caller != Process.myUid() && caller != Process.SYSTEM_UID && caller != Process.ROOT_UID
&& caller != Process.SHELL_UID) {
throw new SecurityException("No dump permissions for caller: " + caller);
}
diff --git a/tests/unit/src/com/android/server/NetworkStackServiceTest.kt b/tests/unit/src/com/android/server/NetworkStackServiceTest.kt
new file mode 100644
index 0000000..52556bc
--- /dev/null
+++ b/tests/unit/src/com/android/server/NetworkStackServiceTest.kt
@@ -0,0 +1,192 @@
+/*
+ * 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 com.android.server
+
+import android.content.Context
+import android.net.IIpMemoryStoreCallbacks
+import android.net.INetd
+import android.net.INetworkMonitorCallbacks
+import android.net.INetworkStackConnector
+import android.net.InetAddresses.parseNumericAddress
+import android.net.Network
+import android.net.dhcp.DhcpServer
+import android.net.dhcp.DhcpServingParamsParcel
+import android.net.dhcp.IDhcpServer
+import android.net.dhcp.IDhcpServerCallbacks
+import android.net.ip.IIpClientCallbacks
+import android.net.ip.IpClient
+import android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH
+import android.os.Build
+import android.os.IBinder
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.NetworkStackService.Dependencies
+import com.android.server.NetworkStackService.NetworkStackConnector
+import com.android.server.NetworkStackService.PermissionChecker
+import com.android.server.connectivity.NetworkMonitor
+import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.net.Inet4Address
+import kotlin.reflect.KVisibility
+import kotlin.reflect.full.declaredMemberFunctions
+import kotlin.test.assertEquals
+
+private val TEST_NETD_VERSION = 9991001
+private val TEST_NETD_HASH = "test_netd_hash"
+
+private val TEST_IPMEMORYSTORE_VERSION = 9991002
+private val TEST_IPMEMORYSTORE_HASH = "test_ipmemorystore_hash"
+
+private val TEST_IFACE = "test_iface"
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkStackServiceTest {
+ @Rule @JvmField
+ val ignoreRule = DevSdkIgnoreRule()
+
+ private val permChecker = mock(PermissionChecker::class.java)
+ private val mockIpMemoryStoreService = mock(IpMemoryStoreService::class.java)
+ private val mockDhcpServer = mock(DhcpServer::class.java)
+ private val mockNetworkMonitor = mock(NetworkMonitor::class.java)
+ private val mockIpClient = mock(IpClient::class.java)
+ private val deps = mock(Dependencies::class.java).apply {
+ doReturn(mockIpMemoryStoreService).`when`(this).makeIpMemoryStoreService(any())
+ doReturn(mockDhcpServer).`when`(this).makeDhcpServer(any(), any(), any(), any())
+ doReturn(mockNetworkMonitor).`when`(this).makeNetworkMonitor(any(), any(), any(), any(),
+ any())
+ doReturn(mockIpClient).`when`(this).makeIpClient(any(), any(), any(), any(), any())
+ }
+ private val netd = mock(INetd::class.java).apply {
+ doReturn(TEST_NETD_VERSION).`when`(this).interfaceVersion
+ doReturn(TEST_NETD_HASH).`when`(this).interfaceHash
+ }
+ private val netdBinder = mock(IBinder::class.java).apply {
+ doReturn(netd).`when`(this).queryLocalInterface(any())
+ }
+ private val context = mock(Context::class.java).apply {
+ doReturn(netdBinder).`when`(this).getSystemService(Context.NETD_SERVICE)
+ }
+
+ private val connector = NetworkStackConnector(context, permChecker, deps)
+
+ @Test
+ fun testDumpVersion_Q() {
+ prepareDumpVersionTest()
+
+ val dumpsysOut = StringWriter()
+ connector.dump(FileDescriptor(), PrintWriter(dumpsysOut, true /* autoFlush */),
+ arrayOf("version") /* args */)
+
+ assertEquals("NetworkStack version:\n" +
+ "NetworkStackConnector: ${INetworkStackConnector.VERSION}\n" +
+ "SystemServer: {9990001, 9990002, 9990003, 9990004, 9990005}\n" +
+ "Netd: $TEST_NETD_VERSION\n\n",
+ dumpsysOut.toString())
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testDumpVersion() {
+ // TODO: log interface hash on R+ and test it
+ }
+
+ fun prepareDumpVersionTest() {
+ // Call each method on INetworkStackConnector and verify that it notes down the version of
+ // the remote.
+ // Call fetchIpMemoryStore
+ val mockIpMemoryStoreCb = mock(IIpMemoryStoreCallbacks::class.java)
+ doReturn(9990001).`when`(mockIpMemoryStoreCb).interfaceVersion
+ doReturn("fetch_ipmemorystore_hash").`when`(mockIpMemoryStoreCb).interfaceHash
+
+ connector.fetchIpMemoryStore(mockIpMemoryStoreCb)
+ // IpMemoryStore was created at initialization time
+ verify(mockIpMemoryStoreCb).onIpMemoryStoreFetched(any())
+
+ // Call makeDhcpServer
+ val testParams = DhcpServingParamsParcel()
+ testParams.linkMtu = 1500
+ testParams.dhcpLeaseTimeSecs = 3600L
+ testParams.serverAddr = inet4AddressToIntHTH(
+ parseNumericAddress("192.168.1.1") as Inet4Address)
+ testParams.serverAddrPrefixLength = 24
+
+ val mockDhcpCb = mock(IDhcpServerCallbacks::class.java)
+ doReturn(9990002).`when`(mockDhcpCb).interfaceVersion
+ doReturn("dhcp_server_hash").`when`(mockDhcpCb).interfaceHash
+
+ connector.makeDhcpServer(TEST_IFACE, testParams, mockDhcpCb)
+
+ verify(deps).makeDhcpServer(any(), eq(TEST_IFACE), any(), any())
+ verify(mockDhcpCb).onDhcpServerCreated(eq(IDhcpServer.STATUS_SUCCESS), any())
+
+ // Call makeNetworkMonitor
+ // Use a spy of INetworkMonitorCallbacks and not a mock, as mockito can't create a mock on Q
+ // because of the missing CaptivePortalData class that is an argument of one of the methods
+ val mockNetworkMonitorCb = spy(INetworkMonitorCallbacks.Stub.asInterface(
+ mock(IBinder::class.java)))
+ doReturn(9990003).`when`(mockNetworkMonitorCb).interfaceVersion
+ doReturn("networkmonitor_hash").`when`(mockNetworkMonitorCb).interfaceHash
+
+ connector.makeNetworkMonitor(Network(123), "test_nm", mockNetworkMonitorCb)
+
+ verify(deps).makeNetworkMonitor(any(), any(), eq(Network(123)), any(), any())
+ verify(mockNetworkMonitorCb).onNetworkMonitorCreated(any())
+
+ // Call makeIpClient
+ // Use a spy of IIpClientCallbacks instead of a mock, as mockito cannot create a mock on Q
+ // because of the missing CaptivePortalData class that is an argument on one of the methods
+ val mockIpClientCb = mock(IIpClientCallbacks::class.java)
+ doReturn(9990004).`when`(mockIpClientCb).interfaceVersion
+ doReturn("ipclient_hash").`when`(mockIpClientCb).interfaceHash
+
+ connector.makeIpClient(TEST_IFACE, mockIpClientCb)
+
+ verify(deps).makeIpClient(any(), eq(TEST_IFACE), any(), any(), any())
+ verify(mockIpClientCb).onIpClientCreated(any())
+
+ // Call some methods one more time with a shared version number and hash to verify no
+ // duplicates are reported
+ doReturn(9990005).`when`(mockIpMemoryStoreCb).interfaceVersion
+ doReturn("multiple_use_hash").`when`(mockIpMemoryStoreCb).interfaceHash
+ connector.fetchIpMemoryStore(mockIpMemoryStoreCb)
+ verify(mockIpMemoryStoreCb, times(2)).onIpMemoryStoreFetched(any())
+
+ doReturn(9990005).`when`(mockDhcpCb).interfaceVersion
+ doReturn("multiple_use_hash").`when`(mockDhcpCb).interfaceHash
+ connector.makeDhcpServer(TEST_IFACE, testParams, mockDhcpCb)
+ verify(mockDhcpCb, times(2)).onDhcpServerCreated(eq(IDhcpServer.STATUS_SUCCESS), any())
+
+ // Verify all methods were covered by the test (4 methods + getVersion + getHash)
+ assertEquals(6, INetworkStackConnector::class.declaredMemberFunctions.count {
+ it.visibility == KVisibility.PUBLIC
+ })
+ }
+} \ No newline at end of file