diff options
7 files changed, 152 insertions, 9 deletions
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java index b3d8d4e614da..0d26c2de8698 100644 --- a/core/java/android/net/NetworkStateSnapshot.java +++ b/core/java/android/net/NetworkStateSnapshot.java @@ -24,6 +24,8 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import com.android.net.module.util.NetworkIdentityUtils; + import java.util.Objects; /** @@ -124,4 +126,15 @@ public final class NetworkStateSnapshot implements Parcelable { public int hashCode() { return Objects.hash(network, networkCapabilities, linkProperties, subscriberId, legacyType); } + + @Override + public String toString() { + return "NetworkStateSnapshot{" + + "network=" + network + + ", networkCapabilities=" + networkCapabilities + + ", linkProperties=" + linkProperties + + ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + '\'' + + ", legacyType=" + legacyType + + '}'; + } } diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index a9fd6f248560..d2ed73ef8298 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -6,6 +6,7 @@ package android.net { } public class ConnectivityManager { + method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 66e7da43cb66..a6dc9ce0517f 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -1259,6 +1259,25 @@ public class ConnectivityManager { } /** + * Return a list of {@link NetworkStateSnapshot}s, one for each network that is currently + * connected. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + @NonNull + public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() { + try { + return mService.getAllNetworkStateSnapshot(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the {@link Network} object currently serving a given type, or * null if the given type is not connected. * diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 160338d396af..cd49258d1c47 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -31,6 +31,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.NetworkStateSnapshot; import android.net.OemNetworkPreferences; import android.net.ProxyInfo; import android.net.UidRange; @@ -79,6 +80,8 @@ interface IConnectivityManager @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) NetworkState[] getAllNetworkState(); + List<NetworkStateSnapshot> getAllNetworkStateSnapshot(); + boolean isActiveNetworkMetered(); boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e55307f44618..bcd67a034530 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1886,24 +1886,46 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // TODO: Consider delete this function or turn it into a no-op method. @Override public NetworkState[] getAllNetworkState() { // This contains IMSI details, so make sure the caller is privileged. PermissionUtils.enforceNetworkStackPermission(mContext); final ArrayList<NetworkState> result = new ArrayList<>(); + for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshot()) { + // NetworkStateSnapshot doesn't contain NetworkInfo, so need to fetch it from the + // NetworkAgentInfo. + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(snapshot.network); + if (nai != null && nai.networkInfo.isConnected()) { + result.add(new NetworkState(new NetworkInfo(nai.networkInfo), + snapshot.linkProperties, snapshot.networkCapabilities, snapshot.network, + snapshot.subscriberId)); + } + } + return result.toArray(new NetworkState[result.size()]); + } + + @Override + @NonNull + public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() { + // This contains IMSI details, so make sure the caller is privileged. + PermissionUtils.enforceNetworkStackPermission(mContext); + + final ArrayList<NetworkStateSnapshot> result = new ArrayList<>(); for (Network network : getAllNetworks()) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - // TODO: Consider include SUSPENDED networks. + // TODO: Consider include SUSPENDED networks, which should be considered as + // temporary shortage of connectivity of a connected network. if (nai != null && nai.networkInfo.isConnected()) { - // TODO (b/73321673) : NetworkState contains a copy of the + // TODO (b/73321673) : NetworkStateSnapshot contains a copy of the // NetworkCapabilities, which may contain UIDs of apps to which the // network applies. Should the UIDs be cleared so as not to leak or // interfere ? - result.add(nai.getNetworkState()); + result.add(nai.getNetworkStateSnapshot()); } } - return result.toArray(new NetworkState[result.size()]); + return result; } @Override diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index cac6cab7074e..1d0e11569c80 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -35,7 +35,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; -import android.net.NetworkState; +import android.net.NetworkStateSnapshot; import android.net.QosCallbackException; import android.net.QosFilter; import android.net.QosFilterParcelable; @@ -890,15 +890,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mScore = score; } - public NetworkState getNetworkState() { + /** + * Return a {@link NetworkStateSnapshot} for this network. + */ + @NonNull + public NetworkStateSnapshot getNetworkStateSnapshot() { synchronized (this) { // Network objects are outwardly immutable so there is no point in duplicating. // Duplicating also precludes sharing socket factories and connection pools. final String subscriberId = (networkAgentConfig != null) ? networkAgentConfig.subscriberId : null; - return new NetworkState(new NetworkInfo(networkInfo), - new LinkProperties(linkProperties), - new NetworkCapabilities(networkCapabilities), network, subscriberId); + return new NetworkStateSnapshot(network, new NetworkCapabilities(networkCapabilities), + new LinkProperties(linkProperties), subscriberId, networkInfo.getType()); } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index d9830c38ff3e..d5580e08d4da 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -103,6 +103,7 @@ import static com.android.testutils.ConcurrentUtils.await; import static com.android.testutils.ConcurrentUtils.durationOf; import static com.android.testutils.ExceptionUtils.ignoreExceptions; import static com.android.testutils.HandlerUtils.waitForIdleSerialExecutor; +import static com.android.testutils.MiscAsserts.assertContainsAll; import static com.android.testutils.MiscAsserts.assertContainsExactly; import static com.android.testutils.MiscAsserts.assertEmpty; import static com.android.testutils.MiscAsserts.assertLength; @@ -203,6 +204,7 @@ import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; +import android.net.NetworkStateSnapshot; import android.net.NetworkTestResultParcelable; import android.net.OemNetworkPreferences; import android.net.ProxyInfo; @@ -1663,6 +1665,7 @@ public class ConnectivityServiceTest { assertNull(mCm.getActiveNetworkForUid(Process.myUid())); // Test getAllNetworks() assertEmpty(mCm.getAllNetworks()); + assertEmpty(mCm.getAllNetworkStateSnapshot()); } /** @@ -10659,4 +10662,83 @@ public class ConnectivityServiceTest { // default NCs will be unregistered in tearDown } + + @Test + public void testGetAllNetworkStateSnapshot() throws Exception { + verifyNoNetwork(); + + // Setup test cellular network with specified LinkProperties and NetworkCapabilities, + // verify the content of the snapshot matches. + final LinkProperties cellLp = new LinkProperties(); + final LinkAddress myIpv4Addr = new LinkAddress(InetAddress.getByName("192.0.2.129"), 25); + final LinkAddress myIpv6Addr = new LinkAddress(InetAddress.getByName("2001:db8::1"), 64); + cellLp.setInterfaceName("test01"); + cellLp.addLinkAddress(myIpv4Addr); + cellLp.addLinkAddress(myIpv6Addr); + cellLp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + cellLp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + cellLp.addRoute(new RouteInfo(myIpv4Addr, null)); + cellLp.addRoute(new RouteInfo(myIpv6Addr, null)); + final NetworkCapabilities cellNcTemplate = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_MMS).build(); + + final TestNetworkCallback cellCb = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(), + cellCb); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp, cellNcTemplate); + mCellNetworkAgent.connect(true); + cellCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + + // Compose the expected cellular snapshot for verification. + final NetworkCapabilities cellNc = + mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()); + final NetworkStateSnapshot cellSnapshot = new NetworkStateSnapshot( + mCellNetworkAgent.getNetwork(), cellNc, cellLp, + null, ConnectivityManager.TYPE_MOBILE); + assertEquals(cellSnapshot, snapshots.get(0)); + + // Connect wifi and verify the snapshots. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + // Compose the expected wifi snapshot for verification. + final NetworkCapabilities wifiNc = + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); + final NetworkStateSnapshot wifiSnapshot = new NetworkStateSnapshot( + mWiFiNetworkAgent.getNetwork(), wifiNc, new LinkProperties(), null, + ConnectivityManager.TYPE_WIFI); + + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(2, snapshots); + assertContainsAll(snapshots, cellSnapshot, wifiSnapshot); + + // Set cellular as suspended, verify the snapshots will not contain suspended networks. + // TODO: Consider include SUSPENDED networks, which should be considered as + // temporary shortage of connectivity of a connected network. + mCellNetworkAgent.suspend(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + assertEquals(wifiSnapshot, snapshots.get(0)); + + // Disconnect wifi, verify the snapshots contain nothing. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertLength(0, snapshots); + + mCellNetworkAgent.resume(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshot(); + assertLength(1, snapshots); + assertEquals(cellSnapshot, snapshots.get(0)); + + mCellNetworkAgent.disconnect(); + waitForIdle(); + verifyNoNetwork(); + mCm.unregisterNetworkCallback(cellCb); + } } |