From 007a886baf9f4e6737f2b4378f68c023dc7e7b31 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Tue, 11 May 2021 13:37:06 +0000 Subject: Move net unit tests to packages/Connectivity Move the tests together with packages/Connectivity code, so both can be moved to packages/modules/Connectivity together. Also reorganize unit tests in a unit/ directory, as other tests (integration/, common/ etc.) have been added in tests/net since they were created. This makes the directory structure consistent. Test: atest FrameworksNetTests Bug: 187814163 Ignore-AOSP-First: needs per-branch move for merge conflicts Change-Id: I254ffd1c08ec058d594b4ea55cbae5505f8497cc --- packages/Connectivity/framework/Android.bp | 2 +- packages/Connectivity/tests/OWNERS | 8 + packages/Connectivity/tests/TEST_MAPPING | 34 + packages/Connectivity/tests/common/Android.bp | 48 + .../tests/common/java/ParseExceptionTest.kt | 52 + .../java/android/net/CaptivePortalDataTest.kt | 190 + .../common/java/android/net/CaptivePortalTest.java | 121 + .../common/java/android/net/DependenciesTest.java | 113 + .../common/java/android/net/DhcpInfoTest.java | 112 + .../common/java/android/net/IpPrefixTest.java | 374 + .../java/android/net/KeepalivePacketDataTest.kt | 120 + .../common/java/android/net/LinkAddressTest.java | 518 + .../java/android/net/LinkPropertiesTest.java | 1271 ++ .../android/net/MatchAllNetworkSpecifierTest.kt | 75 + .../android/net/NattKeepalivePacketDataTest.kt | 114 + .../java/android/net/NetworkAgentConfigTest.kt | 91 + .../java/android/net/NetworkCapabilitiesTest.java | 1158 ++ .../common/java/android/net/NetworkProviderTest.kt | 201 + .../java/android/net/NetworkSpecifierTest.kt | 67 + .../common/java/android/net/NetworkStackTest.java | 51 + .../java/android/net/NetworkStateSnapshotTest.kt | 73 + .../tests/common/java/android/net/NetworkTest.java | 202 + .../android/net/OemNetworkPreferencesTest.java | 152 + .../common/java/android/net/RouteInfoTest.java | 434 + .../android/net/StaticIpConfigurationTest.java | 269 + .../java/android/net/TcpKeepalivePacketDataTest.kt | 106 + .../common/java/android/net/UidRangeTest.java | 113 + .../java/android/net/UnderlyingNetworkInfoTest.kt | 50 + .../java/android/net/apf/ApfCapabilitiesTest.java | 99 + .../android/net/metrics/ApfProgramEventTest.kt | 72 + .../java/android/net/metrics/ApfStatsTest.kt | 57 + .../android/net/metrics/DhcpClientEventTest.kt | 43 + .../java/android/net/metrics/DhcpErrorEventTest.kt | 65 + .../android/net/metrics/IpConnectivityLogTest.java | 161 + .../java/android/net/metrics/IpManagerEventTest.kt | 39 + .../android/net/metrics/IpReachabilityEventTest.kt | 38 + .../java/android/net/metrics/NetworkEventTest.kt | 43 + .../common/java/android/net/metrics/RaEventTest.kt | 72 + .../net/metrics/ValidationProbeEventTest.kt | 72 + .../android/net/netstats/NetworkStatsApiTest.kt | 209 + .../java/android/net/util/SocketUtilsTest.kt | 90 + packages/Connectivity/tests/deflake/Android.bp | 39 + .../android/server/net/FrameworksNetDeflakeTest.kt | 28 + packages/Connectivity/tests/integration/Android.bp | 78 + .../tests/integration/AndroidManifest.xml | 73 + .../tests/integration/res/values/config.xml | 15 + .../src/android/net/TestNetworkStackClient.kt | 75 + .../ConnectivityServiceIntegrationTest.kt | 257 + .../server/net/integrationtests/HttpResponse.aidl | 19 + .../server/net/integrationtests/HttpResponse.kt | 49 + .../INetworkStackInstrumentation.aidl | 25 + .../NetworkStackInstrumentationService.kt | 84 + .../integrationtests/TestNetworkStackService.kt | 100 + .../android/server/ConnectivityServiceTestUtils.kt | 43 + .../com/android/server/NetworkAgentWrapper.java | 388 + .../util/com/android/server/TestNetIdManager.kt | 39 + packages/Connectivity/tests/smoketest/Android.bp | 31 + .../tests/smoketest/AndroidManifest.xml | 27 + .../Connectivity/tests/smoketest/AndroidTest.xml | 28 + .../tests/smoketest/java/SmokeTest.java | 33 + packages/Connectivity/tests/unit/Android.bp | 89 + .../Connectivity/tests/unit/AndroidManifest.xml | 62 + packages/Connectivity/tests/unit/AndroidTest.xml | 28 + packages/Connectivity/tests/unit/jarjar-rules.txt | 2 + .../android/app/usage/NetworkStatsManagerTest.java | 212 + .../net/ConnectivityDiagnosticsManagerTest.java | 384 + .../java/android/net/ConnectivityManagerTest.java | 430 + .../unit/java/android/net/Ikev2VpnProfileTest.java | 439 + .../unit/java/android/net/IpMemoryStoreTest.java | 332 + .../unit/java/android/net/IpSecAlgorithmTest.java | 226 + .../unit/java/android/net/IpSecConfigTest.java | 103 + .../unit/java/android/net/IpSecManagerTest.java | 305 + .../unit/java/android/net/IpSecTransformTest.java | 62 + .../android/net/KeepalivePacketDataUtilTest.java | 205 + .../unit/java/android/net/MacAddressTest.java | 312 + .../unit/java/android/net/NetworkIdentityTest.kt | 54 + .../java/android/net/NetworkStatsHistoryTest.java | 599 + .../unit/java/android/net/NetworkStatsTest.java | 1024 ++ .../unit/java/android/net/NetworkTemplateTest.kt | 351 + .../unit/java/android/net/NetworkUtilsTest.java | 128 + .../unit/java/android/net/QosSocketFilterTest.java | 75 + .../android/net/TelephonyNetworkSpecifierTest.java | 113 + .../unit/java/android/net/VpnManagerTest.java | 138 + .../java/android/net/VpnTransportInfoTest.java | 68 + .../android/net/ipmemorystore/ParcelableTests.java | 142 + .../unit/java/android/net/nsd/NsdManagerTest.java | 388 + .../java/android/net/nsd/NsdServiceInfoTest.java | 188 + .../unit/java/android/net/util/DnsUtilsTest.java | 216 + .../java/android/net/util/KeepaliveUtilsTest.kt | 145 + .../net/util/MultinetworkPolicyTrackerTest.kt | 148 + .../internal/net/NetworkUtilsInternalTest.java | 89 + .../com/android/internal/net/VpnProfileTest.java | 218 + .../com/android/internal/util/BitUtilsTest.java | 201 + .../com/android/internal/util/RingBufferTest.java | 178 + .../android/server/ConnectivityServiceTest.java | 12792 +++++++++++++++++++ .../server/IpSecServiceParameterizedTest.java | 1004 ++ .../server/IpSecServiceRefcountedResourceTest.java | 358 + .../java/com/android/server/IpSecServiceTest.java | 670 + .../com/android/server/LegacyTypeTrackerTest.kt | 197 + .../java/com/android/server/NetIdManagerTest.kt | 53 + .../server/NetworkManagementServiceTest.java | 315 + .../java/com/android/server/NsdServiceTest.java | 194 + .../server/connectivity/DnsManagerTest.java | 433 + .../android/server/connectivity/FullScoreTest.kt | 135 + .../IpConnectivityEventBuilderTest.java | 561 + .../connectivity/IpConnectivityMetricsTest.java | 645 + .../server/connectivity/LingerMonitorTest.java | 395 + .../server/connectivity/MetricsTestUtil.java | 81 + .../connectivity/MultipathPolicyTrackerTest.java | 381 + .../server/connectivity/Nat464XlatTest.java | 555 + .../connectivity/NetdEventListenerServiceTest.java | 554 + .../NetworkNotificationManagerTest.java | 333 + .../server/connectivity/NetworkOfferTest.kt | 71 + .../server/connectivity/NetworkRankerTest.kt | 90 + .../server/connectivity/PermissionMonitorTest.java | 803 ++ .../com/android/server/connectivity/VpnTest.java | 1283 ++ .../android/server/net/NetworkStatsAccessTest.java | 189 + .../android/server/net/NetworkStatsBaseTest.java | 119 + .../server/net/NetworkStatsCollectionTest.java | 594 + .../server/net/NetworkStatsFactoryTest.java | 578 + .../server/net/NetworkStatsObserversTest.java | 447 + .../server/net/NetworkStatsServiceTest.java | 1767 +++ .../net/NetworkStatsSubscriptionsMonitorTest.java | 386 + .../net/ipmemorystore/NetworkAttributesTest.java | 70 + packages/Connectivity/tests/unit/jni/Android.bp | 32 + .../Connectivity/tests/unit/jni/test_onload.cpp | 44 + .../Connectivity/tests/unit/res/raw/history_v1 | Bin 0 -> 144 bytes .../tests/unit/res/raw/net_dev_typical | 8 + .../tests/unit/res/raw/netstats_uid_v4 | Bin 0 -> 156516 bytes .../Connectivity/tests/unit/res/raw/netstats_v1 | Bin 0 -> 18742 bytes .../unit/res/raw/xt_qtaguid_iface_fmt_typical | 4 + .../tests/unit/res/raw/xt_qtaguid_iface_typical | 6 + .../tests/unit/res/raw/xt_qtaguid_typical | 71 + .../unit/res/raw/xt_qtaguid_vpn_incorrect_iface | 3 + .../unit/res/raw/xt_qtaguid_vpn_one_underlying | 5 + .../raw/xt_qtaguid_vpn_one_underlying_compression | 4 + .../raw/xt_qtaguid_vpn_one_underlying_own_traffic | 6 + .../res/raw/xt_qtaguid_vpn_one_underlying_two_vpn | 9 + .../res/raw/xt_qtaguid_vpn_rewrite_through_self | 6 + .../raw/xt_qtaguid_vpn_two_underlying_duplication | 5 + .../res/raw/xt_qtaguid_vpn_two_underlying_split | 4 + ...xt_qtaguid_vpn_two_underlying_split_compression | 4 + .../tests/unit/res/raw/xt_qtaguid_vpn_with_clat | 8 + .../tests/unit/res/raw/xt_qtaguid_with_clat | 43 + .../raw/xt_qtaguid_with_clat_100mb_download_after | 189 + .../raw/xt_qtaguid_with_clat_100mb_download_before | 187 + .../tests/unit/res/raw/xt_qtaguid_with_clat_simple | 4 + tests/net/Android.bp | 89 - tests/net/AndroidManifest.xml | 62 - tests/net/AndroidTest.xml | 28 - tests/net/OWNERS | 8 - tests/net/TEST_MAPPING | 34 - tests/net/common/Android.bp | 48 - tests/net/common/java/ParseExceptionTest.kt | 52 - .../java/android/net/CaptivePortalDataTest.kt | 190 - .../common/java/android/net/CaptivePortalTest.java | 121 - .../common/java/android/net/DependenciesTest.java | 113 - .../net/common/java/android/net/DhcpInfoTest.java | 112 - .../net/common/java/android/net/IpPrefixTest.java | 374 - .../java/android/net/KeepalivePacketDataTest.kt | 120 - .../common/java/android/net/LinkAddressTest.java | 518 - .../java/android/net/LinkPropertiesTest.java | 1271 -- .../android/net/MatchAllNetworkSpecifierTest.kt | 75 - .../android/net/NattKeepalivePacketDataTest.kt | 114 - .../java/android/net/NetworkAgentConfigTest.kt | 91 - .../java/android/net/NetworkCapabilitiesTest.java | 1158 -- .../common/java/android/net/NetworkProviderTest.kt | 201 - .../java/android/net/NetworkSpecifierTest.kt | 67 - .../common/java/android/net/NetworkStackTest.java | 51 - .../java/android/net/NetworkStateSnapshotTest.kt | 73 - tests/net/common/java/android/net/NetworkTest.java | 202 - .../android/net/OemNetworkPreferencesTest.java | 152 - .../net/common/java/android/net/RouteInfoTest.java | 434 - .../android/net/StaticIpConfigurationTest.java | 269 - .../java/android/net/TcpKeepalivePacketDataTest.kt | 106 - .../net/common/java/android/net/UidRangeTest.java | 113 - .../java/android/net/UnderlyingNetworkInfoTest.kt | 50 - .../java/android/net/apf/ApfCapabilitiesTest.java | 99 - .../android/net/metrics/ApfProgramEventTest.kt | 72 - .../java/android/net/metrics/ApfStatsTest.kt | 57 - .../android/net/metrics/DhcpClientEventTest.kt | 43 - .../java/android/net/metrics/DhcpErrorEventTest.kt | 65 - .../android/net/metrics/IpConnectivityLogTest.java | 161 - .../java/android/net/metrics/IpManagerEventTest.kt | 39 - .../android/net/metrics/IpReachabilityEventTest.kt | 38 - .../java/android/net/metrics/NetworkEventTest.kt | 43 - .../common/java/android/net/metrics/RaEventTest.kt | 72 - .../net/metrics/ValidationProbeEventTest.kt | 72 - .../android/net/netstats/NetworkStatsApiTest.kt | 209 - .../java/android/net/util/SocketUtilsTest.kt | 90 - tests/net/deflake/Android.bp | 39 - .../android/server/net/FrameworksNetDeflakeTest.kt | 28 - tests/net/integration/Android.bp | 78 - tests/net/integration/AndroidManifest.xml | 73 - tests/net/integration/res/values/config.xml | 15 - .../src/android/net/TestNetworkStackClient.kt | 75 - .../ConnectivityServiceIntegrationTest.kt | 257 - .../server/net/integrationtests/HttpResponse.aidl | 19 - .../server/net/integrationtests/HttpResponse.kt | 49 - .../INetworkStackInstrumentation.aidl | 25 - .../NetworkStackInstrumentationService.kt | 84 - .../integrationtests/TestNetworkStackService.kt | 100 - .../android/server/ConnectivityServiceTestUtils.kt | 43 - .../com/android/server/NetworkAgentWrapper.java | 388 - .../util/com/android/server/TestNetIdManager.kt | 39 - tests/net/jarjar-rules.txt | 2 - .../android/app/usage/NetworkStatsManagerTest.java | 212 - .../net/ConnectivityDiagnosticsManagerTest.java | 384 - .../java/android/net/ConnectivityManagerTest.java | 430 - .../net/java/android/net/Ikev2VpnProfileTest.java | 439 - tests/net/java/android/net/IpMemoryStoreTest.java | 332 - tests/net/java/android/net/IpSecAlgorithmTest.java | 226 - tests/net/java/android/net/IpSecConfigTest.java | 103 - tests/net/java/android/net/IpSecManagerTest.java | 305 - tests/net/java/android/net/IpSecTransformTest.java | 62 - .../android/net/KeepalivePacketDataUtilTest.java | 205 - tests/net/java/android/net/MacAddressTest.java | 312 - tests/net/java/android/net/NetworkIdentityTest.kt | 54 - .../java/android/net/NetworkStatsHistoryTest.java | 599 - tests/net/java/android/net/NetworkStatsTest.java | 1024 -- tests/net/java/android/net/NetworkTemplateTest.kt | 351 - tests/net/java/android/net/NetworkUtilsTest.java | 128 - .../net/java/android/net/QosSocketFilterTest.java | 75 - .../android/net/TelephonyNetworkSpecifierTest.java | 113 - tests/net/java/android/net/VpnManagerTest.java | 138 - .../net/java/android/net/VpnTransportInfoTest.java | 68 - .../android/net/ipmemorystore/ParcelableTests.java | 142 - tests/net/java/android/net/nsd/NsdManagerTest.java | 388 - .../java/android/net/nsd/NsdServiceInfoTest.java | 188 - tests/net/java/android/net/util/DnsUtilsTest.java | 216 - .../java/android/net/util/KeepaliveUtilsTest.kt | 145 - .../net/util/MultinetworkPolicyTrackerTest.kt | 148 - .../internal/net/NetworkUtilsInternalTest.java | 89 - .../com/android/internal/net/VpnProfileTest.java | 218 - .../com/android/internal/util/BitUtilsTest.java | 201 - .../com/android/internal/util/RingBufferTest.java | 178 - .../android/server/ConnectivityServiceTest.java | 12792 ------------------- .../server/IpSecServiceParameterizedTest.java | 1004 -- .../server/IpSecServiceRefcountedResourceTest.java | 358 - .../java/com/android/server/IpSecServiceTest.java | 670 - .../com/android/server/LegacyTypeTrackerTest.kt | 197 - .../java/com/android/server/NetIdManagerTest.kt | 53 - .../server/NetworkManagementServiceTest.java | 315 - .../java/com/android/server/NsdServiceTest.java | 194 - .../server/connectivity/DnsManagerTest.java | 433 - .../android/server/connectivity/FullScoreTest.kt | 135 - .../IpConnectivityEventBuilderTest.java | 561 - .../connectivity/IpConnectivityMetricsTest.java | 645 - .../server/connectivity/LingerMonitorTest.java | 395 - .../server/connectivity/MetricsTestUtil.java | 81 - .../connectivity/MultipathPolicyTrackerTest.java | 381 - .../server/connectivity/Nat464XlatTest.java | 555 - .../connectivity/NetdEventListenerServiceTest.java | 554 - .../NetworkNotificationManagerTest.java | 333 - .../server/connectivity/NetworkOfferTest.kt | 71 - .../server/connectivity/NetworkRankerTest.kt | 90 - .../server/connectivity/PermissionMonitorTest.java | 803 -- .../com/android/server/connectivity/VpnTest.java | 1283 -- .../android/server/net/NetworkStatsAccessTest.java | 189 - .../android/server/net/NetworkStatsBaseTest.java | 119 - .../server/net/NetworkStatsCollectionTest.java | 594 - .../server/net/NetworkStatsFactoryTest.java | 578 - .../server/net/NetworkStatsObserversTest.java | 447 - .../server/net/NetworkStatsServiceTest.java | 1767 --- .../net/NetworkStatsSubscriptionsMonitorTest.java | 386 - .../net/ipmemorystore/NetworkAttributesTest.java | 70 - tests/net/jni/Android.bp | 32 - tests/net/jni/test_onload.cpp | 44 - tests/net/res/raw/history_v1 | Bin 144 -> 0 bytes tests/net/res/raw/net_dev_typical | 8 - tests/net/res/raw/netstats_uid_v4 | Bin 156516 -> 0 bytes tests/net/res/raw/netstats_v1 | Bin 18742 -> 0 bytes tests/net/res/raw/xt_qtaguid_iface_fmt_typical | 4 - tests/net/res/raw/xt_qtaguid_iface_typical | 6 - tests/net/res/raw/xt_qtaguid_typical | 71 - tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface | 3 - tests/net/res/raw/xt_qtaguid_vpn_one_underlying | 5 - .../raw/xt_qtaguid_vpn_one_underlying_compression | 4 - .../raw/xt_qtaguid_vpn_one_underlying_own_traffic | 6 - .../res/raw/xt_qtaguid_vpn_one_underlying_two_vpn | 9 - .../res/raw/xt_qtaguid_vpn_rewrite_through_self | 6 - .../raw/xt_qtaguid_vpn_two_underlying_duplication | 5 - .../res/raw/xt_qtaguid_vpn_two_underlying_split | 4 - ...xt_qtaguid_vpn_two_underlying_split_compression | 4 - tests/net/res/raw/xt_qtaguid_vpn_with_clat | 8 - tests/net/res/raw/xt_qtaguid_with_clat | 43 - .../raw/xt_qtaguid_with_clat_100mb_download_after | 189 - .../raw/xt_qtaguid_with_clat_100mb_download_before | 187 - tests/net/res/raw/xt_qtaguid_with_clat_simple | 4 - tests/net/smoketest/Android.bp | 31 - tests/net/smoketest/AndroidManifest.xml | 27 - tests/net/smoketest/AndroidTest.xml | 28 - tests/net/smoketest/java/SmokeTest.java | 33 - 293 files changed, 42958 insertions(+), 42958 deletions(-) create mode 100644 packages/Connectivity/tests/OWNERS create mode 100644 packages/Connectivity/tests/TEST_MAPPING create mode 100644 packages/Connectivity/tests/common/Android.bp create mode 100644 packages/Connectivity/tests/common/java/ParseExceptionTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/DependenciesTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/NetworkTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/UidRangeTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt create mode 100644 packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt create mode 100644 packages/Connectivity/tests/deflake/Android.bp create mode 100644 packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt create mode 100644 packages/Connectivity/tests/integration/Android.bp create mode 100644 packages/Connectivity/tests/integration/AndroidManifest.xml create mode 100644 packages/Connectivity/tests/integration/res/values/config.xml create mode 100644 packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt create mode 100644 packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt create mode 100644 packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt create mode 100644 packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java create mode 100644 packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt create mode 100644 packages/Connectivity/tests/smoketest/Android.bp create mode 100644 packages/Connectivity/tests/smoketest/AndroidManifest.xml create mode 100644 packages/Connectivity/tests/smoketest/AndroidTest.xml create mode 100644 packages/Connectivity/tests/smoketest/java/SmokeTest.java create mode 100644 packages/Connectivity/tests/unit/Android.bp create mode 100644 packages/Connectivity/tests/unit/AndroidManifest.xml create mode 100644 packages/Connectivity/tests/unit/AndroidTest.xml create mode 100644 packages/Connectivity/tests/unit/jarjar-rules.txt create mode 100644 packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt create mode 100644 packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt create mode 100644 packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java create mode 100644 packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt create mode 100644 packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java create mode 100644 packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java create mode 100644 packages/Connectivity/tests/unit/jni/Android.bp create mode 100644 packages/Connectivity/tests/unit/jni/test_onload.cpp create mode 100644 packages/Connectivity/tests/unit/res/raw/history_v1 create mode 100644 packages/Connectivity/tests/unit/res/raw/net_dev_typical create mode 100644 packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 create mode 100644 packages/Connectivity/tests/unit/res/raw/netstats_v1 create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before create mode 100644 packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple delete mode 100644 tests/net/Android.bp delete mode 100644 tests/net/AndroidManifest.xml delete mode 100644 tests/net/AndroidTest.xml delete mode 100644 tests/net/OWNERS delete mode 100644 tests/net/TEST_MAPPING delete mode 100644 tests/net/common/Android.bp delete mode 100644 tests/net/common/java/ParseExceptionTest.kt delete mode 100644 tests/net/common/java/android/net/CaptivePortalDataTest.kt delete mode 100644 tests/net/common/java/android/net/CaptivePortalTest.java delete mode 100644 tests/net/common/java/android/net/DependenciesTest.java delete mode 100644 tests/net/common/java/android/net/DhcpInfoTest.java delete mode 100644 tests/net/common/java/android/net/IpPrefixTest.java delete mode 100644 tests/net/common/java/android/net/KeepalivePacketDataTest.kt delete mode 100644 tests/net/common/java/android/net/LinkAddressTest.java delete mode 100644 tests/net/common/java/android/net/LinkPropertiesTest.java delete mode 100644 tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt delete mode 100644 tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt delete mode 100644 tests/net/common/java/android/net/NetworkAgentConfigTest.kt delete mode 100644 tests/net/common/java/android/net/NetworkCapabilitiesTest.java delete mode 100644 tests/net/common/java/android/net/NetworkProviderTest.kt delete mode 100644 tests/net/common/java/android/net/NetworkSpecifierTest.kt delete mode 100644 tests/net/common/java/android/net/NetworkStackTest.java delete mode 100644 tests/net/common/java/android/net/NetworkStateSnapshotTest.kt delete mode 100644 tests/net/common/java/android/net/NetworkTest.java delete mode 100644 tests/net/common/java/android/net/OemNetworkPreferencesTest.java delete mode 100644 tests/net/common/java/android/net/RouteInfoTest.java delete mode 100644 tests/net/common/java/android/net/StaticIpConfigurationTest.java delete mode 100644 tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt delete mode 100644 tests/net/common/java/android/net/UidRangeTest.java delete mode 100644 tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt delete mode 100644 tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java delete mode 100644 tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/ApfStatsTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java delete mode 100644 tests/net/common/java/android/net/metrics/IpManagerEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/NetworkEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/RaEventTest.kt delete mode 100644 tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt delete mode 100644 tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt delete mode 100644 tests/net/common/java/android/net/util/SocketUtilsTest.kt delete mode 100644 tests/net/deflake/Android.bp delete mode 100644 tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt delete mode 100644 tests/net/integration/Android.bp delete mode 100644 tests/net/integration/AndroidManifest.xml delete mode 100644 tests/net/integration/res/values/config.xml delete mode 100644 tests/net/integration/src/android/net/TestNetworkStackClient.kt delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt delete mode 100644 tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt delete mode 100644 tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt delete mode 100644 tests/net/integration/util/com/android/server/NetworkAgentWrapper.java delete mode 100644 tests/net/integration/util/com/android/server/TestNetIdManager.kt delete mode 100644 tests/net/jarjar-rules.txt delete mode 100644 tests/net/java/android/app/usage/NetworkStatsManagerTest.java delete mode 100644 tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java delete mode 100644 tests/net/java/android/net/ConnectivityManagerTest.java delete mode 100644 tests/net/java/android/net/Ikev2VpnProfileTest.java delete mode 100644 tests/net/java/android/net/IpMemoryStoreTest.java delete mode 100644 tests/net/java/android/net/IpSecAlgorithmTest.java delete mode 100644 tests/net/java/android/net/IpSecConfigTest.java delete mode 100644 tests/net/java/android/net/IpSecManagerTest.java delete mode 100644 tests/net/java/android/net/IpSecTransformTest.java delete mode 100644 tests/net/java/android/net/KeepalivePacketDataUtilTest.java delete mode 100644 tests/net/java/android/net/MacAddressTest.java delete mode 100644 tests/net/java/android/net/NetworkIdentityTest.kt delete mode 100644 tests/net/java/android/net/NetworkStatsHistoryTest.java delete mode 100644 tests/net/java/android/net/NetworkStatsTest.java delete mode 100644 tests/net/java/android/net/NetworkTemplateTest.kt delete mode 100644 tests/net/java/android/net/NetworkUtilsTest.java delete mode 100644 tests/net/java/android/net/QosSocketFilterTest.java delete mode 100644 tests/net/java/android/net/TelephonyNetworkSpecifierTest.java delete mode 100644 tests/net/java/android/net/VpnManagerTest.java delete mode 100644 tests/net/java/android/net/VpnTransportInfoTest.java delete mode 100644 tests/net/java/android/net/ipmemorystore/ParcelableTests.java delete mode 100644 tests/net/java/android/net/nsd/NsdManagerTest.java delete mode 100644 tests/net/java/android/net/nsd/NsdServiceInfoTest.java delete mode 100644 tests/net/java/android/net/util/DnsUtilsTest.java delete mode 100644 tests/net/java/android/net/util/KeepaliveUtilsTest.kt delete mode 100644 tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt delete mode 100644 tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java delete mode 100644 tests/net/java/com/android/internal/net/VpnProfileTest.java delete mode 100644 tests/net/java/com/android/internal/util/BitUtilsTest.java delete mode 100644 tests/net/java/com/android/internal/util/RingBufferTest.java delete mode 100644 tests/net/java/com/android/server/ConnectivityServiceTest.java delete mode 100644 tests/net/java/com/android/server/IpSecServiceParameterizedTest.java delete mode 100644 tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java delete mode 100644 tests/net/java/com/android/server/IpSecServiceTest.java delete mode 100644 tests/net/java/com/android/server/LegacyTypeTrackerTest.kt delete mode 100644 tests/net/java/com/android/server/NetIdManagerTest.kt delete mode 100644 tests/net/java/com/android/server/NetworkManagementServiceTest.java delete mode 100644 tests/net/java/com/android/server/NsdServiceTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/DnsManagerTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/FullScoreTest.kt delete mode 100644 tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/LingerMonitorTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/MetricsTestUtil.java delete mode 100644 tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/Nat464XlatTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt delete mode 100644 tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt delete mode 100644 tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java delete mode 100644 tests/net/java/com/android/server/connectivity/VpnTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsAccessTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsBaseTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsObserversTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsServiceTest.java delete mode 100644 tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java delete mode 100644 tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java delete mode 100644 tests/net/jni/Android.bp delete mode 100644 tests/net/jni/test_onload.cpp delete mode 100644 tests/net/res/raw/history_v1 delete mode 100644 tests/net/res/raw/net_dev_typical delete mode 100644 tests/net/res/raw/netstats_uid_v4 delete mode 100644 tests/net/res/raw/netstats_v1 delete mode 100644 tests/net/res/raw/xt_qtaguid_iface_fmt_typical delete mode 100644 tests/net/res/raw/xt_qtaguid_iface_typical delete mode 100644 tests/net/res/raw/xt_qtaguid_typical delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_one_underlying delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression delete mode 100644 tests/net/res/raw/xt_qtaguid_vpn_with_clat delete mode 100644 tests/net/res/raw/xt_qtaguid_with_clat delete mode 100644 tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after delete mode 100644 tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before delete mode 100644 tests/net/res/raw/xt_qtaguid_with_clat_simple delete mode 100644 tests/net/smoketest/Android.bp delete mode 100644 tests/net/smoketest/AndroidManifest.xml delete mode 100644 tests/net/smoketest/AndroidTest.xml delete mode 100644 tests/net/smoketest/java/SmokeTest.java diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp index bb93af90a9be..a75792c940c3 100644 --- a/packages/Connectivity/framework/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -102,7 +102,7 @@ java_sdk_library { // Tests using hidden APIs "//cts/tests/netlegacy22.api", "//external/sl4a:__subpackages__", - "//frameworks/base/tests/net:__subpackages__", + "//frameworks/base/packages/Connectivity/tests:__subpackages__", "//frameworks/libs/net/common/testutils", "//frameworks/libs/net/common/tests:__subpackages__", "//frameworks/opt/telephony/tests/telephonytests", diff --git a/packages/Connectivity/tests/OWNERS b/packages/Connectivity/tests/OWNERS new file mode 100644 index 000000000000..d3836d4c6c57 --- /dev/null +++ b/packages/Connectivity/tests/OWNERS @@ -0,0 +1,8 @@ +set noparent + +codewiz@google.com +jchalard@google.com +junyulai@google.com +lorenzo@google.com +reminv@google.com +satk@google.com diff --git a/packages/Connectivity/tests/TEST_MAPPING b/packages/Connectivity/tests/TEST_MAPPING new file mode 100644 index 000000000000..502f885ceb78 --- /dev/null +++ b/packages/Connectivity/tests/TEST_MAPPING @@ -0,0 +1,34 @@ +{ + "presubmit": [ + { + "name": "FrameworksNetIntegrationTests" + } + ], + "postsubmit": [ + { + "name": "FrameworksNetDeflakeTest" + } + ], + "auto-postsubmit": [ + // Test tag for automotive targets. These are only running in postsubmit so as to harden the + // automotive targets to avoid introducing additional test flake and build time. The plan for + // presubmit testing for auto is to augment the existing tests to cover auto use cases as well. + // Additionally, this tag is used in targeted test suites to limit resource usage on the test + // infra during the hardening phase. + // TODO: this tag to be removed once the above is no longer an issue. + { + "name": "FrameworksNetTests" + }, + { + "name": "FrameworksNetIntegrationTests" + }, + { + "name": "FrameworksNetDeflakeTest" + } + ], + "imports": [ + { + "path": "packages/modules/Connectivity" + } + ] +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/Android.bp b/packages/Connectivity/tests/common/Android.bp new file mode 100644 index 000000000000..439665b67ab7 --- /dev/null +++ b/packages/Connectivity/tests/common/Android.bp @@ -0,0 +1,48 @@ +// +// Copyright (C) 2019 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. +// + +// Tests in this folder are included both in unit tests and CTS. +// They must be fast and stable, and exercise public or test APIs. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +java_library { + name: "FrameworksNetCommonTests", + defaults: ["framework-connectivity-test-defaults"], + srcs: [ + "java/**/*.java", + "java/**/*.kt", + ], + static_libs: [ + "androidx.core_core", + "androidx.test.rules", + "junit", + "mockito-target-minus-junit4", + "modules-utils-build", + "net-tests-utils", + "net-utils-framework-common", + "platform-test-annotations", + ], + libs: [ + "android.test.base.stubs", + ], +} diff --git a/packages/Connectivity/tests/common/java/ParseExceptionTest.kt b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt new file mode 100644 index 000000000000..b702d61a9fe1 --- /dev/null +++ b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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. + */ + +import android.net.ParseException +import android.os.Build +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertNull +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ParseExceptionTest { + @get:Rule + val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.R) + + @Test + fun testConstructor_WithCause() { + val testMessage = "Test message" + val base = Exception("Test") + val exception = ParseException(testMessage, base) + + assertEquals(testMessage, exception.response) + assertEquals(base, exception.cause) + } + + @Test + fun testConstructor_NoCause() { + val testMessage = "Test message" + val exception = ParseException(testMessage) + + assertEquals(testMessage, exception.response) + assertNull(exception.cause) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt new file mode 100644 index 000000000000..18a93319b271 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 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 + +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.modules.utils.build.SdkLevel +import com.android.testutils.assertParcelSane +import com.android.testutils.assertParcelingIsLossless +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@IgnoreUpTo(Build.VERSION_CODES.Q) +class CaptivePortalDataTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + + private val data = CaptivePortalData.Builder() + .setRefreshTime(123L) + .setUserPortalUrl(Uri.parse("https://portal.example.com/test")) + .setVenueInfoUrl(Uri.parse("https://venue.example.com/test")) + .setSessionExtendable(true) + .setBytesRemaining(456L) + .setExpiryTime(789L) + .setCaptive(true) + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + } + } + .build() + + private val dataFromPasspoint = CaptivePortalData.Builder() + .setCaptive(true) + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + } + } + .build() + + private fun makeBuilder() = CaptivePortalData.Builder(data) + + @Test + fun testParcelUnparcel() { + val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7 + assertParcelSane(data, fieldCount) + assertParcelSane(dataFromPasspoint, fieldCount) + + assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) + assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) + } + + @Test + fun testEquals() { + assertEquals(data, makeBuilder().build()) + + assertNotEqualsAfterChange { it.setRefreshTime(456L) } + assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) } + assertNotEqualsAfterChange { it.setUserPortalUrl(null) } + assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl(null) } + assertNotEqualsAfterChange { it.setSessionExtendable(false) } + assertNotEqualsAfterChange { it.setBytesRemaining(789L) } + assertNotEqualsAfterChange { it.setExpiryTime(12L) } + assertNotEqualsAfterChange { it.setCaptive(false) } + + if (SdkLevel.isAtLeastS()) { + assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } + assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } + + assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + } + } + + @Test + fun testUserPortalUrl() { + assertEquals(Uri.parse("https://portal.example.com/test"), data.userPortalUrl) + } + + @Test + fun testVenueInfoUrl() { + assertEquals(Uri.parse("https://venue.example.com/test"), data.venueInfoUrl) + } + + @Test + fun testIsSessionExtendable() { + assertTrue(data.isSessionExtendable) + } + + @Test + fun testByteLimit() { + assertEquals(456L, data.byteLimit) + // Test byteLimit unset. + assertEquals(-1L, CaptivePortalData.Builder(null).build().byteLimit) + } + + @Test + fun testRefreshTimeMillis() { + assertEquals(123L, data.refreshTimeMillis) + } + + @Test + fun testExpiryTimeMillis() { + assertEquals(789L, data.expiryTimeMillis) + // Test expiryTimeMillis unset. + assertEquals(-1L, CaptivePortalData.Builder(null).build().expiryTimeMillis) + } + + @Test + fun testIsCaptive() { + assertTrue(data.isCaptive) + assertFalse(makeBuilder().setCaptive(false).build().isCaptive) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testVenueFriendlyName() { + assertEquals("venue friendly name", data.venueFriendlyName) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetVenueInfoUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.venueInfoUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.venueInfoUrlSource) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetUserPortalUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.userPortalUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.userPortalUrlSource) + } + + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = + CaptivePortalData.Builder(this).apply { mutator(this) }.build() + + private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) { + assertNotEquals(data, data.mutate(mutator)) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java new file mode 100644 index 000000000000..15d3398d43c0 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 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; + +import static org.junit.Assert.assertEquals; + +import android.os.Build; +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CaptivePortalTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final int DEFAULT_TIMEOUT_MS = 5000; + private static final String TEST_PACKAGE_NAME = "com.google.android.test"; + + private final class MyCaptivePortalImpl extends ICaptivePortal.Stub { + int mCode = -1; + String mPackageName = null; + + @Override + public void appResponse(final int response) throws RemoteException { + mCode = response; + } + + @Override + public void appRequest(final int request) throws RemoteException { + mCode = request; + } + + // This is only @Override on R- + public void logEvent(int eventId, String packageName) throws RemoteException { + mCode = eventId; + mPackageName = packageName; + } + } + + private interface TestFunctor { + void useCaptivePortal(CaptivePortal o); + } + + private MyCaptivePortalImpl runCaptivePortalTest(TestFunctor f) { + final MyCaptivePortalImpl cp = new MyCaptivePortalImpl(); + f.useCaptivePortal(new CaptivePortal(cp.asBinder())); + return cp; + } + + @Test + public void testReportCaptivePortalDismissed() { + final MyCaptivePortalImpl result = + runCaptivePortalTest(c -> c.reportCaptivePortalDismissed()); + assertEquals(result.mCode, CaptivePortal.APP_RETURN_DISMISSED); + } + + @Test + public void testIgnoreNetwork() { + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.ignoreNetwork()); + assertEquals(result.mCode, CaptivePortal.APP_RETURN_UNWANTED); + } + + @Test + public void testUseNetwork() { + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.useNetwork()); + assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS); + } + + @IgnoreUpTo(Build.VERSION_CODES.Q) + @Test + public void testReevaluateNetwork() { + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork()); + assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED); + } + + @IgnoreUpTo(Build.VERSION_CODES.R) + @Test + public void testLogEvent() { + /** + * From S testLogEvent is expected to do nothing but shouldn't crash (the API + * logEvent has been deprecated). + */ + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent( + 0, + TEST_PACKAGE_NAME)); + } + + @IgnoreAfter(Build.VERSION_CODES.R) + @Test + public void testLogEvent_UntilR() { + final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent( + 42, TEST_PACKAGE_NAME)); + assertEquals(result.mCode, 42); + assertEquals(result.mPackageName, TEST_PACKAGE_NAME); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java new file mode 100644 index 000000000000..ac1c28a45462 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java @@ -0,0 +1,113 @@ +/* + * 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; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +/** + * A simple class that tests dependencies to java standard tools from the + * Network stack. These tests are not meant to be comprehensive tests of + * the relevant APIs : such tests belong in the relevant test suite for + * these dependencies. Instead, this just makes sure coverage is present + * by calling the methods in the exact way (or a representative way of how) + * they are called in the network stack. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DependenciesTest { + // Used to in ipmemorystore's RegularMaintenanceJobService to convert + // 24 hours into seconds + @Test + public void testTimeUnit() { + final int hours = 24; + final long inSeconds = TimeUnit.HOURS.toMillis(hours); + assertEquals(inSeconds, hours * 60 * 60 * 1000); + } + + private byte[] makeTrivialArray(final int size) { + final byte[] src = new byte[size]; + for (int i = 0; i < size; ++i) { + src[i] = (byte) i; + } + return src; + } + + // Used in ApfFilter to find an IP address from a byte array + @Test + public void testArrays() { + final int size = 128; + final byte[] src = makeTrivialArray(size); + + // Test copy + final int copySize = 16; + final int offset = 24; + final byte[] expected = new byte[copySize]; + for (int i = 0; i < copySize; ++i) { + expected[i] = (byte) (offset + i); + } + + final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize); + assertArrayEquals(expected, copy); + assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size)); + } + + // Used mainly in the Dhcp code + @Test + public void testCopyOf() { + final byte[] src = makeTrivialArray(128); + final byte[] copy = Arrays.copyOf(src, src.length); + assertArrayEquals(src, copy); + assertFalse(src == copy); + + assertArrayEquals(new byte[0], Arrays.copyOf(src, 0)); + + final int excess = 16; + final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess); + for (int i = src.length; i < src.length + excess; ++i) { + assertEquals(0, biggerCopy[i]); + } + for (int i = src.length - 1; i >= 0; --i) { + assertEquals(src[i], biggerCopy[i]); + } + } + + // Used mainly in DnsUtils but also various other places + @Test + public void testAsList() { + final int size = 24; + final Object[] src = new Object[size]; + final ArrayList expected = new ArrayList<>(size); + for (int i = 0; i < size; ++i) { + final Object o = new Object(); + src[i] = o; + expected.add(o); + } + assertEquals(expected, Arrays.asList(src)); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java new file mode 100644 index 000000000000..ab4726bab573 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009 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; + +import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.annotation.Nullable; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.net.InetAddress; + +@RunWith(AndroidJUnit4.class) +public class DhcpInfoTest { + private static final String STR_ADDR1 = "255.255.255.255"; + private static final String STR_ADDR2 = "127.0.0.1"; + private static final String STR_ADDR3 = "192.168.1.1"; + private static final String STR_ADDR4 = "192.168.1.0"; + private static final int LEASE_TIME = 9999; + + private int ipToInteger(String ipString) throws Exception { + return inet4AddressToIntHTL((Inet4Address) InetAddress.getByName(ipString)); + } + + private DhcpInfo createDhcpInfoObject() throws Exception { + final DhcpInfo dhcpInfo = new DhcpInfo(); + dhcpInfo.ipAddress = ipToInteger(STR_ADDR1); + dhcpInfo.gateway = ipToInteger(STR_ADDR2); + dhcpInfo.netmask = ipToInteger(STR_ADDR3); + dhcpInfo.dns1 = ipToInteger(STR_ADDR4); + dhcpInfo.dns2 = ipToInteger(STR_ADDR4); + dhcpInfo.serverAddress = ipToInteger(STR_ADDR2); + dhcpInfo.leaseDuration = LEASE_TIME; + return dhcpInfo; + } + + @Test + public void testConstructor() { + new DhcpInfo(); + } + + @Test + public void testToString() throws Exception { + final String expectedDefault = "ipaddr 0.0.0.0 gateway 0.0.0.0 netmask 0.0.0.0 " + + "dns1 0.0.0.0 dns2 0.0.0.0 DHCP server 0.0.0.0 lease 0 seconds"; + + DhcpInfo dhcpInfo = new DhcpInfo(); + + // Test default string. + assertEquals(expectedDefault, dhcpInfo.toString()); + + dhcpInfo = createDhcpInfoObject(); + + final String expected = "ipaddr " + STR_ADDR1 + " gateway " + STR_ADDR2 + " netmask " + + STR_ADDR3 + " dns1 " + STR_ADDR4 + " dns2 " + STR_ADDR4 + " DHCP server " + + STR_ADDR2 + " lease " + LEASE_TIME + " seconds"; + // Test with new values + assertEquals(expected, dhcpInfo.toString()); + } + + private boolean dhcpInfoEquals(@Nullable DhcpInfo left, @Nullable DhcpInfo right) { + if (left == null && right == null) return true; + + if (left == null || right == null) return false; + + return left.ipAddress == right.ipAddress + && left.gateway == right.gateway + && left.netmask == right.netmask + && left.dns1 == right.dns1 + && left.dns2 == right.dns2 + && left.serverAddress == right.serverAddress + && left.leaseDuration == right.leaseDuration; + } + + @Test + public void testParcelDhcpInfo() throws Exception { + // Cannot use assertParcelSane() here because this requires .equals() to work as + // defined, but DhcpInfo has a different legacy behavior that we cannot change. + final DhcpInfo dhcpInfo = createDhcpInfoObject(); + assertFieldCountEquals(7, DhcpInfo.class); + + final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo); + assertTrue(dhcpInfoEquals(null, null)); + assertFalse(dhcpInfoEquals(null, dhcpInfoRoundTrip)); + assertFalse(dhcpInfoEquals(dhcpInfo, null)); + assertTrue(dhcpInfoEquals(dhcpInfo, dhcpInfoRoundTrip)); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java new file mode 100644 index 000000000000..50ecb428359e --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2014 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; + +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertArrayEquals; +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 androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.util.Random; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpPrefixTest { + + private static InetAddress address(String addr) { + return InetAddress.parseNumericAddress(addr); + } + + // Explicitly cast everything to byte because "error: possible loss of precision". + private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; + private static final byte[] IPV6_BYTES = { + (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, + (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0 + }; + + @Test + public void testConstructor() { + IpPrefix p; + try { + p = new IpPrefix((byte[]) null, 9); + fail("Expected NullPointerException: null byte array"); + } catch (RuntimeException expected) { } + + try { + p = new IpPrefix((InetAddress) null, 10); + fail("Expected NullPointerException: null InetAddress"); + } catch (RuntimeException expected) { } + + try { + p = new IpPrefix((String) null); + fail("Expected NullPointerException: null String"); + } catch (RuntimeException expected) { } + + + try { + byte[] b2 = {1, 2, 3, 4, 5}; + p = new IpPrefix(b2, 29); + fail("Expected IllegalArgumentException: invalid array length"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("1.2.3.4"); + fail("Expected IllegalArgumentException: no prefix length"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("1.2.3.4/"); + fail("Expected IllegalArgumentException: empty prefix length"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("foo/32"); + fail("Expected IllegalArgumentException: invalid address"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("1/32"); + fail("Expected IllegalArgumentException: deprecated IPv4 format"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("1.2.3.256/32"); + fail("Expected IllegalArgumentException: invalid IPv4 address"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("foo/32"); + fail("Expected IllegalArgumentException: non-address"); + } catch (IllegalArgumentException expected) { } + + try { + p = new IpPrefix("f00:::/32"); + fail("Expected IllegalArgumentException: invalid IPv6 address"); + } catch (IllegalArgumentException expected) { } + + p = new IpPrefix("/64"); + assertEquals("::/64", p.toString()); + + p = new IpPrefix("/128"); + assertEquals("::1/128", p.toString()); + + p = new IpPrefix("[2001:db8::123]/64"); + assertEquals("2001:db8::/64", p.toString()); + } + + @Test + public void testTruncation() { + IpPrefix p; + + p = new IpPrefix(IPV4_BYTES, 32); + assertEquals("192.0.2.4/32", p.toString()); + + p = new IpPrefix(IPV4_BYTES, 29); + assertEquals("192.0.2.0/29", p.toString()); + + p = new IpPrefix(IPV4_BYTES, 8); + assertEquals("192.0.0.0/8", p.toString()); + + p = new IpPrefix(IPV4_BYTES, 0); + assertEquals("0.0.0.0/0", p.toString()); + + try { + p = new IpPrefix(IPV4_BYTES, 33); + fail("Expected IllegalArgumentException: invalid prefix length"); + } catch (RuntimeException expected) { } + + try { + p = new IpPrefix(IPV4_BYTES, 128); + fail("Expected IllegalArgumentException: invalid prefix length"); + } catch (RuntimeException expected) { } + + try { + p = new IpPrefix(IPV4_BYTES, -1); + fail("Expected IllegalArgumentException: negative prefix length"); + } catch (RuntimeException expected) { } + + p = new IpPrefix(IPV6_BYTES, 128); + assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString()); + + p = new IpPrefix(IPV6_BYTES, 122); + assertEquals("2001:db8:dead:beef:f00::80/122", p.toString()); + + p = new IpPrefix(IPV6_BYTES, 64); + assertEquals("2001:db8:dead:beef::/64", p.toString()); + + p = new IpPrefix(IPV6_BYTES, 3); + assertEquals("2000::/3", p.toString()); + + p = new IpPrefix(IPV6_BYTES, 0); + assertEquals("::/0", p.toString()); + + try { + p = new IpPrefix(IPV6_BYTES, -1); + fail("Expected IllegalArgumentException: negative prefix length"); + } catch (RuntimeException expected) { } + + try { + p = new IpPrefix(IPV6_BYTES, 129); + fail("Expected IllegalArgumentException: negative prefix length"); + } catch (RuntimeException expected) { } + + } + + @Test + public void testEquals() { + IpPrefix p1, p2; + + p1 = new IpPrefix("192.0.2.251/23"); + p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); + assertEqualBothWays(p1, p2); + + p1 = new IpPrefix("192.0.2.5/23"); + assertEqualBothWays(p1, p2); + + p1 = new IpPrefix("192.0.2.5/24"); + assertNotEqualEitherWay(p1, p2); + + p1 = new IpPrefix("192.0.4.5/23"); + assertNotEqualEitherWay(p1, p2); + + + p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); + p2 = new IpPrefix(IPV6_BYTES, 122); + assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); + assertEqualBothWays(p1, p2); + + p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); + assertEqualBothWays(p1, p2); + + p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); + assertNotEqualEitherWay(p1, p2); + + p1 = new IpPrefix("2001:db8:dead:beef::/122"); + assertNotEqualEitherWay(p1, p2); + + // 192.0.2.4/32 != c000:0204::/32. + byte[] ipv6bytes = new byte[16]; + System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); + p1 = new IpPrefix(ipv6bytes, 32); + assertEqualBothWays(p1, new IpPrefix("c000:0204::/32")); + + p2 = new IpPrefix(IPV4_BYTES, 32); + assertNotEqualEitherWay(p1, p2); + } + + @Test + public void testContainsInetAddress() { + IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127"); + assertTrue(p.contains(address("2001:db8:f00::ace:d00c"))); + assertTrue(p.contains(address("2001:db8:f00::ace:d00d"))); + assertFalse(p.contains(address("2001:db8:f00::ace:d00e"))); + assertFalse(p.contains(address("2001:db8:f00::bad:d00d"))); + assertFalse(p.contains(address("2001:4868:4860::8888"))); + assertFalse(p.contains(address("8.8.8.8"))); + + p = new IpPrefix("192.0.2.0/23"); + assertTrue(p.contains(address("192.0.2.43"))); + assertTrue(p.contains(address("192.0.3.21"))); + assertFalse(p.contains(address("192.0.0.21"))); + assertFalse(p.contains(address("8.8.8.8"))); + assertFalse(p.contains(address("2001:4868:4860::8888"))); + + IpPrefix ipv6Default = new IpPrefix("::/0"); + assertTrue(ipv6Default.contains(address("2001:db8::f00"))); + assertFalse(ipv6Default.contains(address("192.0.2.1"))); + + IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0"); + assertTrue(ipv4Default.contains(address("255.255.255.255"))); + assertTrue(ipv4Default.contains(address("192.0.2.1"))); + assertFalse(ipv4Default.contains(address("2001:db8::f00"))); + } + + @Test + public void testContainsIpPrefix() { + assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0"))); + assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0"))); + assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8"))); + assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24"))); + assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23"))); + + assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8"))); + assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8"))); + assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21"))); + assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32"))); + + assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24"))); + + assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32"))); + assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8"))); + assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15"))); + assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8"))); + + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113"))); + assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128"))); + + assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( + new IpPrefix("2001:db8:f00::ace:d00d/64"))); + assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( + new IpPrefix("2001:db8:f00::ace:d00d/120"))); + assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( + new IpPrefix("2001:db8:f00::ace:d00d/32"))); + assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( + new IpPrefix("2006:db8:f00::ace:d00d/96"))); + + assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( + new IpPrefix("2001:db8:f00::ace:d00d/128"))); + assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix( + new IpPrefix("2001:db8:f00::ace:ccaf/110"))); + + assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( + new IpPrefix("2001:db8:f00::ace:d00e/128"))); + assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29"))); + } + + @Test + public void testHashCode() { + IpPrefix p = new IpPrefix(new byte[4], 0); + Random random = new Random(); + for (int i = 0; i < 100; i++) { + final IpPrefix oldP = p; + if (random.nextBoolean()) { + // IPv4. + byte[] b = new byte[4]; + random.nextBytes(b); + p = new IpPrefix(b, random.nextInt(33)); + } else { + // IPv6. + byte[] b = new byte[16]; + random.nextBytes(b); + p = new IpPrefix(b, random.nextInt(129)); + } + if (p.equals(oldP)) { + assertEquals(p.hashCode(), oldP.hashCode()); + } + if (p.hashCode() != oldP.hashCode()) { + assertNotEquals(p, oldP); + } + } + } + + @Test + public void testHashCodeIsNotConstant() { + IpPrefix[] prefixes = { + new IpPrefix("2001:db8:f00::ace:d00d/127"), + new IpPrefix("192.0.2.0/23"), + new IpPrefix("::/0"), + new IpPrefix("0.0.0.0/0"), + }; + for (int i = 0; i < prefixes.length; i++) { + for (int j = i + 1; j < prefixes.length; j++) { + assertNotEquals(prefixes[i].hashCode(), prefixes[j].hashCode()); + } + } + } + + @Test + public void testMappedAddressesAreBroken() { + // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress, + // we are unable to comprehend that. + byte[] ipv6bytes = { + (byte) 0, (byte) 0, (byte) 0, (byte) 0, + (byte) 0, (byte) 0, (byte) 0, (byte) 0, + (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff, + (byte) 192, (byte) 0, (byte) 2, (byte) 0}; + IpPrefix p = new IpPrefix(ipv6bytes, 120); + assertEquals(16, p.getRawAddress().length); // Fine. + assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine. + + // Broken. + assertEquals("192.0.2.0/120", p.toString()); + assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); + } + + @Test + public void testParceling() { + IpPrefix p; + + p = new IpPrefix("2001:4860:db8::/64"); + assertParcelingIsLossless(p); + assertTrue(p.isIPv6()); + + p = new IpPrefix("192.0.2.0/25"); + assertParcelingIsLossless(p); + assertTrue(p.isIPv4()); + + assertFieldCountEquals(2, IpPrefix.class); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt new file mode 100644 index 000000000000..f464ec6cf0e5 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt @@ -0,0 +1,120 @@ +/* + * 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 + +import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS +import android.net.InvalidPacketException.ERROR_INVALID_PORT +import android.os.Build +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import java.net.InetAddress +import java.util.Arrays +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class KeepalivePacketDataTest { + @Rule @JvmField + val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() + + private val INVALID_PORT = 65537 + private val TEST_DST_PORT = 4244 + private val TEST_SRC_PORT = 4243 + + private val TESTBYTES = byteArrayOf(12, 31, 22, 44) + private val TEST_SRC_ADDRV4 = "198.168.0.2".address() + private val TEST_DST_ADDRV4 = "198.168.0.1".address() + private val TEST_ADDRV6 = "2001:db8::1".address() + + private fun String.address() = InetAddresses.parseNumericAddress(this) + + // Add for test because constructor of KeepalivePacketData is protected. + private inner class TestKeepalivePacketData( + srcAddress: InetAddress? = TEST_SRC_ADDRV4, + srcPort: Int = TEST_SRC_PORT, + dstAddress: InetAddress? = TEST_DST_ADDRV4, + dstPort: Int = TEST_DST_PORT, + data: ByteArray = TESTBYTES + ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data) + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testConstructor() { + var data: TestKeepalivePacketData + + try { + data = TestKeepalivePacketData(srcAddress = null) + fail("Null src address should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) + } + + try { + data = TestKeepalivePacketData(dstAddress = null) + fail("Null dst address should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) + } + + try { + data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6) + fail("Ip family mismatched should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) + } + + try { + data = TestKeepalivePacketData(srcPort = INVALID_PORT) + fail("Invalid srcPort should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_PORT) + } + + try { + data = TestKeepalivePacketData(dstPort = INVALID_PORT) + fail("Invalid dstPort should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_PORT) + } + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress) + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress) + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort) + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort) + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet)) +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java new file mode 100644 index 000000000000..2cf3cf9c11da --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2013 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; + +import static android.system.OsConstants.IFA_F_DADFAILED; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_OPTIMISTIC; +import static android.system.OsConstants.IFA_F_PERMANENT; +import static android.system.OsConstants.IFA_F_TEMPORARY; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; + +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +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 android.os.Build; +import android.os.SystemClock; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LinkAddressTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final String V4 = "192.0.2.1"; + private static final String V6 = "2001:db8::1"; + private static final InetAddress V4_ADDRESS = InetAddresses.parseNumericAddress(V4); + private static final InetAddress V6_ADDRESS = InetAddresses.parseNumericAddress(V6); + + @Test + public void testConstants() { + // RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero. + assertNotEquals(0, RT_SCOPE_HOST); + assertNotEquals(0, RT_SCOPE_LINK); + assertNotEquals(0, RT_SCOPE_SITE); + + assertNotEquals(0, IFA_F_DEPRECATED); + assertNotEquals(0, IFA_F_PERMANENT); + assertNotEquals(0, IFA_F_TENTATIVE); + } + + @Test + public void testConstructors() throws SocketException { + LinkAddress address; + + // Valid addresses work as expected. + address = new LinkAddress(V4_ADDRESS, 25); + assertEquals(V4_ADDRESS, address.getAddress()); + assertEquals(25, address.getPrefixLength()); + assertEquals(0, address.getFlags()); + assertEquals(RT_SCOPE_UNIVERSE, address.getScope()); + assertTrue(address.isIpv4()); + + address = new LinkAddress(V6_ADDRESS, 127); + assertEquals(V6_ADDRESS, address.getAddress()); + assertEquals(127, address.getPrefixLength()); + assertEquals(0, address.getFlags()); + assertEquals(RT_SCOPE_UNIVERSE, address.getScope()); + assertTrue(address.isIpv6()); + + // Nonsensical flags/scopes or combinations thereof are acceptable. + address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK); + assertEquals(V6_ADDRESS, address.getAddress()); + assertEquals(64, address.getPrefixLength()); + assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags()); + assertEquals(RT_SCOPE_LINK, address.getScope()); + assertTrue(address.isIpv6()); + + address = new LinkAddress(V4 + "/23", 123, 456); + assertEquals(V4_ADDRESS, address.getAddress()); + assertEquals(23, address.getPrefixLength()); + assertEquals(123, address.getFlags()); + assertEquals(456, address.getScope()); + assertTrue(address.isIpv4()); + + address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */); + assertEquals(Inet6Address.LOOPBACK, address.getAddress()); + assertEquals(64, address.getPrefixLength()); + assertEquals(1, address.getFlags()); + assertEquals(2, address.getScope()); + assertTrue(address.isIpv6()); + + address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */); + assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress()); + assertEquals(64, address.getPrefixLength()); + assertEquals(3, address.getFlags()); + assertEquals(4, address.getScope()); + assertTrue(address.isIpv6()); + + // InterfaceAddress doesn't have a constructor. Fetch some from an interface. + List addrs = NetworkInterface.getByName("lo").getInterfaceAddresses(); + + // We expect to find 127.0.0.1/8 and ::1/128, in any order. + LinkAddress ipv4Loopback, ipv6Loopback; + assertEquals(2, addrs.size()); + if (addrs.get(0).getAddress() instanceof Inet4Address) { + ipv4Loopback = new LinkAddress(addrs.get(0)); + ipv6Loopback = new LinkAddress(addrs.get(1)); + } else { + ipv4Loopback = new LinkAddress(addrs.get(1)); + ipv6Loopback = new LinkAddress(addrs.get(0)); + } + + assertEquals(InetAddresses.parseNumericAddress("127.0.0.1"), ipv4Loopback.getAddress()); + assertEquals(8, ipv4Loopback.getPrefixLength()); + + assertEquals(InetAddresses.parseNumericAddress("::1"), ipv6Loopback.getAddress()); + assertEquals(128, ipv6Loopback.getPrefixLength()); + + // Null addresses are rejected. + try { + address = new LinkAddress(null, 24); + fail("Null InetAddress should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress((String) null, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + fail("Null string should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress((InterfaceAddress) null); + fail("Null string should cause NullPointerException"); + } catch(NullPointerException expected) {} + + // Invalid prefix lengths are rejected. + try { + address = new LinkAddress(V4_ADDRESS, -1); + fail("Negative IPv4 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress(V6_ADDRESS, -1); + fail("Negative IPv6 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress(V4_ADDRESS, 33); + fail("/33 IPv4 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress(V4 + "/33", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + fail("/33 IPv4 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + + try { + address = new LinkAddress(V6_ADDRESS, 129, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + fail("/129 IPv6 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress(V6 + "/129", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + fail("/129 IPv6 prefix length should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + // Multicast addresses are rejected. + try { + address = new LinkAddress("224.0.0.2/32"); + fail("IPv4 multicast address should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + + try { + address = new LinkAddress("ff02::1/128"); + fail("IPv6 multicast address should cause IllegalArgumentException"); + } catch(IllegalArgumentException expected) {} + } + + @Test + public void testAddressScopes() { + assertEquals(RT_SCOPE_HOST, new LinkAddress("::/128").getScope()); + assertEquals(RT_SCOPE_HOST, new LinkAddress("0.0.0.0/32").getScope()); + + assertEquals(RT_SCOPE_LINK, new LinkAddress("::1/128").getScope()); + assertEquals(RT_SCOPE_LINK, new LinkAddress("127.0.0.5/8").getScope()); + assertEquals(RT_SCOPE_LINK, new LinkAddress("fe80::ace:d00d/64").getScope()); + assertEquals(RT_SCOPE_LINK, new LinkAddress("169.254.5.12/16").getScope()); + + assertEquals(RT_SCOPE_SITE, new LinkAddress("fec0::dead/64").getScope()); + + assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("10.1.2.3/21").getScope()); + assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("192.0.2.1/25").getScope()); + assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("2001:db8::/64").getScope()); + assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("5000::/127").getScope()); + } + + private void assertIsSameAddressAs(LinkAddress l1, LinkAddress l2) { + assertTrue(l1 + " unexpectedly does not have same address as " + l2, + l1.isSameAddressAs(l2)); + assertTrue(l2 + " unexpectedly does not have same address as " + l1, + l2.isSameAddressAs(l1)); + } + + private void assertIsNotSameAddressAs(LinkAddress l1, LinkAddress l2) { + assertFalse(l1 + " unexpectedly has same address as " + l2, + l1.isSameAddressAs(l2)); + assertFalse(l2 + " unexpectedly has same address as " + l1, + l1.isSameAddressAs(l2)); + } + + @Test + public void testEqualsAndSameAddressAs() { + LinkAddress l1, l2, l3; + + l1 = new LinkAddress("2001:db8::1/64"); + l2 = new LinkAddress("2001:db8::1/64"); + assertEqualBothWays(l1, l2); + assertIsSameAddressAs(l1, l2); + + l2 = new LinkAddress("2001:db8::1/65"); + assertNotEqualEitherWay(l1, l2); + assertIsNotSameAddressAs(l1, l2); + + l2 = new LinkAddress("2001:db8::2/64"); + assertNotEqualEitherWay(l1, l2); + assertIsNotSameAddressAs(l1, l2); + + + l1 = new LinkAddress("192.0.2.1/24"); + l2 = new LinkAddress("192.0.2.1/24"); + assertEqualBothWays(l1, l2); + assertIsSameAddressAs(l1, l2); + + l2 = new LinkAddress("192.0.2.1/23"); + assertNotEqualEitherWay(l1, l2); + assertIsNotSameAddressAs(l1, l2); + + l2 = new LinkAddress("192.0.2.2/24"); + assertNotEqualEitherWay(l1, l2); + assertIsNotSameAddressAs(l1, l2); + + + // Check equals() and isSameAddressAs() on identical addresses with different flags. + l1 = new LinkAddress(V6_ADDRESS, 64); + l2 = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); + assertEqualBothWays(l1, l2); + assertIsSameAddressAs(l1, l2); + + l2 = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE); + assertNotEqualEitherWay(l1, l2); + assertIsSameAddressAs(l1, l2); + + // Check equals() and isSameAddressAs() on identical addresses with different scope. + l1 = new LinkAddress(V4_ADDRESS, 24); + l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_UNIVERSE); + assertEqualBothWays(l1, l2); + assertIsSameAddressAs(l1, l2); + + l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_HOST); + assertNotEqualEitherWay(l1, l2); + assertIsSameAddressAs(l1, l2); + + // Addresses with the same start or end bytes aren't equal between families. + l1 = new LinkAddress("32.1.13.184/24"); + l2 = new LinkAddress("2001:db8::1/24"); + l3 = new LinkAddress("::2001:db8/24"); + + byte[] ipv4Bytes = l1.getAddress().getAddress(); + byte[] l2FirstIPv6Bytes = Arrays.copyOf(l2.getAddress().getAddress(), 4); + byte[] l3LastIPv6Bytes = Arrays.copyOfRange(l3.getAddress().getAddress(), 12, 16); + assertTrue(Arrays.equals(ipv4Bytes, l2FirstIPv6Bytes)); + assertTrue(Arrays.equals(ipv4Bytes, l3LastIPv6Bytes)); + + assertNotEqualEitherWay(l1, l2); + assertIsNotSameAddressAs(l1, l2); + + assertNotEqualEitherWay(l1, l3); + assertIsNotSameAddressAs(l1, l3); + + // Because we use InetAddress, an IPv4 address is equal to its IPv4-mapped address. + // TODO: Investigate fixing this. + String addressString = V4 + "/24"; + l1 = new LinkAddress(addressString); + l2 = new LinkAddress("::ffff:" + addressString); + assertEqualBothWays(l1, l2); + assertIsSameAddressAs(l1, l2); + } + + @Test + public void testHashCode() { + LinkAddress l1, l2; + + l1 = new LinkAddress(V4_ADDRESS, 23); + l2 = new LinkAddress(V4_ADDRESS, 23, 0, RT_SCOPE_HOST); + assertNotEquals(l1.hashCode(), l2.hashCode()); + + l1 = new LinkAddress(V6_ADDRESS, 128); + l2 = new LinkAddress(V6_ADDRESS, 128, IFA_F_TENTATIVE, RT_SCOPE_UNIVERSE); + assertNotEquals(l1.hashCode(), l2.hashCode()); + } + + @Test + public void testParceling() { + LinkAddress l; + + l = new LinkAddress(V6_ADDRESS, 64, 123, 456); + assertParcelingIsLossless(l); + + l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); + assertParcelingIsLossless(l); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testLifetimeParceling() { + final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, 456, 1L, 3600000L); + assertParcelingIsLossless(l); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testFieldCount_Q() { + assertFieldCountEquals(4, LinkAddress.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testFieldCount() { + // Make sure any new field is covered by the above parceling tests when changing this number + assertFieldCountEquals(6, LinkAddress.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testDeprecationTime() { + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + LinkAddress.LIFETIME_UNKNOWN, 100000L); + fail("Only one time provided should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 200000L, 100000L); + fail("deprecation time later than expiration time should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + -2, 100000L); + fail("negative deprecation time should cause exception"); + } catch (IllegalArgumentException expected) { } + + LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L); + assertEquals(100000L, addr.getDeprecationTime()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testExpirationTime() { + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 200000L, LinkAddress.LIFETIME_UNKNOWN); + fail("Only one time provided should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 100000L, -2); + fail("negative expiration time should cause exception"); + } catch (IllegalArgumentException expected) { } + + LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L); + assertEquals(200000L, addr.getExpirationTime()); + } + + @Test + public void testGetFlags() { + LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST); + assertEquals(123, l.getFlags()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testGetFlags_Deprecation() { + // Test if deprecated bit was added/remove automatically based on the provided deprecation + // time + LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST, + 1L, LinkAddress.LIFETIME_PERMANENT); + // Check if the flag is added automatically. + assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, + SystemClock.elapsedRealtime() + 100000L, LinkAddress.LIFETIME_PERMANENT); + // Check if the flag is removed automatically. + assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, + LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT); + // Check if the permanent flag is added. + assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST, + 1000L, SystemClock.elapsedRealtime() + 100000L); + // Check if the permanent flag is removed + assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0); + } + + private void assertGlobalPreferred(LinkAddress l, String msg) { + assertTrue(msg, l.isGlobalPreferred()); + } + + private void assertNotGlobalPreferred(LinkAddress l, String msg) { + assertFalse(msg, l.isGlobalPreferred()); + } + + @Test + public void testIsGlobalPreferred() { + LinkAddress l; + + l = new LinkAddress(V4_ADDRESS, 32, 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v4,global,noflags"); + + l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v4-rfc1918,global,noflags"); + + l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_SITE); + assertNotGlobalPreferred(l, "v4-rfc1918,site-local,noflags"); + + l = new LinkAddress("127.0.0.7/8", 0, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v4-localhost,node-local,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,permanent"); + + // IPv6 ULAs are not acceptable "global preferred" addresses. + l = new LinkAddress("fc12::1/64", 0, RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,ula1,noflags"); + + l = new LinkAddress("fd34::1/64", 0, RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,ula2,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DADFAILED), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+dadfailed"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DEPRECATED), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+deprecated"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_SITE); + assertNotGlobalPreferred(l, "v6,site-local,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_LINK); + assertNotGlobalPreferred(l, "v6,link-local,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v6,node-local,tempaddr"); + + l = new LinkAddress("::1/128", IFA_F_PERMANENT, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v6-localhost,node-local,permanent"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_TENTATIVE), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+tentative"); + + l = new LinkAddress(V6_ADDRESS, 64, + (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC), + RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,tempaddr+optimistic"); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testIsGlobalPreferred_DeprecatedInFuture() { + final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, + RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000, + SystemClock.elapsedRealtime() + 200000); + // Although the deprecated bit is set, but the deprecation time is in the future, test + // if the flag is removed automatically. + assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future"); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java new file mode 100644 index 000000000000..550953d0612d --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java @@ -0,0 +1,1271 @@ +/* + * Copyright (C) 2010 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; + +import static android.net.RouteInfo.RTN_THROW; +import static android.net.RouteInfo.RTN_UNICAST; +import static android.net.RouteInfo.RTN_UNREACHABLE; + +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.LinkProperties.ProvisioningChange; +import android.os.Build; +import android.system.OsConstants; +import android.util.ArraySet; + +import androidx.core.os.BuildCompat; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.LinkPropertiesUtils.CompareResult; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LinkPropertiesTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final InetAddress ADDRV4 = address("75.208.6.1"); + private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + private static final InetAddress DNS1 = address("75.208.7.1"); + private static final InetAddress DNS2 = address("69.78.7.1"); + private static final InetAddress DNS6 = address("2001:4860:4860::8888"); + private static final InetAddress PRIVDNS1 = address("1.1.1.1"); + private static final InetAddress PRIVDNS2 = address("1.0.0.1"); + private static final InetAddress PRIVDNS6 = address("2606:4700:4700::1111"); + private static final InetAddress PCSCFV4 = address("10.77.25.37"); + private static final InetAddress PCSCFV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:1"); + private static final InetAddress GATEWAY1 = address("75.208.8.1"); + private static final InetAddress GATEWAY2 = address("69.78.8.1"); + private static final InetAddress GATEWAY61 = address("fe80::6:0000:613"); + private static final InetAddress GATEWAY62 = address("fe80::6:22%lo"); + private static final InetAddress TESTIPV4ADDR = address("192.168.47.42"); + private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43"); + private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1"); + private static final String NAME = "qmi0"; + private static final String DOMAINS = "google.com"; + private static final String PRIV_DNS_SERVER_NAME = "private.dns.com"; + private static final String TCP_BUFFER_SIZES = "524288,1048576,2097152,262144,524288,1048576"; + private static final int MTU = 1500; + private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32); + private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128); + private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64"); + private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi"); + + // CaptivePortalData cannot be in a constant as it does not exist on Q. + // The test runner also crashes when scanning for tests if it is a return type. + private static Object getCaptivePortalData() { + return new CaptivePortalData.Builder() + .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build(); + } + + private static InetAddress address(String addrString) { + return InetAddresses.parseNumericAddress(addrString); + } + + private static boolean isAtLeastR() { + // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) + return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); + } + + private void checkEmpty(final LinkProperties lp) { + assertEquals(0, lp.getAllInterfaceNames().size()); + assertEquals(0, lp.getAllAddresses().size()); + assertEquals(0, lp.getDnsServers().size()); + assertEquals(0, lp.getValidatedPrivateDnsServers().size()); + assertEquals(0, lp.getPcscfServers().size()); + assertEquals(0, lp.getAllRoutes().size()); + assertEquals(0, lp.getAllLinkAddresses().size()); + assertEquals(0, lp.getStackedLinks().size()); + assertEquals(0, lp.getMtu()); + assertNull(lp.getPrivateDnsServerName()); + assertNull(lp.getDomains()); + assertNull(lp.getHttpProxy()); + assertNull(lp.getTcpBufferSizes()); + assertNull(lp.getNat64Prefix()); + assertFalse(lp.isProvisioned()); + assertFalse(lp.isIpv4Provisioned()); + assertFalse(lp.isIpv6Provisioned()); + assertFalse(lp.isPrivateDnsActive()); + + if (isAtLeastR()) { + assertNull(lp.getDhcpServerAddress()); + assertFalse(lp.isWakeOnLanSupported()); + assertNull(lp.getCaptivePortalApiUrl()); + assertNull(lp.getCaptivePortalData()); + } + } + + private LinkProperties makeTestObject() { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(NAME); + lp.addLinkAddress(LINKADDRV4); + lp.addLinkAddress(LINKADDRV6); + lp.addDnsServer(DNS1); + lp.addDnsServer(DNS2); + lp.addValidatedPrivateDnsServer(PRIVDNS1); + lp.addValidatedPrivateDnsServer(PRIVDNS2); + lp.setUsePrivateDns(true); + lp.setPrivateDnsServerName(PRIV_DNS_SERVER_NAME); + lp.addPcscfServer(PCSCFV6); + lp.setDomains(DOMAINS); + lp.addRoute(new RouteInfo(GATEWAY1)); + lp.addRoute(new RouteInfo(GATEWAY2)); + lp.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888)); + lp.setMtu(MTU); + lp.setTcpBufferSizes(TCP_BUFFER_SIZES); + lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); + if (isAtLeastR()) { + lp.setDhcpServerAddress(DHCPSERVER); + lp.setWakeOnLanSupported(true); + lp.setCaptivePortalApiUrl(CAPPORT_API_URL); + lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData()); + } + return lp; + } + + public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) { + // Check implementation of equals(), element by element. + assertTrue(source.isIdenticalInterfaceName(target)); + assertTrue(target.isIdenticalInterfaceName(source)); + + assertTrue(source.isIdenticalAddresses(target)); + assertTrue(target.isIdenticalAddresses(source)); + + assertTrue(source.isIdenticalDnses(target)); + assertTrue(target.isIdenticalDnses(source)); + + assertTrue(source.isIdenticalPrivateDns(target)); + assertTrue(target.isIdenticalPrivateDns(source)); + + assertTrue(source.isIdenticalValidatedPrivateDnses(target)); + assertTrue(target.isIdenticalValidatedPrivateDnses(source)); + + assertTrue(source.isIdenticalPcscfs(target)); + assertTrue(target.isIdenticalPcscfs(source)); + + assertTrue(source.isIdenticalRoutes(target)); + assertTrue(target.isIdenticalRoutes(source)); + + assertTrue(source.isIdenticalHttpProxy(target)); + assertTrue(target.isIdenticalHttpProxy(source)); + + assertTrue(source.isIdenticalStackedLinks(target)); + assertTrue(target.isIdenticalStackedLinks(source)); + + assertTrue(source.isIdenticalMtu(target)); + assertTrue(target.isIdenticalMtu(source)); + + assertTrue(source.isIdenticalTcpBufferSizes(target)); + assertTrue(target.isIdenticalTcpBufferSizes(source)); + + if (isAtLeastR()) { + assertTrue(source.isIdenticalDhcpServerAddress(target)); + assertTrue(source.isIdenticalDhcpServerAddress(source)); + + assertTrue(source.isIdenticalWakeOnLan(target)); + assertTrue(target.isIdenticalWakeOnLan(source)); + + assertTrue(source.isIdenticalCaptivePortalApiUrl(target)); + assertTrue(target.isIdenticalCaptivePortalApiUrl(source)); + + assertTrue(source.isIdenticalCaptivePortalData(target)); + assertTrue(target.isIdenticalCaptivePortalData(source)); + } + + // Check result of equals(). + assertTrue(source.equals(target)); + assertTrue(target.equals(source)); + + // Check hashCode. + assertEquals(source.hashCode(), target.hashCode()); + } + + @Test + public void testEqualsNull() { + LinkProperties source = new LinkProperties(); + LinkProperties target = new LinkProperties(); + + assertFalse(source == target); + assertLinkPropertiesEqual(source, target); + } + + @Test + public void testEqualsSameOrder() throws Exception { + LinkProperties source = new LinkProperties(); + source.setInterfaceName(NAME); + // set 2 link addresses + source.addLinkAddress(LINKADDRV4); + source.addLinkAddress(LINKADDRV6); + // set 2 dnses + source.addDnsServer(DNS1); + source.addDnsServer(DNS2); + // set 1 pcscf + source.addPcscfServer(PCSCFV6); + // set 2 gateways + source.addRoute(new RouteInfo(GATEWAY1)); + source.addRoute(new RouteInfo(GATEWAY2)); + source.setMtu(MTU); + + LinkProperties target = new LinkProperties(); + + // All fields are same + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(DNS1); + target.addDnsServer(DNS2); + target.addPcscfServer(PCSCFV6); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + target.setMtu(MTU); + + assertLinkPropertiesEqual(source, target); + + target.clear(); + // change Interface Name + target.setInterfaceName("qmi1"); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(DNS1); + target.addDnsServer(DNS2); + target.addPcscfServer(PCSCFV6); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + target.setMtu(MTU); + assertFalse(source.equals(target)); + + target.clear(); + target.setInterfaceName(NAME); + // change link addresses + target.addLinkAddress(new LinkAddress(address("75.208.6.2"), 32)); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(DNS1); + target.addDnsServer(DNS2); + target.addPcscfServer(PCSCFV6); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + target.setMtu(MTU); + assertFalse(source.equals(target)); + + target.clear(); + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + // change dnses + target.addDnsServer(address("75.208.7.2")); + target.addDnsServer(DNS2); + target.addPcscfServer(PCSCFV6); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + target.setMtu(MTU); + assertFalse(source.equals(target)); + + target.clear(); + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(address("75.208.7.2")); + target.addDnsServer(DNS2); + // change pcscf + target.addPcscfServer(address("2001::1")); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + target.setMtu(MTU); + assertFalse(source.equals(target)); + + target.clear(); + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(DNS1); + target.addDnsServer(DNS2); + // change gateway + target.addRoute(new RouteInfo(address("75.208.8.2"))); + target.setMtu(MTU); + target.addRoute(new RouteInfo(GATEWAY2)); + assertFalse(source.equals(target)); + + target.clear(); + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addDnsServer(DNS1); + target.addDnsServer(DNS2); + target.addRoute(new RouteInfo(GATEWAY1)); + target.addRoute(new RouteInfo(GATEWAY2)); + // change mtu + target.setMtu(1440); + assertFalse(source.equals(target)); + } + + @Test + public void testEqualsDifferentOrder() throws Exception { + LinkProperties source = new LinkProperties(); + source.setInterfaceName(NAME); + // set 2 link addresses + source.addLinkAddress(LINKADDRV4); + source.addLinkAddress(LINKADDRV6); + // set 2 dnses + source.addDnsServer(DNS1); + source.addDnsServer(DNS2); + // set 2 gateways + source.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1)); + source.addRoute(new RouteInfo(GATEWAY2)); + source.setMtu(MTU); + + LinkProperties target = new LinkProperties(); + // Exchange order + target.setInterfaceName(NAME); + target.addLinkAddress(LINKADDRV6); + target.addLinkAddress(LINKADDRV4); + target.addDnsServer(DNS2); + target.addDnsServer(DNS1); + target.addRoute(new RouteInfo(GATEWAY2)); + target.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1)); + target.setMtu(MTU); + + assertLinkPropertiesEqual(source, target); + } + + @Test + public void testEqualsDuplicated() throws Exception { + LinkProperties source = new LinkProperties(); + // set 3 link addresses, eg, [A, A, B] + source.addLinkAddress(LINKADDRV4); + source.addLinkAddress(LINKADDRV4); + source.addLinkAddress(LINKADDRV6); + + LinkProperties target = new LinkProperties(); + // set 3 link addresses, eg, [A, B, B] + target.addLinkAddress(LINKADDRV4); + target.addLinkAddress(LINKADDRV6); + target.addLinkAddress(LINKADDRV6); + + assertLinkPropertiesEqual(source, target); + } + + private void assertAllRoutesHaveInterface(String iface, LinkProperties lp) { + for (RouteInfo r : lp.getRoutes()) { + assertEquals(iface, r.getInterface()); + } + } + + private void assertAllRoutesNotHaveInterface(String iface, LinkProperties lp) { + for (RouteInfo r : lp.getRoutes()) { + assertNotEquals(iface, r.getInterface()); + } + } + + @Test + public void testRouteInterfaces() { + LinkAddress prefix1 = new LinkAddress(address("2001:db8:1::"), 48); + LinkAddress prefix2 = new LinkAddress(address("2001:db8:2::"), 48); + InetAddress address = ADDRV6; + + // Add a route with no interface to a LinkProperties with no interface. No errors. + LinkProperties lp = new LinkProperties(); + RouteInfo r = new RouteInfo(prefix1, address, null); + assertTrue(lp.addRoute(r)); + assertEquals(1, lp.getRoutes().size()); + assertAllRoutesHaveInterface(null, lp); + + // Adding the same route twice has no effect. + assertFalse(lp.addRoute(r)); + assertEquals(1, lp.getRoutes().size()); + + // Add a route with an interface. Expect an exception. + r = new RouteInfo(prefix2, address, "wlan0"); + try { + lp.addRoute(r); + fail("Adding wlan0 route to LP with no interface, expect exception"); + } catch (IllegalArgumentException expected) {} + + // Change the interface name. All the routes should change their interface name too. + lp.setInterfaceName("rmnet0"); + assertAllRoutesHaveInterface("rmnet0", lp); + assertAllRoutesNotHaveInterface(null, lp); + assertAllRoutesNotHaveInterface("wlan0", lp); + + // Now add a route with the wrong interface. This causes an exception too. + try { + lp.addRoute(r); + fail("Adding wlan0 route to rmnet0 LP, expect exception"); + } catch (IllegalArgumentException expected) {} + + // If the interface name matches, the route is added. + r = new RouteInfo(prefix2, null, "wlan0"); + lp.setInterfaceName("wlan0"); + lp.addRoute(r); + assertEquals(2, lp.getRoutes().size()); + assertAllRoutesHaveInterface("wlan0", lp); + assertAllRoutesNotHaveInterface("rmnet0", lp); + + // Routes with null interfaces are converted to wlan0. + r = RouteInfo.makeHostRoute(ADDRV6, null); + lp.addRoute(r); + assertEquals(3, lp.getRoutes().size()); + assertAllRoutesHaveInterface("wlan0", lp); + + // Check routes are updated correctly when calling setInterfaceName. + LinkProperties lp2 = new LinkProperties(lp); + assertAllRoutesHaveInterface("wlan0", lp2); + final CompareResult cr1 = + new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); + assertEquals(0, cr1.added.size()); + assertEquals(0, cr1.removed.size()); + + lp2.setInterfaceName("p2p0"); + assertAllRoutesHaveInterface("p2p0", lp2); + assertAllRoutesNotHaveInterface("wlan0", lp2); + final CompareResult cr2 = + new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); + assertEquals(3, cr2.added.size()); + assertEquals(3, cr2.removed.size()); + + // Remove route with incorrect interface, no route removed. + lp.removeRoute(new RouteInfo(prefix2, null, null)); + assertEquals(3, lp.getRoutes().size()); + + // Check remove works when interface is correct. + lp.removeRoute(new RouteInfo(prefix2, null, "wlan0")); + assertEquals(2, lp.getRoutes().size()); + assertAllRoutesHaveInterface("wlan0", lp); + assertAllRoutesNotHaveInterface("p2p0", lp); + } + + @Test + public void testStackedInterfaces() { + LinkProperties rmnet0 = new LinkProperties(); + rmnet0.setInterfaceName("rmnet0"); + rmnet0.addLinkAddress(LINKADDRV6); + + LinkProperties clat4 = new LinkProperties(); + clat4.setInterfaceName("clat4"); + clat4.addLinkAddress(LINKADDRV4); + + assertEquals(0, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(1, rmnet0.getAllAddresses().size()); + assertEquals(1, rmnet0.getAllLinkAddresses().size()); + assertEquals(1, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + + rmnet0.addStackedLink(clat4); + assertEquals(1, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(2, rmnet0.getAllAddresses().size()); + assertEquals(2, rmnet0.getAllLinkAddresses().size()); + assertEquals(2, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); + + rmnet0.addStackedLink(clat4); + assertEquals(1, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(2, rmnet0.getAllAddresses().size()); + assertEquals(2, rmnet0.getAllLinkAddresses().size()); + assertEquals(2, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); + + assertEquals(0, clat4.getStackedLinks().size()); + + // Modify an item in the returned collection to see what happens. + for (LinkProperties link : rmnet0.getStackedLinks()) { + if (link.getInterfaceName().equals("clat4")) { + link.setInterfaceName("newname"); + } + } + for (LinkProperties link : rmnet0.getStackedLinks()) { + assertFalse("newname".equals(link.getInterfaceName())); + } + + assertTrue(rmnet0.removeStackedLink("clat4")); + assertEquals(0, rmnet0.getStackedLinks().size()); + assertEquals(1, rmnet0.getAddresses().size()); + assertEquals(1, rmnet0.getLinkAddresses().size()); + assertEquals(1, rmnet0.getAllAddresses().size()); + assertEquals(1, rmnet0.getAllLinkAddresses().size()); + assertEquals(1, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + + assertFalse(rmnet0.removeStackedLink("clat4")); + } + + private LinkAddress getFirstLinkAddress(LinkProperties lp) { + return lp.getLinkAddresses().iterator().next(); + } + + @Test + public void testAddressMethods() { + LinkProperties lp = new LinkProperties(); + + // No addresses. + assertFalse(lp.hasIpv4Address()); + assertFalse(lp.hasGlobalIpv6Address()); + + // Addresses on stacked links don't count. + LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName("stacked"); + lp.addStackedLink(stacked); + stacked.addLinkAddress(LINKADDRV4); + stacked.addLinkAddress(LINKADDRV6); + assertTrue(stacked.hasIpv4Address()); + assertTrue(stacked.hasGlobalIpv6Address()); + assertFalse(lp.hasIpv4Address()); + assertFalse(lp.hasGlobalIpv6Address()); + lp.removeStackedLink("stacked"); + assertFalse(lp.hasIpv4Address()); + assertFalse(lp.hasGlobalIpv6Address()); + + // Addresses on the base link. + // Check the return values of hasIpvXAddress and ensure the add/remove methods return true + // iff something changes. + assertEquals(0, lp.getLinkAddresses().size()); + assertTrue(lp.addLinkAddress(LINKADDRV6)); + assertEquals(1, lp.getLinkAddresses().size()); + assertFalse(lp.hasIpv4Address()); + assertTrue(lp.hasGlobalIpv6Address()); + + assertTrue(lp.removeLinkAddress(LINKADDRV6)); + assertEquals(0, lp.getLinkAddresses().size()); + + assertTrue(lp.addLinkAddress(LINKADDRV6LINKLOCAL)); + assertEquals(1, lp.getLinkAddresses().size()); + assertFalse(lp.hasGlobalIpv6Address()); + + assertTrue(lp.addLinkAddress(LINKADDRV4)); + assertEquals(2, lp.getLinkAddresses().size()); + assertTrue(lp.hasIpv4Address()); + assertFalse(lp.hasGlobalIpv6Address()); + + assertTrue(lp.addLinkAddress(LINKADDRV6)); + assertEquals(3, lp.getLinkAddresses().size()); + assertTrue(lp.hasIpv4Address()); + assertTrue(lp.hasGlobalIpv6Address()); + + assertTrue(lp.removeLinkAddress(LINKADDRV6LINKLOCAL)); + assertEquals(2, lp.getLinkAddresses().size()); + assertTrue(lp.hasIpv4Address()); + assertTrue(lp.hasGlobalIpv6Address()); + + // Adding an address twice has no effect. + // Removing an address that's not present has no effect. + assertFalse(lp.addLinkAddress(LINKADDRV4)); + assertEquals(2, lp.getLinkAddresses().size()); + assertTrue(lp.hasIpv4Address()); + assertTrue(lp.removeLinkAddress(LINKADDRV4)); + assertEquals(1, lp.getLinkAddresses().size()); + assertFalse(lp.hasIpv4Address()); + assertFalse(lp.removeLinkAddress(LINKADDRV4)); + assertEquals(1, lp.getLinkAddresses().size()); + + // Adding an address that's already present but with different properties causes the + // existing address to be updated and returns true. + // Start with only LINKADDRV6. + assertEquals(1, lp.getLinkAddresses().size()); + assertEquals(LINKADDRV6, getFirstLinkAddress(lp)); + + // Create a LinkAddress object for the same address, but with different flags. + LinkAddress deprecated = new LinkAddress(ADDRV6, 128, + OsConstants.IFA_F_DEPRECATED, OsConstants.RT_SCOPE_UNIVERSE); + assertTrue(deprecated.isSameAddressAs(LINKADDRV6)); + assertFalse(deprecated.equals(LINKADDRV6)); + + // Check that adding it updates the existing address instead of adding a new one. + assertTrue(lp.addLinkAddress(deprecated)); + assertEquals(1, lp.getLinkAddresses().size()); + assertEquals(deprecated, getFirstLinkAddress(lp)); + assertFalse(LINKADDRV6.equals(getFirstLinkAddress(lp))); + + // Removing LINKADDRV6 removes deprecated, because removing addresses ignores properties. + assertTrue(lp.removeLinkAddress(LINKADDRV6)); + assertEquals(0, lp.getLinkAddresses().size()); + } + + @Test + public void testLinkAddresses() { + final LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(LINKADDRV4); + lp.addLinkAddress(LINKADDRV6); + + final LinkProperties lp2 = new LinkProperties(); + lp2.addLinkAddress(LINKADDRV6); + + final LinkProperties lp3 = new LinkProperties(); + final List linkAddresses = Arrays.asList(LINKADDRV4); + lp3.setLinkAddresses(linkAddresses); + + assertFalse(lp.equals(lp2)); + assertFalse(lp2.equals(lp3)); + + lp.removeLinkAddress(LINKADDRV4); + assertTrue(lp.equals(lp2)); + + lp2.setLinkAddresses(lp3.getLinkAddresses()); + assertTrue(lp2.equals(lp3)); + } + + @Test + public void testNat64Prefix() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(LINKADDRV4); + lp.addLinkAddress(LINKADDRV6); + + assertNull(lp.getNat64Prefix()); + + IpPrefix p = new IpPrefix("64:ff9b::/96"); + lp.setNat64Prefix(p); + assertEquals(p, lp.getNat64Prefix()); + + p = new IpPrefix("2001:db8:a:b:1:2:3::/96"); + lp.setNat64Prefix(p); + assertEquals(p, lp.getNat64Prefix()); + + p = new IpPrefix("2001:db8:a:b:1:2::/80"); + try { + lp.setNat64Prefix(p); + } catch (IllegalArgumentException expected) { + } + + p = new IpPrefix("64:ff9b::/64"); + try { + lp.setNat64Prefix(p); + } catch (IllegalArgumentException expected) { + } + + assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix()); + + lp.setNat64Prefix(null); + assertNull(lp.getNat64Prefix()); + } + + @Test + public void testIsProvisioned() { + LinkProperties lp4 = new LinkProperties(); + assertFalse("v4only:empty", lp4.isProvisioned()); + lp4.addLinkAddress(LINKADDRV4); + assertFalse("v4only:addr-only", lp4.isProvisioned()); + lp4.addDnsServer(DNS1); + assertFalse("v4only:addr+dns", lp4.isProvisioned()); + lp4.addRoute(new RouteInfo(GATEWAY1)); + assertTrue("v4only:addr+dns+route", lp4.isProvisioned()); + assertTrue("v4only:addr+dns+route", lp4.isIpv4Provisioned()); + assertFalse("v4only:addr+dns+route", lp4.isIpv6Provisioned()); + + LinkProperties lp6 = new LinkProperties(); + assertFalse("v6only:empty", lp6.isProvisioned()); + lp6.addLinkAddress(LINKADDRV6LINKLOCAL); + assertFalse("v6only:fe80-only", lp6.isProvisioned()); + lp6.addDnsServer(DNS6); + assertFalse("v6only:fe80+dns", lp6.isProvisioned()); + lp6.addRoute(new RouteInfo(GATEWAY61)); + assertFalse("v6only:fe80+dns+route", lp6.isProvisioned()); + lp6.addLinkAddress(LINKADDRV6); + assertTrue("v6only:fe80+global+dns+route", lp6.isIpv6Provisioned()); + assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned()); + lp6.removeLinkAddress(LINKADDRV6LINKLOCAL); + assertFalse("v6only:global+dns+route", lp6.isIpv4Provisioned()); + assertTrue("v6only:global+dns+route", lp6.isIpv6Provisioned()); + assertTrue("v6only:global+dns+route", lp6.isProvisioned()); + + LinkProperties lp46 = new LinkProperties(); + lp46.addLinkAddress(LINKADDRV4); + lp46.addLinkAddress(LINKADDRV6); + lp46.addDnsServer(DNS1); + lp46.addDnsServer(DNS6); + assertFalse("dualstack:missing-routes", lp46.isProvisioned()); + lp46.addRoute(new RouteInfo(GATEWAY1)); + assertTrue("dualstack:v4-provisioned", lp46.isIpv4Provisioned()); + assertFalse("dualstack:v4-provisioned", lp46.isIpv6Provisioned()); + assertTrue("dualstack:v4-provisioned", lp46.isProvisioned()); + lp46.addRoute(new RouteInfo(GATEWAY61)); + assertTrue("dualstack:both-provisioned", lp46.isIpv4Provisioned()); + assertTrue("dualstack:both-provisioned", lp46.isIpv6Provisioned()); + assertTrue("dualstack:both-provisioned", lp46.isProvisioned()); + + // A link with an IPv6 address and default route, but IPv4 DNS server. + LinkProperties mixed = new LinkProperties(); + mixed.addLinkAddress(LINKADDRV6); + mixed.addDnsServer(DNS1); + mixed.addRoute(new RouteInfo(GATEWAY61)); + assertFalse("mixed:addr6+route6+dns4", mixed.isIpv4Provisioned()); + assertFalse("mixed:addr6+route6+dns4", mixed.isIpv6Provisioned()); + assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned()); + } + + @Test + public void testCompareProvisioning() { + LinkProperties v4lp = new LinkProperties(); + v4lp.addLinkAddress(LINKADDRV4); + v4lp.addRoute(new RouteInfo(GATEWAY1)); + v4lp.addDnsServer(DNS1); + assertTrue(v4lp.isProvisioned()); + + LinkProperties v4r = new LinkProperties(v4lp); + v4r.removeDnsServer(DNS1); + assertFalse(v4r.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_NOT_PROVISIONED, + LinkProperties.compareProvisioning(v4r, v4r)); + assertEquals(ProvisioningChange.LOST_PROVISIONING, + LinkProperties.compareProvisioning(v4lp, v4r)); + assertEquals(ProvisioningChange.GAINED_PROVISIONING, + LinkProperties.compareProvisioning(v4r, v4lp)); + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v4lp, v4lp)); + + // Check that losing IPv4 provisioning on a dualstack network is + // seen as a total loss of provisioning. + LinkProperties v6lp = new LinkProperties(); + v6lp.addLinkAddress(LINKADDRV6); + v6lp.addRoute(new RouteInfo(GATEWAY61)); + v6lp.addDnsServer(DNS6); + assertFalse(v6lp.isIpv4Provisioned()); + assertTrue(v6lp.isIpv6Provisioned()); + assertTrue(v6lp.isProvisioned()); + + LinkProperties v46lp = new LinkProperties(v6lp); + v46lp.addLinkAddress(LINKADDRV4); + v46lp.addRoute(new RouteInfo(GATEWAY1)); + v46lp.addDnsServer(DNS1); + assertTrue(v46lp.isIpv4Provisioned()); + assertTrue(v46lp.isIpv6Provisioned()); + assertTrue(v46lp.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v4lp, v46lp)); + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp, v46lp)); + assertEquals(ProvisioningChange.LOST_PROVISIONING, + LinkProperties.compareProvisioning(v46lp, v6lp)); + assertEquals(ProvisioningChange.LOST_PROVISIONING, + LinkProperties.compareProvisioning(v46lp, v4lp)); + + // Check that losing and gaining a secondary router does not change + // the provisioning status. + LinkProperties v6lp2 = new LinkProperties(v6lp); + v6lp2.addRoute(new RouteInfo(GATEWAY62)); + assertTrue(v6lp2.isProvisioned()); + + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp2, v6lp)); + assertEquals(ProvisioningChange.STILL_PROVISIONED, + LinkProperties.compareProvisioning(v6lp, v6lp2)); + } + + @Test + public void testIsReachable() { + final LinkProperties v4lp = new LinkProperties(); + assertFalse(v4lp.isReachable(DNS1)); + assertFalse(v4lp.isReachable(DNS2)); + + // Add an on-link route, making the on-link DNS server reachable, + // but there is still no IPv4 address. + assertTrue(v4lp.addRoute(new RouteInfo(new IpPrefix(address("75.208.0.0"), 16)))); + assertFalse(v4lp.isReachable(DNS1)); + assertFalse(v4lp.isReachable(DNS2)); + + // Adding an IPv4 address (right now, any IPv4 address) means we use + // the routes to compute likely reachability. + assertTrue(v4lp.addLinkAddress(new LinkAddress(ADDRV4, 16))); + assertTrue(v4lp.isReachable(DNS1)); + assertFalse(v4lp.isReachable(DNS2)); + + // Adding a default route makes the off-link DNS server reachable. + assertTrue(v4lp.addRoute(new RouteInfo(GATEWAY1))); + assertTrue(v4lp.isReachable(DNS1)); + assertTrue(v4lp.isReachable(DNS2)); + + final LinkProperties v6lp = new LinkProperties(); + final InetAddress kLinkLocalDns = address("fe80::6:1"); + final InetAddress kLinkLocalDnsWithScope = address("fe80::6:2%43"); + final InetAddress kOnLinkDns = address("2001:db8:85a3::53"); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertFalse(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertFalse(v6lp.isReachable(kOnLinkDns)); + assertFalse(v6lp.isReachable(DNS6)); + + // Add a link-local route, making the link-local DNS servers reachable. Because + // we assume the presence of an IPv6 link-local address, link-local DNS servers + // are considered reachable, but only those with a non-zero scope identifier. + assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(address("fe80::"), 64)))); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertFalse(v6lp.isReachable(kOnLinkDns)); + assertFalse(v6lp.isReachable(DNS6)); + + // Add a link-local address--nothing changes. + assertTrue(v6lp.addLinkAddress(LINKADDRV6LINKLOCAL)); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertFalse(v6lp.isReachable(kOnLinkDns)); + assertFalse(v6lp.isReachable(DNS6)); + + // Add a global route on link, but no global address yet. DNS servers reachable + // via a route that doesn't require a gateway: give them the benefit of the + // doubt and hope the link-local source address suffices for communication. + assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(address("2001:db8:85a3::"), 64)))); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertTrue(v6lp.isReachable(kOnLinkDns)); + assertFalse(v6lp.isReachable(DNS6)); + + // Add a global address; the on-link global address DNS server is (still) + // presumed reachable. + assertTrue(v6lp.addLinkAddress(new LinkAddress(ADDRV6, 64))); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertTrue(v6lp.isReachable(kOnLinkDns)); + assertFalse(v6lp.isReachable(DNS6)); + + // Adding a default route makes the off-link DNS server reachable. + assertTrue(v6lp.addRoute(new RouteInfo(GATEWAY62))); + assertFalse(v6lp.isReachable(kLinkLocalDns)); + assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); + assertTrue(v6lp.isReachable(kOnLinkDns)); + assertTrue(v6lp.isReachable(DNS6)); + + // Check isReachable on stacked links. This requires that the source IP address be assigned + // on the interface returned by the route lookup. + LinkProperties stacked = new LinkProperties(); + + // Can't add a stacked link without an interface name. + stacked.setInterfaceName("v4-test0"); + v6lp.addStackedLink(stacked); + + InetAddress stackedAddress = address("192.0.0.4"); + LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32); + assertFalse(v6lp.isReachable(stackedAddress)); + stacked.addLinkAddress(stackedLinkAddress); + assertFalse(v6lp.isReachable(stackedAddress)); + stacked.addRoute(new RouteInfo(stackedLinkAddress)); + assertTrue(stacked.isReachable(stackedAddress)); + assertTrue(v6lp.isReachable(stackedAddress)); + + assertFalse(v6lp.isReachable(DNS1)); + stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress)); + assertTrue(v6lp.isReachable(DNS1)); + } + + @Test + public void testLinkPropertiesEnsureDirectlyConnectedRoutes() { + // IPv4 case: no route added initially + LinkProperties rmnet0 = new LinkProperties(); + rmnet0.setInterfaceName("rmnet0"); + rmnet0.addLinkAddress(new LinkAddress("10.0.0.2/8")); + RouteInfo directRoute0 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null, + rmnet0.getInterfaceName()); + + // Since no routes is added explicitly, getAllRoutes() should return empty. + assertTrue(rmnet0.getAllRoutes().isEmpty()); + rmnet0.ensureDirectlyConnectedRoutes(); + // ensureDirectlyConnectedRoutes() should have added the missing local route. + assertEqualRoutes(Collections.singletonList(directRoute0), rmnet0.getAllRoutes()); + + // IPv4 case: both direct and default routes added initially + LinkProperties rmnet1 = new LinkProperties(); + rmnet1.setInterfaceName("rmnet1"); + rmnet1.addLinkAddress(new LinkAddress("10.0.0.3/8")); + RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null, address("10.0.0.1"), + rmnet1.getInterfaceName()); + RouteInfo directRoute1 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null, + rmnet1.getInterfaceName()); + rmnet1.addRoute(defaultRoute1); + rmnet1.addRoute(directRoute1); + + // Check added routes + assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes()); + // ensureDirectlyConnectedRoutes() shouldn't change the routes since direct connected + // route is already part of the configuration. + rmnet1.ensureDirectlyConnectedRoutes(); + assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes()); + + // IPv6 case: only default routes added initially + LinkProperties rmnet2 = new LinkProperties(); + rmnet2.setInterfaceName("rmnet2"); + rmnet2.addLinkAddress(new LinkAddress("fe80::cafe/64")); + rmnet2.addLinkAddress(new LinkAddress("2001:db8::2/64")); + RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null, address("2001:db8::1"), + rmnet2.getInterfaceName()); + RouteInfo directRoute2 = new RouteInfo(new IpPrefix("2001:db8::/64"), null, + rmnet2.getInterfaceName()); + RouteInfo linkLocalRoute2 = new RouteInfo(new IpPrefix("fe80::/64"), null, + rmnet2.getInterfaceName()); + rmnet2.addRoute(defaultRoute2); + + assertEqualRoutes(Arrays.asList(defaultRoute2), rmnet2.getAllRoutes()); + rmnet2.ensureDirectlyConnectedRoutes(); + assertEqualRoutes(Arrays.asList(defaultRoute2, directRoute2, linkLocalRoute2), + rmnet2.getAllRoutes()); + + // Corner case: no interface name + LinkProperties rmnet3 = new LinkProperties(); + rmnet3.addLinkAddress(new LinkAddress("192.168.0.2/24")); + RouteInfo directRoute3 = new RouteInfo(new IpPrefix("192.168.0.0/24"), null, + rmnet3.getInterfaceName()); + + assertTrue(rmnet3.getAllRoutes().isEmpty()); + rmnet3.ensureDirectlyConnectedRoutes(); + assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes()); + } + + private void assertEqualRoutes(Collection expected, Collection actual) { + Set expectedSet = new ArraySet<>(expected); + Set actualSet = new ArraySet<>(actual); + // Duplicated entries in actual routes are considered failures + assertEquals(actual.size(), actualSet.size()); + + assertEquals(expectedSet, actualSet); + } + + private static LinkProperties makeLinkPropertiesForParceling() { + LinkProperties source = new LinkProperties(); + source.setInterfaceName(NAME); + + source.addLinkAddress(LINKADDRV4); + source.addLinkAddress(LINKADDRV6); + + source.addDnsServer(DNS1); + source.addDnsServer(DNS2); + source.addDnsServer(GATEWAY62); + + source.addPcscfServer(TESTIPV4ADDR); + source.addPcscfServer(TESTIPV6ADDR); + + source.setUsePrivateDns(true); + source.setPrivateDnsServerName(PRIV_DNS_SERVER_NAME); + + source.setDomains(DOMAINS); + + source.addRoute(new RouteInfo(GATEWAY1)); + source.addRoute(new RouteInfo(GATEWAY2)); + + source.addValidatedPrivateDnsServer(DNS6); + source.addValidatedPrivateDnsServer(GATEWAY61); + source.addValidatedPrivateDnsServer(TESTIPV6ADDR); + + source.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888)); + + source.setMtu(MTU); + + source.setTcpBufferSizes(TCP_BUFFER_SIZES); + + source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); + + final LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName("test-stacked"); + source.addStackedLink(stacked); + + return source; + } + + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testLinkPropertiesParcelable_Q() throws Exception { + final LinkProperties source = makeLinkPropertiesForParceling(); + assertParcelSane(source, 14 /* fieldCount */); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testLinkPropertiesParcelable() throws Exception { + final LinkProperties source = makeLinkPropertiesForParceling(); + + source.setWakeOnLanSupported(true); + source.setCaptivePortalApiUrl(CAPPORT_API_URL); + source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData()); + source.setDhcpServerAddress((Inet4Address) GATEWAY1); + assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */), + 18 /* fieldCount */); + + // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared. + final LinkProperties sanitized = new LinkProperties(source); + sanitized.setCaptivePortalApiUrl(null); + sanitized.setCaptivePortalData(null); + assertEquals(sanitized, parcelingRoundTrip(source)); + } + + // Parceling of the scope was broken until Q-QPR2 + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testLinkLocalDnsServerParceling() throws Exception { + final String strAddress = "fe80::1%lo"; + final LinkProperties lp = new LinkProperties(); + lp.addDnsServer(address(strAddress)); + final LinkProperties unparceled = parcelingRoundTrip(lp); + // Inet6Address#equals does not test for the scope id + assertEquals(strAddress, unparceled.getDnsServers().get(0).getHostAddress()); + } + + @Test + public void testParcelUninitialized() throws Exception { + LinkProperties empty = new LinkProperties(); + assertParcelingIsLossless(empty); + } + + @Test + public void testConstructor() { + LinkProperties lp = new LinkProperties(); + checkEmpty(lp); + assertLinkPropertiesEqual(lp, new LinkProperties(lp)); + assertLinkPropertiesEqual(lp, new LinkProperties()); + + lp = makeTestObject(); + assertLinkPropertiesEqual(lp, new LinkProperties(lp)); + } + + @Test + public void testDnsServers() { + final LinkProperties lp = new LinkProperties(); + final List dnsServers = Arrays.asList(DNS1, DNS2); + lp.setDnsServers(dnsServers); + assertEquals(2, lp.getDnsServers().size()); + assertEquals(DNS1, lp.getDnsServers().get(0)); + assertEquals(DNS2, lp.getDnsServers().get(1)); + + lp.removeDnsServer(DNS1); + assertEquals(1, lp.getDnsServers().size()); + assertEquals(DNS2, lp.getDnsServers().get(0)); + + lp.addDnsServer(DNS6); + assertEquals(2, lp.getDnsServers().size()); + assertEquals(DNS2, lp.getDnsServers().get(0)); + assertEquals(DNS6, lp.getDnsServers().get(1)); + } + + @Test + public void testValidatedPrivateDnsServers() { + final LinkProperties lp = new LinkProperties(); + final List privDnsServers = Arrays.asList(PRIVDNS1, PRIVDNS2); + lp.setValidatedPrivateDnsServers(privDnsServers); + assertEquals(2, lp.getValidatedPrivateDnsServers().size()); + assertEquals(PRIVDNS1, lp.getValidatedPrivateDnsServers().get(0)); + assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(1)); + + lp.removeValidatedPrivateDnsServer(PRIVDNS1); + assertEquals(1, lp.getValidatedPrivateDnsServers().size()); + assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0)); + + lp.addValidatedPrivateDnsServer(PRIVDNS6); + assertEquals(2, lp.getValidatedPrivateDnsServers().size()); + assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0)); + assertEquals(PRIVDNS6, lp.getValidatedPrivateDnsServers().get(1)); + } + + @Test + public void testPcscfServers() { + final LinkProperties lp = new LinkProperties(); + final List pcscfServers = Arrays.asList(PCSCFV4); + lp.setPcscfServers(pcscfServers); + assertEquals(1, lp.getPcscfServers().size()); + assertEquals(PCSCFV4, lp.getPcscfServers().get(0)); + + lp.removePcscfServer(PCSCFV4); + assertEquals(0, lp.getPcscfServers().size()); + + lp.addPcscfServer(PCSCFV6); + assertEquals(1, lp.getPcscfServers().size()); + assertEquals(PCSCFV6, lp.getPcscfServers().get(0)); + } + + @Test + public void testTcpBufferSizes() { + final LinkProperties lp = makeTestObject(); + assertEquals(TCP_BUFFER_SIZES, lp.getTcpBufferSizes()); + + lp.setTcpBufferSizes(null); + assertNull(lp.getTcpBufferSizes()); + } + + @Test + public void testHasIpv6DefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIPv6DefaultRoute()); + + lp.addRoute(new RouteInfo(GATEWAY61)); + assertTrue(lp.hasIPv6DefaultRoute()); + } + + @Test + public void testHttpProxy() { + final LinkProperties lp = makeTestObject(); + assertTrue(lp.getHttpProxy().equals(ProxyInfo.buildDirectProxy("test", 8888))); + } + + @Test + public void testPrivateDnsServerName() { + final LinkProperties lp = makeTestObject(); + assertEquals(PRIV_DNS_SERVER_NAME, lp.getPrivateDnsServerName()); + + lp.setPrivateDnsServerName(null); + assertNull(lp.getPrivateDnsServerName()); + } + + @Test + public void testUsePrivateDns() { + final LinkProperties lp = makeTestObject(); + assertTrue(lp.isPrivateDnsActive()); + + lp.clear(); + assertFalse(lp.isPrivateDnsActive()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testDhcpServerAddress() { + final LinkProperties lp = makeTestObject(); + assertEquals(DHCPSERVER, lp.getDhcpServerAddress()); + + lp.clear(); + assertNull(lp.getDhcpServerAddress()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testWakeOnLanSupported() { + final LinkProperties lp = makeTestObject(); + assertTrue(lp.isWakeOnLanSupported()); + + lp.clear(); + assertFalse(lp.isWakeOnLanSupported()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testCaptivePortalApiUrl() { + final LinkProperties lp = makeTestObject(); + assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl()); + + lp.clear(); + assertNull(lp.getCaptivePortalApiUrl()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testCaptivePortalData() { + final LinkProperties lp = makeTestObject(); + assertEquals(getCaptivePortalData(), lp.getCaptivePortalData()); + + lp.clear(); + assertNull(lp.getCaptivePortalData()); + } + + private LinkProperties makeIpv4LinkProperties() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(NAME); + linkProperties.addLinkAddress(LINKADDRV4); + linkProperties.addDnsServer(DNS1); + linkProperties.addRoute(new RouteInfo(GATEWAY1)); + linkProperties.addRoute(new RouteInfo(GATEWAY2)); + return linkProperties; + } + + private LinkProperties makeIpv6LinkProperties() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(NAME); + linkProperties.addLinkAddress(LINKADDRV6); + linkProperties.addDnsServer(DNS6); + linkProperties.addRoute(new RouteInfo(GATEWAY61)); + linkProperties.addRoute(new RouteInfo(GATEWAY62)); + return linkProperties; + } + + @Test + public void testHasIpv4DefaultRoute() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertTrue(Ipv4.hasIpv4DefaultRoute()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertFalse(Ipv6.hasIpv4DefaultRoute()); + } + + @Test + public void testHasIpv4DnsServer() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertTrue(Ipv4.hasIpv4DnsServer()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertFalse(Ipv6.hasIpv4DnsServer()); + } + + @Test + public void testHasIpv6DnsServer() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertFalse(Ipv4.hasIpv6DnsServer()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertTrue(Ipv6.hasIpv6DnsServer()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv4UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv6UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testRouteAddWithSameKey() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan0"); + final IpPrefix v6 = new IpPrefix("64:ff9b::/96"); + lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1280)); + assertEquals(1, lp.getRoutes().size()); + lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1500)); + assertEquals(1, lp.getRoutes().size()); + final IpPrefix v4 = new IpPrefix("192.0.2.128/25"); + lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_UNICAST, 1460)); + assertEquals(2, lp.getRoutes().size()); + lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_THROW, 1460)); + assertEquals(2, lp.getRoutes().size()); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt new file mode 100644 index 000000000000..a5e44d59fcab --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt @@ -0,0 +1,75 @@ +/* + * 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 + +import android.net.wifi.aware.DiscoverySession +import android.net.wifi.aware.PeerHandle +import android.net.wifi.aware.WifiAwareNetworkSpecifier +import android.os.Build +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 + +import com.android.testutils.assertParcelSane +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo + +import java.lang.IllegalStateException + +import org.junit.Assert.assertFalse +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito + +@RunWith(AndroidJUnit4::class) +@SmallTest +class MatchAllNetworkSpecifierTest { + @Rule @JvmField + val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() + + private val specifier = MatchAllNetworkSpecifier() + private val discoverySession = Mockito.mock(DiscoverySession::class.java) + private val peerHandle = Mockito.mock(PeerHandle::class.java) + private val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, + peerHandle).build() + + @Test + fun testParcel() { + assertParcelSane(MatchAllNetworkSpecifier(), 0) + } + + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + @IgnoreAfter(Build.VERSION_CODES.R) + // Only run this test on Android R. + // The method - satisfiedBy() has changed to canBeSatisfiedBy() starting from Android R, so the + // method - canBeSatisfiedBy() cannot be found when running this test on Android Q. + fun testCanBeSatisfiedBy_OnlyForR() { + // MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to + // canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the + // NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned. + // Although it's not meeting the expectation, the behavior still needs to be verified. + assertFalse(specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier)) + } + + @Test(expected = IllegalStateException::class) + @IgnoreUpTo(Build.VERSION_CODES.R) + fun testCanBeSatisfiedBy() { + specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt new file mode 100644 index 000000000000..46f39dd016fd --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt @@ -0,0 +1,114 @@ +/* + * 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 + +import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS +import android.net.InvalidPacketException.ERROR_INVALID_PORT +import android.net.NattSocketKeepalive.NATT_PORT +import android.os.Build +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertEqualBothWays +import com.android.testutils.assertFieldCountEquals +import com.android.testutils.assertParcelSane +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.parcelingRoundTrip +import java.net.InetAddress +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.fail +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NattKeepalivePacketDataTest { + @Rule @JvmField + val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() + + /* Refer to the definition in {@code NattKeepalivePacketData} */ + private val IPV4_HEADER_LENGTH = 20 + private val UDP_HEADER_LENGTH = 8 + + private val TEST_PORT = 4243 + private val TEST_PORT2 = 4244 + private val TEST_SRC_ADDRV4 = "198.168.0.2".address() + private val TEST_DST_ADDRV4 = "198.168.0.1".address() + private val TEST_ADDRV6 = "2001:db8::1".address() + + private fun String.address() = InetAddresses.parseNumericAddress(this) + private fun nattKeepalivePacket( + srcAddress: InetAddress? = TEST_SRC_ADDRV4, + srcPort: Int = TEST_PORT, + dstAddress: InetAddress? = TEST_DST_ADDRV4, + dstPort: Int = NATT_PORT + ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort) + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testConstructor() { + try { + nattKeepalivePacket(dstPort = TEST_PORT) + fail("Dst port is not NATT port should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_PORT) + } + + try { + nattKeepalivePacket(srcAddress = TEST_ADDRV6) + fail("A v6 srcAddress should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) + } + + try { + nattKeepalivePacket(dstAddress = TEST_ADDRV6) + fail("A v6 dstAddress should cause exception") + } catch (e: InvalidPacketException) { + assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) + } + + try { + parcelingRoundTrip( + NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT, + byteArrayOf(12, 31, 22, 44))) + fail("Invalid data should cause exception") + } catch (e: IllegalArgumentException) { } + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testParcel() { + assertParcelSane(nattKeepalivePacket(), 0) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testEquals() { + assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket()) + assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket()) + assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket()) + // Test src port only because dst port have to be NATT_PORT + assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket()) + // Make sure the parceling test is updated if fields are added in the base class. + assertFieldCountEquals(5, KeepalivePacketData::class.java) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testHashCode() { + assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode()) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt new file mode 100644 index 000000000000..2b45b3d69ce9 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2019 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 + +import android.os.Build +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.modules.utils.build.SdkLevel.isAtLeastS +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetworkAgentConfigTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testParcelNetworkAgentConfig() { + val config = NetworkAgentConfig.Builder().apply { + setExplicitlySelected(true) + setLegacyType(ConnectivityManager.TYPE_ETHERNET) + setSubscriberId("MySubId") + setPartialConnectivityAcceptable(false) + setUnvalidatedConnectivityAcceptable(true) + if (isAtLeastS()) { + setBypassableVpn(true) + } + }.build() + if (isAtLeastS()) { + // From S, the config will have 12 items + assertParcelSane(config, 12) + } else { + // For R or below, the config will have 10 items + assertParcelSane(config, 10) + } + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testBuilder() { + val config = NetworkAgentConfig.Builder().apply { + setExplicitlySelected(true) + setLegacyType(ConnectivityManager.TYPE_ETHERNET) + setSubscriberId("MySubId") + setPartialConnectivityAcceptable(false) + setUnvalidatedConnectivityAcceptable(true) + setLegacyTypeName("TEST_NETWORK") + if (isAtLeastS()) { + setNat64DetectionEnabled(false) + setProvisioningNotificationEnabled(false) + setBypassableVpn(true) + } + }.build() + + assertTrue(config.isExplicitlySelected()) + assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType()) + assertEquals("MySubId", config.getSubscriberId()) + assertFalse(config.isPartialConnectivityAcceptable()) + assertTrue(config.isUnvalidatedConnectivityAcceptable()) + assertEquals("TEST_NETWORK", config.getLegacyTypeName()) + if (isAtLeastS()) { + assertFalse(config.isNat64DetectionEnabled()) + assertFalse(config.isProvisioningNotificationEnabled()) + assertTrue(config.isBypassableVpn()) + } else { + assertTrue(config.isNat64DetectionEnabled()) + assertTrue(config.isProvisioningNotificationEnabled()) + } + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java new file mode 100644 index 000000000000..9efdde4da0c0 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -0,0 +1,1158 @@ +/* + * Copyright (C) 2017 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; + +import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; +import static android.net.NetworkCapabilities.MAX_TRANSPORT; +import static android.net.NetworkCapabilities.MIN_TRANSPORT; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; +import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; +import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; +import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_TEST; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; +import static android.os.Process.INVALID_UID; + +import static com.android.modules.utils.build.SdkLevel.isAtLeastR; +import static com.android.modules.utils.build.SdkLevel.isAtLeastS; +import static com.android.net.module.util.NetworkCapabilitiesUtils.TRANSPORT_USB; +import static com.android.testutils.MiscAsserts.assertEmpty; +import static com.android.testutils.MiscAsserts.assertThrows; +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import android.net.wifi.aware.DiscoverySession; +import android.net.wifi.aware.PeerHandle; +import android.net.wifi.aware.WifiAwareNetworkSpecifier; +import android.os.Build; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArraySet; +import android.util.Range; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.CompatUtil; +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.Mockito; + +import java.util.Arrays; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkCapabilitiesTest { + private static final String TEST_SSID = "TEST_SSID"; + private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID"; + private static final int TEST_SUBID1 = 1; + private static final int TEST_SUBID2 = 2; + private static final int TEST_SUBID3 = 3; + + @Rule + public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); + + private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class); + private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class); + + @Test + public void testMaybeMarkCapabilitiesRestricted() { + // check that internet does not get restricted + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // metered-ness shouldn't matter + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // add EIMS - bundled with unrestricted means it's unrestricted + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // just a restricted cap should be restricted regardless of meteredness + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // try 2 restricted caps + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_CBS); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.addCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_CBS); + netCap.addCapability(NET_CAPABILITY_EIMS); + netCap.removeCapability(NET_CAPABILITY_NOT_METERED); + netCap.maybeMarkCapabilitiesRestricted(); + assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } + + @Test + public void testDescribeImmutableDifferences() { + NetworkCapabilities nc1; + NetworkCapabilities nc2; + + // Transports changing + nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR); + nc2 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); + assertNotEquals("", nc1.describeImmutableDifferences(nc2)); + assertEquals("", nc1.describeImmutableDifferences(nc1)); + + // Mutable capability changing + nc1 = new NetworkCapabilities().addCapability(NET_CAPABILITY_VALIDATED); + nc2 = new NetworkCapabilities(); + assertEquals("", nc1.describeImmutableDifferences(nc2)); + assertEquals("", nc1.describeImmutableDifferences(nc1)); + + // NOT_METERED changing (http://b/63326103) + nc1 = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_INTERNET); + nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET); + assertEquals("", nc1.describeImmutableDifferences(nc2)); + assertEquals("", nc1.describeImmutableDifferences(nc1)); + + // Immutable capability changing + nc1 = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET); + assertNotEquals("", nc1.describeImmutableDifferences(nc2)); + assertEquals("", nc1.describeImmutableDifferences(nc1)); + + // Specifier changing + nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); + nc2 = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth42")); + assertNotEquals("", nc1.describeImmutableDifferences(nc2)); + assertEquals("", nc1.describeImmutableDifferences(nc1)); + } + + @Test + public void testLinkBandwidthUtils() { + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities + .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); + assertEquals(10, NetworkCapabilities + .minBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .minBandwidth(10, 20)); + + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities + .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(10, NetworkCapabilities + .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); + assertEquals(10, NetworkCapabilities + .maxBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); + assertEquals(20, NetworkCapabilities + .maxBandwidth(10, 20)); + } + + @Test + public void testSetUids() { + final NetworkCapabilities netCap = new NetworkCapabilities(); + // Null uids match all UIDs + netCap.setUids(null); + assertTrue(netCap.appliesToUid(10)); + assertTrue(netCap.appliesToUid(200)); + assertTrue(netCap.appliesToUid(3000)); + assertTrue(netCap.appliesToUid(10010)); + assertTrue(netCap.appliesToUidRange(new UidRange(50, 100))); + assertTrue(netCap.appliesToUidRange(new UidRange(70, 72))); + assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912))); + assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000))); + + if (isAtLeastS()) { + final Set> uids = new ArraySet<>(); + uids.add(uidRange(50, 100)); + uids.add(uidRange(3000, 4000)); + netCap.setUids(uids); + assertTrue(netCap.appliesToUid(50)); + assertTrue(netCap.appliesToUid(80)); + assertTrue(netCap.appliesToUid(100)); + assertTrue(netCap.appliesToUid(3000)); + assertTrue(netCap.appliesToUid(3001)); + assertFalse(netCap.appliesToUid(10)); + assertFalse(netCap.appliesToUid(25)); + assertFalse(netCap.appliesToUid(49)); + assertFalse(netCap.appliesToUid(101)); + assertFalse(netCap.appliesToUid(2000)); + assertFalse(netCap.appliesToUid(100000)); + + assertTrue(netCap.appliesToUidRange(new UidRange(50, 100))); + assertTrue(netCap.appliesToUidRange(new UidRange(70, 72))); + assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912))); + assertFalse(netCap.appliesToUidRange(new UidRange(1, 100))); + assertFalse(netCap.appliesToUidRange(new UidRange(49, 100))); + assertFalse(netCap.appliesToUidRange(new UidRange(1, 10))); + assertFalse(netCap.appliesToUidRange(new UidRange(60, 101))); + assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400))); + + NetworkCapabilities netCap2 = new NetworkCapabilities(); + // A new netcap object has null UIDs, so anything will satisfy it. + assertTrue(netCap2.satisfiedByUids(netCap)); + // Still not equal though. + assertFalse(netCap2.equalsUids(netCap)); + netCap2.setUids(uids); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.equalsUids(netCap2)); + assertTrue(netCap2.equalsUids(netCap)); + + uids.add(uidRange(600, 700)); + netCap2.setUids(uids); + assertFalse(netCap2.satisfiedByUids(netCap)); + assertFalse(netCap.appliesToUid(650)); + assertTrue(netCap2.appliesToUid(650)); + netCap.combineCapabilities(netCap2); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.appliesToUid(650)); + assertFalse(netCap.appliesToUid(500)); + + assertTrue(new NetworkCapabilities().satisfiedByUids(netCap)); + netCap.combineCapabilities(new NetworkCapabilities()); + assertTrue(netCap.appliesToUid(500)); + assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000))); + assertFalse(netCap2.appliesToUid(500)); + assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000))); + assertTrue(new NetworkCapabilities().satisfiedByUids(netCap)); + + // Null uids satisfies everything. + netCap.setUids(null); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.satisfiedByUids(netCap2)); + netCap2.setUids(null); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.satisfiedByUids(netCap2)); + } + } + + @Test + public void testParcelNetworkCapabilities() { + final Set> uids = new ArraySet<>(); + uids.add(uidRange(50, 100)); + uids.add(uidRange(3000, 4000)); + final NetworkCapabilities netCap = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED); + if (isAtLeastS()) { + netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)); + netCap.setUids(uids); + } + if (isAtLeastR()) { + netCap.setOwnerUid(123); + netCap.setAdministratorUids(new int[] {5, 11}); + } + assertParcelingIsLossless(netCap); + netCap.setSSID(TEST_SSID); + testParcelSane(netCap); + } + + @Test + public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() { + final NetworkCapabilities netCap = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED); + if (isAtLeastR()) { + netCap.setRequestorPackageName("com.android.test"); + netCap.setRequestorUid(9304); + } + assertParcelingIsLossless(netCap); + netCap.setSSID(TEST_SSID); + testParcelSane(netCap); + } + + private void testParcelSane(NetworkCapabilities cap) { + if (isAtLeastS()) { + assertParcelSane(cap, 16); + } else if (isAtLeastR()) { + assertParcelSane(cap, 15); + } else { + assertParcelSane(cap, 11); + } + } + + private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() { + return new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED) + .setSSID(TEST_SSID) + .setTransportInfo(new TestTransportInfo()) + .setRequestorPackageName("com.android.test") + .setRequestorUid(9304); + } + + @Test + public void testNetworkCapabilitiesCopyWithNoRedactions() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo(); + final NetworkCapabilities netCapWithNoRedactions = + new NetworkCapabilities(netCap, NetworkCapabilities.REDACT_NONE); + TestTransportInfo testTransportInfo = + (TestTransportInfo) netCapWithNoRedactions.getTransportInfo(); + assertFalse(testTransportInfo.locationRedacted); + assertFalse(testTransportInfo.localMacAddressRedacted); + assertFalse(testTransportInfo.settingsRedacted); + } + + @Test + public void testNetworkCapabilitiesCopyWithoutLocationSensitiveFields() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo(); + final NetworkCapabilities netCapWithNoRedactions = + new NetworkCapabilities(netCap, REDACT_FOR_ACCESS_FINE_LOCATION); + TestTransportInfo testTransportInfo = + (TestTransportInfo) netCapWithNoRedactions.getTransportInfo(); + assertTrue(testTransportInfo.locationRedacted); + assertFalse(testTransportInfo.localMacAddressRedacted); + assertFalse(testTransportInfo.settingsRedacted); + } + + @Test + public void testOemPaid() { + NetworkCapabilities nc = new NetworkCapabilities(); + // By default OEM_PAID is neither in the required or forbidden lists and the network is not + // restricted. + if (isAtLeastS()) { + assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PAID)); + } + assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID)); + nc.maybeMarkCapabilitiesRestricted(); + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Adding OEM_PAID to capability list should make network restricted. + nc.addCapability(NET_CAPABILITY_OEM_PAID); + nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability. + nc.maybeMarkCapabilitiesRestricted(); + assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PAID)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Now let's make request for OEM_PAID network. + NetworkCapabilities nr = new NetworkCapabilities(); + nr.addCapability(NET_CAPABILITY_OEM_PAID); + nr.maybeMarkCapabilitiesRestricted(); + assertTrue(nr.satisfiedByNetworkCapabilities(nc)); + + // Request fails for network with the default capabilities. + assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities())); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testOemPrivate() { + NetworkCapabilities nc = new NetworkCapabilities(); + // By default OEM_PRIVATE is neither in the required or forbidden lists and the network is + // not restricted. + assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PRIVATE)); + assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE)); + nc.maybeMarkCapabilitiesRestricted(); + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Adding OEM_PRIVATE to capability list should make network restricted. + nc.addCapability(NET_CAPABILITY_OEM_PRIVATE); + nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability. + nc.maybeMarkCapabilitiesRestricted(); + assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Now let's make request for OEM_PRIVATE network. + NetworkCapabilities nr = new NetworkCapabilities(); + nr.addCapability(NET_CAPABILITY_OEM_PRIVATE); + nr.maybeMarkCapabilitiesRestricted(); + assertTrue(nr.satisfiedByNetworkCapabilities(nc)); + + // Request fails for network with the default capabilities. + assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities())); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testForbiddenCapabilities() { + NetworkCapabilities network = new NetworkCapabilities(); + + NetworkCapabilities request = new NetworkCapabilities(); + assertTrue("Request: " + request + ", Network:" + network, + request.satisfiedByNetworkCapabilities(network)); + + // Requesting absence of capabilities that network doesn't have. Request should satisfy. + request.addForbiddenCapability(NET_CAPABILITY_WIFI_P2P); + request.addForbiddenCapability(NET_CAPABILITY_NOT_METERED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + assertArrayEquals(new int[]{NET_CAPABILITY_WIFI_P2P, + NET_CAPABILITY_NOT_METERED}, + request.getForbiddenCapabilities()); + + // This is a default capability, just want to make sure its there because we use it below. + assertTrue(network.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Verify that adding forbidden capability will effectively remove it from capability list. + request.addForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Now this request won't be satisfied because network contains NOT_RESTRICTED. + assertFalse(request.satisfiedByNetworkCapabilities(network)); + network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + + // Verify that adding capability will effectively remove it from forbidden list + request.addCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertFalse(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + assertFalse(request.satisfiedByNetworkCapabilities(network)); + network.addCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + } + + @Test + public void testConnectivityManagedCapabilities() { + NetworkCapabilities nc = new NetworkCapabilities(); + assertFalse(nc.hasConnectivityManagedCapability()); + // Check every single system managed capability. + nc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + assertTrue(nc.hasConnectivityManagedCapability()); + nc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + nc.addCapability(NET_CAPABILITY_FOREGROUND); + assertTrue(nc.hasConnectivityManagedCapability()); + nc.removeCapability(NET_CAPABILITY_FOREGROUND); + nc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); + assertTrue(nc.hasConnectivityManagedCapability()); + nc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); + nc.addCapability(NET_CAPABILITY_VALIDATED); + assertTrue(nc.hasConnectivityManagedCapability()); + } + + @Test + public void testEqualsNetCapabilities() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + assertTrue(nc1.equalsNetCapabilities(nc2)); + assertEquals(nc1, nc2); + + nc1.addCapability(NET_CAPABILITY_MMS); + assertFalse(nc1.equalsNetCapabilities(nc2)); + assertNotEquals(nc1, nc2); + nc2.addCapability(NET_CAPABILITY_MMS); + assertTrue(nc1.equalsNetCapabilities(nc2)); + assertEquals(nc1, nc2); + + if (isAtLeastS()) { + nc1.addForbiddenCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.addForbiddenCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + + // Remove a required capability doesn't affect forbidden capabilities. + // This is a behaviour change from R to S. + nc1.removeCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + + nc1.removeForbiddenCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeForbiddenCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } + } + + @Test + public void testSSID() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + assertTrue(nc2.satisfiedBySSID(nc1)); + + nc1.setSSID(TEST_SSID); + assertTrue(nc2.satisfiedBySSID(nc1)); + nc2.setSSID("different " + TEST_SSID); + assertFalse(nc2.satisfiedBySSID(nc1)); + + assertTrue(nc1.satisfiedByImmutableNetworkCapabilities(nc2)); + assertFalse(nc1.satisfiedByNetworkCapabilities(nc2)); + } + + private ArraySet> uidRanges(int from, int to) { + final ArraySet> range = new ArraySet<>(1); + range.add(uidRange(from, to)); + return range; + } + + private Range uidRange(int from, int to) { + return new Range(from, to); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testSetAdministratorUids() { + NetworkCapabilities nc = + new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3}); + + assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testSetAdministratorUidsWithDuplicates() { + try { + new NetworkCapabilities().setAdministratorUids(new int[] {1, 1}); + fail("Expected IllegalArgumentException for duplicate uids"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testCombineCapabilities() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + if (isAtLeastS()) { + nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + } + nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); + assertNotEquals(nc1, nc2); + nc2.combineCapabilities(nc1); + assertEquals(nc1, nc2); + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + if (isAtLeastS()) { + assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); + } + + if (isAtLeastS()) { + // This will effectively move NOT_ROAMING capability from required to forbidden for nc1. + nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); + // It is not allowed to have the same capability in both wanted and forbidden list. + assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1)); + // Remove forbidden capability to continue other tests. + nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); + } + + nc1.setSSID(TEST_SSID); + nc2.combineCapabilities(nc1); + if (isAtLeastR()) { + assertTrue(TEST_SSID.equals(nc2.getSsid())); + } + + // Because they now have the same SSID, the following call should not throw + nc2.combineCapabilities(nc1); + + nc1.setSSID(DIFFERENT_TEST_SSID); + try { + nc2.combineCapabilities(nc1); + fail("Expected IllegalStateException: can't combine different SSIDs"); + } catch (IllegalStateException expected) {} + nc1.setSSID(TEST_SSID); + + if (isAtLeastS()) { + nc1.setUids(uidRanges(10, 13)); + assertNotEquals(nc1, nc2); + nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything. + assertNotEquals(nc1, nc2); + nc1.combineCapabilities(nc2); // 10~13 + everything is everything. + assertEquals(nc1, nc2); + nc1.setUids(uidRanges(10, 13)); + nc2.setUids(uidRanges(20, 23)); + assertNotEquals(nc1, nc2); + nc1.combineCapabilities(nc2); + assertTrue(nc1.appliesToUid(12)); + assertFalse(nc2.appliesToUid(12)); + assertTrue(nc1.appliesToUid(22)); + assertTrue(nc2.appliesToUid(22)); + + // Verify the subscription id list can be combined only when they are equal. + nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)); + nc2.setSubscriptionIds(Set.of(TEST_SUBID2)); + assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); + + nc2.setSubscriptionIds(Set.of()); + assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); + + nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1)); + nc2.combineCapabilities(nc1); + assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds()); + } + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testCombineCapabilities_AdministratorUids() { + final NetworkCapabilities nc1 = new NetworkCapabilities(); + final NetworkCapabilities nc2 = new NetworkCapabilities(); + + final int[] adminUids = {3, 6, 12}; + nc1.setAdministratorUids(adminUids); + nc2.combineCapabilities(nc1); + assertTrue(nc2.equalsAdministratorUids(nc1)); + assertArrayEquals(nc2.getAdministratorUids(), adminUids); + + final int[] adminUidsOtherOrder = {3, 12, 6}; + nc1.setAdministratorUids(adminUidsOtherOrder); + assertTrue(nc2.equalsAdministratorUids(nc1)); + + final int[] adminUids2 = {11, 1, 12, 3, 6}; + nc1.setAdministratorUids(adminUids2); + assertFalse(nc2.equalsAdministratorUids(nc1)); + assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2)); + try { + nc2.combineCapabilities(nc1); + fail("Shouldn't be able to combine different lists of admin UIDs"); + } catch (IllegalStateException expected) { } + } + + @Test + public void testSetCapabilities() { + final int[] REQUIRED_CAPABILITIES = new int[] { + NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN }; + + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + nc1.setCapabilities(REQUIRED_CAPABILITIES); + assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities()); + + // Verify that setting and adding capabilities leads to the same object state. + nc2.clearAll(); + for (int cap : REQUIRED_CAPABILITIES) { + nc2.addCapability(cap); + } + assertEquals(nc1, nc2); + + if (isAtLeastS()) { + final int[] forbiddenCapabilities = new int[]{ + NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_RESTRICTED }; + + nc1.setCapabilities(REQUIRED_CAPABILITIES, forbiddenCapabilities); + assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities()); + assertArrayEquals(forbiddenCapabilities, nc1.getForbiddenCapabilities()); + + nc2.clearAll(); + for (int cap : REQUIRED_CAPABILITIES) { + nc2.addCapability(cap); + } + for (int cap : forbiddenCapabilities) { + nc2.addForbiddenCapability(cap); + } + assertEquals(nc1, nc2); + } + } + + @Test + public void testSetNetworkSpecifierOnMultiTransportNc() { + // Sequence 1: Transport + Transport + NetworkSpecifier + NetworkCapabilities nc1 = new NetworkCapabilities(); + nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI); + try { + nc1.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth0")); + fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!"); + } catch (IllegalStateException expected) { + // empty + } + + // Sequence 2: Transport + NetworkSpecifier + Transport + NetworkCapabilities nc2 = new NetworkCapabilities(); + nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier( + CompatUtil.makeEthernetNetworkSpecifier("testtap3")); + try { + nc2.addTransportType(TRANSPORT_WIFI); + fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!"); + } catch (IllegalStateException expected) { + // empty + } + } + + @Test + public void testSetTransportInfoOnMultiTransportNc() { + // Sequence 1: Transport + Transport + TransportInfo + NetworkCapabilities nc1 = new NetworkCapabilities(); + nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI) + .setTransportInfo(new TestTransportInfo()); + + // Sequence 2: Transport + NetworkSpecifier + Transport + NetworkCapabilities nc2 = new NetworkCapabilities(); + nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo()) + .addTransportType(TRANSPORT_WIFI); + } + + @Test + public void testCombineTransportInfo() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + nc1.setTransportInfo(new TestTransportInfo()); + + NetworkCapabilities nc2 = new NetworkCapabilities(); + // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where + // combine fails) + nc2.setTransportInfo(new TestTransportInfo()); + + try { + nc1.combineCapabilities(nc2); + fail("Should not be able to combine NetworkCabilities which contain TransportInfos"); + } catch (IllegalStateException expected) { + // empty + } + + // verify that can combine with identical TransportInfo objects + NetworkCapabilities nc3 = new NetworkCapabilities(); + nc3.setTransportInfo(nc1.getTransportInfo()); + nc1.combineCapabilities(nc3); + } + + @Test + public void testSet() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + if (isAtLeastS()) { + nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + } + nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); + assertNotEquals(nc1, nc2); + nc2.set(nc1); + assertEquals(nc1, nc2); + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + if (isAtLeastS()) { + assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); + } + + if (isAtLeastS()) { + // This will effectively move NOT_ROAMING capability from required to forbidden for nc1. + nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); + } + nc1.setSSID(TEST_SSID); + nc2.set(nc1); + assertEquals(nc1, nc2); + if (isAtLeastS()) { + // Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability + // from nc2. + assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_NOT_ROAMING)); + } + + if (isAtLeastR()) { + assertTrue(TEST_SSID.equals(nc2.getSsid())); + } + + nc1.setSSID(DIFFERENT_TEST_SSID); + nc2.set(nc1); + assertEquals(nc1, nc2); + if (isAtLeastR()) { + assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid())); + } + if (isAtLeastS()) { + nc1.setUids(uidRanges(10, 13)); + } else { + nc1.setUids(null); + } + nc2.set(nc1); // Overwrites, as opposed to combineCapabilities + assertEquals(nc1, nc2); + + if (isAtLeastS()) { + assertThrows(NullPointerException.class, () -> nc1.setSubscriptionIds(null)); + nc1.setSubscriptionIds(Set.of()); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc1.setSubscriptionIds(Set.of(TEST_SUBID1)); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1)); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc2.setSubscriptionIds(Set.of(TEST_SUBID3, TEST_SUBID2)); + assertNotEquals(nc1, nc2); + } + } + + @Test + public void testGetTransportTypes() { + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.addTransportType(TRANSPORT_CELLULAR); + nc.addTransportType(TRANSPORT_WIFI); + nc.addTransportType(TRANSPORT_VPN); + nc.addTransportType(TRANSPORT_TEST); + + final int[] transportTypes = nc.getTransportTypes(); + assertEquals(4, transportTypes.length); + assertEquals(TRANSPORT_CELLULAR, transportTypes[0]); + assertEquals(TRANSPORT_WIFI, transportTypes[1]); + assertEquals(TRANSPORT_VPN, transportTypes[2]); + assertEquals(TRANSPORT_TEST, transportTypes[3]); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testTelephonyNetworkSpecifier() { + final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1); + final NetworkCapabilities nc1 = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .setNetworkSpecifier(specifier) + .build(); + assertEquals(specifier, nc1.getNetworkSpecifier()); + try { + final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() + .setNetworkSpecifier(specifier) + .build(); + fail("Must have a single transport type. Without transport type or multiple transport" + + " types is invalid."); + } catch (IllegalStateException expected) { } + } + + @Test + public void testWifiAwareNetworkSpecifier() { + final NetworkCapabilities nc = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI_AWARE); + // If NetworkSpecifier is not set, the default value is null. + assertNull(nc.getNetworkSpecifier()); + final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder( + mDiscoverySession, mPeerHandle).build(); + nc.setNetworkSpecifier(specifier); + assertEquals(specifier, nc.getNetworkSpecifier()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testAdministratorUidsAndOwnerUid() { + // Test default owner uid. + // If the owner uid is not set, the default value should be Process.INVALID_UID. + final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build(); + assertEquals(INVALID_UID, nc1.getOwnerUid()); + // Test setAdministratorUids and getAdministratorUids. + final int[] administratorUids = {1001, 10001}; + final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() + .setAdministratorUids(administratorUids) + .build(); + assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids())); + // Test setOwnerUid and getOwnerUid. + // The owner UID must be included in administrator UIDs, or throw IllegalStateException. + try { + final NetworkCapabilities nc3 = new NetworkCapabilities.Builder() + .setOwnerUid(1001) + .build(); + fail("The owner UID must be included in administrator UIDs."); + } catch (IllegalStateException expected) { } + final NetworkCapabilities nc4 = new NetworkCapabilities.Builder() + .setAdministratorUids(administratorUids) + .setOwnerUid(1001) + .build(); + assertEquals(1001, nc4.getOwnerUid()); + try { + final NetworkCapabilities nc5 = new NetworkCapabilities.Builder() + .setAdministratorUids(null) + .build(); + fail("Should not set null into setAdministratorUids"); + } catch (NullPointerException expected) { } + } + + private static NetworkCapabilities capsWithSubIds(Integer ... subIds) { + // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for + // every NetworkCapabilities that simulates networks needs to add it too in order to + // satisfy these requests. + final NetworkCapabilities nc = new NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setSubscriptionIds(new ArraySet<>(subIds)).build(); + assertEquals(new ArraySet<>(subIds), nc.getSubscriptionIds()); + return nc; + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testSubIds() throws Exception { + final NetworkCapabilities ncWithoutId = capsWithSubIds(); + final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1); + final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3); + final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3); + + final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build(); + assertEmpty(requestWithoutId.networkCapabilities.getSubscriptionIds()); + final NetworkRequest requestWithIds = new NetworkRequest.Builder() + .setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build(); + assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2), + requestWithIds.networkCapabilities.getSubscriptionIds()); + + assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId)); + assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds)); + assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds)); + assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId)); + assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId)); + assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId)); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testEqualsSubIds() throws Exception { + assertEquals(capsWithSubIds(), capsWithSubIds()); + assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1)); + assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1)); + assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2)); + assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); + assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2), + capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); + } + + @Test + public void testLinkBandwidthKbps() { + final NetworkCapabilities nc = new NetworkCapabilities(); + // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED. + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps()); + assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps()); + nc.setLinkDownstreamBandwidthKbps(512); + nc.setLinkUpstreamBandwidthKbps(128); + assertEquals(512, nc.getLinkDownstreamBandwidthKbps()); + assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps()); + assertEquals(128, nc.getLinkUpstreamBandwidthKbps()); + assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps()); + } + + private int getMaxTransport() { + if (!isAtLeastS() && MAX_TRANSPORT == TRANSPORT_USB) return MAX_TRANSPORT - 1; + return MAX_TRANSPORT; + } + + @Test + public void testSignalStrength() { + final NetworkCapabilities nc = new NetworkCapabilities(); + // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED. + assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength()); + nc.setSignalStrength(-80); + assertEquals(-80, nc.getSignalStrength()); + assertNotEquals(-50, nc.getSignalStrength()); + } + + private void assertNoTransport(NetworkCapabilities nc) { + for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { + assertFalse(nc.hasTransport(i)); + } + } + + // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all + // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence + // is true. If positiveSequence is false, then the check sequence is opposite. + private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType, + boolean positiveSequence) { + for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) { + if (positiveSequence) { + assertTrue(nc.hasTransport(i)); + } else { + assertFalse(nc.hasTransport(i)); + } + } + for (int i = getMaxTransport(); i > maxTransportType; i--) { + if (positiveSequence) { + assertFalse(nc.hasTransport(i)); + } else { + assertTrue(nc.hasTransport(i)); + } + } + } + + @Test + public void testMultipleTransportTypes() { + final NetworkCapabilities nc = new NetworkCapabilities(); + assertNoTransport(nc); + // Test adding multiple transport types. + for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { + nc.addTransportType(i); + checkCurrentTransportTypes(nc, i, true /* positiveSequence */); + } + // Test removing multiple transport types. + for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { + nc.removeTransportType(i); + checkCurrentTransportTypes(nc, i, false /* positiveSequence */); + } + assertNoTransport(nc); + nc.addTransportType(TRANSPORT_WIFI); + assertTrue(nc.hasTransport(TRANSPORT_WIFI)); + assertFalse(nc.hasTransport(TRANSPORT_VPN)); + nc.addTransportType(TRANSPORT_VPN); + assertTrue(nc.hasTransport(TRANSPORT_WIFI)); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + nc.removeTransportType(TRANSPORT_WIFI); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + nc.removeTransportType(TRANSPORT_VPN); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + assertFalse(nc.hasTransport(TRANSPORT_VPN)); + assertNoTransport(nc); + } + + @Test + public void testAddAndRemoveTransportType() { + final NetworkCapabilities nc = new NetworkCapabilities(); + try { + nc.addTransportType(-1); + fail("Should not set invalid transport type into addTransportType"); + } catch (IllegalArgumentException expected) { } + try { + nc.removeTransportType(-1); + fail("Should not set invalid transport type into removeTransportType"); + } catch (IllegalArgumentException e) { } + } + + /** + * Test TransportInfo to verify redaction mechanism. + */ + private static class TestTransportInfo implements TransportInfo { + public final boolean locationRedacted; + public final boolean localMacAddressRedacted; + public final boolean settingsRedacted; + + TestTransportInfo() { + locationRedacted = false; + localMacAddressRedacted = false; + settingsRedacted = false; + } + + TestTransportInfo(boolean locationRedacted, + boolean localMacAddressRedacted, + boolean settingsRedacted) { + this.locationRedacted = locationRedacted; + this.localMacAddressRedacted = + localMacAddressRedacted; + this.settingsRedacted = settingsRedacted; + } + + @Override + public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { + return new TestTransportInfo( + (redactions & NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION) != 0, + (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, + (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 + ); + } + + @Override + public @NetworkCapabilities.RedactionType long getApplicableRedactions() { + return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS + | REDACT_FOR_NETWORK_SETTINGS; + } + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testBuilder() { + final int ownerUid = 1001; + final int signalStrength = -80; + final int requestUid = 10100; + final int[] administratorUids = {ownerUid, 10001}; + final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1); + final TransportInfo transportInfo = new TransportInfo() {}; + final String ssid = "TEST_SSID"; + final String packageName = "com.google.test.networkcapabilities"; + final NetworkCapabilities nc = new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addTransportType(TRANSPORT_CELLULAR) + .removeTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_CBS) + .removeCapability(NET_CAPABILITY_CBS) + .setAdministratorUids(administratorUids) + .setOwnerUid(ownerUid) + .setLinkDownstreamBandwidthKbps(512) + .setLinkUpstreamBandwidthKbps(128) + .setNetworkSpecifier(specifier) + .setTransportInfo(transportInfo) + .setSignalStrength(signalStrength) + .setSsid(ssid) + .setRequestorUid(requestUid) + .setRequestorPackageName(packageName) + .build(); + assertEquals(1, nc.getTransportTypes().length); + assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]); + assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS)); + assertFalse(nc.hasCapability(NET_CAPABILITY_CBS)); + assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids())); + assertEquals(ownerUid, nc.getOwnerUid()); + assertEquals(512, nc.getLinkDownstreamBandwidthKbps()); + assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps()); + assertEquals(128, nc.getLinkUpstreamBandwidthKbps()); + assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps()); + assertEquals(specifier, nc.getNetworkSpecifier()); + assertEquals(transportInfo, nc.getTransportInfo()); + assertEquals(signalStrength, nc.getSignalStrength()); + assertNotEquals(-50, nc.getSignalStrength()); + assertEquals(ssid, nc.getSsid()); + assertEquals(requestUid, nc.getRequestorUid()); + assertEquals(packageName, nc.getRequestorPackageName()); + // Cannot assign null into NetworkCapabilities.Builder + try { + final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null); + fail("Should not set null into NetworkCapabilities.Builder"); + } catch (NullPointerException expected) { } + assertEquals(nc, new NetworkCapabilities.Builder(nc).build()); + + if (isAtLeastS()) { + final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() + .setSubscriptionIds(Set.of(TEST_SUBID1)).build(); + assertEquals(Set.of(TEST_SUBID1), nc2.getSubscriptionIds()); + } + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt new file mode 100644 index 000000000000..7424157bea74 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt @@ -0,0 +1,201 @@ +/* + * 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 + +import android.app.Instrumentation +import android.content.Context +import android.net.NetworkCapabilities.TRANSPORT_TEST +import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable +import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn +import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested +import android.os.Build +import android.os.HandlerThread +import android.os.Looper +import androidx.test.InstrumentationRegistry +import com.android.net.module.util.ArrayTrackRecord +import com.android.testutils.CompatUtil +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.isDevSdkInRange +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.verifyNoMoreInteractions +import java.util.UUID +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +private const val DEFAULT_TIMEOUT_MS = 5000L +private val instrumentation: Instrumentation + get() = InstrumentationRegistry.getInstrumentation() +private val context: Context get() = InstrumentationRegistry.getContext() +private val PROVIDER_NAME = "NetworkProviderTest" + +@RunWith(DevSdkIgnoreRunner::class) +@IgnoreUpTo(Build.VERSION_CODES.Q) +class NetworkProviderTest { + private val mCm = context.getSystemService(ConnectivityManager::class.java) + private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread") + + @Before + fun setUp() { + instrumentation.getUiAutomation().adoptShellPermissionIdentity() + mHandlerThread.start() + } + + @After + fun tearDown() { + mHandlerThread.quitSafely() + instrumentation.getUiAutomation().dropShellPermissionIdentity() + } + + private class TestNetworkProvider(context: Context, looper: Looper) : + NetworkProvider(context, looper, PROVIDER_NAME) { + private val seenEvents = ArrayTrackRecord().newReadHead() + + sealed class CallbackEntry { + data class OnNetworkRequested( + val request: NetworkRequest, + val score: Int, + val id: Int + ) : CallbackEntry() + data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry() + } + + override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) { + seenEvents.add(OnNetworkRequested(request, score, id)) + } + + override fun onNetworkRequestWithdrawn(request: NetworkRequest) { + seenEvents.add(OnNetworkRequestWithdrawn(request)) + } + + inline fun expectCallback( + crossinline predicate: (T) -> Boolean + ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } + } + + private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider { + return TestNetworkProvider(ctx, mHandlerThread.looper) + } + + @Test + fun testOnNetworkRequested() { + val provider = createNetworkProvider() + assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + mCm.registerNetworkProvider(provider) + assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + + val specifier = CompatUtil.makeTestNetworkSpecifier( + UUID.randomUUID().toString()) + val nr: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier) + .build() + val cb = ConnectivityManager.NetworkCallback() + mCm.requestNetwork(nr, cb) + provider.expectCallback() { callback -> + callback.request.getNetworkSpecifier() == specifier && + callback.request.hasTransport(TRANSPORT_TEST) + } + + val initialScore = 40 + val updatedScore = 60 + val nc = NetworkCapabilities().apply { + addTransportType(NetworkCapabilities.TRANSPORT_TEST) + removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) + addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + setNetworkSpecifier(specifier) + } + val lp = LinkProperties() + val config = NetworkAgentConfig.Builder().build() + val agent = object : NetworkAgent(context, mHandlerThread.looper, "TestAgent", nc, lp, + initialScore, config, provider) {} + + provider.expectCallback() { callback -> + callback.request.getNetworkSpecifier() == specifier && + callback.score == initialScore && + callback.id == agent.providerId + } + + agent.sendNetworkScore(updatedScore) + provider.expectCallback() { callback -> + callback.request.getNetworkSpecifier() == specifier && + callback.score == updatedScore && + callback.id == agent.providerId + } + + mCm.unregisterNetworkCallback(cb) + provider.expectCallback() { callback -> + callback.request.getNetworkSpecifier() == specifier && + callback.request.hasTransport(TRANSPORT_TEST) + } + mCm.unregisterNetworkProvider(provider) + // Provider id should be ID_NONE after unregister network provider + assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + // unregisterNetworkProvider should not crash even if it's called on an + // already unregistered provider. + mCm.unregisterNetworkProvider(provider) + } + + private class TestNetworkCallback : ConnectivityManager.NetworkCallback() { + private val seenEvents = ArrayTrackRecord().newReadHead() + sealed class CallbackEntry { + object OnUnavailable : CallbackEntry() + } + + override fun onUnavailable() { + seenEvents.add(OnUnavailable) + } + + inline fun expectCallback( + crossinline predicate: (T) -> Boolean + ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } + } + + @Test + fun testDeclareNetworkRequestUnfulfillable() { + val mockContext = mock(Context::class.java) + doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE) + val provider = createNetworkProvider(mockContext) + // ConnectivityManager not required at creation time after R + if (!isDevSdkInRange(0, Build.VERSION_CODES.R)) { + verifyNoMoreInteractions(mockContext) + } + + mCm.registerNetworkProvider(provider) + + val specifier = CompatUtil.makeTestNetworkSpecifier( + UUID.randomUUID().toString()) + val nr: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier) + .build() + + val cb = TestNetworkCallback() + mCm.requestNetwork(nr, cb) + provider.declareNetworkRequestUnfulfillable(nr) + cb.expectCallback() { nr.getNetworkSpecifier() == specifier } + mCm.unregisterNetworkProvider(provider) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt new file mode 100644 index 000000000000..f3409f53596f --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt @@ -0,0 +1,67 @@ +/* + * 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 + +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import kotlin.test.assertTrue +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotEquals +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@IgnoreUpTo(Build.VERSION_CODES.Q) +class NetworkSpecifierTest { + private class TestNetworkSpecifier( + val intData: Int = 123, + val stringData: String = "init" + ) : NetworkSpecifier() { + override fun canBeSatisfiedBy(other: NetworkSpecifier?): Boolean = + other != null && + other is TestNetworkSpecifier && + other.intData >= intData && + stringData.equals(other.stringData) + + override fun redact(): NetworkSpecifier = TestNetworkSpecifier(intData, "redact") + } + + @Test + fun testRedact() { + val ns: TestNetworkSpecifier = TestNetworkSpecifier() + val redactNs = ns.redact() + assertTrue(redactNs is TestNetworkSpecifier) + assertEquals(ns.intData, redactNs.intData) + assertNotEquals(ns.stringData, redactNs.stringData) + assertTrue("redact".equals(redactNs.stringData)) + } + + @Test + fun testcanBeSatisfiedBy() { + val target: TestNetworkSpecifier = TestNetworkSpecifier() + assertFalse(target.canBeSatisfiedBy(null)) + assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier())) + val otherNs = TelephonyNetworkSpecifier.Builder().setSubscriptionId(123).build() + assertFalse(target.canBeSatisfiedBy(otherNs)) + assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 999))) + assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 1))) + assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(stringData = "diff"))) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java new file mode 100644 index 000000000000..f8f9c72374ad --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 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; + +import static org.junit.Assert.assertEquals; + +import android.os.Build; +import android.os.IBinder; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class NetworkStackTest { + @Rule + public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); + + @Mock private IBinder mConnectorBinder; + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testGetService() { + NetworkStack.setServiceForTest(mConnectorBinder); + assertEquals(NetworkStack.getService(), mConnectorBinder); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt new file mode 100644 index 000000000000..0ca4d9551f39 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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 + +import android.net.ConnectivityManager.TYPE_NONE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.InetAddresses.parseNumericAddress +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import java.net.Inet4Address +import java.net.Inet6Address + +private const val TEST_IMSI = "imsi1" +private const val TEST_SSID = "SSID1" +private const val TEST_NETID = 123 + +private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address +private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address +private val TEST_IPV4_LINKADDR = LinkAddress("192.168.222.123/24") +private val TEST_IPV6_LINKADDR = LinkAddress("2001:db8::123/64") +private val TEST_IFACE = "fake0" +private val TEST_LINK_PROPERTIES = LinkProperties().apply { + interfaceName = TEST_IFACE + addLinkAddress(TEST_IPV4_LINKADDR) + addLinkAddress(TEST_IPV6_LINKADDR) + + // Add default routes + addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY)) + addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_GATEWAY)) +} + +private val TEST_CAPABILITIES = NetworkCapabilities().apply { + addTransportType(TRANSPORT_WIFI) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) + setSSID(TEST_SSID) +} + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class NetworkStateSnapshotTest { + + @Test + fun testParcelUnparcel() { + val emptySnapshot = NetworkStateSnapshot(Network(TEST_NETID), NetworkCapabilities(), + LinkProperties(), null, TYPE_NONE) + val snapshot = NetworkStateSnapshot( + Network(TEST_NETID), TEST_CAPABILITIES, TEST_LINK_PROPERTIES, TEST_IMSI, TYPE_WIFI) + assertParcelSane(emptySnapshot, 5) + assertParcelSane(snapshot, 5) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java new file mode 100644 index 000000000000..7423c733c6eb --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015 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; + +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 android.os.Build; +import android.platform.test.annotations.AppModeFull; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.net.DatagramSocket; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.SocketException; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkTest { + final Network mNetwork = new Network(99); + + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + + @Test + public void testBindSocketOfInvalidFdThrows() throws Exception { + + final FileDescriptor fd = new FileDescriptor(); + assertFalse(fd.valid()); + + try { + mNetwork.bindSocket(fd); + fail("SocketException not thrown"); + } catch (SocketException expected) {} + } + + @Test + public void testBindSocketOfNonSocketFdThrows() throws Exception { + final File devNull = new File("/dev/null"); + assertTrue(devNull.canRead()); + + final FileInputStream fis = new FileInputStream(devNull); + assertTrue(null != fis.getFD()); + assertTrue(fis.getFD().valid()); + + try { + mNetwork.bindSocket(fis.getFD()); + fail("SocketException not thrown"); + } catch (SocketException expected) {} + } + + @Test + @AppModeFull(reason = "Socket cannot bind in instant app mode") + public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception { + final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY); + mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53); + assertTrue(mDgramSocket.isConnected()); + + try { + mNetwork.bindSocket(mDgramSocket); + fail("SocketException not thrown"); + } catch (SocketException expected) {} + } + + @Test + public void testBindSocketOfLocalSocketThrows() throws Exception { + final LocalSocket mLocalClient = new LocalSocket(); + mLocalClient.bind(new LocalSocketAddress("testClient")); + assertTrue(mLocalClient.getFileDescriptor().valid()); + + try { + mNetwork.bindSocket(mLocalClient.getFileDescriptor()); + fail("SocketException not thrown"); + } catch (SocketException expected) {} + + final LocalServerSocket mLocalServer = new LocalServerSocket("testServer"); + mLocalClient.connect(mLocalServer.getLocalSocketAddress()); + assertTrue(mLocalClient.isConnected()); + + try { + mNetwork.bindSocket(mLocalClient.getFileDescriptor()); + fail("SocketException not thrown"); + } catch (SocketException expected) {} + } + + @Test + public void testZeroIsObviousForDebugging() { + Network zero = new Network(0); + assertEquals(0, zero.hashCode()); + assertEquals(0, zero.getNetworkHandle()); + assertEquals("0", zero.toString()); + } + + @Test + public void testGetNetworkHandle() { + Network one = new Network(1); + Network two = new Network(2); + Network three = new Network(3); + + // None of the hashcodes are zero. + assertNotEquals(0, one.hashCode()); + assertNotEquals(0, two.hashCode()); + assertNotEquals(0, three.hashCode()); + + // All the hashcodes are distinct. + assertNotEquals(one.hashCode(), two.hashCode()); + assertNotEquals(one.hashCode(), three.hashCode()); + assertNotEquals(two.hashCode(), three.hashCode()); + + // None of the handles are zero. + assertNotEquals(0, one.getNetworkHandle()); + assertNotEquals(0, two.getNetworkHandle()); + assertNotEquals(0, three.getNetworkHandle()); + + // All the handles are distinct. + assertNotEquals(one.getNetworkHandle(), two.getNetworkHandle()); + assertNotEquals(one.getNetworkHandle(), three.getNetworkHandle()); + assertNotEquals(two.getNetworkHandle(), three.getNetworkHandle()); + + // The handles are not equal to the hashcodes. + assertNotEquals(one.hashCode(), one.getNetworkHandle()); + assertNotEquals(two.hashCode(), two.getNetworkHandle()); + assertNotEquals(three.hashCode(), three.getNetworkHandle()); + + // Adjust as necessary to test an implementation's specific constants. + // When running with runtest, "adb logcat -s TestRunner" can be useful. + assertEquals(7700664333L, one.getNetworkHandle()); + assertEquals(11995631629L, two.getNetworkHandle()); + assertEquals(16290598925L, three.getNetworkHandle()); + } + + // getNetId() did not exist in Q + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testGetNetId() { + assertEquals(1234, new Network(1234).getNetId()); + assertEquals(2345, new Network(2345, true).getNetId()); + } + + @Test + public void testFromNetworkHandle() { + final Network network = new Network(1234); + assertEquals(network.netId, Network.fromNetworkHandle(network.getNetworkHandle()).netId); + } + + // Parsing private DNS bypassing handle was not supported until S + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testFromNetworkHandlePrivateDnsBypass_S() { + final Network network = new Network(1234, true); + + final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle()); + assertEquals(network.netId, recreatedNetwork.netId); + assertEquals(network.getNetIdForResolv(), recreatedNetwork.getNetIdForResolv()); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.R) + public void testFromNetworkHandlePrivateDnsBypass_R() { + final Network network = new Network(1234, true); + + final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle()); + assertEquals(network.netId, recreatedNetwork.netId); + // Until R included, fromNetworkHandle would not parse the private DNS bypass flag + assertEquals(network.netId, recreatedNetwork.getNetIdForResolv()); + } + + @Test + public void testGetPrivateDnsBypassingCopy() { + final Network copy = mNetwork.getPrivateDnsBypassingCopy(); + assertEquals(mNetwork.netId, copy.netId); + assertNotEquals(copy.netId, copy.getNetIdForResolv()); + assertNotEquals(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv()); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java new file mode 100644 index 000000000000..fd29a9539de8 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021 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; + +import static com.android.testutils.MiscAsserts.assertThrows; +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Build; + +import androidx.test.filters.SmallTest; + +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; +import com.android.testutils.DevSdkIgnoreRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; + +@IgnoreUpTo(Build.VERSION_CODES.R) +@RunWith(DevSdkIgnoreRunner.class) +@SmallTest +public class OemNetworkPreferencesTest { + + private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; + private static final String TEST_PACKAGE = "com.google.apps.contacts"; + + private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder(); + + @Test + public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() { + assertThrows(NullPointerException.class, + () -> mBuilder.addNetworkPreference(null, TEST_PREF)); + } + + @Test + public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { + assertThrows(NullPointerException.class, + () -> mBuilder.clearNetworkPreference(null)); + } + + @Test + public void testGetNetworkPreferenceReturnsCorrectValue() { + final int expectedNumberOfMappings = 1; + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + + final Map networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertEquals(expectedNumberOfMappings, networkPreferences.size()); + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + } + + @Test + public void testGetNetworkPreferenceReturnsUnmodifiableValue() { + final String newPackage = "new.com.google.apps.contacts"; + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + + final Map networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertThrows(UnsupportedOperationException.class, + () -> networkPreferences.put(newPackage, TEST_PREF)); + + assertThrows(UnsupportedOperationException.class, + () -> networkPreferences.remove(TEST_PACKAGE)); + + } + + @Test + public void testToStringReturnsCorrectValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + + final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString(); + + assertTrue(networkPreferencesString.contains(Integer.toString(TEST_PREF))); + assertTrue(networkPreferencesString.contains(TEST_PACKAGE)); + } + + @Test + public void testOemNetworkPreferencesParcelable() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + + final OemNetworkPreferences prefs = mBuilder.build(); + + assertParcelSane(prefs, 1 /* fieldCount */); + } + + @Test + public void testAddNetworkPreferenceOverwritesPriorPreference() { + final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + + mBuilder.addNetworkPreference(TEST_PACKAGE, newPref); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref); + } + + @Test + public void testRemoveNetworkPreferenceRemovesValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + + mBuilder.clearNetworkPreference(TEST_PACKAGE); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertFalse(networkPreferences.containsKey(TEST_PACKAGE)); + } + + @Test + public void testConstructorByOemNetworkPreferencesSetsValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + OemNetworkPreferences networkPreference = mBuilder.build(); + + final Map networkPreferences = + new OemNetworkPreferences + .Builder(networkPreference) + .build() + .getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java new file mode 100644 index 000000000000..71689f919726 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2010 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; + +import static android.net.RouteInfo.RTN_UNREACHABLE; + +import static com.android.testutils.MiscAsserts.assertEqualBothWays; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; +import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Build; + +import androidx.core.os.BuildCompat; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RouteInfoTest { + @Rule + public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); + + private static final int INVALID_ROUTE_TYPE = -1; + + private InetAddress Address(String addr) { + return InetAddresses.parseNumericAddress(addr); + } + + private IpPrefix Prefix(String prefix) { + return new IpPrefix(prefix); + } + + private static boolean isAtLeastR() { + // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) + return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); + } + + @Test + public void testConstructor() { + RouteInfo r; + // Invalid input. + try { + r = new RouteInfo((IpPrefix) null, null, "rmnet0"); + fail("Expected RuntimeException: destination and gateway null"); + } catch (RuntimeException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0", + INVALID_ROUTE_TYPE); + fail("Invalid route type should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } + + try { + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0", + RTN_UNREACHABLE); + fail("Address family mismatch should cause exception"); + } catch (IllegalArgumentException e) { } + + // Null destination is default route. + r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); + assertEquals(Prefix("::/0"), r.getDestination()); + assertEquals(Address("2001:db8::1"), r.getGateway()); + assertNull(r.getInterface()); + + r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0"); + assertEquals(Prefix("0.0.0.0/0"), r.getDestination()); + assertEquals(Address("192.0.2.1"), r.getGateway()); + assertEquals("wlan0", r.getInterface()); + + // Null gateway sets gateway to unspecified address (why?). + r = new RouteInfo(Prefix("2001:db8:beef:cafe::/48"), null, "lo"); + assertEquals(Prefix("2001:db8:beef::/48"), r.getDestination()); + assertEquals(Address("::"), r.getGateway()); + assertEquals("lo", r.getInterface()); + + r = new RouteInfo(Prefix("192.0.2.5/24"), null); + assertEquals(Prefix("192.0.2.0/24"), r.getDestination()); + assertEquals(Address("0.0.0.0"), r.getGateway()); + assertNull(r.getInterface()); + } + + @Test + public void testMatches() { + class PatchedRouteInfo { + private final RouteInfo mRouteInfo; + + public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) { + mRouteInfo = new RouteInfo(destination, gateway, iface); + } + + public boolean matches(InetAddress destination) { + return mRouteInfo.matches(destination); + } + } + + PatchedRouteInfo r; + + r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0"); + assertTrue(r.matches(Address("2001:db8:f00::ace:d00c"))); + assertTrue(r.matches(Address("2001:db8:f00::ace:d00d"))); + assertFalse(r.matches(Address("2001:db8:f00::ace:d00e"))); + assertFalse(r.matches(Address("2001:db8:f00::bad:d00d"))); + assertFalse(r.matches(Address("2001:4868:4860::8888"))); + assertFalse(r.matches(Address("8.8.8.8"))); + + r = new PatchedRouteInfo(Prefix("192.0.2.0/23"), null, "wlan0"); + assertTrue(r.matches(Address("192.0.2.43"))); + assertTrue(r.matches(Address("192.0.3.21"))); + assertFalse(r.matches(Address("192.0.0.21"))); + assertFalse(r.matches(Address("8.8.8.8"))); + + PatchedRouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0"); + assertTrue(ipv6Default.matches(Address("2001:db8::f00"))); + assertFalse(ipv6Default.matches(Address("192.0.2.1"))); + + PatchedRouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0"); + assertTrue(ipv4Default.matches(Address("255.255.255.255"))); + assertTrue(ipv4Default.matches(Address("192.0.2.1"))); + assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); + } + + @Test + public void testEquals() { + // IPv4 + RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); + RouteInfo r2 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); + assertEqualBothWays(r1, r2); + + RouteInfo r3 = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "wlan0"); + RouteInfo r4 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::2"), "wlan0"); + RouteInfo r5 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "rmnet0"); + assertNotEqualEitherWay(r1, r3); + assertNotEqualEitherWay(r1, r4); + assertNotEqualEitherWay(r1, r5); + + // IPv6 + r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); + r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); + assertEqualBothWays(r1, r2); + + r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); + r4 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.2"), "wlan0"); + r5 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "rmnet0"); + assertNotEqualEitherWay(r1, r3); + assertNotEqualEitherWay(r1, r4); + assertNotEqualEitherWay(r1, r5); + + // Interfaces (but not destinations or gateways) can be null. + r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); + r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); + r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); + assertEqualBothWays(r1, r2); + assertNotEqualEitherWay(r1, r3); + } + + @Test + public void testHostAndDefaultRoutes() { + RouteInfo r; + + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0"); + assertFalse(r.isHostRoute()); + assertTrue(r.isDefaultRoute()); + assertTrue(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0"); + assertFalse(r.isHostRoute()); + assertTrue(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertTrue(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); + assertFalse(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0"); + assertFalse(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); + assertTrue(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE); + assertFalse(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertTrue(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } + + r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE); + assertFalse(r.isHostRoute()); + assertFalse(r.isDefaultRoute()); + assertFalse(r.isIPv4Default()); + assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertTrue(r.isIPv6UnreachableDefault()); + } + } + + @Test + public void testTruncation() { + LinkAddress l; + RouteInfo r; + + l = new LinkAddress("192.0.2.5/30"); + r = new RouteInfo(l, Address("192.0.2.1"), "wlan0"); + assertEquals("192.0.2.4", r.getDestination().getAddress().getHostAddress()); + + l = new LinkAddress("2001:db8:1:f::5/63"); + r = new RouteInfo(l, Address("2001:db8:5::1"), "wlan0"); + assertEquals("2001:db8:1:e::", r.getDestination().getAddress().getHostAddress()); + } + + // Make sure that creating routes to multicast addresses doesn't throw an exception. Even though + // there's nothing we can do with them, we don't want to crash if, e.g., someone calls + // requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI); + @Test + public void testMulticastRoute() { + RouteInfo r; + r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0"); + r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), "wlan0"); + // No exceptions? Good. + } + + @Test + public void testParceling() { + RouteInfo r; + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); + assertParcelingIsLossless(r); + r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE); + assertParcelingIsLossless(r); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testMtuParceling() { + final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface", + RTN_UNREACHABLE, 1450 /* mtu */); + assertParcelingIsLossless(r); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.Q) + public void testFieldCount_Q() { + assertFieldCountEquals(6, RouteInfo.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testFieldCount() { + // Make sure any new field is covered by the above parceling tests when changing this number + assertFieldCountEquals(7, RouteInfo.class); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testMtu() { + RouteInfo r; + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0", + RouteInfo.RTN_UNICAST, 1500); + assertEquals(1500, r.getMtu()); + + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0"); + assertEquals(0, r.getMtu()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testRouteKey() { + RouteInfo.RouteKey k1, k2; + // Only prefix, null gateway and null interface + k1 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey(); + k2 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey(); + assertEquals(k1, k2); + assertEquals(k1.hashCode(), k2.hashCode()); + + // With prefix, gateway and interface. Type and MTU does not affect RouteKey equality + k1 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", + RTN_UNREACHABLE, 1450).getRouteKey(); + k2 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", + RouteInfo.RTN_UNICAST, 1400).getRouteKey(); + assertEquals(k1, k2); + assertEquals(k1.hashCode(), k2.hashCode()); + + // Different scope IDs are ignored by the kernel, so we consider them equal here too. + k1 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%1"), "wlan0").getRouteKey(); + k2 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%2"), "wlan0").getRouteKey(); + assertEquals(k1, k2); + assertEquals(k1.hashCode(), k2.hashCode()); + + // Different prefix + k1 = new RouteInfo(Prefix("192.0.2.0/24"), null).getRouteKey(); + k2 = new RouteInfo(Prefix("192.0.3.0/24"), null).getRouteKey(); + assertNotEquals(k1, k2); + + // Different gateway + k1 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), null).getRouteKey(); + k2 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::2"), null).getRouteKey(); + assertNotEquals(k1, k2); + + // Different interface + k1 = new RouteInfo(Prefix("ff02::1/128"), null, "tun0").getRouteKey(); + k2 = new RouteInfo(Prefix("ff02::1/128"), null, "tun1").getRouteKey(); + assertNotEquals(k1, k2); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java new file mode 100644 index 000000000000..b5f23bf19a3c --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2014 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class StaticIpConfigurationTest { + + private static final String ADDRSTR = "192.0.2.2/25"; + private static final LinkAddress ADDR = new LinkAddress(ADDRSTR); + private static final InetAddress GATEWAY = IpAddress("192.0.2.1"); + private static final InetAddress OFFLINKGATEWAY = IpAddress("192.0.2.129"); + private static final InetAddress DNS1 = IpAddress("8.8.8.8"); + private static final InetAddress DNS2 = IpAddress("8.8.4.4"); + private static final InetAddress DNS3 = IpAddress("4.2.2.2"); + private static final String IFACE = "eth0"; + private static final String FAKE_DOMAINS = "google.com"; + + private static InetAddress IpAddress(String addr) { + return InetAddress.parseNumericAddress(addr); + } + + private void checkEmpty(StaticIpConfiguration s) { + assertNull(s.ipAddress); + assertNull(s.gateway); + assertNull(s.domains); + assertEquals(0, s.dnsServers.size()); + } + + private StaticIpConfiguration makeTestObject() { + StaticIpConfiguration s = new StaticIpConfiguration(); + s.ipAddress = ADDR; + s.gateway = GATEWAY; + s.dnsServers.add(DNS1); + s.dnsServers.add(DNS2); + s.dnsServers.add(DNS3); + s.domains = FAKE_DOMAINS; + return s; + } + + @Test + public void testConstructor() { + StaticIpConfiguration s = new StaticIpConfiguration(); + checkEmpty(s); + } + + @Test + public void testCopyAndClear() { + StaticIpConfiguration empty = new StaticIpConfiguration((StaticIpConfiguration) null); + checkEmpty(empty); + + StaticIpConfiguration s1 = makeTestObject(); + StaticIpConfiguration s2 = new StaticIpConfiguration(s1); + assertEquals(s1, s2); + s2.clear(); + assertEquals(empty, s2); + } + + @Test + public void testHashCodeAndEquals() { + HashSet hashCodes = new HashSet(); + hashCodes.add(0); + + StaticIpConfiguration s = new StaticIpConfiguration(); + // Check that this hash code is nonzero and different from all the ones seen so far. + assertTrue(hashCodes.add(s.hashCode())); + + s.ipAddress = ADDR; + assertTrue(hashCodes.add(s.hashCode())); + + s.gateway = GATEWAY; + assertTrue(hashCodes.add(s.hashCode())); + + s.dnsServers.add(DNS1); + assertTrue(hashCodes.add(s.hashCode())); + + s.dnsServers.add(DNS2); + assertTrue(hashCodes.add(s.hashCode())); + + s.dnsServers.add(DNS3); + assertTrue(hashCodes.add(s.hashCode())); + + s.domains = "example.com"; + assertTrue(hashCodes.add(s.hashCode())); + + assertFalse(s.equals(null)); + assertEquals(s, s); + + StaticIpConfiguration s2 = new StaticIpConfiguration(s); + assertEquals(s, s2); + + s.ipAddress = new LinkAddress(DNS1, 32); + assertNotEquals(s, s2); + + s2 = new StaticIpConfiguration(s); + s.domains = "foo"; + assertNotEquals(s, s2); + + s2 = new StaticIpConfiguration(s); + s.gateway = DNS2; + assertNotEquals(s, s2); + + s2 = new StaticIpConfiguration(s); + s.dnsServers.add(DNS3); + assertNotEquals(s, s2); + } + + @Test + public void testToLinkProperties() { + LinkProperties expected = new LinkProperties(); + expected.setInterfaceName(IFACE); + + StaticIpConfiguration s = new StaticIpConfiguration(); + assertEquals(expected, s.toLinkProperties(IFACE)); + + final RouteInfo connectedRoute = new RouteInfo(new IpPrefix(ADDRSTR), null, IFACE); + s.ipAddress = ADDR; + expected.addLinkAddress(ADDR); + expected.addRoute(connectedRoute); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.gateway = GATEWAY; + RouteInfo defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), GATEWAY, IFACE); + expected.addRoute(defaultRoute); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.gateway = OFFLINKGATEWAY; + expected.removeRoute(defaultRoute); + defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), OFFLINKGATEWAY, IFACE); + expected.addRoute(defaultRoute); + + RouteInfo gatewayRoute = new RouteInfo(new IpPrefix("192.0.2.129/32"), null, IFACE); + expected.addRoute(gatewayRoute); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.dnsServers.add(DNS1); + expected.addDnsServer(DNS1); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.dnsServers.add(DNS2); + s.dnsServers.add(DNS3); + expected.addDnsServer(DNS2); + expected.addDnsServer(DNS3); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.domains = FAKE_DOMAINS; + expected.setDomains(FAKE_DOMAINS); + assertEquals(expected, s.toLinkProperties(IFACE)); + + s.gateway = null; + expected.removeRoute(defaultRoute); + expected.removeRoute(gatewayRoute); + assertEquals(expected, s.toLinkProperties(IFACE)); + + // Without knowing the IP address, we don't have a directly-connected route, so we can't + // tell if the gateway is off-link or not and we don't add a host route. This isn't a real + // configuration, but we should at least not crash. + s.gateway = OFFLINKGATEWAY; + s.ipAddress = null; + expected.removeLinkAddress(ADDR); + expected.removeRoute(connectedRoute); + expected.addRoute(defaultRoute); + assertEquals(expected, s.toLinkProperties(IFACE)); + } + + private StaticIpConfiguration passThroughParcel(StaticIpConfiguration s) { + Parcel p = Parcel.obtain(); + StaticIpConfiguration s2 = null; + try { + s.writeToParcel(p, 0); + p.setDataPosition(0); + s2 = StaticIpConfiguration.readFromParcel(p); + } finally { + p.recycle(); + } + assertNotNull(s2); + return s2; + } + + @Test + public void testParceling() { + StaticIpConfiguration s = makeTestObject(); + StaticIpConfiguration s2 = passThroughParcel(s); + assertEquals(s, s2); + } + + @Test + public void testBuilder() { + final ArrayList dnsServers = new ArrayList<>(); + dnsServers.add(DNS1); + + final StaticIpConfiguration s = new StaticIpConfiguration.Builder() + .setIpAddress(ADDR) + .setGateway(GATEWAY) + .setDomains(FAKE_DOMAINS) + .setDnsServers(dnsServers) + .build(); + + assertEquals(s.ipAddress, s.getIpAddress()); + assertEquals(ADDR, s.getIpAddress()); + assertEquals(s.gateway, s.getGateway()); + assertEquals(GATEWAY, s.getGateway()); + assertEquals(s.domains, s.getDomains()); + assertEquals(FAKE_DOMAINS, s.getDomains()); + assertTrue(s.dnsServers.equals(s.getDnsServers())); + assertEquals(1, s.getDnsServers().size()); + assertEquals(DNS1, s.getDnsServers().get(0)); + } + + @Test + public void testAddDnsServers() { + final StaticIpConfiguration s = new StaticIpConfiguration((StaticIpConfiguration) null); + checkEmpty(s); + + s.addDnsServer(DNS1); + assertEquals(1, s.getDnsServers().size()); + assertEquals(DNS1, s.getDnsServers().get(0)); + + s.addDnsServer(DNS2); + s.addDnsServer(DNS3); + assertEquals(3, s.getDnsServers().size()); + assertEquals(DNS2, s.getDnsServers().get(1)); + assertEquals(DNS3, s.getDnsServers().get(2)); + } + + @Test + public void testGetRoutes() { + final StaticIpConfiguration s = makeTestObject(); + final List routeInfoList = s.getRoutes(IFACE); + + assertEquals(2, routeInfoList.size()); + assertEquals(new RouteInfo(ADDR, (InetAddress) null, IFACE), routeInfoList.get(0)); + assertEquals(new RouteInfo((IpPrefix) null, GATEWAY, IFACE), routeInfoList.get(1)); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt new file mode 100644 index 000000000000..7a18bb08faa8 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt @@ -0,0 +1,106 @@ +/* + * 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 + +import android.net.InetAddresses.parseNumericAddress +import android.os.Build +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertFieldCountEquals +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import java.net.InetAddress +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) // TcpKeepalivePacketData added to SDK in S +class TcpKeepalivePacketDataTest { + private fun makeData( + srcAddress: InetAddress = parseNumericAddress("192.0.2.123"), + srcPort: Int = 1234, + dstAddress: InetAddress = parseNumericAddress("192.0.2.231"), + dstPort: Int = 4321, + data: ByteArray = byteArrayOf(1, 2, 3), + tcpSeq: Int = 135, + tcpAck: Int = 246, + tcpWnd: Int = 1234, + tcpWndScale: Int = 2, + ipTos: Int = 0x12, + ipTtl: Int = 10 + ) = TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data, tcpSeq, tcpAck, + tcpWnd, tcpWndScale, ipTos, ipTtl) + + @Test + fun testEquals() { + val data1 = makeData() + val data2 = makeData() + assertEquals(data1, data2) + assertEquals(data1.hashCode(), data2.hashCode()) + } + + @Test + fun testNotEquals() { + assertNotEquals(makeData(srcAddress = parseNumericAddress("192.0.2.124")), makeData()) + assertNotEquals(makeData(srcPort = 1235), makeData()) + assertNotEquals(makeData(dstAddress = parseNumericAddress("192.0.2.232")), makeData()) + assertNotEquals(makeData(dstPort = 4322), makeData()) + // .equals does not test .packet, as it should be generated from the other fields + assertNotEquals(makeData(tcpSeq = 136), makeData()) + assertNotEquals(makeData(tcpAck = 247), makeData()) + assertNotEquals(makeData(tcpWnd = 1235), makeData()) + assertNotEquals(makeData(tcpWndScale = 3), makeData()) + assertNotEquals(makeData(ipTos = 0x14), makeData()) + assertNotEquals(makeData(ipTtl = 11), makeData()) + + // Update above assertions if field is added + assertFieldCountEquals(5, KeepalivePacketData::class.java) + assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) + } + + @Test + fun testParcelUnparcel() { + assertParcelSane(makeData(), fieldCount = 6) { a, b -> + // .equals() does not verify .packet + a == b && a.packet contentEquals b.packet + } + } + + @Test + fun testToString() { + val data = makeData() + val str = data.toString() + + assertTrue(str.contains(data.srcAddress.hostAddress)) + assertTrue(str.contains(data.srcPort.toString())) + assertTrue(str.contains(data.dstAddress.hostAddress)) + assertTrue(str.contains(data.dstPort.toString())) + // .packet not included in toString() + assertTrue(str.contains(data.getTcpSeq().toString())) + assertTrue(str.contains(data.getTcpAck().toString())) + assertTrue(str.contains(data.getTcpWindow().toString())) + assertTrue(str.contains(data.getTcpWindowScale().toString())) + assertTrue(str.contains(data.getIpTos().toString())) + assertTrue(str.contains(data.getIpTtl().toString())) + + // Update above assertions if field is added + assertFieldCountEquals(5, KeepalivePacketData::class.java) + assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java new file mode 100644 index 000000000000..1b1c95431d6f --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016 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; + +import static android.os.UserHandle.MIN_SECONDARY_USER_ID; +import static android.os.UserHandle.SYSTEM; +import static android.os.UserHandle.USER_SYSTEM; +import static android.os.UserHandle.getUid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Build; +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class UidRangeTest { + + /* + * UidRange is no longer passed to netd. UID ranges between the framework and netd are passed as + * UidRangeParcel objects. + */ + + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + + @Test + public void testSingleItemUidRangeAllowed() { + new UidRange(123, 123); + new UidRange(0, 0); + new UidRange(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Test + public void testNegativeUidsDisallowed() { + try { + new UidRange(-2, 100); + fail("Exception not thrown for negative start UID"); + } catch (IllegalArgumentException expected) { + } + + try { + new UidRange(-200, -100); + fail("Exception not thrown for negative stop UID"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testStopLessThanStartDisallowed() { + final int x = 4195000; + try { + new UidRange(x, x - 1); + fail("Exception not thrown for negative-length UID range"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testGetStartAndEndUser() throws Exception { + final UidRange uidRangeOfPrimaryUser = new UidRange( + getUid(USER_SYSTEM, 10000), getUid(USER_SYSTEM, 10100)); + final UidRange uidRangeOfSecondaryUser = new UidRange( + getUid(MIN_SECONDARY_USER_ID, 10000), getUid(MIN_SECONDARY_USER_ID, 10100)); + assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); + assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser()); + assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getStartUser()); + assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser()); + + final UidRange uidRangeForDifferentUsers = new UidRange( + getUid(USER_SYSTEM, 10000), getUid(MIN_SECONDARY_USER_ID, 10100)); + assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); + assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testCreateForUser() throws Exception { + final UidRange uidRangeOfPrimaryUser = UidRange.createForUser(SYSTEM); + final UidRange uidRangeOfSecondaryUser = UidRange.createForUser( + UserHandle.of(USER_SYSTEM + 1)); + assertTrue(uidRangeOfPrimaryUser.stop < uidRangeOfSecondaryUser.start); + assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); + assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser()); + assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getStartUser()); + assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getEndUser()); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt new file mode 100644 index 000000000000..f23ba26d0039 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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 + +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +private const val TEST_OWNER_UID = 123 +private const val TEST_IFACE = "test_tun0" +private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0") + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class UnderlyingNetworkInfoTest { + @Test + fun testParcelUnparcel() { + val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST) + assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid()) + assertEquals(TEST_IFACE, testInfo.getInterface()) + assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces()) + assertParcelSane(testInfo, 3) + + val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf()) + assertEquals(0, emptyInfo.getOwnerUid()) + assertEquals(String(), emptyInfo.getInterface()) + assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces()) + assertParcelSane(emptyInfo, 3) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java new file mode 100644 index 000000000000..d50406fd3a1c --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 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.apf; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ApfCapabilitiesTest { + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getContext(); + } + + @Test + public void testConstructAndParcel() { + final ApfCapabilities caps = new ApfCapabilities(123, 456, 789); + assertEquals(123, caps.apfVersionSupported); + assertEquals(456, caps.maximumApfProgramSize); + assertEquals(789, caps.apfPacketFormat); + + assertParcelSane(caps, 3); + } + + @Test + public void testEquals() { + assertEquals(new ApfCapabilities(1, 2, 3), new ApfCapabilities(1, 2, 3)); + assertNotEquals(new ApfCapabilities(2, 2, 3), new ApfCapabilities(1, 2, 3)); + assertNotEquals(new ApfCapabilities(1, 3, 3), new ApfCapabilities(1, 2, 3)); + assertNotEquals(new ApfCapabilities(1, 2, 4), new ApfCapabilities(1, 2, 3)); + } + + @Test + public void testHasDataAccess() { + //hasDataAccess is only supported starting at apf version 4. + ApfCapabilities caps = new ApfCapabilities(1 /* apfVersionSupported */, 2, 3); + assertFalse(caps.hasDataAccess()); + + caps = new ApfCapabilities(4 /* apfVersionSupported */, 5, 6); + assertTrue(caps.hasDataAccess()); + } + + @Test + public void testGetApfDrop8023Frames() { + // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly + // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID. + final int resId = mContext.getResources().getIdentifier("config_apfDrop802_3Frames", + "bool", "android"); + final boolean shouldDrop8023Frames = mContext.getResources().getBoolean(resId); + final boolean actual = ApfCapabilities.getApfDrop8023Frames(); + assertEquals(shouldDrop8023Frames, actual); + } + + @Test + public void testGetApfEtherTypeBlackList() { + // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly + // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID. + final int resId = mContext.getResources().getIdentifier("config_apfEthTypeBlackList", + "array", "android"); + final int[] blacklistedEtherTypeArray = mContext.getResources().getIntArray(resId); + final int[] actual = ApfCapabilities.getApfEtherTypeBlackList(); + assertNotNull(actual); + assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual)); + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt new file mode 100644 index 000000000000..0b7b74097cc6 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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.metrics; + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class ApfProgramEventTest { + private infix fun Int.hasFlag(flag: Int) = (this and (1 shl flag)) != 0 + + @Test + fun testBuilderAndParcel() { + val apfProgramEvent = ApfProgramEvent.Builder() + .setLifetime(1) + .setActualLifetime(2) + .setFilteredRas(3) + .setCurrentRas(4) + .setProgramLength(5) + .setFlags(true, true) + .build() + + assertEquals(1, apfProgramEvent.lifetime) + assertEquals(2, apfProgramEvent.actualLifetime) + assertEquals(3, apfProgramEvent.filteredRas) + assertEquals(4, apfProgramEvent.currentRas) + assertEquals(5, apfProgramEvent.programLength) + assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags) + + assertParcelSane(apfProgramEvent, 6) + } + + @Test + fun testFlagsFor() { + var flags = ApfProgramEvent.flagsFor(false, false) + assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) + assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) + + flags = ApfProgramEvent.flagsFor(true, false) + assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) + assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) + + flags = ApfProgramEvent.flagsFor(false, true) + assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) + assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) + + flags = ApfProgramEvent.flagsFor(true, true) + assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) + assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt new file mode 100644 index 000000000000..46a8c8e5b509 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class ApfStatsTest { + @Test + fun testBuilderAndParcel() { + val apfStats = ApfStats.Builder() + .setDurationMs(Long.MAX_VALUE) + .setReceivedRas(1) + .setMatchingRas(2) + .setDroppedRas(3) + .setZeroLifetimeRas(4) + .setParseErrors(5) + .setProgramUpdates(6) + .setProgramUpdatesAll(7) + .setProgramUpdatesAllowingMulticast(8) + .setMaxProgramSize(9) + .build() + + assertEquals(Long.MAX_VALUE, apfStats.durationMs) + assertEquals(1, apfStats.receivedRas) + assertEquals(2, apfStats.matchingRas) + assertEquals(3, apfStats.droppedRas) + assertEquals(4, apfStats.zeroLifetimeRas) + assertEquals(5, apfStats.parseErrors) + assertEquals(6, apfStats.programUpdates) + assertEquals(7, apfStats.programUpdatesAll) + assertEquals(8, apfStats.programUpdatesAllowingMulticast) + assertEquals(9, apfStats.maxProgramSize) + + assertParcelSane(apfStats, 10) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt new file mode 100644 index 000000000000..8d7a9c405024 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +private const val FAKE_MESSAGE = "test" + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DhcpClientEventTest { + @Test + fun testBuilderAndParcel() { + val dhcpClientEvent = DhcpClientEvent.Builder() + .setMsg(FAKE_MESSAGE) + .setDurationMs(Integer.MAX_VALUE) + .build() + + assertEquals(FAKE_MESSAGE, dhcpClientEvent.msg) + assertEquals(Integer.MAX_VALUE, dhcpClientEvent.durationMs) + + assertParcelSane(dhcpClientEvent, 2) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt new file mode 100644 index 000000000000..236f72eafbdc --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt @@ -0,0 +1,65 @@ +package android.net.metrics + +import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH +import android.net.metrics.DhcpErrorEvent.errorCodeWithOption +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.parcelingRoundTrip +import java.lang.reflect.Modifier +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_ERROR_CODE = 12345 +//DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java due to it's protected) +private const val DHCP_SUBNET_MASK = 1 + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DhcpErrorEventTest { + + @Test + fun testConstructor() { + val event = DhcpErrorEvent(TEST_ERROR_CODE) + assertEquals(TEST_ERROR_CODE, event.errorCode) + } + + @Test + fun testParcelUnparcel() { + val event = DhcpErrorEvent(TEST_ERROR_CODE) + val parceled = parcelingRoundTrip(event) + assertEquals(TEST_ERROR_CODE, parceled.errorCode) + } + + @Test + fun testErrorCodeWithOption() { + val errorCode = errorCodeWithOption(DHCP_INVALID_OPTION_LENGTH, DHCP_SUBNET_MASK); + assertTrue((DHCP_INVALID_OPTION_LENGTH and errorCode) == DHCP_INVALID_OPTION_LENGTH); + assertTrue((DHCP_SUBNET_MASK and errorCode) == DHCP_SUBNET_MASK); + } + + @Test + fun testToString() { + val names = listOf("L2_ERROR", "L3_ERROR", "L4_ERROR", "DHCP_ERROR", "MISC_ERROR") + val errorFields = DhcpErrorEvent::class.java.declaredFields.filter { + it.type == Int::class.javaPrimitiveType + && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) + && it.name !in names + } + + errorFields.forEach { + val intValue = it.getInt(null) + val stringValue = DhcpErrorEvent(intValue).toString() + assertTrue("Invalid string for error 0x%08X (field %s): %s".format(intValue, it.name, + stringValue), + stringValue.contains(it.name)) + } + } + + @Test + fun testToString_InvalidErrorCode() { + assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString()) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java new file mode 100644 index 000000000000..d4780d3a5d7b --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2019 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.metrics; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + +import android.net.ConnectivityMetricsEvent; +import android.net.IIpConnectivityMetrics; +import android.net.Network; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.BitUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConnectivityLogTest { + private static final int FAKE_NET_ID = 100; + private static final int[] FAKE_TRANSPORT_TYPES = BitUtils.unpackBits(TRANSPORT_WIFI); + private static final long FAKE_TIME_STAMP = System.currentTimeMillis(); + private static final String FAKE_INTERFACE_NAME = "test"; + private static final IpReachabilityEvent FAKE_EV = + new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); + + @Mock IIpConnectivityMetrics mMockService; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testLoggingEvents() throws Exception { + IpConnectivityLog logger = new IpConnectivityLog(mMockService); + + assertTrue(logger.log(FAKE_EV)); + assertTrue(logger.log(FAKE_TIME_STAMP, FAKE_EV)); + assertTrue(logger.log(FAKE_NET_ID, FAKE_TRANSPORT_TYPES, FAKE_EV)); + assertTrue(logger.log(new Network(FAKE_NET_ID), FAKE_TRANSPORT_TYPES, FAKE_EV)); + assertTrue(logger.log(FAKE_INTERFACE_NAME, FAKE_EV)); + assertTrue(logger.log(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, TRANSPORT_WIFI, + FAKE_INTERFACE_NAME))); + + List got = verifyEvents(6); + assertEventsEqual(makeExpectedEvent(got.get(0).timestamp, 0, 0, null), got.get(0)); + assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, 0, 0, null), got.get(1)); + assertEventsEqual(makeExpectedEvent(got.get(2).timestamp, FAKE_NET_ID, + TRANSPORT_WIFI, null), got.get(2)); + assertEventsEqual(makeExpectedEvent(got.get(3).timestamp, FAKE_NET_ID, + TRANSPORT_WIFI, null), got.get(3)); + assertEventsEqual(makeExpectedEvent(got.get(4).timestamp, 0, 0, FAKE_INTERFACE_NAME), + got.get(4)); + assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, + TRANSPORT_WIFI, FAKE_INTERFACE_NAME), got.get(5)); + } + + @Test + public void testLoggingEventsWithMultipleCallers() throws Exception { + IpConnectivityLog logger = new IpConnectivityLog(mMockService); + + final int nCallers = 10; + final int nEvents = 10; + for (int n = 0; n < nCallers; n++) { + final int i = n; + new Thread() { + public void run() { + for (int j = 0; j < nEvents; j++) { + assertTrue(logger.log(makeExpectedEvent( + FAKE_TIME_STAMP + i * 100 + j, + FAKE_NET_ID + i * 100 + j, + ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR, + FAKE_INTERFACE_NAME))); + } + } + }.start(); + } + + List got = verifyEvents(nCallers * nEvents, 200); + Collections.sort(got, EVENT_COMPARATOR); + Iterator iter = got.iterator(); + for (int i = 0; i < nCallers; i++) { + for (int j = 0; j < nEvents; j++) { + final long expectedTimestamp = FAKE_TIME_STAMP + i * 100 + j; + final int expectedNetId = FAKE_NET_ID + i * 100 + j; + final long expectedTransports = + ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR; + assertEventsEqual(makeExpectedEvent(expectedTimestamp, expectedNetId, + expectedTransports, FAKE_INTERFACE_NAME), iter.next()); + } + } + } + + private List verifyEvents(int n, int timeoutMs) throws Exception { + ArgumentCaptor captor = + ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); + verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture()); + return captor.getAllValues(); + } + + private List verifyEvents(int n) throws Exception { + return verifyEvents(n, 10); + } + + + private ConnectivityMetricsEvent makeExpectedEvent(long timestamp, int netId, long transports, + String ifname) { + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = timestamp; + ev.data = FAKE_EV; + ev.netId = netId; + ev.transports = transports; + ev.ifname = ifname; + return ev; + } + + /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ + private void assertEventsEqual(ConnectivityMetricsEvent expected, + ConnectivityMetricsEvent got) { + assertEquals(expected.data, got.data); + assertEquals(expected.timestamp, got.timestamp); + assertEquals(expected.netId, got.netId); + assertEquals(expected.transports, got.transports); + assertEquals(expected.ifname, got.ifname); + } + + static final Comparator EVENT_COMPARATOR = + Comparator.comparingLong((ev) -> ev.timestamp); +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt new file mode 100644 index 000000000000..64be50837fc9 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class IpManagerEventTest { + @Test + fun testConstructorAndParcel() { + (IpManagerEvent.PROVISIONING_OK..IpManagerEvent.ERROR_INTERFACE_NOT_FOUND).forEach { + val ipManagerEvent = IpManagerEvent(it, Long.MAX_VALUE) + assertEquals(it, ipManagerEvent.eventType) + assertEquals(Long.MAX_VALUE, ipManagerEvent.durationMs) + + assertParcelSane(ipManagerEvent, 2) + } + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt new file mode 100644 index 000000000000..55b5e492dd47 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class IpReachabilityEventTest { + @Test + fun testConstructorAndParcel() { + (IpReachabilityEvent.PROBE..IpReachabilityEvent.PROVISIONING_LOST_ORGANIC).forEach { + val ipReachabilityEvent = IpReachabilityEvent(it) + assertEquals(it, ipReachabilityEvent.eventType) + + assertParcelSane(ipReachabilityEvent, 1) + } + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt new file mode 100644 index 000000000000..41430b03a1eb --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetworkEventTest { + @Test + fun testConstructorAndParcel() { + (NetworkEvent.NETWORK_CONNECTED..NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY).forEach { + var networkEvent = NetworkEvent(it) + assertEquals(it, networkEvent.eventType) + assertEquals(0, networkEvent.durationMs) + + networkEvent = NetworkEvent(it, Long.MAX_VALUE) + assertEquals(it, networkEvent.eventType) + assertEquals(Long.MAX_VALUE, networkEvent.durationMs) + + assertParcelSane(networkEvent, 2) + } + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt new file mode 100644 index 000000000000..d9b720332fbe --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +private const val NO_LIFETIME: Long = -1L + +@RunWith(AndroidJUnit4::class) +@SmallTest +class RaEventTest { + @Test + fun testConstructorAndParcel() { + var raEvent = RaEvent.Builder().build() + assertEquals(NO_LIFETIME, raEvent.routerLifetime) + assertEquals(NO_LIFETIME, raEvent.prefixValidLifetime) + assertEquals(NO_LIFETIME, raEvent.prefixPreferredLifetime) + assertEquals(NO_LIFETIME, raEvent.routeInfoLifetime) + assertEquals(NO_LIFETIME, raEvent.rdnssLifetime) + assertEquals(NO_LIFETIME, raEvent.dnsslLifetime) + + raEvent = RaEvent.Builder() + .updateRouterLifetime(1) + .updatePrefixValidLifetime(2) + .updatePrefixPreferredLifetime(3) + .updateRouteInfoLifetime(4) + .updateRdnssLifetime(5) + .updateDnsslLifetime(6) + .build() + assertEquals(1, raEvent.routerLifetime) + assertEquals(2, raEvent.prefixValidLifetime) + assertEquals(3, raEvent.prefixPreferredLifetime) + assertEquals(4, raEvent.routeInfoLifetime) + assertEquals(5, raEvent.rdnssLifetime) + assertEquals(6, raEvent.dnsslLifetime) + + raEvent = RaEvent.Builder() + .updateRouterLifetime(Long.MIN_VALUE) + .updateRouterLifetime(Long.MAX_VALUE) + .build() + assertEquals(Long.MIN_VALUE, raEvent.routerLifetime) + + raEvent = RaEvent(1, 2, 3, 4, 5, 6) + assertEquals(1, raEvent.routerLifetime) + assertEquals(2, raEvent.prefixValidLifetime) + assertEquals(3, raEvent.prefixPreferredLifetime) + assertEquals(4, raEvent.routeInfoLifetime) + assertEquals(5, raEvent.rdnssLifetime) + assertEquals(6, raEvent.dnsslLifetime) + + assertParcelSane(raEvent, 6) + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt new file mode 100644 index 000000000000..51c0d41bf4d5 --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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.metrics + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import java.lang.reflect.Modifier +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +private const val FIRST_VALIDATION: Int = 1 shl 8 +private const val REVALIDATION: Int = 2 shl 8 + +@RunWith(AndroidJUnit4::class) +@SmallTest +class ValidationProbeEventTest { + private infix fun Int.hasType(type: Int) = (type and this) == type + + @Test + fun testBuilderAndParcel() { + var validationProbeEvent = ValidationProbeEvent.Builder() + .setProbeType(ValidationProbeEvent.PROBE_DNS, false).build() + + assertTrue(validationProbeEvent.probeType hasType REVALIDATION) + + validationProbeEvent = ValidationProbeEvent.Builder() + .setDurationMs(Long.MAX_VALUE) + .setProbeType(ValidationProbeEvent.PROBE_DNS, true) + .setReturnCode(ValidationProbeEvent.DNS_SUCCESS) + .build() + + assertEquals(Long.MAX_VALUE, validationProbeEvent.durationMs) + assertTrue(validationProbeEvent.probeType hasType ValidationProbeEvent.PROBE_DNS) + assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION) + assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode) + + assertParcelSane(validationProbeEvent, 3) + } + + @Test + fun testGetProbeName() { + val probeFields = ValidationProbeEvent::class.java.declaredFields.filter { + it.type == Int::class.javaPrimitiveType + && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) + && it.name.contains("PROBE") + } + + probeFields.forEach { + val intValue = it.getInt(null) + val stringValue = ValidationProbeEvent.getProbeName(intValue) + assertEquals(it.name, stringValue) + } + + } +} diff --git a/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt new file mode 100644 index 000000000000..7b22e45db90a --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt @@ -0,0 +1,209 @@ +/* + * 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.netstats + +import android.net.NetworkStats +import android.net.NetworkStats.DEFAULT_NETWORK_NO +import android.net.NetworkStats.DEFAULT_NETWORK_YES +import android.net.NetworkStats.Entry +import android.net.NetworkStats.IFACE_VT +import android.net.NetworkStats.METERED_NO +import android.net.NetworkStats.METERED_YES +import android.net.NetworkStats.ROAMING_NO +import android.net.NetworkStats.ROAMING_YES +import android.net.NetworkStats.SET_DEFAULT +import android.net.NetworkStats.SET_FOREGROUND +import android.net.NetworkStats.TAG_NONE +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.assertFieldCountEquals +import com.android.testutils.assertNetworkStatsEquals +import com.android.testutils.assertParcelingIsLossless +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals + +@RunWith(JUnit4::class) +@SmallTest +class NetworkStatsApiTest { + @Rule + @JvmField + val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q) + + private val testStatsEmpty = NetworkStats(0L, 0) + + // Note that these variables need to be initialized outside of constructor, initialize + // here with methods that don't exist in Q devices will result in crash. + + // stats1 and stats2 will have some entries with common keys, which are expected to + // be merged if performing add on these 2 stats. + private lateinit var testStats1: NetworkStats + private lateinit var testStats2: NetworkStats + + // This is a result of adding stats1 and stats2, while the merging of common key items is + // subject to test later, this should not be initialized with for a loop to add stats1 + // and stats2 above. + private lateinit var testStats3: NetworkStats + + companion object { + private const val TEST_IFACE = "test0" + private const val TEST_UID1 = 1001 + private const val TEST_UID2 = 1002 + } + + @Before + fun setUp() { + testStats1 = NetworkStats(0L, 0) + // Entries which only appear in set1. + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4)) + // Entries which are common for set1 and set2. + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0)) + .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8)) + .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0)) + assertEquals(8, testStats1.size()) + + testStats2 = NetworkStats(0L, 0) + // Entries which are common for set1 and set2. + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45)) + .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7)) + .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0)) + // Entry which only appears in set2. + .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) + assertEquals(5, testStats2.size()) + + testStats3 = NetworkStats(0L, 9) + // Entries which are unique either in stats1 or stats2. + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2)) + .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) + // Entries which are common for stats1 and stats2 are being merged. + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1)) + .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49)) + .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15)) + .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0)) + assertEquals(9, testStats3.size()) + } + + @Test + fun testAddEntry() { + val expectedEntriesInStats2 = arrayOf( + Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1), + Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45), + Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7), + Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0), + Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) + + // While testStats* are already initialized with addEntry, verify content added + // matches expectation. + for (i in expectedEntriesInStats2.indices) { + val entry = testStats2.getValues(i, null) + assertEquals(expectedEntriesInStats2[i], entry) + } + + // Verify entry updated with addEntry. + val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9)) + assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9), + stats.getValues(3, null)) + } + + @Test + fun testAdd() { + var stats = NetworkStats(0L, 0) + assertNetworkStatsEquals(testStatsEmpty, stats) + stats = stats.add(testStats2) + assertNetworkStatsEquals(testStats2, stats) + stats = stats.add(testStats1) + // EMPTY + STATS2 + STATS1 = STATS3 + assertNetworkStatsEquals(testStats3, stats) + } + + @Test + fun testParcelUnparcel() { + assertParcelingIsLossless(testStatsEmpty) + assertParcelingIsLossless(testStats1) + assertParcelingIsLossless(testStats2) + assertFieldCountEquals(15, NetworkStats::class.java) + } + + @Test + fun testDescribeContents() { + assertEquals(0, testStatsEmpty.describeContents()) + assertEquals(0, testStats1.describeContents()) + assertEquals(0, testStats2.describeContents()) + assertEquals(0, testStats3.describeContents()) + } + + @Test + fun testSubtract() { + // STATS3 - STATS2 = STATS1 + assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2)) + // STATS3 - STATS1 = STATS2 + assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1)) + } + + @Test + fun testMethodsDontModifyReceiver() { + listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach { + val origStats = it.clone() + it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45)) + it.add(testStats3) + it.subtract(testStats1) + assertNetworkStatsEquals(origStats, it) + } + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt new file mode 100644 index 000000000000..aaf97f36889b --- /dev/null +++ b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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.util + +import android.os.Build +import android.system.NetlinkSocketAddress +import android.system.Os +import android.system.OsConstants.AF_INET +import android.system.OsConstants.ETH_P_ALL +import android.system.OsConstants.IPPROTO_UDP +import android.system.OsConstants.RTMGRP_NEIGH +import android.system.OsConstants.SOCK_DGRAM +import android.system.PacketSocketAddress +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_INDEX = 123 +private const val TEST_PORT = 555 +private const val FF_BYTE = 0xff.toByte() + +@RunWith(AndroidJUnit4::class) +@SmallTest +class SocketUtilsTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + + @Test + fun testMakeNetlinkSocketAddress() { + val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH) + if (nlAddress is NetlinkSocketAddress) { + assertEquals(TEST_PORT, nlAddress.getPortId()) + assertEquals(RTMGRP_NEIGH, nlAddress.getGroupsMask()) + } else { + fail("Not NetlinkSocketAddress object") + } + } + + @Test + fun testMakePacketSocketAddress_Q() { + val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX) + assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) + + val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE }) + assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + fun testMakePacketSocketAddress() { + val pkAddress = SocketUtils.makePacketSocketAddress( + ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE }) + assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) + } + + @Test + fun testCloseSocket() { + // Expect no exception happening with null object. + SocketUtils.closeSocket(null) + + val fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + assertTrue(fd.valid()) + SocketUtils.closeSocket(fd) + assertFalse(fd.valid()) + // Expecting socket should be still invalid even closed socket again. + SocketUtils.closeSocket(fd) + assertFalse(fd.valid()) + } +} diff --git a/packages/Connectivity/tests/deflake/Android.bp b/packages/Connectivity/tests/deflake/Android.bp new file mode 100644 index 000000000000..58ece37ef647 --- /dev/null +++ b/packages/Connectivity/tests/deflake/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2019 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +java_test_host { + name: "FrameworksNetDeflakeTest", + srcs: ["src/**/*.kt"], + libs: [ + "junit", + "tradefed", + ], + static_libs: [ + "kotlin-test", + "net-host-tests-utils", + ], + data: [":FrameworksNetTests"], + test_suites: ["device-tests"], +} diff --git a/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt new file mode 100644 index 000000000000..62855255fec2 --- /dev/null +++ b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 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.net + +import com.android.testutils.host.DeflakeHostTestBase +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import org.junit.runner.RunWith + +@RunWith(DeviceJUnit4ClassRunner::class) +class FrameworksNetDeflakeTest: DeflakeHostTestBase() { + override val runCount = 20 + override val testApkFilename = "FrameworksNetTests.apk" + override val testClasses = listOf("com.android.server.ConnectivityServiceTest") +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/Android.bp b/packages/Connectivity/tests/integration/Android.bp new file mode 100644 index 000000000000..39c424e31f0e --- /dev/null +++ b/packages/Connectivity/tests/integration/Android.bp @@ -0,0 +1,78 @@ +// +// Copyright (C) 2019 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "FrameworksNetIntegrationTests", + defaults: ["framework-connectivity-test-defaults"], + platform_apis: true, + certificate: "platform", + srcs: [ + "src/**/*.kt", + "src/**/*.aidl", + ], + libs: [ + "android.test.mock", + ], + static_libs: [ + "NetworkStackApiStableLib", + "androidx.test.ext.junit", + "frameworks-net-integration-testutils", + "kotlin-reflect", + "mockito-target-extended-minus-junit4", + "net-tests-utils", + "service-connectivity", + "services.core", + "services.net", + "testables", + ], + test_suites: ["device-tests"], + use_embedded_native_libs: true, + jni_libs: [ + // For mockito extended + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + // android_library does not include JNI libs: include NetworkStack dependencies here + "libnativehelper_compat_libc++", + "libnetworkstackutilsjni", + ], +} + +// Utilities for testing framework code both in integration and unit tests. +java_library { + name: "frameworks-net-integration-testutils", + defaults: ["framework-connectivity-test-defaults"], + srcs: ["util/**/*.java", "util/**/*.kt"], + static_libs: [ + "androidx.annotation_annotation", + "androidx.test.rules", + "junit", + "net-tests-utils", + ], + libs: [ + "service-connectivity", + "services.core", + "services.net", + ], +} diff --git a/packages/Connectivity/tests/integration/AndroidManifest.xml b/packages/Connectivity/tests/integration/AndroidManifest.xml new file mode 100644 index 000000000000..2e1368935759 --- /dev/null +++ b/packages/Connectivity/tests/integration/AndroidManifest.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/Connectivity/tests/integration/res/values/config.xml b/packages/Connectivity/tests/integration/res/values/config.xml new file mode 100644 index 000000000000..2c8046ffd781 --- /dev/null +++ b/packages/Connectivity/tests/integration/res/values/config.xml @@ -0,0 +1,15 @@ + + + + 12500 + http://test.android.com + https://secure.test.android.com + + http://fallback1.android.com + http://fallback2.android.com + + + + diff --git a/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt new file mode 100644 index 000000000000..61ef5bdca487 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 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 + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.networkstack.NetworkStackClientBase +import android.os.IBinder +import com.android.server.net.integrationtests.TestNetworkStackService +import org.mockito.Mockito.any +import org.mockito.Mockito.spy +import org.mockito.Mockito.timeout +import org.mockito.Mockito.verify +import kotlin.test.fail + +const val TEST_ACTION_SUFFIX = ".Test" + +class TestNetworkStackClient(private val context: Context) : NetworkStackClientBase() { + // TODO: consider switching to TrackRecord for more expressive checks + private val lastCallbacks = HashMap() + private val moduleConnector = ConnectivityModuleConnector { _, action, _, _ -> + val intent = Intent(action) + val serviceName = TestNetworkStackService::class.qualifiedName + ?: fail("TestNetworkStackService name not found") + intent.component = ComponentName(context.packageName, serviceName) + return@ConnectivityModuleConnector intent + }.also { it.init(context) } + + fun start() { + moduleConnector.startModuleService( + INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) { connector -> + onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(connector)) + } + } + + // base may be an instance of an inaccessible subclass, so non-spyable. + // Use a known open class that delegates to the original instance for all methods except + // asBinder. asBinder needs to use its own non-delegated implementation as otherwise it would + // return a binder token to a class that is not spied on. + open class NetworkMonitorCallbacksWrapper(private val base: INetworkMonitorCallbacks) : + INetworkMonitorCallbacks.Stub(), INetworkMonitorCallbacks by base { + // asBinder is implemented by both base class and delegate: specify explicitly + override fun asBinder(): IBinder { + return super.asBinder() + } + } + + override fun makeNetworkMonitor(network: Network, name: String?, cb: INetworkMonitorCallbacks) { + val cbSpy = spy(NetworkMonitorCallbacksWrapper(cb)) + lastCallbacks[network] = cbSpy + super.makeNetworkMonitor(network, name, cbSpy) + } + + fun verifyNetworkMonitorCreated(network: Network, timeoutMs: Long) { + val cb = lastCallbacks[network] + ?: fail("NetworkMonitor for network $network not requested") + verify(cb, timeout(timeoutMs)).onNetworkMonitorCreated(any()) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt new file mode 100644 index 000000000000..e039ef072542 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2019 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.net.integrationtests + +import android.app.usage.NetworkStatsManager +import android.content.ComponentName +import android.content.Context +import android.content.Context.BIND_AUTO_CREATE +import android.content.Context.BIND_IMPORTANT +import android.content.Intent +import android.content.ServiceConnection +import android.net.ConnectivityManager +import android.net.IDnsResolver +import android.net.INetd +import android.net.LinkProperties +import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkRequest +import android.net.TestNetworkStackClient +import android.net.Uri +import android.net.metrics.IpConnectivityLog +import android.os.ConditionVariable +import android.os.IBinder +import android.os.SystemConfigManager +import android.os.UserHandle +import android.testing.TestableContext +import android.util.Log +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.ConnectivityService +import com.android.server.NetworkAgentWrapper +import com.android.server.TestNetIdManager +import com.android.server.connectivity.MockableSystemProperties +import com.android.server.connectivity.ProxyTracker +import com.android.testutils.TestableNetworkCallback +import org.junit.After +import org.junit.Before +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.AdditionalAnswers +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.eq +import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import org.mockito.MockitoAnnotations +import org.mockito.Spy +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertTrue +import kotlin.test.fail + +const val SERVICE_BIND_TIMEOUT_MS = 5_000L +const val TEST_TIMEOUT_MS = 10_000L + +/** + * Test that exercises an instrumented version of ConnectivityService against an instrumented + * NetworkStack in a different test process. + */ +@RunWith(AndroidJUnit4::class) +class ConnectivityServiceIntegrationTest { + // lateinit used here for mocks as they need to be reinitialized between each test and the test + // should crash if they are used before being initialized. + @Mock + private lateinit var statsManager: NetworkStatsManager + @Mock + private lateinit var log: IpConnectivityLog + @Mock + private lateinit var netd: INetd + @Mock + private lateinit var dnsResolver: IDnsResolver + @Mock + private lateinit var systemConfigManager: SystemConfigManager + @Spy + private var context = TestableContext(realContext) + + // lateinit for these three classes under test, as they should be reset to a different instance + // for every test but should always be initialized before use (or the test should crash). + private lateinit var networkStackClient: TestNetworkStackClient + private lateinit var service: ConnectivityService + private lateinit var cm: ConnectivityManager + + companion object { + // lateinit for this binder token, as it must be initialized before any test code is run + // and use of it before init should crash the test. + private lateinit var nsInstrumentation: INetworkStackInstrumentation + private val bindingCondition = ConditionVariable(false) + + private val realContext get() = InstrumentationRegistry.getInstrumentation().context + private val httpProbeUrl get() = + realContext.getResources().getString(R.string.config_captive_portal_http_url) + private val httpsProbeUrl get() = + realContext.getResources().getString(R.string.config_captive_portal_https_url) + + private class InstrumentationServiceConnection : ServiceConnection { + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + Log.i("TestNetworkStack", "Service connected") + try { + if (service == null) fail("Error binding to NetworkStack instrumentation") + if (::nsInstrumentation.isInitialized) fail("Service already connected") + nsInstrumentation = INetworkStackInstrumentation.Stub.asInterface(service) + } finally { + bindingCondition.open() + } + } + + override fun onServiceDisconnected(name: ComponentName?) = Unit + } + + @BeforeClass + @JvmStatic + fun setUpClass() { + val intent = Intent(realContext, NetworkStackInstrumentationService::class.java) + intent.action = INetworkStackInstrumentation::class.qualifiedName + assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(), + BIND_AUTO_CREATE or BIND_IMPORTANT), + "Error binding to instrumentation service") + assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS), + "Timed out binding to instrumentation service " + + "after $SERVICE_BIND_TIMEOUT_MS ms") + } + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo(context)) + doReturn(UserHandle.ALL).`when`(asUserCtx).user + doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt()) + doNothing().`when`(context).sendStickyBroadcast(any(), any()) + doReturn(Context.SYSTEM_CONFIG_SERVICE).`when`(context) + .getSystemServiceName(SystemConfigManager::class.java) + doReturn(systemConfigManager).`when`(context) + .getSystemService(Context.SYSTEM_CONFIG_SERVICE) + doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString()) + + networkStackClient = TestNetworkStackClient(realContext) + networkStackClient.start() + + service = TestConnectivityService(makeDependencies()) + cm = ConnectivityManager(context, service) + context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm) + context.addMockSystemService(Context.NETWORK_STATS_SERVICE, statsManager) + + service.systemReadyInternal() + } + + private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService( + context, dnsResolver, log, netd, deps) + + private fun makeDependencies(): ConnectivityService.Dependencies { + val deps = spy(ConnectivityService.Dependencies()) + doReturn(networkStackClient).`when`(deps).networkStack + doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any()) + doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties + doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager() + return deps + } + + @After + fun tearDown() { + nsInstrumentation.clearAllState() + } + + @Test + fun testValidation() { + val request = NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .build() + val testCallback = TestableNetworkCallback() + + cm.registerNetworkCallback(request, testCallback) + nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204)) + nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) + + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */, + context) + networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) + + na.addCapability(NET_CAPABILITY_INTERNET) + na.connect() + + testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS) + assertEquals(2, nsInstrumentation.getRequestUrls().size) + } + + @Test + fun testCapportApi() { + val request = NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .build() + val testCb = TestableNetworkCallback() + val apiUrl = "https://capport.android.com" + + cm.registerNetworkCallback(request, testCb) + nsInstrumentation.addHttpResponse(HttpResponse( + apiUrl, + """ + |{ + | "captive": true, + | "user-portal-url": "https://login.capport.android.com", + | "venue-info-url": "https://venueinfo.capport.android.com" + |} + """.trimMargin())) + + // Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the + // HTTP probe as it should not be sent. + // Even if the HTTPS probe succeeds, a portal should be detected as the API takes precedence + // in that case. + nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) + + val lp = LinkProperties() + lp.captivePortalApiUrl = Uri.parse(apiUrl) + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context) + networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) + + na.addCapability(NET_CAPABILITY_INTERNET) + na.connect() + + testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS) + + val capportData = testCb.expectLinkPropertiesThat(na, TEST_TIMEOUT_MS) { + it.captivePortalData != null + }.lp.captivePortalData + assertNotNull(capportData) + assertTrue(capportData.isCaptive) + assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl) + assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl) + + val nc = testCb.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, na, TEST_TIMEOUT_MS) + assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl new file mode 100644 index 000000000000..9a2bcfea7641 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 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.net.integrationtests; + +parcelable HttpResponse; \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt new file mode 100644 index 000000000000..e2063138fef1 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 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.net.integrationtests + +import android.os.Parcel +import android.os.Parcelable + +data class HttpResponse( + val requestUrl: String, + val responseCode: Int, + val content: String = "", + val redirectUrl: String? = null +) : Parcelable { + constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString()) + constructor(requestUrl: String, contentBody: String): this( + requestUrl, + responseCode = 200, + content = contentBody, + redirectUrl = null) + + override fun writeToParcel(dest: Parcel, flags: Int) { + with(dest) { + writeString(requestUrl) + writeInt(responseCode) + writeString(content) + writeString(redirectUrl) + } + } + + override fun describeContents() = 0 + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(source: Parcel) = HttpResponse(source) + override fun newArray(size: Int) = arrayOfNulls(size) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl new file mode 100644 index 000000000000..efc58add9cf5 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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.net.integrationtests; + +import com.android.server.net.integrationtests.HttpResponse; + +interface INetworkStackInstrumentation { + void clearAllState(); + void addHttpResponse(in HttpResponse response); + List getRequestUrls(); +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt new file mode 100644 index 000000000000..e807952cec11 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 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.net.integrationtests + +import android.app.Service +import android.content.Intent +import java.net.URL +import java.util.Collections +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.collections.ArrayList +import kotlin.test.fail + +/** + * An instrumentation interface for the NetworkStack that allows controlling behavior to + * facilitate integration tests. + */ +class NetworkStackInstrumentationService : Service() { + override fun onBind(intent: Intent) = InstrumentationConnector.asBinder() + + object InstrumentationConnector : INetworkStackInstrumentation.Stub() { + private val httpResponses = ConcurrentHashMap>() + .run { + withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } } + } + private val httpRequestUrls = Collections.synchronizedList(ArrayList()) + + /** + * Called when an HTTP request is being processed by NetworkMonitor. Returns the response + * that should be simulated. + */ + fun processRequest(url: URL): HttpResponse { + val strUrl = url.toString() + httpRequestUrls.add(strUrl) + return httpResponses[strUrl]?.poll() + ?: fail("No mocked response for request: $strUrl. " + + "Mocked URL keys are: ${httpResponses.keys}") + } + + /** + * Clear all state of this connector. This is intended for use between two tests, so all + * state should be reset as if the connector was just created. + */ + override fun clearAllState() { + httpResponses.clear() + httpRequestUrls.clear() + } + + /** + * Add a response to a future HTTP request. + * + *

For any subsequent HTTP/HTTPS query, the first response with a matching URL will be + * used to mock the query response. + * + *

All requests that are expected to be sent must have a mock response: if an unexpected + * request is seen, the test will fail. + */ + override fun addHttpResponse(response: HttpResponse) { + httpResponses.getValue(response.requestUrl).add(response) + } + + /** + * Get the ordered list of request URLs that have been sent by NetworkMonitor, and were + * answered based on mock responses. + */ + override fun getRequestUrls(): List { + return ArrayList(httpRequestUrls) + } + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt new file mode 100644 index 000000000000..eff66584d6c1 --- /dev/null +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 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.net.integrationtests + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.net.INetworkMonitorCallbacks +import android.net.Network +import android.net.metrics.IpConnectivityLog +import android.net.util.SharedLog +import android.os.IBinder +import com.android.networkstack.netlink.TcpSocketTracker +import com.android.server.NetworkStackService +import com.android.server.NetworkStackService.NetworkMonitorConnector +import com.android.server.NetworkStackService.NetworkStackConnector +import com.android.server.connectivity.NetworkMonitor +import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import java.io.ByteArrayInputStream +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import java.nio.charset.StandardCharsets + +private const val TEST_NETID = 42 + +/** + * Android service that can return an [android.net.INetworkStackConnector] which can be instrumented + * through [NetworkStackInstrumentationService]. + * Useful in tests to create test instrumented NetworkStack components that can receive + * instrumentation commands through [NetworkStackInstrumentationService]. + */ +class TestNetworkStackService : Service() { + override fun onBind(intent: Intent): IBinder = TestNetworkStackConnector(makeTestContext()) + + private fun makeTestContext() = spy(applicationContext).also { + doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE) + } + + private class TestPermissionChecker : NetworkStackService.PermissionChecker() { + override fun enforceNetworkStackCallingPermission() = Unit + } + + private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) : + NetworkMonitor.Dependencies() { + override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork + } + + private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector( + context, TestPermissionChecker(), NetworkStackService.Dependencies()) { + + private val network = Network(TEST_NETID) + private val privateDnsBypassNetwork = TestNetwork(TEST_NETID) + + private inner class TestNetwork(netId: Int) : Network(netId) { + override fun openConnection(url: URL): URLConnection { + val response = InstrumentationConnector.processRequest(url) + val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8) + + val connection = mock(HttpURLConnection::class.java) + doReturn(response.responseCode).`when`(connection).responseCode + doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong + doReturn(response.redirectUrl).`when`(connection).getHeaderField("location") + doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream + return connection + } + } + + override fun makeNetworkMonitor( + network: Network, + name: String?, + cb: INetworkMonitorCallbacks + ) { + val nm = NetworkMonitor(this@TestNetworkStackService, cb, + this.network, + mock(IpConnectivityLog::class.java), mock(SharedLog::class.java), + mock(NetworkStackService.NetworkStackServiceManager::class.java), + NetworkMonitorDeps(privateDnsBypassNetwork), + mock(TcpSocketTracker::class.java)) + cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker())) + } + } +} diff --git a/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt new file mode 100644 index 000000000000..165fd3728281 --- /dev/null +++ b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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 + */ + +@file:JvmName("ConnectivityServiceTestUtils") + +package com.android.server + +import android.net.ConnectivityManager.TYPE_BLUETOOTH +import android.net.ConnectivityManager.TYPE_ETHERNET +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_NONE +import android.net.ConnectivityManager.TYPE_TEST +import android.net.ConnectivityManager.TYPE_VPN +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkCapabilities.TRANSPORT_ETHERNET +import android.net.NetworkCapabilities.TRANSPORT_TEST +import android.net.NetworkCapabilities.TRANSPORT_VPN +import android.net.NetworkCapabilities.TRANSPORT_WIFI + +fun transportToLegacyType(transport: Int) = when (transport) { + TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH + TRANSPORT_CELLULAR -> TYPE_MOBILE + TRANSPORT_ETHERNET -> TYPE_ETHERNET + TRANSPORT_TEST -> TYPE_TEST + TRANSPORT_VPN -> TYPE_VPN + TRANSPORT_WIFI -> TYPE_WIFI + else -> TYPE_NONE +} \ No newline at end of file diff --git a/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java new file mode 100644 index 000000000000..17db17923f4d --- /dev/null +++ b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2019 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 static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; + +import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; +import android.net.NetworkCapabilities; +import android.net.NetworkProvider; +import android.net.NetworkScore; +import android.net.NetworkSpecifier; +import android.net.QosFilter; +import android.net.SocketKeepalive; +import android.os.ConditionVariable; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; +import android.util.Range; + +import com.android.net.module.util.ArrayTrackRecord; +import com.android.testutils.HandlerUtils; +import com.android.testutils.TestableNetworkCallback; + +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { + private final NetworkCapabilities mNetworkCapabilities; + private final HandlerThread mHandlerThread; + private final Context mContext; + private final String mLogTag; + private final NetworkAgentConfig mNetworkAgentConfig; + + private final ConditionVariable mDisconnected = new ConditionVariable(); + private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); + private final AtomicBoolean mConnected = new AtomicBoolean(false); + private NetworkScore mScore; + private NetworkAgent mNetworkAgent; + private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED; + private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE; + // Controls how test network agent is going to wait before responding to keepalive + // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem. + private long mKeepaliveResponseDelay = 0L; + private Integer mExpectedKeepaliveSlot = null; + private final ArrayTrackRecord.ReadHead mCallbackHistory = + new ArrayTrackRecord().newReadHead(); + + public NetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate, Context context) throws Exception { + final int type = transportToLegacyType(transport); + final String typeName = ConnectivityManager.getNetworkTypeName(type); + mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + mNetworkCapabilities.addTransportType(transport); + switch (transport) { + case TRANSPORT_ETHERNET: + mScore = new NetworkScore.Builder().setLegacyInt(70).build(); + break; + case TRANSPORT_WIFI: + mScore = new NetworkScore.Builder().setLegacyInt(60).build(); + break; + case TRANSPORT_CELLULAR: + mScore = new NetworkScore.Builder().setLegacyInt(50).build(); + break; + case TRANSPORT_WIFI_AWARE: + mScore = new NetworkScore.Builder().setLegacyInt(20).build(); + break; + case TRANSPORT_VPN: + mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN); + // VPNs deduce the SUSPENDED capability from their underlying networks and there + // is no public API to let VPN services set it. + mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + mScore = new NetworkScore.Builder().setLegacyInt(101).build(); + break; + default: + throw new UnsupportedOperationException("unimplemented network type"); + } + mContext = context; + mLogTag = "Mock-" + typeName; + mHandlerThread = new HandlerThread(mLogTag); + mHandlerThread.start(); + + // extraInfo is set to "" by default in NetworkAgentConfig. + final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : ""; + mNetworkAgentConfig = new NetworkAgentConfig.Builder() + .setLegacyType(type) + .setLegacyTypeName(typeName) + .setLegacyExtraInfo(extraInfo) + .build(); + mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig); + } + + protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties, + final NetworkAgentConfig nac) throws Exception { + return new InstrumentedNetworkAgent(this, linkProperties, nac); + } + + public static class InstrumentedNetworkAgent extends NetworkAgent { + private final NetworkAgentWrapper mWrapper; + private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider"; + + public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp, + NetworkAgentConfig nac) { + super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag, + wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac, + new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(), + PROVIDER_NAME)); + mWrapper = wrapper; + register(); + } + + @Override + public void unwanted() { + mWrapper.mDisconnected.open(); + } + + @Override + public void startSocketKeepalive(Message msg) { + int slot = msg.arg1; + if (mWrapper.mExpectedKeepaliveSlot != null) { + assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot); + } + mWrapper.mHandlerThread.getThreadHandler().postDelayed( + () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError), + mWrapper.mKeepaliveResponseDelay); + } + + @Override + public void stopSocketKeepalive(Message msg) { + final int slot = msg.arg1; + mWrapper.mHandlerThread.getThreadHandler().postDelayed( + () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError), + mWrapper.mKeepaliveResponseDelay); + } + + @Override + public void onQosCallbackRegistered(final int qosCallbackId, + final @NonNull QosFilter filter) { + Log.i(mWrapper.mLogTag, "onQosCallbackRegistered"); + mWrapper.mCallbackHistory.add( + new CallbackType.OnQosCallbackRegister(qosCallbackId, filter)); + } + + @Override + public void onQosCallbackUnregistered(final int qosCallbackId) { + Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered"); + mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId)); + } + + @Override + protected void preventAutomaticReconnect() { + mWrapper.mPreventReconnectReceived.open(); + } + + @Override + protected void addKeepalivePacketFilter(Message msg) { + Log.i(mWrapper.mLogTag, "Add keepalive packet filter."); + } + + @Override + protected void removeKeepalivePacketFilter(Message msg) { + Log.i(mWrapper.mLogTag, "Remove keepalive packet filter."); + } + } + + public void setScore(@NonNull final NetworkScore score) { + mScore = score; + mNetworkAgent.sendNetworkScore(score); + } + + public void adjustScore(int change) { + final int newLegacyScore = mScore.getLegacyInt() + change; + final NetworkScore.Builder builder = new NetworkScore.Builder() + .setLegacyInt(newLegacyScore); + if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI) && newLegacyScore < 50) { + builder.setExiting(true); + } + mScore = builder.build(); + mNetworkAgent.sendNetworkScore(mScore); + } + + public NetworkScore getScore() { + return mScore; + } + + public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { + mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated); + } + + public void addCapability(int capability) { + mNetworkCapabilities.addCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void removeCapability(int capability) { + mNetworkCapabilities.removeCapability(capability); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setUids(Set> uids) { + mNetworkCapabilities.setUids(uids); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setSignalStrength(int signalStrength) { + mNetworkCapabilities.setSignalStrength(signalStrength); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) { + mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + + public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) { + mNetworkCapabilities.set(nc); + if (sendToConnectivityService) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + } + + public void connect() { + if (!mConnected.compareAndSet(false /* expect */, true /* update */)) { + // compareAndSet returns false when the value couldn't be updated because it did not + // match the expected value. + fail("Test NetworkAgents can only be connected once"); + } + mNetworkAgent.markConnected(); + } + + public void suspend() { + removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + } + + public void resume() { + addCapability(NET_CAPABILITY_NOT_SUSPENDED); + } + + public void disconnect() { + mNetworkAgent.unregister(); + } + + @Override + public Network getNetwork() { + return mNetworkAgent.getNetwork(); + } + + public void expectPreventReconnectReceived(long timeoutMs) { + assertTrue(mPreventReconnectReceived.block(timeoutMs)); + } + + public void expectDisconnected(long timeoutMs) { + assertTrue(mDisconnected.block(timeoutMs)); + } + + public void sendLinkProperties(LinkProperties lp) { + mNetworkAgent.sendLinkProperties(lp); + } + + public void setStartKeepaliveEvent(int reason) { + mStartKeepaliveError = reason; + } + + public void setStopKeepaliveEvent(int reason) { + mStopKeepaliveError = reason; + } + + public void setKeepaliveResponseDelay(long delay) { + mKeepaliveResponseDelay = delay; + } + + public void setExpectedKeepaliveSlot(Integer slot) { + mExpectedKeepaliveSlot = slot; + } + + public NetworkAgent getNetworkAgent() { + return mNetworkAgent; + } + + public NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + public int getLegacyType() { + return mNetworkAgentConfig.getLegacyType(); + } + + public String getExtraInfo() { + return mNetworkAgentConfig.getLegacyExtraInfo(); + } + + public @NonNull ArrayTrackRecord.ReadHead getCallbackHistory() { + return mCallbackHistory; + } + + public void waitForIdle(long timeoutMs) { + HandlerUtils.waitForIdle(mHandlerThread, timeoutMs); + } + + abstract static class CallbackType { + final int mQosCallbackId; + + protected CallbackType(final int qosCallbackId) { + mQosCallbackId = qosCallbackId; + } + + static class OnQosCallbackRegister extends CallbackType { + final QosFilter mFilter; + OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) { + super(qosCallbackId); + mFilter = filter; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final OnQosCallbackRegister that = (OnQosCallbackRegister) o; + return mQosCallbackId == that.mQosCallbackId + && Objects.equals(mFilter, that.mFilter); + } + + @Override + public int hashCode() { + return Objects.hash(mQosCallbackId, mFilter); + } + } + + static class OnQosCallbackUnregister extends CallbackType { + OnQosCallbackUnregister(final int qosCallbackId) { + super(qosCallbackId); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o; + return mQosCallbackId == that.mQosCallbackId; + } + + @Override + public int hashCode() { + return Objects.hash(mQosCallbackId); + } + } + } + + public boolean isBypassableVpn() { + return mNetworkAgentConfig.isBypassableVpn(); + } +} diff --git a/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt new file mode 100644 index 000000000000..938a694e8ba9 --- /dev/null +++ b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 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 java.util.concurrent.atomic.AtomicInteger + +/** + * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather + * than starting from [NetIdManager.MIN_NET_ID] and increasing. + * + * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs + * overlapping with netIDs used by the real ConnectivityService on the device. + * + * IDs may still overlap if many networks have been used on the device (so the "real" netIDs + * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there + * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as + * the real ConnectivityService could start using netIds that have been used by the test in the + * past. + */ +class TestNetIdManager : NetIdManager() { + private val nextId = AtomicInteger(MAX_NET_ID) + override fun reserveNetId() = nextId.decrementAndGet() + override fun releaseNetId(id: Int) = Unit + fun peekNextNetId() = nextId.get() - 1 +} diff --git a/packages/Connectivity/tests/smoketest/Android.bp b/packages/Connectivity/tests/smoketest/Android.bp new file mode 100644 index 000000000000..1535f3ddcb38 --- /dev/null +++ b/packages/Connectivity/tests/smoketest/Android.bp @@ -0,0 +1,31 @@ +// This test exists only because the jni_libs list for these tests is difficult to +// maintain: the test itself only depends on libnetworkstatsfactorytestjni, but the test +// fails to load that library unless *all* the dependencies of that library are explicitly +// listed in jni_libs. This means that whenever any of the dependencies changes the test +// starts failing and breaking presubmits in frameworks/base. We cannot easily put +// FrameworksNetTests into global presubmit because they are at times flaky, but this +// test is effectively empty beyond validating that the libraries load correctly, and +// thus should be stable enough to put in global presubmit. +// +// TODO: remove this hack when there is a better solution for jni_libs that includes +// dependent libraries. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "FrameworksNetSmokeTests", + defaults: ["FrameworksNetTests-jni-defaults"], + srcs: ["java/SmokeTest.java"], + test_suites: ["device-tests"], + static_libs: [ + "androidx.test.rules", + "mockito-target-minus-junit4", + "services.core", + ], +} diff --git a/packages/Connectivity/tests/smoketest/AndroidManifest.xml b/packages/Connectivity/tests/smoketest/AndroidManifest.xml new file mode 100644 index 000000000000..f1b9febb9f57 --- /dev/null +++ b/packages/Connectivity/tests/smoketest/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/packages/Connectivity/tests/smoketest/AndroidTest.xml b/packages/Connectivity/tests/smoketest/AndroidTest.xml new file mode 100644 index 000000000000..ac366e4ac544 --- /dev/null +++ b/packages/Connectivity/tests/smoketest/AndroidTest.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/packages/Connectivity/tests/smoketest/java/SmokeTest.java b/packages/Connectivity/tests/smoketest/java/SmokeTest.java new file mode 100644 index 000000000000..7d6655fde15e --- /dev/null +++ b/packages/Connectivity/tests/smoketest/java/SmokeTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 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.net; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class SmokeTest { + + @Test + public void testLoadJni() { + System.loadLibrary("networkstatsfactorytestjni"); + assertEquals(0, 0x00); + } +} diff --git a/packages/Connectivity/tests/unit/Android.bp b/packages/Connectivity/tests/unit/Android.bp new file mode 100644 index 000000000000..6f503ace037f --- /dev/null +++ b/packages/Connectivity/tests/unit/Android.bp @@ -0,0 +1,89 @@ +//######################################################################## +// Build FrameworksNetTests package +//######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +java_defaults { + name: "FrameworksNetTests-jni-defaults", + jni_libs: [ + "ld-android", + "libbacktrace", + "libbase", + "libbinder", + "libbpf", + "libbpf_android", + "libc++", + "libcgrouprc", + "libcrypto", + "libcutils", + "libdl_android", + "libhidl-gen-utils", + "libhidlbase", + "libjsoncpp", + "liblog", + "liblzma", + "libnativehelper", + "libnetdbpf", + "libnetdutils", + "libnetworkstatsfactorytestjni", + "libpackagelistparser", + "libpcre2", + "libprocessgroup", + "libselinux", + "libtinyxml2", + "libui", + "libunwindstack", + "libutils", + "libutilscallstack", + "libvndksupport", + "libziparchive", + "libz", + "netd_aidl_interface-V5-cpp", + ], +} + +android_test { + name: "FrameworksNetTests", + defaults: [ + "framework-connectivity-test-defaults", + "FrameworksNetTests-jni-defaults", + ], + srcs: [ + "java/**/*.java", + "java/**/*.kt", + ], + test_suites: ["device-tests"], + certificate: "platform", + jarjar_rules: "jarjar-rules.txt", + static_libs: [ + "androidx.test.rules", + "bouncycastle-repackaged-unbundled", + "FrameworksNetCommonTests", + "frameworks-base-testutils", + "frameworks-net-integration-testutils", + "framework-protos", + "mockito-target-minus-junit4", + "net-tests-utils", + "platform-test-annotations", + "service-connectivity-pre-jarjar", + "services.core", + "services.net", + ], + libs: [ + "android.net.ipsec.ike.stubs.module_lib", + "android.test.runner", + "android.test.base", + "android.test.mock", + "ServiceConnectivityResources", + ], + jni_libs: [ + "libservice-connectivity", + ], +} diff --git a/packages/Connectivity/tests/unit/AndroidManifest.xml b/packages/Connectivity/tests/unit/AndroidManifest.xml new file mode 100644 index 000000000000..4c60ccf60615 --- /dev/null +++ b/packages/Connectivity/tests/unit/AndroidManifest.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/Connectivity/tests/unit/AndroidTest.xml b/packages/Connectivity/tests/unit/AndroidTest.xml new file mode 100644 index 000000000000..939ae493b280 --- /dev/null +++ b/packages/Connectivity/tests/unit/AndroidTest.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/packages/Connectivity/tests/unit/jarjar-rules.txt b/packages/Connectivity/tests/unit/jarjar-rules.txt new file mode 100644 index 000000000000..ca8867206dda --- /dev/null +++ b/packages/Connectivity/tests/unit/jarjar-rules.txt @@ -0,0 +1,2 @@ +# Module library in frameworks/libs/net +rule com.android.net.module.util.** android.net.frameworktests.util.@1 diff --git a/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java new file mode 100644 index 000000000000..899295a019d2 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2018 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.app.usage; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.ConnectivityManager; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkStats.Entry; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.os.RemoteException; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsManagerTest { + + private @Mock INetworkStatsService mService; + private @Mock INetworkStatsSession mStatsSession; + + private NetworkStatsManager mManager; + + // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp. + private static final int MATCH_MOBILE_ALL = 1; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService); + } + + @Test + public void testQueryDetails() throws RemoteException { + final String subscriberId = "subid"; + final long startTime = 1; + final long endTime = 100; + final int uid1 = 10001; + final int uid2 = 10002; + final int uid3 = 10003; + + Entry uid1Entry1 = new Entry("if1", uid1, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 100, 10, 200, 20, 0); + + Entry uid1Entry2 = new Entry( + "if2", uid1, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 100, 10, 200, 20, 0); + + Entry uid2Entry1 = new Entry("if1", uid2, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 150, 10, 250, 20, 0); + + Entry uid2Entry2 = new Entry( + "if2", uid2, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 150, 10, 250, 20, 0); + + NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2); + history1.recordData(10, 20, uid1Entry1); + history1.recordData(20, 30, uid1Entry2); + + NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2); + history1.recordData(30, 40, uid2Entry1); + history1.recordData(35, 45, uid2Entry2); + + + when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); + when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 }); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + eq(uid1), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) + .then((InvocationOnMock inv) -> { + NetworkTemplate template = inv.getArgument(0); + assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); + assertEquals(subscriberId, template.getSubscriberId()); + return history1; + }); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + eq(uid2), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) + .then((InvocationOnMock inv) -> { + NetworkTemplate template = inv.getArgument(0); + assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); + assertEquals(subscriberId, template.getSubscriberId()); + return history2; + }); + + + NetworkStats stats = mManager.queryDetails( + ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime); + + NetworkStats.Bucket bucket = new NetworkStats.Bucket(); + + // First 2 buckets exactly match entry timings + assertTrue(stats.getNextBucket(bucket)); + assertEquals(10, bucket.getStartTimeStamp()); + assertEquals(20, bucket.getEndTimeStamp()); + assertBucketMatches(uid1Entry1, bucket); + + assertTrue(stats.getNextBucket(bucket)); + assertEquals(20, bucket.getStartTimeStamp()); + assertEquals(30, bucket.getEndTimeStamp()); + assertBucketMatches(uid1Entry2, bucket); + + // 30 -> 40: contains uid2Entry1 and half of uid2Entry2 + assertTrue(stats.getNextBucket(bucket)); + assertEquals(30, bucket.getStartTimeStamp()); + assertEquals(40, bucket.getEndTimeStamp()); + assertEquals(225, bucket.getRxBytes()); + assertEquals(15, bucket.getRxPackets()); + assertEquals(375, bucket.getTxBytes()); + assertEquals(30, bucket.getTxPackets()); + + // 40 -> 50: contains half of uid2Entry2 + assertTrue(stats.getNextBucket(bucket)); + assertEquals(40, bucket.getStartTimeStamp()); + assertEquals(50, bucket.getEndTimeStamp()); + assertEquals(75, bucket.getRxBytes()); + assertEquals(5, bucket.getRxPackets()); + assertEquals(125, bucket.getTxBytes()); + assertEquals(10, bucket.getTxPackets()); + + assertFalse(stats.hasNextBucket()); + } + + @Test + public void testQueryDetails_NoSubscriberId() throws RemoteException { + final long startTime = 1; + final long endTime = 100; + final int uid1 = 10001; + final int uid2 = 10002; + + when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); + when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 }); + + NetworkStats stats = mManager.queryDetails( + ConnectivityManager.TYPE_MOBILE, null, startTime, endTime); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong())) + .thenReturn(new NetworkStatsHistory(10, 0)); + + verify(mStatsSession, times(1)).getHistoryIntervalForUid( + argThat((NetworkTemplate t) -> + // No subscriberId: MATCH_MOBILE_WILDCARD template + t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), + eq(uid1), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); + + verify(mStatsSession, times(1)).getHistoryIntervalForUid( + argThat((NetworkTemplate t) -> + // No subscriberId: MATCH_MOBILE_WILDCARD template + t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), + eq(uid2), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); + + assertFalse(stats.hasNextBucket()); + } + + private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) { + assertEquals(expected.uid, actual.getUid()); + assertEquals(expected.rxBytes, actual.getRxBytes()); + assertEquals(expected.rxPackets, actual.getRxPackets()); + assertEquals(expected.txBytes, actual.getTxBytes()); + assertEquals(expected.txPackets, actual.getTxPackets()); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java new file mode 100644 index 000000000000..06e9405a6a79 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java @@ -0,0 +1,384 @@ +/* + * 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; + +import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsBinder; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; +import static android.net.ConnectivityDiagnosticsManager.DataStallReport; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +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 androidx.test.InstrumentationRegistry; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; + +import java.util.concurrent.Executor; + +@RunWith(JUnit4.class) +public class ConnectivityDiagnosticsManagerTest { + private static final int NET_ID = 1; + private static final int DETECTION_METHOD = 2; + private static final long TIMESTAMP = 10L; + private static final String INTERFACE_NAME = "interface"; + private static final String BUNDLE_KEY = "key"; + private static final String BUNDLE_VALUE = "value"; + + private static final Executor INLINE_EXECUTOR = x -> x.run(); + + @Mock private IConnectivityManager mService; + @Mock private ConnectivityDiagnosticsCallback mCb; + + private Context mContext; + private ConnectivityDiagnosticsBinder mBinder; + private ConnectivityDiagnosticsManager mManager; + + private String mPackageName; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getContext(); + + mService = mock(IConnectivityManager.class); + mCb = mock(ConnectivityDiagnosticsCallback.class); + + mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR); + mManager = new ConnectivityDiagnosticsManager(mContext, mService); + + mPackageName = mContext.getOpPackageName(); + } + + @After + public void tearDown() { + // clear ConnectivityDiagnosticsManager callbacks map + ConnectivityDiagnosticsManager.sCallbacks.clear(); + } + + private ConnectivityReport createSampleConnectivityReport() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(INTERFACE_NAME); + + final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); + networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); + + final PersistableBundle bundle = new PersistableBundle(); + bundle.putString(BUNDLE_KEY, BUNDLE_VALUE); + + return new ConnectivityReport( + new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle); + } + + private ConnectivityReport createDefaultConnectivityReport() { + return new ConnectivityReport( + new Network(0), + 0L, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY); + } + + @Test + public void testPersistableBundleEquals() { + assertFalse( + ConnectivityDiagnosticsManager.persistableBundleEquals( + null, PersistableBundle.EMPTY)); + assertFalse( + ConnectivityDiagnosticsManager.persistableBundleEquals( + PersistableBundle.EMPTY, null)); + assertTrue( + ConnectivityDiagnosticsManager.persistableBundleEquals( + PersistableBundle.EMPTY, PersistableBundle.EMPTY)); + + final PersistableBundle a = new PersistableBundle(); + a.putString(BUNDLE_KEY, BUNDLE_VALUE); + + final PersistableBundle b = new PersistableBundle(); + b.putString(BUNDLE_KEY, BUNDLE_VALUE); + + final PersistableBundle c = new PersistableBundle(); + c.putString(BUNDLE_KEY, null); + + assertFalse( + ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a)); + assertFalse( + ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY)); + + assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b)); + assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a)); + + assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c)); + assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a)); + } + + @Test + public void testConnectivityReportEquals() { + final ConnectivityReport defaultReport = createDefaultConnectivityReport(); + final ConnectivityReport sampleReport = createSampleConnectivityReport(); + assertEquals(sampleReport, createSampleConnectivityReport()); + assertEquals(defaultReport, createDefaultConnectivityReport()); + + final LinkProperties linkProperties = sampleReport.getLinkProperties(); + final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities(); + final PersistableBundle bundle = sampleReport.getAdditionalInfo(); + + assertNotEquals( + createDefaultConnectivityReport(), + new ConnectivityReport( + new Network(NET_ID), + 0L, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + createDefaultConnectivityReport(), + new ConnectivityReport( + new Network(0), + TIMESTAMP, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + createDefaultConnectivityReport(), + new ConnectivityReport( + new Network(0), + 0L, + linkProperties, + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + createDefaultConnectivityReport(), + new ConnectivityReport( + new Network(0), + TIMESTAMP, + new LinkProperties(), + networkCapabilities, + PersistableBundle.EMPTY)); + assertNotEquals( + createDefaultConnectivityReport(), + new ConnectivityReport( + new Network(0), + TIMESTAMP, + new LinkProperties(), + new NetworkCapabilities(), + bundle)); + } + + @Test + public void testConnectivityReportParcelUnparcel() { + assertParcelSane(createSampleConnectivityReport(), 5); + } + + private DataStallReport createSampleDataStallReport() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(INTERFACE_NAME); + + final PersistableBundle bundle = new PersistableBundle(); + bundle.putString(BUNDLE_KEY, BUNDLE_VALUE); + + final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); + networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); + + return new DataStallReport( + new Network(NET_ID), + TIMESTAMP, + DETECTION_METHOD, + linkProperties, + networkCapabilities, + bundle); + } + + private DataStallReport createDefaultDataStallReport() { + return new DataStallReport( + new Network(0), + 0L, + 0, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY); + } + + @Test + public void testDataStallReportEquals() { + final DataStallReport defaultReport = createDefaultDataStallReport(); + final DataStallReport sampleReport = createSampleDataStallReport(); + assertEquals(sampleReport, createSampleDataStallReport()); + assertEquals(defaultReport, createDefaultDataStallReport()); + + final LinkProperties linkProperties = sampleReport.getLinkProperties(); + final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities(); + final PersistableBundle bundle = sampleReport.getStallDetails(); + + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(NET_ID), + 0L, + 0, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(0), + TIMESTAMP, + 0, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(0), + 0L, + DETECTION_METHOD, + new LinkProperties(), + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(0), + 0L, + 0, + linkProperties, + new NetworkCapabilities(), + PersistableBundle.EMPTY)); + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(0), + 0L, + 0, + new LinkProperties(), + networkCapabilities, + PersistableBundle.EMPTY)); + assertNotEquals( + defaultReport, + new DataStallReport( + new Network(0), + 0L, + 0, + new LinkProperties(), + new NetworkCapabilities(), + bundle)); + } + + @Test + public void testDataStallReportParcelUnparcel() { + assertParcelSane(createSampleDataStallReport(), 6); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() { + mBinder.onConnectivityReportAvailable(createSampleConnectivityReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() { + mBinder.onDataStallSuspected(createSampleDataStallReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() { + final Network n = new Network(NET_ID); + final boolean connectivity = true; + + mBinder.onNetworkConnectivityReported(n, connectivity); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // 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), eq(mPackageName)); + 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), eq(mPackageName)); + assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb)); + } + + @Test + public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception { + mManager.unregisterConnectivityDiagnosticsCallback(mCb); + + verifyNoMoreInteractions(mService); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java new file mode 100644 index 000000000000..591e0cc3504e --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2017 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; + +import static android.net.ConnectivityManager.TYPE_NONE; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; +import static android.net.NetworkRequest.Type.REQUEST; +import static android.net.NetworkRequest.Type.TRACK_DEFAULT; +import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.net.ConnectivityManager.NetworkCallback; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Process; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ConnectivityManagerTest { + + @Mock Context mCtx; + @Mock IConnectivityManager mService; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + static NetworkCapabilities verifyNetworkCapabilities( + int legacyType, int transportType, int... capabilities) { + final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType); + assertNotNull(nc); + assertTrue(nc.hasTransport(transportType)); + for (int capability : capabilities) { + assertTrue(nc.hasCapability(capability)); + } + + return nc; + } + + static void verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType) { + verifyNetworkCapabilities( + legacyType, + transportType, + NET_CAPABILITY_INTERNET, + NET_CAPABILITY_NOT_RESTRICTED, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + } + + static void verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability) { + final NetworkCapabilities nc = verifyNetworkCapabilities( + legacyType, + TRANSPORT_CELLULAR, + capability, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } + + @Test + public void testNetworkCapabilitiesForTypeMobile() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE, TRANSPORT_CELLULAR); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileCbs() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_CBS, NET_CAPABILITY_CBS); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileDun() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_DUN, NET_CAPABILITY_DUN); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileFota() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_FOTA, NET_CAPABILITY_FOTA); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileHipri() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_HIPRI, TRANSPORT_CELLULAR); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileIms() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_IMS, NET_CAPABILITY_IMS); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileMms() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_MMS, + TRANSPORT_CELLULAR, + NET_CAPABILITY_MMS, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileSupl() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_SUPL, + TRANSPORT_CELLULAR, + NET_CAPABILITY_SUPL, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeWifi() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_WIFI, TRANSPORT_WIFI); + } + + @Test + public void testNetworkCapabilitiesForTypeWifiP2p() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_WIFI_P2P, + TRANSPORT_WIFI, + NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED, NET_CAPABILITY_WIFI_P2P); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeBluetooth() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH); + } + + @Test + public void testNetworkCapabilitiesForTypeEthernet() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET); + } + + @Test + public void testCallbackRelease() throws Exception { + ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + NetworkRequest request = makeRequest(1); + NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class); + Handler handler = new Handler(Looper.getMainLooper()); + ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); + + // register callback + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request); + manager.requestNetwork(request, callback, handler); + + // callback triggers + captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE)); + verify(callback, timeout(500).times(1)).onAvailable(any(Network.class), + any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean()); + + // unregister callback + manager.unregisterNetworkCallback(callback); + verify(mService, times(1)).releaseNetworkRequest(request); + + // callback does not trigger anymore. + captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING)); + verify(callback, timeout(500).times(0)).onLosing(any(), anyInt()); + } + + @Test + public void testCallbackRecycling() throws Exception { + ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + NetworkRequest req1 = makeRequest(1); + NetworkRequest req2 = makeRequest(2); + NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class); + Handler handler = new Handler(Looper.getMainLooper()); + ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); + + // register callback + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1); + manager.requestNetwork(req1, callback, handler); + + // callback triggers + captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE)); + verify(callback, timeout(100).times(1)).onAvailable(any(Network.class), + any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean()); + + // unregister callback + manager.unregisterNetworkCallback(callback); + verify(mService, times(1)).releaseNetworkRequest(req1); + + // callback does not trigger anymore. + captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING)); + verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); + + // callback can be registered again + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2); + manager.requestNetwork(req2, callback, handler); + + // callback triggers + captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST)); + verify(callback, timeout(100).times(1)).onLost(any()); + + // unregister callback + manager.unregisterNetworkCallback(callback); + verify(mService, times(1)).releaseNetworkRequest(req2); + } + + // TODO: turn on this test when request callback 1:1 mapping is enforced + //@Test + private void noDoubleCallbackRegistration() throws Exception { + ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + NetworkRequest request = makeRequest(1); + NetworkCallback callback = new ConnectivityManager.NetworkCallback(); + ApplicationInfo info = new ApplicationInfo(); + // TODO: update version when starting to enforce 1:1 mapping + info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; + + when(mCtx.getApplicationInfo()).thenReturn(info); + when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), + anyInt(), any(), nullable(String.class))).thenReturn(request); + + Handler handler = new Handler(Looper.getMainLooper()); + manager.requestNetwork(request, callback, handler); + + // callback is already registered, reregistration should fail. + Class wantException = IllegalArgumentException.class; + expectThrowable(() -> manager.requestNetwork(request, callback), wantException); + + manager.unregisterNetworkCallback(callback); + verify(mService, times(1)).releaseNetworkRequest(request); + + // unregistering the callback should make it registrable again. + manager.requestNetwork(request, callback); + } + + @Test + public void testArgumentValidation() throws Exception { + ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + + NetworkRequest request = mock(NetworkRequest.class); + NetworkCallback callback = mock(NetworkCallback.class); + Handler handler = mock(Handler.class); + NetworkCallback nullCallback = null; + PendingIntent nullIntent = null; + + mustFail(() -> { manager.requestNetwork(null, callback); }); + mustFail(() -> { manager.requestNetwork(request, nullCallback); }); + mustFail(() -> { manager.requestNetwork(request, callback, null); }); + mustFail(() -> { manager.requestNetwork(request, callback, -1); }); + mustFail(() -> { manager.requestNetwork(request, nullIntent); }); + + mustFail(() -> { manager.registerNetworkCallback(null, callback, handler); }); + mustFail(() -> { manager.registerNetworkCallback(request, null, handler); }); + mustFail(() -> { manager.registerNetworkCallback(request, callback, null); }); + mustFail(() -> { manager.registerNetworkCallback(request, nullIntent); }); + + mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); }); + mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); }); + + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); }); + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); }); + + mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); }); + mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); }); + mustFail(() -> { manager.releaseNetworkRequest(nullIntent); }); + } + + static void mustFail(Runnable fn) { + try { + fn.run(); + fail(); + } catch (Exception expected) { + } + } + + @Test + public void testRequestType() throws Exception { + final String testPkgName = "MyPackage"; + final String testAttributionTag = "MyTag"; + final ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + when(mCtx.getOpPackageName()).thenReturn(testPkgName); + when(mCtx.getAttributionTag()).thenReturn(testAttributionTag); + final NetworkRequest request = makeRequest(1); + final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); + + manager.requestNetwork(request, callback); + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), + eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + + // Verify that register network callback does not calls requestNetwork at all. + manager.registerNetworkCallback(request, callback); + verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), + anyInt(), anyInt(), any(), any()); + verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + + Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + + manager.registerDefaultNetworkCallback(callback); + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + + manager.registerDefaultNetworkCallbackForUid(42, callback, handler); + verify(mService).requestNetwork(eq(42), eq(null), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + + manager.requestBackgroundNetwork(request, callback, handler); + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), + eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + + manager.registerSystemDefaultNetworkCallback(callback, handler); + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), + eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + } + + static Message makeMessage(NetworkRequest req, int messageType) { + Bundle bundle = new Bundle(); + bundle.putParcelable(NetworkRequest.class.getSimpleName(), req); + // Pass default objects as we don't care which get passed here + bundle.putParcelable(Network.class.getSimpleName(), new Network(1)); + bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities()); + bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties()); + Message msg = Message.obtain(); + msg.what = messageType; + msg.setData(bundle); + return msg; + } + + static NetworkRequest makeRequest(int requestId) { + NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + return new NetworkRequest(request.networkCapabilities, ConnectivityManager.TYPE_NONE, + requestId, NetworkRequest.Type.NONE); + } + + static void expectThrowable(Runnable block, Class throwableType) { + try { + block.run(); + } catch (Throwable t) { + if (t.getClass().equals(throwableType)) { + return; + } + fail("expected exception of type " + throwableType + ", but was " + t.getClass()); + } + fail("expected exception of type " + throwableType); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java new file mode 100644 index 000000000000..1abd39a32bdf --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2019 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; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.net.VpnProfile; +import com.android.net.module.util.ProxyUtils; +import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.security.auth.x500.X500Principal; + +/** Unit tests for {@link Ikev2VpnProfile.Builder}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class Ikev2VpnProfileTest { + private static final String SERVER_ADDR_STRING = "1.2.3.4"; + private static final String IDENTITY_STRING = "Identity"; + private static final String USERNAME_STRING = "username"; + private static final String PASSWORD_STRING = "pa55w0rd"; + private static final String EXCL_LIST = "exclList"; + private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); + private static final int TEST_MTU = 1300; + + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + private final ProxyInfo mProxy = ProxyInfo.buildDirectProxy( + SERVER_ADDR_STRING, -1, ProxyUtils.exclusionStringAsList(EXCL_LIST)); + + private X509Certificate mUserCert; + private X509Certificate mServerRootCa; + private PrivateKey mPrivateKey; + + @Before + public void setUp() throws Exception { + mServerRootCa = generateRandomCertAndKeyPair().cert; + + final CertificateAndKey userCertKey = generateRandomCertAndKeyPair(); + mUserCert = userCertKey.cert; + mPrivateKey = userCertKey.key; + } + + private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() { + final Ikev2VpnProfile.Builder builder = + new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING); + + builder.setBypassable(true); + builder.setProxy(mProxy); + builder.setMaxMtu(TEST_MTU); + builder.setMetered(true); + + return builder; + } + + @Test + public void testBuildValidProfileWithOptions() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + // Check non-auth parameters correctly stored + assertEquals(SERVER_ADDR_STRING, profile.getServerAddr()); + assertEquals(IDENTITY_STRING, profile.getUserIdentity()); + assertEquals(mProxy, profile.getProxyInfo()); + assertTrue(profile.isBypassable()); + assertTrue(profile.isMetered()); + assertEquals(TEST_MTU, profile.getMaxMtu()); + assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms()); + } + + @Test + public void testBuildUsernamePasswordProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(USERNAME_STRING, profile.getUsername()); + assertEquals(PASSWORD_STRING, profile.getPassword()); + assertEquals(mServerRootCa, profile.getServerRootCaCert()); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildDigitalSignatureProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(profile.getUserCert(), mUserCert); + assertEquals(mPrivateKey, profile.getRsaPrivateKey()); + assertEquals(profile.getServerRootCaCert(), mServerRootCa); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + } + + @Test + public void testBuildPresharedKeyProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertArrayEquals(PSK_BYTES, profile.getPresharedKey()); + + assertNull(profile.getServerRootCaCert()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildWithAllowedAlgorithmsAead() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + builder.setAuthPsk(PSK_BYTES); + + List allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); + builder.setAllowedAlgorithms(allowedAlgorithms); + + final Ikev2VpnProfile profile = builder.build(); + assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); + } + + @Test + public void testBuildWithAllowedAlgorithmsNormal() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + builder.setAuthPsk(PSK_BYTES); + + List allowedAlgorithms = + Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC); + builder.setAllowedAlgorithms(allowedAlgorithms); + + final Ikev2VpnProfile profile = builder.build(); + assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); + } + + @Test + public void testSetAllowedAlgorithmsEmptyList() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.setAllowedAlgorithms(new ArrayList<>()); + fail("Expected exception due to no valid algorithm set"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetAllowedAlgorithmsInvalidList() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + List allowedAlgorithms = new ArrayList<>(); + + try { + builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256)); + fail("Expected exception due to missing encryption"); + } catch (IllegalArgumentException expected) { + } + + try { + builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC)); + fail("Expected exception due to missing authentication"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + List allowedAlgorithms = new ArrayList<>(); + + try { + builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5)); + fail("Expected exception due to insecure algorithm"); + } catch (IllegalArgumentException expected) { + } + + try { + builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1)); + fail("Expected exception due to insecure algorithm"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testBuildNoAuthMethodSet() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.build(); + fail("Expected exception due to lack of auth method"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testBuildInvalidMtu() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.setMaxMtu(500); + fail("Expected exception due to too-small MTU"); + } catch (IllegalArgumentException expected) { + } + } + + private void verifyVpnProfileCommon(VpnProfile profile) { + assertEquals(SERVER_ADDR_STRING, profile.server); + assertEquals(IDENTITY_STRING, profile.ipsecIdentifier); + assertEquals(mProxy, profile.proxy); + assertTrue(profile.isBypassable); + assertTrue(profile.isMetered); + assertEquals(TEST_MTU, profile.maxMtu); + } + + @Test + public void testPskConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecCaCert); + } + + @Test + public void testUsernamePasswordConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(USERNAME_STRING, profile.username); + assertEquals(PASSWORD_STRING, profile.password); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecSecret); + } + + @Test + public void testRsaConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE + + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()); + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert); + assertEquals( + expectedSecret, + profile.ipsecSecret); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + } + + @Test + public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + assertNull(result.getServerRootCaCert()); + } + + @Test + public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.ipsecSecret = new String(PSK_BYTES); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getPresharedKey()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + } + + @Test + public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getPresharedKey()); + } + + @Test + public void testPskConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testUsernamePasswordConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testRsaConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + private static class CertificateAndKey { + public final X509Certificate cert; + public final PrivateKey key; + + CertificateAndKey(X509Certificate cert, PrivateKey key) { + this.cert = cert; + this.key = key; + } + } + + private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception { + final Date validityBeginDate = + new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L)); + final Date validityEndDate = + new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L)); + + // Generate a keypair + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(512); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + final X500Principal dnName = new X500Principal("CN=test.android.com"); + final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); + certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); + certGen.setSubjectDN(dnName); + certGen.setIssuerDN(dnName); + certGen.setNotBefore(validityBeginDate); + certGen.setNotAfter(validityEndDate); + certGen.setPublicKey(keyPair.getPublic()); + certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL"); + return new CertificateAndKey(cert, keyPair.getPrivate()); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java new file mode 100644 index 000000000000..0b13800bc5c9 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2018 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.ipmemorystore.Blob; +import android.net.ipmemorystore.IOnStatusListener; +import android.net.ipmemorystore.NetworkAttributes; +import android.net.ipmemorystore.NetworkAttributesParcelable; +import android.net.ipmemorystore.Status; +import android.net.networkstack.ModuleNetworkStackClient; +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.net.UnknownHostException; +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpMemoryStoreTest { + private static final String TAG = IpMemoryStoreTest.class.getSimpleName(); + private static final String TEST_CLIENT_ID = "testClientId"; + private static final String TEST_DATA_NAME = "testData"; + private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other"; + private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12, + -128, 0, 89, 112, 91, -34 }; + private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes( + "hint", 219); + + @Mock + Context mMockContext; + @Mock + ModuleNetworkStackClient mModuleNetworkStackClient; + @Mock + IIpMemoryStore mMockService; + @Mock + IOnStatusListener mIOnStatusListener; + IpMemoryStore mStore; + + @Captor + ArgumentCaptor mCbCaptor; + @Captor + ArgumentCaptor mNapCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + private void startIpMemoryStore(boolean supplyService) { + if (supplyService) { + doAnswer(invocation -> { + ((IIpMemoryStoreCallbacks) invocation.getArgument(0)) + .onIpMemoryStoreFetched(mMockService); + return null; + }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any()); + } else { + doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture()); + } + mStore = new IpMemoryStore(mMockContext) { + @Override + protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) { + return mModuleNetworkStackClient; + } + }; + } + + private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) { + return new NetworkAttributes.Builder() + .setCluster(hint) + .setMtu(mtu) + .build(); + } + + @Test + public void testNetworkAttributes() throws Exception { + startIpMemoryStore(true /* supplyService */); + final String l2Key = "fakeKey"; + + mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key), + mNapCaptor.capture(), any()); + assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); + + mStore.retrieveNetworkAttributes(l2Key, + (status, key, attr) -> { + assertTrue("Retrieve network attributes not successful : " + + status.resultCode, status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(TEST_NETWORK_ATTRIBUTES, attr); + }); + + verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any()); + } + + @Test + public void testPrivateData() throws RemoteException { + startIpMemoryStore(true /* supplyService */); + final Blob b = new Blob(); + b.data = TEST_BLOB_DATA; + final String l2Key = "fakeKey"; + + mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, + status -> { + assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); + }); + verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), + eq(b), any()); + + mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, + (status, key, name, data) -> { + assertTrue("Retrieve blob status not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(name, TEST_DATA_NAME); + assertTrue(Arrays.equals(b.data, data.data)); + }); + verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), + eq(TEST_OTHER_DATA_NAME), any()); + } + + @Test + public void testFindL2Key() + throws UnknownHostException, RemoteException, Exception { + startIpMemoryStore(true /* supplyService */); + final String l2Key = "fakeKey"; + + mStore.findL2Key(TEST_NETWORK_ATTRIBUTES, + (status, key) -> { + assertTrue("Retrieve network sameness not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + }); + verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any()); + assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); + } + + @Test + public void testIsSameNetwork() throws UnknownHostException, RemoteException { + startIpMemoryStore(true /* supplyService */); + final String l2Key1 = "fakeKey1"; + final String l2Key2 = "fakeKey2"; + + mStore.isSameNetwork(l2Key1, l2Key2, + (status, answer) -> { + assertFalse("Retrieve network sameness suspiciously successful : " + + status.resultCode, status.isSuccess()); + assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); + assertNull(answer); + }); + verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any()); + } + + @Test + public void testEnqueuedIpMsRequests() throws Exception { + startIpMemoryStore(false /* supplyService */); + + final Blob b = new Blob(); + b.data = TEST_BLOB_DATA; + final String l2Key = "fakeKey"; + + // enqueue multiple ipms requests + mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + mStore.retrieveNetworkAttributes(l2Key, + (status, key, attr) -> { + assertTrue("Retrieve network attributes not successful : " + + status.resultCode, status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(TEST_NETWORK_ATTRIBUTES, attr); + }); + mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, + (status, key, name, data) -> { + assertTrue("Retrieve blob status not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(name, TEST_DATA_NAME); + assertTrue(Arrays.equals(b.data, data.data)); + }); + + // get ipms service ready + mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService); + + InOrder inOrder = inOrder(mMockService); + + inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); + inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); + inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), + eq(b), any()); + inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), + eq(TEST_OTHER_DATA_NAME), any()); + assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); + } + + @Test + public void testEnqueuedIpMsRequestsWithException() throws Exception { + startIpMemoryStore(true /* supplyService */); + doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any()); + + final Blob b = new Blob(); + b.data = TEST_BLOB_DATA; + final String l2Key = "fakeKey"; + + // enqueue multiple ipms requests + mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + mStore.retrieveNetworkAttributes(l2Key, + (status, key, attr) -> { + assertTrue("Retrieve network attributes not successful : " + + status.resultCode, status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(TEST_NETWORK_ATTRIBUTES, attr); + }); + mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, + (status, key, name, data) -> { + assertTrue("Retrieve blob status not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(name, TEST_DATA_NAME); + assertTrue(Arrays.equals(b.data, data.data)); + }); + + // verify the rest of the queue is still processed in order even if the remote exception + // occurs when calling one or more requests + InOrder inOrder = inOrder(mMockService); + + inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); + inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), + eq(b), any()); + inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), + eq(TEST_OTHER_DATA_NAME), any()); + assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); + } + + @Test + public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception { + startIpMemoryStore(true /* supplyService */); + + final Blob b = new Blob(); + b.data = TEST_BLOB_DATA; + final String l2Key = "fakeKey"; + + // enqueue multiple ipms requests + mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, + status -> assertTrue("Store not successful : " + status.resultCode, + status.isSuccess())); + mStore.retrieveNetworkAttributes(l2Key, + (status, key, attr) -> { + throw new RuntimeException("retrieveNetworkAttributes test"); + }); + mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, + status -> { + throw new RuntimeException("storeBlob test"); + }); + mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, + (status, key, name, data) -> { + assertTrue("Retrieve blob status not successful : " + status.resultCode, + status.isSuccess()); + assertEquals(l2Key, key); + assertEquals(name, TEST_DATA_NAME); + assertTrue(Arrays.equals(b.data, data.data)); + }); + + // verify the rest of the queue is still processed in order even if when one or more + // callback throw the remote exception + InOrder inOrder = inOrder(mMockService); + + inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), + any()); + inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); + inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), + eq(b), any()); + inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), + eq(TEST_OTHER_DATA_NAME), any()); + assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); + } + + @Test + public void testFactoryReset() throws RemoteException { + startIpMemoryStore(true /* supplyService */); + mStore.factoryReset(); + verify(mMockService, times(1)).factoryReset(); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java new file mode 100644 index 000000000000..5bd221477412 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2017 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; + +import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.content.res.Resources; +import android.os.Build; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.CollectionUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; + +/** Unit tests for {@link IpSecAlgorithm}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class IpSecAlgorithmTest { + private static final byte[] KEY_MATERIAL; + + private final Resources mMockResources = mock(Resources.class); + + static { + KEY_MATERIAL = new byte[128]; + new Random().nextBytes(KEY_MATERIAL); + }; + + private static byte[] generateKey(int keyLenInBits) { + return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8); + } + + @Test + public void testNoTruncLen() throws Exception { + Entry[] authAndAeadList = + new Entry[] { + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384), + new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512), + new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224), + }; + + // Expect auth and aead algorithms to throw errors if trunclen is omitted. + for (Entry algData : authAndAeadList) { + try { + new IpSecAlgorithm( + algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8)); + fail("Expected exception on unprovided auth trunclen"); + } catch (IllegalArgumentException expected) { + } + } + + // Ensure crypt works with no truncation length supplied. + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); + } + + private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen) + throws Exception { + new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen); + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen)); + fail("Expected exception on unprovided auth trunclen"); + } catch (IllegalArgumentException pass) { + } + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1); + fail("Invalid truncation length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception { + new IpSecAlgorithm(algoName, generateKey(keyLen)); + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen + 8)); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testValidationForAlgosAddedInS() throws Exception { + if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) { + return; + } + + for (int len : new int[] {160, 224, 288}) { + checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len); + } + checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96); + checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_CMAC, 128, 96); + checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128); + } + + @Test + public void testTruncLenValidation() throws Exception { + for (int truncLen : new int[] {256, 512}) { + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, + Arrays.copyOf(KEY_MATERIAL, 512 / 8), + truncLen); + } + + for (int truncLen : new int[] {255, 513}) { + try { + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, + Arrays.copyOf(KEY_MATERIAL, 512 / 8), + truncLen); + fail("Invalid truncation length not validated"); + } catch (IllegalArgumentException pass) { + } + } + } + + @Test + public void testLenValidation() throws Exception { + for (int len : new int[] {128, 192, 256}) { + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, len / 8)); + } + try { + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 384 / 8)); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testAlgoNameValidation() throws Exception { + try { + new IpSecAlgorithm("rot13", Arrays.copyOf(KEY_MATERIAL, 128 / 8)); + fail("Invalid algorithm name not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testParcelUnparcel() throws Exception { + IpSecAlgorithm init = + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, Arrays.copyOf(KEY_MATERIAL, 512 / 8), 256); + + Parcel p = Parcel.obtain(); + p.setDataPosition(0); + init.writeToParcel(p, 0); + + p.setDataPosition(0); + IpSecAlgorithm fin = IpSecAlgorithm.CREATOR.createFromParcel(p); + assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin)); + p.recycle(); + } + + private static Set getMandatoryAlgos() { + return CollectionUtils.filter( + ALGO_TO_REQUIRED_FIRST_SDK.keySet(), + i -> Build.VERSION.DEVICE_INITIAL_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i)); + } + + private static Set getOptionalAlgos() { + return CollectionUtils.filter( + ALGO_TO_REQUIRED_FIRST_SDK.keySet(), + i -> Build.VERSION.DEVICE_INITIAL_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i)); + } + + @Test + public void testGetSupportedAlgorithms() throws Exception { + assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos())); + assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll( + IpSecAlgorithm.getSupportedAlgorithms())); + } + + @Test + public void testLoadAlgos() throws Exception { + final Set optionalAlgoSet = getOptionalAlgos(); + final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]); + + doReturn(optionalAlgos).when(mMockResources) + .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms); + + final Set enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources)); + final Set expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet(); + + assertEquals(expectedAlgos, enabledAlgos); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java new file mode 100644 index 000000000000..25e225ef303a --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 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; + +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecConfig}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecConfigTest { + + @Test + public void testDefaults() throws Exception { + IpSecConfig c = new IpSecConfig(); + assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode()); + assertEquals("", c.getSourceAddress()); + assertEquals("", c.getDestinationAddress()); + assertNull(c.getNetwork()); + assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType()); + assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId()); + assertEquals(0, c.getEncapRemotePort()); + assertEquals(0, c.getNattKeepaliveInterval()); + assertNull(c.getEncryption()); + assertNull(c.getAuthentication()); + assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId()); + assertEquals(0, c.getXfrmInterfaceId()); + } + + private IpSecConfig getSampleConfig() { + IpSecConfig c = new IpSecConfig(); + c.setMode(IpSecTransform.MODE_TUNNEL); + c.setSourceAddress("0.0.0.0"); + c.setDestinationAddress("1.2.3.4"); + c.setSpiResourceId(1984); + c.setEncryption( + new IpSecAlgorithm( + IpSecAlgorithm.CRYPT_AES_CBC, + new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF})); + c.setAuthentication( + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_MD5, + new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}, + 128)); + c.setAuthenticatedEncryption( + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_CRYPT_AES_GCM, + new byte[] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4 + }, + 128)); + c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP); + c.setEncapSocketResourceId(7); + c.setEncapRemotePort(22); + c.setNattKeepaliveInterval(42); + c.setMarkValue(12); + c.setMarkMask(23); + c.setXfrmInterfaceId(34); + + return c; + } + + @Test + public void testCopyConstructor() { + IpSecConfig original = getSampleConfig(); + IpSecConfig copy = new IpSecConfig(original); + + assertEquals(original, copy); + assertNotSame(original, copy); + } + + @Test + public void testParcelUnparcel() { + assertParcelingIsLossless(new IpSecConfig()); + + IpSecConfig c = getSampleConfig(); + assertParcelSane(c, 15); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java new file mode 100644 index 000000000000..730e2d56bd78 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2017 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; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.system.Os; +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.IpSecService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +/** Unit tests for {@link IpSecManager}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class IpSecManagerTest { + + private static final int TEST_UDP_ENCAP_PORT = 34567; + private static final int DROID_SPI = 0xD1201D; + private static final int DUMMY_RESOURCE_ID = 0x1234; + + private static final InetAddress GOOGLE_DNS_4; + private static final String VTI_INTF_NAME = "ipsec_test"; + private static final InetAddress VTI_LOCAL_ADDRESS; + private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24"); + + static { + try { + // Google Public DNS Addresses; + GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8"); + VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4"); + } catch (UnknownHostException e) { + throw new RuntimeException("Could not resolve DNS Addresses", e); + } + } + + private IpSecService mMockIpSecService; + private IpSecManager mIpSecManager; + private MockContext mMockContext = new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + + @Before + public void setUp() throws Exception { + mMockIpSecService = mock(IpSecService.class); + mIpSecManager = new IpSecManager(mMockContext, mMockIpSecService); + } + + /* + * Allocate a specific SPI + * Close SPIs + */ + @Test + public void testAllocSpi() throws Exception { + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); + when(mMockIpSecService.allocateSecurityParameterIndex( + eq(GOOGLE_DNS_4.getHostAddress()), + eq(DROID_SPI), + anyObject())) + .thenReturn(spiResp); + + IpSecManager.SecurityParameterIndex droidSpi = + mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI); + assertEquals(DROID_SPI, droidSpi.getSpi()); + + droidSpi.close(); + + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); + } + + @Test + public void testAllocRandomSpi() throws Exception { + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); + when(mMockIpSecService.allocateSecurityParameterIndex( + eq(GOOGLE_DNS_4.getHostAddress()), + eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), + anyObject())) + .thenReturn(spiResp); + + IpSecManager.SecurityParameterIndex randomSpi = + mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); + + assertEquals(DROID_SPI, randomSpi.getSpi()); + + randomSpi.close(); + + verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); + } + + /* + * Throws resource unavailable exception + */ + @Test + public void testAllocSpiResUnavailableException() throws Exception { + IpSecSpiResponse spiResp = + new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0); + when(mMockIpSecService.allocateSecurityParameterIndex( + anyString(), anyInt(), anyObject())) + .thenReturn(spiResp); + + try { + mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); + fail("ResourceUnavailableException was not thrown"); + } catch (IpSecManager.ResourceUnavailableException e) { + } + } + + /* + * Throws spi unavailable exception + */ + @Test + public void testAllocSpiSpiUnavailableException() throws Exception { + IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0); + when(mMockIpSecService.allocateSecurityParameterIndex( + anyString(), anyInt(), anyObject())) + .thenReturn(spiResp); + + try { + mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); + fail("ResourceUnavailableException was not thrown"); + } catch (IpSecManager.ResourceUnavailableException e) { + } + } + + /* + * Should throw exception when request spi 0 in IpSecManager + */ + @Test + public void testRequestAllocInvalidSpi() throws Exception { + try { + mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0); + fail("Able to allocate invalid spi"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testOpenEncapsulationSocket() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + new IpSecUdpEncapResponse( + IpSecManager.Status.OK, + DUMMY_RESOURCE_ID, + TEST_UDP_ENCAP_PORT, + Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); + when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject())) + .thenReturn(udpEncapResp); + + IpSecManager.UdpEncapsulationSocket encapSocket = + mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT); + assertNotNull(encapSocket.getFileDescriptor()); + assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); + + encapSocket.close(); + + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); + } + + @Test + public void testApplyTransportModeTransformEnsuresSocketCreation() throws Exception { + Socket socket = new Socket(); + IpSecConfig dummyConfig = new IpSecConfig(); + IpSecTransform dummyTransform = new IpSecTransform(null, dummyConfig); + + // Even if underlying SocketImpl is not initalized, this should force the init, and + // thereby succeed. + mIpSecManager.applyTransportModeTransform( + socket, IpSecManager.DIRECTION_IN, dummyTransform); + + // Check to make sure the FileDescriptor is non-null + assertNotNull(socket.getFileDescriptor$()); + } + + @Test + public void testRemoveTransportModeTransformsForcesSocketCreation() throws Exception { + Socket socket = new Socket(); + + // Even if underlying SocketImpl is not initalized, this should force the init, and + // thereby succeed. + mIpSecManager.removeTransportModeTransforms(socket); + + // Check to make sure the FileDescriptor is non-null + assertNotNull(socket.getFileDescriptor$()); + } + + @Test + public void testOpenEncapsulationSocketOnRandomPort() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + new IpSecUdpEncapResponse( + IpSecManager.Status.OK, + DUMMY_RESOURCE_ID, + TEST_UDP_ENCAP_PORT, + Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); + + when(mMockIpSecService.openUdpEncapsulationSocket(eq(0), anyObject())) + .thenReturn(udpEncapResp); + + IpSecManager.UdpEncapsulationSocket encapSocket = + mIpSecManager.openUdpEncapsulationSocket(); + + assertNotNull(encapSocket.getFileDescriptor()); + assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); + + encapSocket.close(); + + verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); + } + + @Test + public void testOpenEncapsulationSocketWithInvalidPort() throws Exception { + try { + mIpSecManager.openUdpEncapsulationSocket(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); + fail("IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + } + } + + // TODO: add test when applicable transform builder interface is available + + private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName) + throws Exception { + IpSecTunnelInterfaceResponse dummyResponse = + new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); + when(mMockIpSecService.createTunnelInterface( + eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()), + anyObject(), anyObject(), anyString())) + .thenReturn(dummyResponse); + + IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface( + VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class)); + + assertNotNull(tunnelIntf); + return tunnelIntf; + } + + @Test + public void testCreateVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName()); + + tunnelIntf.close(); + verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID), anyString()); + } + + @Test + public void testAddRemoveAddressesFromVti() throws Exception { + IpSecManager.IpSecTunnelInterface tunnelIntf = + createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); + + tunnelIntf.addAddress(VTI_INNER_ADDRESS.getAddress(), + VTI_INNER_ADDRESS.getPrefixLength()); + verify(mMockIpSecService) + .addAddressToTunnelInterface( + eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString()); + + tunnelIntf.removeAddress(VTI_INNER_ADDRESS.getAddress(), + VTI_INNER_ADDRESS.getPrefixLength()); + verify(mMockIpSecService) + .addAddressToTunnelInterface( + eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString()); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java new file mode 100644 index 000000000000..424f23dbbaf6 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecTransform}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecTransformTest { + + @Test + public void testCreateTransformCopiesConfig() { + // Create a config with a few parameters to make sure it's not empty + IpSecConfig config = new IpSecConfig(); + config.setSourceAddress("0.0.0.0"); + config.setDestinationAddress("1.2.3.4"); + config.setSpiResourceId(1984); + + IpSecTransform preModification = new IpSecTransform(null, config); + + config.setSpiResourceId(1985); + IpSecTransform postModification = new IpSecTransform(null, config); + + assertNotEquals(preModification, postModification); + } + + @Test + public void testCreateTransformsWithSameConfigEqual() { + // Create a config with a few parameters to make sure it's not empty + IpSecConfig config = new IpSecConfig(); + config.setSourceAddress("0.0.0.0"); + config.setDestinationAddress("1.2.3.4"); + config.setSpiResourceId(1984); + + IpSecTransform config1 = new IpSecTransform(null, config); + IpSecTransform config2 = new IpSecTransform(null, config); + + assertEquals(config1, config2); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java new file mode 100644 index 000000000000..fc739fbfac61 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java @@ -0,0 +1,205 @@ +/* + * 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; + +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.util.KeepalivePacketDataUtil; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.InetAddress; +import java.nio.ByteBuffer; + +@RunWith(JUnit4.class) +public final class KeepalivePacketDataUtilTest { + private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1}; + private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5}; + + @Before + public void setUp() {} + + @Test + public void testFromTcpKeepaliveStableParcelable() throws Exception { + final int srcPort = 1234; + final int dstPort = 4321; + final int seq = 0x11111111; + final int ack = 0x22222222; + final int wnd = 8000; + final int wndScale = 2; + final int tos = 4; + final int ttl = 64; + TcpKeepalivePacketData resultData = null; + final TcpKeepalivePacketDataParcelable testInfo = new TcpKeepalivePacketDataParcelable(); + testInfo.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; + testInfo.srcPort = srcPort; + testInfo.dstAddress = IPV4_KEEPALIVE_DST_ADDR; + testInfo.dstPort = dstPort; + testInfo.seq = seq; + testInfo.ack = ack; + testInfo.rcvWnd = wnd; + testInfo.rcvWndScale = wndScale; + testInfo.tos = tos; + testInfo.ttl = ttl; + try { + resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); + } catch (InvalidPacketException e) { + fail("InvalidPacketException: " + e); + } + + assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.getSrcAddress()); + assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.getDstAddress()); + assertEquals(testInfo.srcPort, resultData.getSrcPort()); + assertEquals(testInfo.dstPort, resultData.getDstPort()); + assertEquals(testInfo.seq, resultData.tcpSeq); + assertEquals(testInfo.ack, resultData.tcpAck); + assertEquals(testInfo.rcvWnd, resultData.tcpWindow); + assertEquals(testInfo.rcvWndScale, resultData.tcpWindowScale); + assertEquals(testInfo.tos, resultData.ipTos); + assertEquals(testInfo.ttl, resultData.ipTtl); + + assertParcelingIsLossless(resultData); + + final byte[] packet = resultData.getPacket(); + // IP version and IHL + assertEquals(packet[0], 0x45); + // TOS + assertEquals(packet[1], tos); + // TTL + assertEquals(packet[8], ttl); + // Source IP address. + byte[] ip = new byte[4]; + ByteBuffer buf = ByteBuffer.wrap(packet, 12, 4); + buf.get(ip); + assertArrayEquals(ip, IPV4_KEEPALIVE_SRC_ADDR); + // Destination IP address. + buf = ByteBuffer.wrap(packet, 16, 4); + buf.get(ip); + assertArrayEquals(ip, IPV4_KEEPALIVE_DST_ADDR); + + buf = ByteBuffer.wrap(packet, 20, 12); + // Source port. + assertEquals(buf.getShort(), srcPort); + // Destination port. + assertEquals(buf.getShort(), dstPort); + // Sequence number. + assertEquals(buf.getInt(), seq); + // Ack. + assertEquals(buf.getInt(), ack); + // Window size. + buf = ByteBuffer.wrap(packet, 34, 2); + assertEquals(buf.getShort(), wnd >> wndScale); + } + + //TODO: add ipv6 test when ipv6 supported + + @Test + public void testToTcpKeepaliveStableParcelable() throws Exception { + final int srcPort = 1234; + final int dstPort = 4321; + final int sequence = 0x11111111; + final int ack = 0x22222222; + final int wnd = 48_000; + final int wndScale = 2; + final int tos = 4; + final int ttl = 64; + final TcpKeepalivePacketDataParcelable testInfo = new TcpKeepalivePacketDataParcelable(); + testInfo.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; + testInfo.srcPort = srcPort; + testInfo.dstAddress = IPV4_KEEPALIVE_DST_ADDR; + testInfo.dstPort = dstPort; + testInfo.seq = sequence; + testInfo.ack = ack; + testInfo.rcvWnd = wnd; + testInfo.rcvWndScale = wndScale; + testInfo.tos = tos; + testInfo.ttl = ttl; + TcpKeepalivePacketData testData = null; + TcpKeepalivePacketDataParcelable resultData = null; + testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); + resultData = KeepalivePacketDataUtil.toStableParcelable(testData); + assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR); + assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR); + assertEquals(resultData.srcPort, srcPort); + assertEquals(resultData.dstPort, dstPort); + assertEquals(resultData.seq, sequence); + assertEquals(resultData.ack, ack); + assertEquals(resultData.rcvWnd, wnd); + assertEquals(resultData.rcvWndScale, wndScale); + assertEquals(resultData.tos, tos); + assertEquals(resultData.ttl, ttl); + + final String expected = "" + + "android.net.TcpKeepalivePacketDataParcelable{srcAddress: [10, 0, 0, 1]," + + " srcPort: 1234, dstAddress: [10, 0, 0, 5], dstPort: 4321, seq: 286331153," + + " ack: 572662306, rcvWnd: 48000, rcvWndScale: 2, tos: 4, ttl: 64}"; + assertEquals(expected, resultData.toString()); + } + + @Test + public void testParseTcpKeepalivePacketData() throws Exception { + final int srcPort = 1234; + final int dstPort = 4321; + final int sequence = 0x11111111; + final int ack = 0x22222222; + final int wnd = 4800; + final int wndScale = 2; + final int tos = 4; + final int ttl = 64; + final TcpKeepalivePacketDataParcelable testParcel = new TcpKeepalivePacketDataParcelable(); + testParcel.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; + testParcel.srcPort = srcPort; + testParcel.dstAddress = IPV4_KEEPALIVE_DST_ADDR; + testParcel.dstPort = dstPort; + testParcel.seq = sequence; + testParcel.ack = ack; + testParcel.rcvWnd = wnd; + testParcel.rcvWndScale = wndScale; + testParcel.tos = tos; + testParcel.ttl = ttl; + + final KeepalivePacketData testData = + KeepalivePacketDataUtil.fromStableParcelable(testParcel); + final TcpKeepalivePacketDataParcelable parsedParcelable = + KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData); + final TcpKeepalivePacketData roundTripData = + KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable); + + // Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero + assertTrue(testData.getPacket().length > 0); + assertArrayEquals(testData.getPacket(), roundTripData.getPacket()); + + testParcel.rcvWndScale = 0; + final KeepalivePacketData noScaleTestData = + KeepalivePacketDataUtil.fromStableParcelable(testParcel); + final TcpKeepalivePacketDataParcelable noScaleParsedParcelable = + KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData); + final TcpKeepalivePacketData noScaleRoundTripData = + KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable); + assertEquals(noScaleTestData, noScaleRoundTripData); + assertTrue(noScaleTestData.getPacket().length > 0); + assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket()); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java new file mode 100644 index 000000000000..6de31f6b4be1 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java @@ -0,0 +1,312 @@ +/* + * Copyright 2017 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; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.MacAddressUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet6Address; +import java.util.Arrays; +import java.util.Random; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class MacAddressTest { + + static class AddrTypeTestCase { + byte[] addr; + int expectedType; + + static AddrTypeTestCase of(int expectedType, int... addr) { + AddrTypeTestCase t = new AddrTypeTestCase(); + t.expectedType = expectedType; + t.addr = toByteArray(addr); + return t; + } + } + + @Test + public void testMacAddrTypes() { + AddrTypeTestCase[] testcases = { + AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN), + AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 0), + AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5), + AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5, 6, 7), + AddrTypeTestCase.of(MacAddress.TYPE_UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0), + AddrTypeTestCase.of(MacAddress.TYPE_BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), + AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 1, 2, 3, 4, 5, 6), + AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 11, 22, 33, 44, 55, 66), + AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd) + }; + + for (AddrTypeTestCase t : testcases) { + int got = MacAddress.macAddressType(t.addr); + String msg = String.format("expected type of %s to be %s, but got %s", + Arrays.toString(t.addr), t.expectedType, got); + assertEquals(msg, t.expectedType, got); + + if (got != MacAddress.TYPE_UNKNOWN) { + assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType()); + } + } + } + + @Test + public void testToOuiString() { + String[][] macs = { + {"07:00:d3:56:8a:c4", "07:00:d3"}, + {"33:33:aa:bb:cc:dd", "33:33:aa"}, + {"06:00:00:00:00:00", "06:00:00"}, + {"07:00:d3:56:8a:c4", "07:00:d3"} + }; + + for (String[] pair : macs) { + String mac = pair[0]; + String expected = pair[1]; + assertEquals(expected, MacAddress.fromString(mac).toOuiString()); + } + } + + @Test + public void testHexPaddingWhenPrinting() { + String[] macs = { + "07:00:d3:56:8a:c4", + "33:33:aa:bb:cc:dd", + "06:00:00:00:00:00", + "07:00:d3:56:8a:c4" + }; + + for (String mac : macs) { + assertEquals(mac, MacAddress.fromString(mac).toString()); + assertEquals(mac, + MacAddress.stringAddrFromByteAddr(MacAddress.byteAddrFromStringAddr(mac))); + } + } + + @Test + public void testIsMulticastAddress() { + MacAddress[] multicastAddresses = { + MacAddress.BROADCAST_ADDRESS, + MacAddress.fromString("07:00:d3:56:8a:c4"), + MacAddress.fromString("33:33:aa:bb:cc:dd"), + }; + MacAddress[] unicastAddresses = { + MacAddress.ALL_ZEROS_ADDRESS, + MacAddress.fromString("00:01:44:55:66:77"), + MacAddress.fromString("08:00:22:33:44:55"), + MacAddress.fromString("06:00:00:00:00:00"), + }; + + for (MacAddress mac : multicastAddresses) { + String msg = mac.toString() + " expected to be a multicast address"; + assertTrue(msg, MacAddressUtils.isMulticastAddress(mac)); + } + for (MacAddress mac : unicastAddresses) { + String msg = mac.toString() + " expected not to be a multicast address"; + assertFalse(msg, MacAddressUtils.isMulticastAddress(mac)); + } + } + + @Test + public void testIsLocallyAssignedAddress() { + MacAddress[] localAddresses = { + MacAddress.fromString("06:00:00:00:00:00"), + MacAddress.fromString("07:00:d3:56:8a:c4"), + MacAddress.fromString("33:33:aa:bb:cc:dd"), + }; + MacAddress[] universalAddresses = { + MacAddress.fromString("00:01:44:55:66:77"), + MacAddress.fromString("08:00:22:33:44:55"), + }; + + for (MacAddress mac : localAddresses) { + String msg = mac.toString() + " expected to be a locally assigned address"; + assertTrue(msg, mac.isLocallyAssigned()); + } + for (MacAddress mac : universalAddresses) { + String msg = mac.toString() + " expected not to be globally unique address"; + assertFalse(msg, mac.isLocallyAssigned()); + } + } + + @Test + public void testMacAddressConversions() { + final int iterations = 10000; + for (int i = 0; i < iterations; i++) { + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); + + String stringRepr = mac.toString(); + byte[] bytesRepr = mac.toByteArray(); + + assertEquals(mac, MacAddress.fromString(stringRepr)); + assertEquals(mac, MacAddress.fromBytes(bytesRepr)); + + assertEquals(mac, MacAddress.fromString(MacAddress.stringAddrFromByteAddr(bytesRepr))); + assertEquals(mac, MacAddress.fromBytes(MacAddress.byteAddrFromStringAddr(stringRepr))); + } + } + + @Test + public void testMacAddressRandomGeneration() { + final int iterations = 1000; + final String expectedAndroidOui = "da:a1:19"; + for (int i = 0; i < iterations; i++) { + MacAddress mac = MacAddress.createRandomUnicastAddressWithGoogleBase(); + String stringRepr = mac.toString(); + + assertTrue(stringRepr + " expected to be a locally assigned address", + mac.isLocallyAssigned()); + assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui, + stringRepr.startsWith(expectedAndroidOui)); + } + + final Random r = new Random(); + final String anotherOui = "24:5f:78"; + final String expectedLocalOui = "26:5f:78"; + final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0"); + for (int i = 0; i < iterations; i++) { + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r); + String stringRepr = mac.toString(); + + assertTrue(stringRepr + " expected to be a locally assigned address", + mac.isLocallyAssigned()); + assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); + assertTrue(stringRepr + " expected to begin with " + expectedLocalOui, + stringRepr.startsWith(expectedLocalOui)); + } + + for (int i = 0; i < iterations; i++) { + MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); + String stringRepr = mac.toString(); + + assertTrue(stringRepr + " expected to be a locally assigned address", + mac.isLocallyAssigned()); + assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); + } + } + + @Test + public void testConstructorInputValidation() { + String[] invalidStringAddresses = { + "", + "abcd", + "1:2:3:4:5", + "1:2:3:4:5:6:7", + "10000:2:3:4:5:6", + }; + + for (String s : invalidStringAddresses) { + try { + MacAddress mac = MacAddress.fromString(s); + fail("MacAddress.fromString(" + s + ") should have failed, but returned " + mac); + } catch (IllegalArgumentException excepted) { + } + } + + try { + MacAddress mac = MacAddress.fromString(null); + fail("MacAddress.fromString(null) should have failed, but returned " + mac); + } catch (NullPointerException excepted) { + } + + byte[][] invalidBytesAddresses = { + {}, + {1,2,3,4,5}, + {1,2,3,4,5,6,7}, + }; + + for (byte[] b : invalidBytesAddresses) { + try { + MacAddress mac = MacAddress.fromBytes(b); + fail("MacAddress.fromBytes(" + Arrays.toString(b) + + ") should have failed, but returned " + mac); + } catch (IllegalArgumentException excepted) { + } + } + + try { + MacAddress mac = MacAddress.fromBytes(null); + fail("MacAddress.fromBytes(null) should have failed, but returned " + mac); + } catch (NullPointerException excepted) { + } + } + + @Test + public void testMatches() { + // match 4 bytes prefix + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:00:00"), + MacAddress.fromString("ff:ff:ff:ff:00:00"))); + + // match bytes 0,1,2 and 5 + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:00:00:11"), + MacAddress.fromString("ff:ff:ff:00:00:ff"))); + + // match 34 bit prefix + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:c0:00"), + MacAddress.fromString("ff:ff:ff:ff:c0:00"))); + + // fail to match 36 bit prefix + assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:40:00"), + MacAddress.fromString("ff:ff:ff:ff:f0:00"))); + + // match all 6 bytes + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("aa:bb:cc:dd:ee:11"), + MacAddress.fromString("ff:ff:ff:ff:ff:ff"))); + + // match none of 6 bytes + assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( + MacAddress.fromString("00:00:00:00:00:00"), + MacAddress.fromString("00:00:00:00:00:00"))); + } + + /** + * Tests that link-local address generation from MAC is valid. + */ + @Test + public void testLinkLocalFromMacGeneration() { + MacAddress mac = MacAddress.fromString("52:74:f2:b1:a8:7f"); + byte[] inet6ll = {(byte) 0xfe, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x74, + (byte) 0xf2, (byte) 0xff, (byte) 0xfe, (byte) 0xb1, (byte) 0xa8, 0x7f}; + Inet6Address llv6 = mac.getLinkLocalIpv6FromEui48Mac(); + assertTrue(llv6.isLinkLocalAddress()); + assertArrayEquals(inet6ll, llv6.getAddress()); + } + + static byte[] toByteArray(int... in) { + byte[] out = new byte[in.length]; + for (int i = 0; i < in.length; i++) { + out[i] = (byte) in[i]; + } + return out; + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt new file mode 100644 index 000000000000..eb2b85c14578 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 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 + +import android.net.NetworkIdentity.OEM_NONE +import android.net.NetworkIdentity.OEM_PAID +import android.net.NetworkIdentity.OEM_PRIVATE +import android.net.NetworkIdentity.getOemBitfield +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals + +@RunWith(JUnit4::class) +class NetworkIdentityTest { + @Test + fun testGetOemBitfield() { + val oemNone = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) + } + val oemPaid = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) + } + val oemPrivate = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) + } + val oemAll = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) + } + + assertEquals(getOemBitfield(oemNone), OEM_NONE) + assertEquals(getOemBitfield(oemPaid), OEM_PAID) + assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE) + assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE) + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java new file mode 100644 index 000000000000..13558cd51c28 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2011 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; + +import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong; +import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong; +import static android.net.NetworkStatsHistory.Entry.UNKNOWN; +import static android.net.NetworkStatsHistory.FIELD_ALL; +import static android.net.NetworkStatsHistory.FIELD_OPERATIONS; +import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; +import static android.net.NetworkStatsHistory.FIELD_RX_PACKETS; +import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; +import static android.net.TrafficStats.GB_IN_BYTES; +import static android.net.TrafficStats.MB_IN_BYTES; +import static android.text.format.DateUtils.DAY_IN_MILLIS; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; +import static android.text.format.DateUtils.SECOND_IN_MILLIS; +import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static android.text.format.DateUtils.YEAR_IN_MILLIS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.tests.net.R; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.Random; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsHistoryTest { + private static final String TAG = "NetworkStatsHistoryTest"; + + private static final long TEST_START = 1194220800000L; + + private NetworkStatsHistory stats; + + @After + public void tearDown() throws Exception { + if (stats != null) { + assertConsistent(stats); + } + } + + @Test + public void testReadOriginalVersion() throws Exception { + final Context context = InstrumentationRegistry.getContext(); + final DataInputStream in = + new DataInputStream(context.getResources().openRawResource(R.raw.history_v1)); + + NetworkStatsHistory.Entry entry = null; + try { + final NetworkStatsHistory history = new NetworkStatsHistory(in); + assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration()); + + entry = history.getValues(0, entry); + assertEquals(29143L, entry.rxBytes); + assertEquals(6223L, entry.txBytes); + + entry = history.getValues(history.size() - 1, entry); + assertEquals(1476L, entry.rxBytes); + assertEquals(838L, entry.txBytes); + + entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry); + assertEquals(332401L, entry.rxBytes); + assertEquals(64314L, entry.txBytes); + + } finally { + in.close(); + } + } + + @Test + public void testRecordSingleBucket() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // record data into narrow window to get single bucket + stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + + assertEquals(1, stats.size()); + assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L); + } + + @Test + public void testRecordEqualBuckets() throws Exception { + final long bucketDuration = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(bucketDuration); + + // split equally across two buckets + final long recordStart = TEST_START + (bucketDuration / 2); + stats.recordData(recordStart, recordStart + bucketDuration, + new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L)); + + assertEquals(2, stats.size()); + assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); + assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); + } + + @Test + public void testRecordTouchingBuckets() throws Exception { + final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // split almost completely into middle bucket, but with a few minutes + // overlap into neighboring buckets. total record is 20 minutes. + final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS; + final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); + stats.recordData(recordStart, recordEnd, + new NetworkStats.Entry(1000L, 2000L, 5000L, 10000L, 100L)); + + assertEquals(3, stats.size()); + // first bucket should have (1/20 of value) + assertValues(stats, 0, MINUTE_IN_MILLIS, 50L, 100L, 250L, 500L, 5L); + // second bucket should have (15/20 of value) + assertValues(stats, 1, 15 * MINUTE_IN_MILLIS, 750L, 1500L, 3750L, 7500L, 75L); + // final bucket should have (4/20 of value) + assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L); + } + + @Test + public void testRecordGapBuckets() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // record some data today and next week with large gap + final long firstStart = TEST_START; + final long lastStart = TEST_START + WEEK_IN_MILLIS; + stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, + new NetworkStats.Entry(128L, 2L, 256L, 4L, 1L)); + stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, + new NetworkStats.Entry(64L, 1L, 512L, 8L, 2L)); + + // we should have two buckets, far apart from each other + assertEquals(2, stats.size()); + assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); + + // now record something in middle, spread across two buckets + final long middleStart = TEST_START + DAY_IN_MILLIS; + final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2); + stats.recordData(middleStart, middleEnd, + new NetworkStats.Entry(2048L, 4L, 2048L, 4L, 2L)); + + // now should have four buckets, with new record in middle two buckets + assertEquals(4, stats.size()); + assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); + assertValues(stats, 1, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 2, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); + assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); + } + + @Test + public void testRecordOverlapBuckets() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // record some data in one bucket, and another overlapping buckets + stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, + new NetworkStats.Entry(256L, 2L, 256L, 2L, 1L)); + final long midStart = TEST_START + (HOUR_IN_MILLIS / 2); + stats.recordData(midStart, midStart + HOUR_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 1024L, 10L, 10L)); + + // should have two buckets, with some data mixed together + assertEquals(2, stats.size()); + assertValues(stats, 0, SECOND_IN_MILLIS + (HOUR_IN_MILLIS / 2), 768L, 7L, 768L, 7L, 6L); + assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L); + } + + @Test + public void testRecordEntireGapIdentical() throws Exception { + // first, create two separate histories far apart + final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L); + + final long TEST_START_2 = TEST_START + DAY_IN_MILLIS; + final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L); + + // combine together with identical bucket size + stats = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L); + + // now inspect internal buckets + assertValues(stats, 0, 1000L, 500L); + assertValues(stats, 1, 1000L, 500L); + assertValues(stats, 2, 500L, 250L); + assertValues(stats, 3, 500L, 250L); + } + + @Test + public void testRecordEntireOverlapVaryingBuckets() throws Exception { + // create history just over hour bucket boundary + final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L); + + final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS; + final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS); + stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L); + + // combine together with minute bucket size + stats = new NetworkStatsHistory(MINUTE_IN_MILLIS); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); + + // now inspect internal buckets + assertValues(stats, 0, 10L, 10L); + assertValues(stats, 1, 20L, 20L); + assertValues(stats, 2, 20L, 20L); + assertValues(stats, 3, 20L, 20L); + assertValues(stats, 4, 20L, 20L); + assertValues(stats, 5, 20L, 20L); + assertValues(stats, 6, 10L, 10L); + + // now combine using 15min buckets + stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); + + // and inspect buckets + assertValues(stats, 0, 200L, 200L); + assertValues(stats, 1, 150L, 150L); + assertValues(stats, 2, 150L, 150L); + assertValues(stats, 3, 150L, 150L); + } + + @Test + public void testRemove() throws Exception { + stats = new NetworkStatsHistory(HOUR_IN_MILLIS); + + // record some data across 24 buckets + stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); + assertEquals(24, stats.size()); + + // try removing invalid data; should be no change + stats.removeBucketsBefore(0 - DAY_IN_MILLIS); + assertEquals(24, stats.size()); + + // try removing far before buckets; should be no change + stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS); + assertEquals(24, stats.size()); + + // try removing just moments into first bucket; should be no change + // since that bucket contains data beyond the cutoff + stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS); + assertEquals(24, stats.size()); + + // try removing single bucket + stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS); + assertEquals(23, stats.size()); + + // try removing multiple buckets + stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS)); + assertEquals(20, stats.size()); + + // try removing all buckets + stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS); + assertEquals(0, stats.size()); + } + + @Test + public void testTotalData() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // record uniform data across day + stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L); + + // verify that total outside range is 0 + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L); + + // verify total in first hour + assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L); + + // verify total across 1.5 hours + assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L); + + // verify total beyond end + assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L); + + // verify everything total + assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L); + + } + + @Test + public void testFuzzing() throws Exception { + try { + // fuzzing with random events, looking for crashes + final NetworkStats.Entry entry = new NetworkStats.Entry(); + final Random r = new Random(); + for (int i = 0; i < 500; i++) { + stats = new NetworkStatsHistory(r.nextLong()); + for (int j = 0; j < 10000; j++) { + if (r.nextBoolean()) { + // add range + final long start = r.nextLong(); + final long end = start + r.nextInt(); + entry.rxBytes = nextPositiveLong(r); + entry.rxPackets = nextPositiveLong(r); + entry.txBytes = nextPositiveLong(r); + entry.txPackets = nextPositiveLong(r); + entry.operations = nextPositiveLong(r); + stats.recordData(start, end, entry); + } else { + // trim something + stats.removeBucketsBefore(r.nextLong()); + } + } + assertConsistent(stats); + } + } catch (Throwable e) { + Log.e(TAG, String.valueOf(stats)); + throw new RuntimeException(e); + } + } + + private static long nextPositiveLong(Random r) { + final long value = r.nextLong(); + return value < 0 ? -value : value; + } + + @Test + public void testIgnoreFields() throws Exception { + final NetworkStatsHistory history = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES); + + history.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + history.recordData(0, 2 * MINUTE_IN_MILLIS, + new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L)); + + assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); + } + + @Test + public void testIgnoreFieldsRecordIn() throws Exception { + final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); + final NetworkStatsHistory partial = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); + + full.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + partial.recordEntireHistory(full); + + assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); + } + + @Test + public void testIgnoreFieldsRecordOut() throws Exception { + final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); + final NetworkStatsHistory partial = new NetworkStatsHistory( + MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); + + partial.recordData(0, MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + full.recordEntireHistory(partial); + + assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L); + } + + @Test + public void testSerialize() throws Exception { + final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL); + before.recordData(0, 4 * MINUTE_IN_MILLIS, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); + before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS, + new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L)); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + before.writeToStream(new DataOutputStream(out)); + out.close(); + + final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in)); + + // must have identical totals before and after + assertFullValues(before, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); + assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); + } + + @Test + public void testVarLong() throws Exception { + assertEquals(0L, performVarLong(0L)); + assertEquals(-1L, performVarLong(-1L)); + assertEquals(1024L, performVarLong(1024L)); + assertEquals(-1024L, performVarLong(-1024L)); + assertEquals(40 * MB_IN_BYTES, performVarLong(40 * MB_IN_BYTES)); + assertEquals(512 * GB_IN_BYTES, performVarLong(512 * GB_IN_BYTES)); + assertEquals(Long.MIN_VALUE, performVarLong(Long.MIN_VALUE)); + assertEquals(Long.MAX_VALUE, performVarLong(Long.MAX_VALUE)); + assertEquals(Long.MIN_VALUE + 40, performVarLong(Long.MIN_VALUE + 40)); + assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40)); + } + + @Test + public void testIndexBeforeAfter() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + final long FIRST_START = TEST_START; + final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS); + final long SECOND_START = TEST_START + WEEK_IN_MILLIS; + final long SECOND_END = SECOND_START + HOUR_IN_MILLIS; + final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS); + final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS); + + stats.recordData(FIRST_START, FIRST_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(SECOND_START, SECOND_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(THIRD_START, THIRD_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + + // should have buckets: 2+1+2 + assertEquals(5, stats.size()); + + assertIndexBeforeAfter(stats, 0, 0, Long.MIN_VALUE); + assertIndexBeforeAfter(stats, 0, 1, FIRST_START); + assertIndexBeforeAfter(stats, 0, 1, FIRST_START + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 0, 2, FIRST_START + HOUR_IN_MILLIS); + assertIndexBeforeAfter(stats, 1, 2, FIRST_START + HOUR_IN_MILLIS + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 1, 2, FIRST_END - MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 1, 2, FIRST_END); + assertIndexBeforeAfter(stats, 1, 2, FIRST_END + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 1, 2, SECOND_START - MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 1, 3, SECOND_START); + assertIndexBeforeAfter(stats, 2, 3, SECOND_END); + assertIndexBeforeAfter(stats, 2, 3, SECOND_END + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 2, 3, THIRD_START - MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 2, 4, THIRD_START); + assertIndexBeforeAfter(stats, 3, 4, THIRD_START + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 3, 4, THIRD_START + HOUR_IN_MILLIS); + assertIndexBeforeAfter(stats, 4, 4, THIRD_END); + assertIndexBeforeAfter(stats, 4, 4, THIRD_END + MINUTE_IN_MILLIS); + assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE); + } + + @Test + public void testIntersects() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + final long FIRST_START = TEST_START; + final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS); + final long SECOND_START = TEST_START + WEEK_IN_MILLIS; + final long SECOND_END = SECOND_START + HOUR_IN_MILLIS; + final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS); + final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS); + + stats.recordData(FIRST_START, FIRST_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(SECOND_START, SECOND_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(THIRD_START, THIRD_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + + assertFalse(stats.intersects(10, 20)); + assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1)); + assertFalse(stats.intersects(Long.MAX_VALUE, Long.MIN_VALUE)); + + assertTrue(stats.intersects(Long.MIN_VALUE, Long.MAX_VALUE)); + assertTrue(stats.intersects(10, TEST_START + YEAR_IN_MILLIS)); + assertTrue(stats.intersects(TEST_START, TEST_START)); + assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, TEST_START + DAY_IN_MILLIS + 1)); + assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, Long.MAX_VALUE)); + assertTrue(stats.intersects(TEST_START + 1, Long.MAX_VALUE)); + + assertFalse(stats.intersects(Long.MIN_VALUE, TEST_START - 1)); + assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START)); + assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1)); + } + + @Test + public void testSetValues() throws Exception { + stats = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats.recordData(TEST_START, TEST_START + 1, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + + assertEquals(1024L + 2048L, stats.getTotalBytes()); + + final NetworkStatsHistory.Entry entry = stats.getValues(0, null); + entry.rxBytes /= 2; + entry.txBytes *= 2; + stats.setValues(0, entry); + + assertEquals(512L + 4096L, stats.getTotalBytes()); + } + + private static void assertIndexBeforeAfter( + NetworkStatsHistory stats, int before, int after, long time) { + assertEquals("unexpected before", before, stats.getIndexBefore(time)); + assertEquals("unexpected after", after, stats.getIndexAfter(time)); + } + + private static long performVarLong(long before) throws Exception { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeVarLong(new DataOutputStream(out), before); + + final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + return readVarLong(new DataInputStream(in)); + } + + private static void assertConsistent(NetworkStatsHistory stats) { + // verify timestamps are monotonic + long lastStart = Long.MIN_VALUE; + NetworkStatsHistory.Entry entry = null; + for (int i = 0; i < stats.size(); i++) { + entry = stats.getValues(i, entry); + assertTrue(lastStart < entry.bucketStart); + lastStart = entry.bucketStart; + } + } + + private static void assertValues( + NetworkStatsHistory stats, int index, long rxBytes, long txBytes) { + final NetworkStatsHistory.Entry entry = stats.getValues(index, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + } + + private static void assertValues( + NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + } + + private static void assertValues(NetworkStatsHistory stats, int index, long activeTime, + long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { + final NetworkStatsHistory.Entry entry = stats.getValues(index, null); + assertEquals("unexpected activeTime", activeTime, entry.activeTime); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } + + private static void assertFullValues(NetworkStatsHistory stats, long activeTime, long rxBytes, + long rxPackets, long txBytes, long txPackets, long operations) { + assertValues(stats, Long.MIN_VALUE, Long.MAX_VALUE, activeTime, rxBytes, rxPackets, txBytes, + txPackets, operations); + } + + private static void assertValues(NetworkStatsHistory stats, long start, long end, + long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets, + long operations) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected activeTime", activeTime, entry.activeTime); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java new file mode 100644 index 000000000000..23d5a7e5d5f8 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2011 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; + +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.INTERFACES_ALL; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.ROAMING_YES; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DBG_VPN_IN; +import static android.net.NetworkStats.SET_DBG_VPN_OUT; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.TAG_ALL; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.os.Process; +import android.util.ArrayMap; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.android.collect.Sets; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.HashSet; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsTest { + + private static final String TEST_IFACE = "test0"; + private static final String TEST_IFACE2 = "test2"; + private static final int TEST_UID = 1001; + private static final long TEST_START = 1194220800000L; + + @Test + public void testFindIndex() throws Exception { + final NetworkStats stats = new NetworkStats(TEST_START, 5) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) + .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12) + .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12); + + assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, + ROAMING_YES, DEFAULT_NETWORK_YES)); + assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO)); + assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, + ROAMING_NO, DEFAULT_NETWORK_YES)); + assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO)); + assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_YES)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO)); + assertEquals(-1, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO)); + } + + @Test + public void testFindIndexHinted() { + final NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10) + .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) + .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) + .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) + .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12); + + // verify that we correctly find across regardless of hinting + for (int hint = 0; hint < stats.size(); hint++) { + assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); + assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); + assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, + METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); + assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, + METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, hint)); + assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); + assertEquals(-1, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, + METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, hint)); + } + } + + @Test + public void testAddEntryGrow() throws Exception { + final NetworkStats stats = new NetworkStats(TEST_START, 4); + + assertEquals(0, stats.size()); + assertEquals(4, stats.internalSize()); + + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); + + assertEquals(4, stats.size()); + assertEquals(4, stats.internalSize()); + + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); + stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); + + assertEquals(9, stats.size()); + assertTrue(stats.internalSize() >= 9); + + assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); + assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); + assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); + assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, + ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); + assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); + assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); + assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); + assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); + assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, + ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); + } + + @Test + public void testCombineExisting() throws Exception { + final NetworkStats stats = new NetworkStats(TEST_START, 10); + + stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10); + stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2); + stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L, + -128L, -1L, -1); + + assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 384L, 3L, 128L, 1L, 9); + assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 2); + + // now try combining that should create row + stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); + assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 3); + stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); + assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 6); + } + + @Test + public void testSubtractIdenticalData() throws Exception { + final NetworkStats before = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + + final NetworkStats after = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + + final NetworkStats result = after.subtract(before); + + // identical data should result in zero delta + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + } + + @Test + public void testSubtractIdenticalRows() throws Exception { + final NetworkStats before = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + + final NetworkStats after = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20); + + final NetworkStats result = after.subtract(before); + + // expect delta between measurements + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1L, 1L, 2L, 1L, 4); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 3L, 1L, 4L, 1L, 8); + } + + @Test + public void testSubtractNewRows() throws Exception { + final NetworkStats before = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); + + final NetworkStats after = new NetworkStats(TEST_START, 3) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12) + .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20); + + final NetworkStats result = after.subtract(before); + + // its okay to have new rows + assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); + assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 20); + } + + @Test + public void testSubtractMissingRows() throws Exception { + final NetworkStats before = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0) + .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0); + + final NetworkStats after = new NetworkStats(TEST_START, 1) + .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0); + + final NetworkStats result = after.subtract(before); + + // should silently drop omitted rows + assertEquals(1, result.size()); + assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 0); + assertEquals(4L, result.getTotalBytes()); + } + + @Test + public void testTotalBytes() throws Exception { + final NetworkStats iface = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L); + assertEquals(384L, iface.getTotalBytes()); + + final NetworkStats uidSet = new NetworkStats(TEST_START, 3) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L); + assertEquals(96L, uidSet.getTotalBytes()); + + final NetworkStats uidTag = new NetworkStats(TEST_START, 6) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L); + assertEquals(64L, uidTag.getTotalBytes()); + + final NetworkStats uidMetered = new NetworkStats(TEST_START, 3) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); + assertEquals(96L, uidMetered.getTotalBytes()); + + final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); + assertEquals(96L, uidRoaming.getTotalBytes()); + } + + @Test + public void testGroupedByIfaceEmpty() throws Exception { + final NetworkStats uidStats = new NetworkStats(TEST_START, 3); + final NetworkStats grouped = uidStats.groupedByIface(); + + assertEquals(0, uidStats.size()); + assertEquals(0, grouped.size()); + } + + @Test + public void testGroupedByIfaceAll() throws Exception { + final NetworkStats uidStats = new NetworkStats(TEST_START, 3) + .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L) + .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L); + final NetworkStats grouped = uidStats.groupedByIface(); + + assertEquals(3, uidStats.size()); + assertEquals(1, grouped.size()); + + assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 6L, 0L); + } + + @Test + public void testGroupedByIface() throws Exception { + final NetworkStats uidStats = new NetworkStats(TEST_START, 7) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L); + + final NetworkStats grouped = uidStats.groupedByIface(); + + assertEquals(7, uidStats.size()); + + assertEquals(2, grouped.size()); + assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 2L, 0L); + assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 1024L, 64L, 0L, 0L, 0L); + } + + @Test + public void testAddAllValues() { + final NetworkStats first = new NetworkStats(TEST_START, 5) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); + + final NetworkStats second = new NetworkStats(TEST_START, 2) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); + + first.combineAllValues(second); + + assertEquals(4, first.size()); + assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); + assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); + assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, + DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); + assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); + } + + @Test + public void testGetTotal() { + final NetworkStats stats = new NetworkStats(TEST_START, 7) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, + DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); + + assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L); + assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L); + assertValues(stats.getTotal(null, 101), 128L, 8L, 0L, 0L, 0L); + + final HashSet ifaces = Sets.newHashSet(); + assertValues(stats.getTotal(null, ifaces), 0L, 0L, 0L, 0L, 0L); + + ifaces.add(TEST_IFACE2); + assertValues(stats.getTotal(null, ifaces), 1024L, 64L, 0L, 0L, 0L); + } + + @Test + public void testRemoveUids() throws Exception { + final NetworkStats before = new NetworkStats(TEST_START, 3); + + // Test 0 item stats. + NetworkStats after = before.clone(); + after.removeUids(new int[0]); + assertEquals(0, after.size()); + after.removeUids(new int[] {100}); + assertEquals(0, after.size()); + + // Test 1 item stats. + before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L); + after = before.clone(); + after.removeUids(new int[0]); + assertEquals(1, after.size()); + assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); + after.removeUids(new int[] {99}); + assertEquals(0, after.size()); + + // Append remaining test items. + before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L); + assertEquals(7, before.size()); + + // Test remove with empty uid list. + after = before.clone(); + after.removeUids(new int[0]); + assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L); + + // Test remove uids don't exist in stats. + after.removeUids(new int[] {98, 0, Integer.MIN_VALUE, Integer.MAX_VALUE}); + assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L); + + // Test remove all uids. + after.removeUids(new int[] {99, 100, 100, 101}); + assertEquals(0, after.size()); + + // Test remove in the middle. + after = before.clone(); + after.removeUids(new int[] {100}); + assertEquals(3, after.size()); + assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); + assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 32L, 4L, 0L, 0L, 0L); + assertValues(after, 2, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 64L, 2L, 0L, 0L, 0L); + } + + @Test + public void testRemoveEmptyEntries() throws Exception { + // Test empty stats. + final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3); + assertEquals(0, statsEmpty.removeEmptyEntries().size()); + + // Test stats with non-zero entry. + final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1) + .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); + assertEquals(1, statsNonZero.size()); + final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries(); + assertEquals(1, expectedNonZero.size()); + assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); + + // Test stats with empty entry. + final NetworkStats statsZero = new NetworkStats(TEST_START, 1) + .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + assertEquals(1, statsZero.size()); + final NetworkStats expectedZero = statsZero.removeEmptyEntries(); + assertEquals(1, statsZero.size()); // Assert immutable. + assertEquals(0, expectedZero.size()); + + // Test stats with multiple entries. + final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L) + .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L); + assertEquals(9, statsMultiple.size()); + final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries(); + assertEquals(9, statsMultiple.size()); // Assert immutable. + assertEquals(7, expectedMultiple.size()); + assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L); + + // Test stats with multiple empty entries. + assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size()); + assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size()); + } + + @Test + public void testClone() throws Exception { + final NetworkStats original = new NetworkStats(TEST_START, 5) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); + + // make clone and mutate original + final NetworkStats clone = original.clone(); + original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L); + + assertEquals(3, original.size()); + assertEquals(2, clone.size()); + + assertEquals(128L + 512L + 128L, original.getTotalBytes()); + assertEquals(128L + 512L, clone.getTotalBytes()); + } + + @Test + public void testAddWhenEmpty() throws Exception { + final NetworkStats red = new NetworkStats(TEST_START, -1); + final NetworkStats blue = new NetworkStats(TEST_START, 5) + .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) + .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); + + // We're mostly checking that we don't crash + red.combineAllValues(blue); + } + + @Test + public void testMigrateTun() throws Exception { + final int tunUid = 10030; + final String tunIface = "tun0"; + final String underlyingIface = "wlan0"; + final int testTag1 = 8888; + NetworkStats delta = new NetworkStats(TEST_START, 17) + .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L) + .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L) + .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L) + // VPN package also uses some traffic through unprotected network. + .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L) + .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + // Tag entries + .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L) + .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L) + // Irrelevant entries + .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L) + // Underlying Iface entries + .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L) + .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) + .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */, + 299L /* smaller than sum(tun0) */, 0L) + .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + + delta.migrateTun(tunUid, tunIface, Arrays.asList(underlyingIface)); + assertEquals(20, delta.size()); + + // tunIface and TEST_IFACE entries are not changed. + assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L); + assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L); + assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L); + assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L); + assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L); + assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L); + assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L); + + // Existing underlying Iface entries are updated + assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 44783L, 54L, 14178L, 62L, 0L); + assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + + // VPN underlying Iface entries are updated + assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 28304L, 27L, 1L, 2L, 0L); + assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); + + // New entries are added for new application's underlying Iface traffic + assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 72667L, 197L, 43123L, 227L, 0L); + assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 9297L, 17L, 4054, 19L, 0L); + assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 21691L, 41L, 13572L, 48L, 0L); + assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 1281L, 2L, 653L, 1L, 0L); + + // New entries are added for debug purpose + assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 39605L, 46L, 12039, 51, 0); + assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 81964, 214, 47177, 246, 0); + assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 121569, 260, 59216, 297, 0); + + } + + // Tests a case where all of the data received by the tun0 interface is echo back into the tun0 + // interface by the vpn app before it's sent out of the underlying interface. The VPN app should + // not be charged for the echoed data but it should still be charged for any extra data it sends + // via the underlying interface. + @Test + public void testMigrateTun_VpnAsLoopback() { + final int tunUid = 10030; + final String tunIface = "tun0"; + final String underlyingIface = "wlan0"; + NetworkStats delta = new NetworkStats(TEST_START, 9) + // 2 different apps sent/receive data via tun0. + .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L) + .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L) + // VPN package resends data through the tunnel (with exaggerated overhead) + .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L) + // 1 app already has some traffic on the underlying interface, the other doesn't yet + .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L) + // Traffic through the underlying interface via the vpn app. + // This test should redistribute this data correctly. + .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); + + delta.migrateTun(tunUid, tunIface, Arrays.asList(underlyingIface)); + assertEquals(9, delta.size()); + + // tunIface entries should not be changed. + assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); + assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 240000L, 100L, 120000L, 60L, 0L); + + // Existing underlying Iface entries are updated + assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L); + + // VPN underlying Iface entries are updated + assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 25000L, 10L, 29800L, 15L, 0L); + + // New entries are added for new application's underlying Iface traffic + assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); + + // New entries are added for debug purpose + assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L); + assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0); + } + + @Test + public void testFilter_NoFilter() { + NetworkStats.Entry entry1 = new NetworkStats.Entry( + "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry3 = new NetworkStats.Entry( + "test2", 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3); + + stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL); + assertEquals(3, stats.size()); + assertEquals(entry1, stats.getValues(0, null)); + assertEquals(entry2, stats.getValues(1, null)); + assertEquals(entry3, stats.getValues(2, null)); + } + + @Test + public void testFilter_UidFilter() { + final int testUid = 10101; + NetworkStats.Entry entry1 = new NetworkStats.Entry( + "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "test2", testUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry3 = new NetworkStats.Entry( + "test2", testUid, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3); + + stats.filter(testUid, INTERFACES_ALL, TAG_ALL); + assertEquals(2, stats.size()); + assertEquals(entry2, stats.getValues(0, null)); + assertEquals(entry3, stats.getValues(1, null)); + } + + @Test + public void testFilter_InterfaceFilter() { + final String testIf1 = "testif1"; + final String testIf2 = "testif2"; + NetworkStats.Entry entry1 = new NetworkStats.Entry( + testIf1, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "otherif", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry3 = new NetworkStats.Entry( + testIf1, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry4 = new NetworkStats.Entry( + testIf2, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 4) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3) + .insertEntry(entry4); + + stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL); + assertEquals(3, stats.size()); + assertEquals(entry1, stats.getValues(0, null)); + assertEquals(entry3, stats.getValues(1, null)); + assertEquals(entry4, stats.getValues(2, null)); + } + + @Test + public void testFilter_EmptyInterfaceFilter() { + NetworkStats.Entry entry1 = new NetworkStats.Entry( + "if1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "if2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(entry1) + .insertEntry(entry2); + + stats.filter(UID_ALL, new String[] { }, TAG_ALL); + assertEquals(0, stats.size()); + } + + @Test + public void testFilter_TagFilter() { + final int testTag = 123; + final int otherTag = 456; + NetworkStats.Entry entry1 = new NetworkStats.Entry( + "test1", 10100, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "test2", 10101, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry3 = new NetworkStats.Entry( + "test2", 10101, SET_DEFAULT, otherTag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3); + + stats.filter(UID_ALL, INTERFACES_ALL, testTag); + assertEquals(2, stats.size()); + assertEquals(entry1, stats.getValues(0, null)); + assertEquals(entry2, stats.getValues(1, null)); + } + + @Test + public void testFilterDebugEntries() { + NetworkStats.Entry entry1 = new NetworkStats.Entry( + "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry2 = new NetworkStats.Entry( + "test2", 10101, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry3 = new NetworkStats.Entry( + "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats.Entry entry4 = new NetworkStats.Entry( + "test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); + + NetworkStats stats = new NetworkStats(TEST_START, 4) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3) + .insertEntry(entry4); + + stats.filterDebugEntries(); + + assertEquals(2, stats.size()); + assertEquals(entry1, stats.getValues(0, null)); + assertEquals(entry3, stats.getValues(1, null)); + } + + @Test + public void testApply464xlatAdjustments() { + final String v4Iface = "v4-wlan0"; + final String baseIface = "wlan0"; + final String otherIface = "other"; + final int appUid = 10001; + final int rootUid = Process.ROOT_UID; + ArrayMap stackedIface = new ArrayMap<>(); + stackedIface.put(v4Iface, baseIface); + + // Ipv4 traffic sent/received by an app on stacked interface. + final NetworkStats.Entry appEntry = new NetworkStats.Entry( + v4Iface, appUid, SET_DEFAULT, TAG_NONE, + 30501490 /* rxBytes */, + 22401 /* rxPackets */, + 876235 /* txBytes */, + 13805 /* txPackets */, + 0 /* operations */); + + // Traffic measured for the root uid on the base interface. + final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry( + baseIface, rootUid, SET_DEFAULT, TAG_NONE, + 163577 /* rxBytes */, + 187 /* rxPackets */, + 17607 /* txBytes */, + 97 /* txPackets */, + 0 /* operations */); + + final NetworkStats.Entry otherEntry = new NetworkStats.Entry( + otherIface, appUid, SET_DEFAULT, TAG_NONE, + 2600 /* rxBytes */, + 2 /* rxPackets */, + 3800 /* txBytes */, + 3 /* txPackets */, + 0 /* operations */); + + final NetworkStats stats = new NetworkStats(TEST_START, 3) + .insertEntry(appEntry) + .insertEntry(rootUidEntry) + .insertEntry(otherEntry); + + stats.apply464xlatAdjustments(stackedIface); + + assertEquals(3, stats.size()); + final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry( + v4Iface, appUid, SET_DEFAULT, TAG_NONE, + 30949510, + 22401, + 1152335, + 13805, + 0); + final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry( + baseIface, 0, SET_DEFAULT, TAG_NONE, + 163577, + 187, + 17607, + 97, + 0); + assertEquals(expectedAppUid, stats.getValues(0, null)); + assertEquals(expectedRootUid, stats.getValues(1, null)); + assertEquals(otherEntry, stats.getValues(2, null)); + } + + @Test + public void testApply464xlatAdjustments_noStackedIface() { + NetworkStats.Entry firstEntry = new NetworkStats.Entry( + "if1", 10002, SET_DEFAULT, TAG_NONE, + 2600 /* rxBytes */, + 2 /* rxPackets */, + 3800 /* txBytes */, + 3 /* txPackets */, + 0 /* operations */); + NetworkStats.Entry secondEntry = new NetworkStats.Entry( + "if2", 10002, SET_DEFAULT, TAG_NONE, + 5000 /* rxBytes */, + 3 /* rxPackets */, + 6000 /* txBytes */, + 4 /* txPackets */, + 0 /* operations */); + + NetworkStats stats = new NetworkStats(TEST_START, 2) + .insertEntry(firstEntry) + .insertEntry(secondEntry); + + // Empty map: no adjustment + stats.apply464xlatAdjustments(new ArrayMap<>()); + + assertEquals(2, stats.size()); + assertEquals(firstEntry, stats.getValues(0, null)); + assertEquals(secondEntry, stats.getValues(1, null)); + } + + private static void assertContains(NetworkStats stats, String iface, int uid, int set, + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + int index = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); + assertTrue(index != -1); + assertValues(stats, index, iface, uid, set, tag, metered, roaming, defaultNetwork, + rxBytes, rxPackets, txBytes, txPackets, operations); + } + + private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set, + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + final NetworkStats.Entry entry = stats.getValues(index, null); + assertValues(entry, iface, uid, set, tag, metered, roaming, defaultNetwork); + assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations); + } + + private static void assertValues( + NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered, + int roaming, int defaultNetwork) { + assertEquals(iface, entry.iface); + assertEquals(uid, entry.uid); + assertEquals(set, entry.set); + assertEquals(tag, entry.tag); + assertEquals(metered, entry.metered); + assertEquals(roaming, entry.roaming); + assertEquals(defaultNetwork, entry.defaultNetwork); + } + + private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + assertEquals(rxBytes, entry.rxBytes); + assertEquals(rxPackets, entry.rxPackets); + assertEquals(txBytes, entry.txBytes); + assertEquals(txPackets, entry.txPackets); + assertEquals(operations, entry.operations); + } + +} diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt new file mode 100644 index 000000000000..ab6b2f409867 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt @@ -0,0 +1,351 @@ +/* + * 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 + +import android.content.Context +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.NetworkIdentity.SUBTYPE_COMBINED +import android.net.NetworkIdentity.OEM_NONE +import android.net.NetworkIdentity.OEM_PAID +import android.net.NetworkIdentity.OEM_PRIVATE +import android.net.NetworkIdentity.buildNetworkIdentity +import android.net.NetworkStats.DEFAULT_NETWORK_ALL +import android.net.NetworkStats.METERED_ALL +import android.net.NetworkStats.ROAMING_ALL +import android.net.NetworkTemplate.MATCH_MOBILE +import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD +import android.net.NetworkTemplate.MATCH_WIFI +import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD +import android.net.NetworkTemplate.WIFI_NETWORKID_ALL +import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA +import android.net.NetworkTemplate.NETWORK_TYPE_ALL +import android.net.NetworkTemplate.OEM_MANAGED_ALL +import android.net.NetworkTemplate.OEM_MANAGED_NO +import android.net.NetworkTemplate.OEM_MANAGED_YES +import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT +import android.net.NetworkTemplate.buildTemplateWifi +import android.net.NetworkTemplate.buildTemplateWifiWildcard +import android.net.NetworkTemplate.buildTemplateCarrier +import android.net.NetworkTemplate.buildTemplateMobileWithRatType +import android.telephony.TelephonyManager +import com.android.testutils.assertParcelSane +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +private const val TEST_IMSI1 = "imsi1" +private const val TEST_IMSI2 = "imsi2" +private const val TEST_SSID1 = "ssid1" +private const val TEST_SSID2 = "ssid2" + +@RunWith(JUnit4::class) +class NetworkTemplateTest { + private val mockContext = mock(Context::class.java) + + private fun buildMobileNetworkState(subscriberId: String): NetworkStateSnapshot = + buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId) + private fun buildWifiNetworkState(subscriberId: String?, ssid: String?): NetworkStateSnapshot = + buildNetworkState(TYPE_WIFI, subscriberId = subscriberId, ssid = ssid) + + private fun buildNetworkState( + type: Int, + subscriberId: String? = null, + ssid: String? = null, + oemManaged: Int = OEM_NONE + ): NetworkStateSnapshot { + val lp = LinkProperties() + val caps = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) + setSSID(ssid) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, + (oemManaged and OEM_PAID) == OEM_PAID) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, + (oemManaged and OEM_PRIVATE) == OEM_PRIVATE) + } + return NetworkStateSnapshot(mock(Network::class.java), caps, lp, subscriberId, type) + } + + private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) = + assertTrue(matches(ident), "$this does not match $ident") + + private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) = + assertFalse(matches(ident), "$this should match $ident") + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testWifiWildcardMatches() { + val templateWifiWildcard = buildTemplateWifiWildcard() + + val identMobileImsi1 = buildNetworkIdentity(mockContext, + buildMobileNetworkState(TEST_IMSI1), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifiImsiNullSsid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) + val identWifiImsi1Ssid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) + + templateWifiWildcard.assertDoesNotMatch(identMobileImsi1) + templateWifiWildcard.assertMatches(identWifiImsiNullSsid1) + templateWifiWildcard.assertMatches(identWifiImsi1Ssid1) + } + + @Test + fun testWifiMatches() { + val templateWifiSsid1 = buildTemplateWifi(TEST_SSID1) + val templateWifiSsid1ImsiNull = buildTemplateWifi(TEST_SSID1, null) + val templateWifiSsid1Imsi1 = buildTemplateWifi(TEST_SSID1, TEST_IMSI1) + val templateWifiSsidAllImsi1 = buildTemplateWifi(WIFI_NETWORKID_ALL, TEST_IMSI1) + + val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifiImsiNullSsid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) + val identWifiImsi1Ssid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) + val identWifiImsi2Ssid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) + val identWifiImsi1Ssid2 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID2), true, 0) + + // Verify that template with SSID only matches any subscriberId and specific SSID. + templateWifiSsid1.assertDoesNotMatch(identMobile1) + templateWifiSsid1.assertMatches(identWifiImsiNullSsid1) + templateWifiSsid1.assertMatches(identWifiImsi1Ssid1) + templateWifiSsid1.assertMatches(identWifiImsi2Ssid1) + templateWifiSsid1.assertDoesNotMatch(identWifiImsi1Ssid2) + + // Verify that template with SSID1 and null imsi matches any network with + // SSID1 and null imsi. + templateWifiSsid1ImsiNull.assertDoesNotMatch(identMobile1) + templateWifiSsid1ImsiNull.assertMatches(identWifiImsiNullSsid1) + templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid1) + templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi2Ssid1) + templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid2) + + // Verify that template with SSID1 and imsi1 matches any network with + // SSID1 and imsi1. + templateWifiSsid1Imsi1.assertDoesNotMatch(identMobile1) + templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsiNullSsid1) + templateWifiSsid1Imsi1.assertMatches(identWifiImsi1Ssid1) + templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi2Ssid1) + templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi1Ssid2) + + // Verify that template with SSID all and imsi1 matches any network with + // any SSID and imsi1. + templateWifiSsidAllImsi1.assertDoesNotMatch(identMobile1) + templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsiNullSsid1) + templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid1) + templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsi2Ssid1) + templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid2) + } + + @Test + fun testCarrierMatches() { + val templateCarrierImsi1 = buildTemplateCarrier(TEST_IMSI1) + + val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identMobile2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifiSsid1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) + val identCarrierWifiImsi1 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) + val identCarrierWifiImsi2 = buildNetworkIdentity( + mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) + + templateCarrierImsi1.assertMatches(identCarrierWifiImsi1) + templateCarrierImsi1.assertDoesNotMatch(identCarrierWifiImsi2) + templateCarrierImsi1.assertDoesNotMatch(identWifiSsid1) + templateCarrierImsi1.assertMatches(identMobile1) + templateCarrierImsi1.assertDoesNotMatch(identMobile2) + } + + @Test + fun testRatTypeGroupMatches() { + val stateMobile = buildMobileNetworkState(TEST_IMSI1) + // Build UMTS template that matches mobile identities with RAT in the same + // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}. + val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS) + // Build normal template that matches mobile identities with any RAT and IMSI. + val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL) + // Build template with UNKNOWN RAT that matches mobile identities with RAT that + // cannot be determined. + val templateUnknown = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN) + + val identUmts = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS) + val identHsdpa = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA) + val identLte = buildNetworkIdentity( + mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE) + val identCombined = buildNetworkIdentity( + mockContext, stateMobile, false, SUBTYPE_COMBINED) + val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), + false, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifi = buildNetworkIdentity( + mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) + + // Assert that identity with the same RAT matches. + templateUmts.assertMatches(identUmts) + templateAll.assertMatches(identUmts) + templateUnknown.assertDoesNotMatch(identUmts) + // Assert that identity with the RAT within the same group matches. + templateUmts.assertMatches(identHsdpa) + templateAll.assertMatches(identHsdpa) + templateUnknown.assertDoesNotMatch(identHsdpa) + // Assert that identity with the RAT out of the same group only matches template with + // NETWORK_TYPE_ALL. + templateUmts.assertDoesNotMatch(identLte) + templateAll.assertMatches(identLte) + templateUnknown.assertDoesNotMatch(identLte) + // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL + // and NETWORK_TYPE_UNKNOWN. + templateUmts.assertDoesNotMatch(identCombined) + templateAll.assertMatches(identCombined) + templateUnknown.assertMatches(identCombined) + // Assert that identity with different IMSI matches. + templateUmts.assertMatches(identImsi2) + templateAll.assertMatches(identImsi2) + templateUnknown.assertDoesNotMatch(identImsi2) + // Assert that wifi identity does not match. + templateUmts.assertDoesNotMatch(identWifi) + templateAll.assertDoesNotMatch(identWifi) + templateUnknown.assertDoesNotMatch(identWifi) + } + + @Test + fun testParcelUnparcel() { + val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE, + OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT) + val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_ALL, + SUBSCRIBER_ID_MATCH_RULE_EXACT) + val templateOem = NetworkTemplate(MATCH_MOBILE, null, null, null, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_YES, + SUBSCRIBER_ID_MATCH_RULE_EXACT) + assertParcelSane(templateMobile, 10) + assertParcelSane(templateWifi, 10) + assertParcelSane(templateOem, 10) + } + + // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with + // TelephonyManager#NETWORK_TYPE_* constants. + @Test + fun testNetworkTypeConstants() { + for (ratType in TelephonyManager.getAllNetworkTypes()) { + assertNotEquals(NETWORK_TYPE_ALL, ratType) + assertNotEquals(NETWORK_TYPE_5G_NSA, ratType) + } + } + + @Test + fun testOemNetworkConstants() { + val constantValues = arrayOf(OEM_MANAGED_YES, OEM_MANAGED_ALL, OEM_MANAGED_NO, + OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) + + // Verify that "not OEM managed network" constants are equal. + assertEquals(OEM_MANAGED_NO, OEM_NONE) + + // Verify the constants don't conflict. + assertEquals(constantValues.size, constantValues.distinct().count()) + } + + /** + * Helper to enumerate and assert OEM managed wifi and mobile {@code NetworkTemplate}s match + * their the appropriate OEM managed {@code NetworkIdentity}s. + * + * @param networkType {@code TYPE_MOBILE} or {@code TYPE_WIFI} + * @param matchType A match rule from {@code NetworkTemplate.MATCH_*} corresponding to the + * networkType. + * @param subscriberId To be populated with {@code TEST_IMSI*} only if networkType is + * {@code TYPE_MOBILE}. May be left as null when matchType is + * {@link NetworkTemplate.MATCH_MOBILE_WILDCARD}. + * @param templateSsid Top be populated with {@code TEST_SSID*} only if networkType is + * {@code TYPE_WIFI}. May be left as null when matchType is + * {@link NetworkTemplate.MATCH_WIFI_WILDCARD}. + * @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide + * one of {@code TEST_SSID*}. + */ + private fun matchOemManagedIdent( + networkType: Int, + matchType: Int, + subscriberId: String? = null, + templateSsid: String? = null, + identSsid: String? = null + ) { + val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) + val matchSubscriberIds = arrayOf(subscriberId) + + val templateOemYes = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_MANAGED_YES, SUBSCRIBER_ID_MATCH_RULE_EXACT) + val templateOemAll = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT) + + for (identityOemManagedState in oemManagedStates) { + val ident = buildNetworkIdentity(mockContext, buildNetworkState(networkType, + subscriberId, identSsid, identityOemManagedState), /*defaultNetwork=*/false, + /*subType=*/0) + + // Create a template with each OEM managed type and match it against the NetworkIdentity + for (templateOemManagedState in oemManagedStates) { + val template = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + NETWORK_TYPE_ALL, templateOemManagedState, SUBSCRIBER_ID_MATCH_RULE_EXACT) + if (identityOemManagedState == templateOemManagedState) { + template.assertMatches(ident) + } else { + template.assertDoesNotMatch(ident) + } + } + // OEM_MANAGED_ALL ignores OEM state. + templateOemAll.assertMatches(ident) + if (identityOemManagedState == OEM_NONE) { + // OEM_MANAGED_YES matches everything except OEM_NONE. + templateOemYes.assertDoesNotMatch(ident) + } else { + templateOemYes.assertMatches(ident) + } + } + } + + @Test + fun testOemManagedMatchesIdent() { + matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE, subscriberId = TEST_IMSI1) + matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE_WILDCARD) + matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateSsid = TEST_SSID1, + identSsid = TEST_SSID1) + matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1) + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java new file mode 100644 index 000000000000..7748288aeb05 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 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; + +import static junit.framework.Assert.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; +import java.util.TreeSet; + +@RunWith(AndroidJUnit4.class) +@androidx.test.filters.SmallTest +public class NetworkUtilsTest { + @Test + public void testRoutedIPv4AddressCount() { + final TreeSet set = new TreeSet<>(IpPrefix.lengthComparator()); + // No routes routes to no addresses. + assertEquals(0, NetworkUtils.routedIPv4AddressCount(set)); + + set.add(new IpPrefix("0.0.0.0/0")); + assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); + + set.add(new IpPrefix("20.18.0.0/16")); + set.add(new IpPrefix("20.18.0.0/24")); + set.add(new IpPrefix("20.18.0.0/8")); + // There is a default route, still covers everything + assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); + + set.clear(); + set.add(new IpPrefix("20.18.0.0/24")); + set.add(new IpPrefix("20.18.0.0/8")); + // The 8-length includes the 24-length prefix + assertEquals(1l << 24, NetworkUtils.routedIPv4AddressCount(set)); + + set.add(new IpPrefix("10.10.10.126/25")); + // The 8-length does not include this 25-length prefix + assertEquals((1l << 24) + (1 << 7), NetworkUtils.routedIPv4AddressCount(set)); + + set.clear(); + set.add(new IpPrefix("1.2.3.4/32")); + set.add(new IpPrefix("1.2.3.4/32")); + set.add(new IpPrefix("1.2.3.4/32")); + set.add(new IpPrefix("1.2.3.4/32")); + assertEquals(1l, NetworkUtils.routedIPv4AddressCount(set)); + + set.add(new IpPrefix("1.2.3.5/32")); + set.add(new IpPrefix("1.2.3.6/32")); + + set.add(new IpPrefix("1.2.3.7/32")); + set.add(new IpPrefix("1.2.3.8/32")); + set.add(new IpPrefix("1.2.3.9/32")); + set.add(new IpPrefix("1.2.3.0/32")); + assertEquals(7l, NetworkUtils.routedIPv4AddressCount(set)); + + // 1.2.3.4/30 eats 1.2.3.{4-7}/32 + set.add(new IpPrefix("1.2.3.4/30")); + set.add(new IpPrefix("6.2.3.4/28")); + set.add(new IpPrefix("120.2.3.4/16")); + assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set)); + } + + @Test + public void testRoutedIPv6AddressCount() { + final TreeSet set = new TreeSet<>(IpPrefix.lengthComparator()); + // No routes routes to no addresses. + assertEquals(BigInteger.ZERO, NetworkUtils.routedIPv6AddressCount(set)); + + set.add(new IpPrefix("::/0")); + assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); + + set.add(new IpPrefix("1234:622a::18/64")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); + // There is a default route, still covers everything + assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); + + set.clear(); + set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); + // The 8-length includes the 96-length prefix + assertEquals(BigInteger.ONE.shiftLeft(120), NetworkUtils.routedIPv6AddressCount(set)); + + set.add(new IpPrefix("10::26/64")); + // The 8-length does not include this 64-length prefix + assertEquals(BigInteger.ONE.shiftLeft(120).add(BigInteger.ONE.shiftLeft(64)), + NetworkUtils.routedIPv6AddressCount(set)); + + set.clear(); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); + assertEquals(BigInteger.ONE, NetworkUtils.routedIPv6AddressCount(set)); + + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad5/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad6/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad7/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad8/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad9/128")); + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad0/128")); + assertEquals(BigInteger.valueOf(7), NetworkUtils.routedIPv6AddressCount(set)); + + // add4:f00:80:f7:1111::6ad4/126 eats add4:f00:8[:f7:1111::6ad{4-7}/128 + set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/126")); + set.add(new IpPrefix("d00d:f00:80:f7:1111::6ade/124")); + set.add(new IpPrefix("f00b:a33::/112")); + assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), + NetworkUtils.routedIPv6AddressCount(set)); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java new file mode 100644 index 000000000000..ad58960eaadd --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java @@ -0,0 +1,75 @@ +/* + * 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; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +@RunWith(AndroidJUnit4.class) +@androidx.test.filters.SmallTest +public class QosSocketFilterTest { + + @Test + public void testPortExactMatch() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertTrue(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 10, 10)); + + } + + @Test + public void testPortLessThanStart() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 8), addressB, 10, 10)); + } + + @Test + public void testPortGreaterThanEnd() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 18), addressB, 10, 10)); + } + + @Test + public void testPortBetweenStartAndEnd() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertTrue(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 8, 18)); + } + + @Test + public void testAddressesDontMatch() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 10, 10)); + } +} + diff --git a/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java new file mode 100644 index 000000000000..6714bb1abbe6 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.net.wifi.WifiNetworkSpecifier; +import android.telephony.SubscriptionManager; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit test for {@link android.net.TelephonyNetworkSpecifier}. + */ +@SmallTest +public class TelephonyNetworkSpecifierTest { + private static final int TEST_SUBID = 5; + private static final String TEST_SSID = "Test123"; + + /** + * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier + * without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. + */ + @Test + public void testBuilderBuildWithDefault() { + try { + new TelephonyNetworkSpecifier.Builder().build(); + } catch (IllegalArgumentException iae) { + // expected, test pass + } + } + + /** + * Validate that no exception will be thrown even if pass invalid subscription id to + * {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. + */ + @Test + public void testBuilderBuildWithInvalidSubId() { + TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + .build(); + assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + /** + * Validate the correctness of TelephonyNetworkSpecifier when provide valid subId. + */ + @Test + public void testBuilderBuildWithValidSubId() { + final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + assertEquals(TEST_SUBID, specifier.getSubscriptionId()); + } + + /** + * Validate that parcel marshalling/unmarshalling works. + */ + @Test + public void testParcel() { + TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + assertParcelSane(specifier, 1 /* fieldCount */); + } + + /** + * Validate the behavior of method canBeSatisfiedBy(). + */ + @Test + public void testCanBeSatisfiedBy() { + final TelephonyNetworkSpecifier tns1 = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + final TelephonyNetworkSpecifier tns2 = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + final WifiNetworkSpecifier wns = new WifiNetworkSpecifier.Builder() + .setSsid(TEST_SSID) + .build(); + final MatchAllNetworkSpecifier mans = new MatchAllNetworkSpecifier(); + + // Test equality + assertEquals(tns1, tns2); + assertTrue(tns1.canBeSatisfiedBy(tns1)); + assertTrue(tns1.canBeSatisfiedBy(tns2)); + + // Test other edge cases. + assertFalse(tns1.canBeSatisfiedBy(null)); + assertFalse(tns1.canBeSatisfiedBy(wns)); + assertTrue(tns1.canBeSatisfiedBy(mans)); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java new file mode 100644 index 000000000000..3135062138ac --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.Intent; +import android.test.mock.MockContext; +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.net.VpnProfile; +import com.android.internal.util.MessageUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link VpnManager}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VpnManagerTest { + private static final String PKG_NAME = "fooPackage"; + + private static final String SESSION_NAME_STRING = "testSession"; + private static final String SERVER_ADDR_STRING = "1.2.3.4"; + private static final String IDENTITY_STRING = "Identity"; + private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); + + private IVpnManager mMockService; + private VpnManager mVpnManager; + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return PKG_NAME; + } + }; + + @Before + public void setUp() throws Exception { + mMockService = mock(IVpnManager.class); + mVpnManager = new VpnManager(mMockContext, mMockService); + } + + @Test + public void testProvisionVpnProfilePreconsented() throws Exception { + final PlatformVpnProfile profile = getPlatformVpnProfile(); + when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))) + .thenReturn(true); + + // Expect there to be no intent returned, as consent has already been granted. + assertNull(mVpnManager.provisionVpnProfile(profile)); + verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); + } + + @Test + public void testProvisionVpnProfileNeedsConsent() throws Exception { + final PlatformVpnProfile profile = getPlatformVpnProfile(); + when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))) + .thenReturn(false); + + // Expect intent to be returned, as consent has not already been granted. + final Intent intent = mVpnManager.provisionVpnProfile(profile); + assertNotNull(intent); + + final ComponentName expectedComponentName = + ComponentName.unflattenFromString( + "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog"); + assertEquals(expectedComponentName, intent.getComponent()); + verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); + } + + @Test + public void testDeleteProvisionedVpnProfile() throws Exception { + mVpnManager.deleteProvisionedVpnProfile(); + verify(mMockService).deleteVpnProfile(eq(PKG_NAME)); + } + + @Test + public void testStartProvisionedVpnProfile() throws Exception { + mVpnManager.startProvisionedVpnProfile(); + verify(mMockService).startVpnProfile(eq(PKG_NAME)); + } + + @Test + public void testStopProvisionedVpnProfile() throws Exception { + mVpnManager.stopProvisionedVpnProfile(); + verify(mMockService).stopVpnProfile(eq(PKG_NAME)); + } + + private Ikev2VpnProfile getPlatformVpnProfile() throws Exception { + return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING) + .setBypassable(true) + .setMaxMtu(1300) + .setMetered(true) + .setAuthPsk(PSK_BYTES) + .build(); + } + + @Test + public void testVpnTypesEqual() throws Exception { + SparseArray vmVpnTypes = MessageUtils.findMessageNames( + new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" }); + SparseArray nativeVpnType = MessageUtils.findMessageNames( + new Class[] { NativeVpnType.class }, new String[]{ "" }); + + // TYPE_VPN_NONE = -1 is only defined in VpnManager. + assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size()); + for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) { + assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i)); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java new file mode 100644 index 000000000000..ccaa5cf7e9f7 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 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; + +import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_NONE; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VpnTransportInfoTest { + + @Test + public void testParceling() { + VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); + assertParcelSane(v, 2 /* fieldCount */); + } + + @Test + public void testEqualsAndHashCode() { + String session1 = "12345"; + String session2 = "6789"; + VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); + VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1); + VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); + VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1); + VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1); + VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2); + + VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS); + + assertNotEquals(v11, v12); + assertNotEquals(v13, v14); + assertNotEquals(v14, v15); + assertNotEquals(v14, v21); + + assertEquals(v11, v13); + assertEquals(v31, v32); + assertEquals(v11.hashCode(), v13.hashCode()); + assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions()); + assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId()); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java new file mode 100644 index 000000000000..603c87519532 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 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.ipmemorystore; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirkParcelable; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Modifier; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ParcelableTests { + @Test + public void testNetworkAttributesParceling() throws Exception { + final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); + NetworkAttributes in = builder.build(); + assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); + + builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); + // lease will expire in two hours + builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); + // cluster stays null this time around + builder.setDnsAddresses(Collections.emptyList()); + builder.setMtu(18); + in = builder.build(); + assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); + + builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9")); + builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000); + builder.setCluster("groupHint"); + builder.setDnsAddresses(Arrays.asList( + InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"), + InetAddress.getByName("6.7.8.9"))); + builder.setMtu(1_000_000); + in = builder.build(); + assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); + + builder.setMtu(null); + in = builder.build(); + assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); + + // Verify that this test does not miss any new field added later. + // If any field is added to NetworkAttributes it must be tested here for parceling + // roundtrip. + assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); + } + + @Test + public void testPrivateDataParceling() throws Exception { + final Blob in = new Blob(); + in.data = new byte[] {89, 111, 108, 111}; + final Blob out = parcelingRoundTrip(in); + // Object.equals on byte[] tests the references + assertEquals(in.data.length, out.data.length); + assertTrue(Arrays.equals(in.data, out.data)); + } + + @Test + public void testSameL3NetworkResponseParceling() throws Exception { + final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable(); + parcelable.l2Key1 = "key 1"; + parcelable.l2Key2 = "key 2"; + parcelable.confidence = 0.43f; + + final SameL3NetworkResponse in = new SameL3NetworkResponse(parcelable); + assertEquals("key 1", in.l2Key1); + assertEquals("key 2", in.l2Key2); + assertEquals(0.43f, in.confidence, 0.01f /* delta */); + + final SameL3NetworkResponse out = + new SameL3NetworkResponse(parcelingRoundTrip(in.toParcelable())); + + assertEquals(in, out); + assertEquals(in.l2Key1, out.l2Key1); + assertEquals(in.l2Key2, out.l2Key2); + assertEquals(in.confidence, out.confidence, 0.01f /* delta */); + } + + @Test + public void testIPv6ProvisioningLossQuirkParceling() throws Exception { + final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); + final IPv6ProvisioningLossQuirkParcelable parcelable = + new IPv6ProvisioningLossQuirkParcelable(); + final long expiry = System.currentTimeMillis() + 7_200_000; + + parcelable.detectionCount = 3; + parcelable.quirkExpiry = expiry; // quirk info will expire in two hours + builder.setIpv6ProvLossQuirk(IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable)); + final NetworkAttributes in = builder.build(); + + final NetworkAttributes out = new NetworkAttributes(parcelingRoundTrip(in.toParcelable())); + assertEquals(out.ipv6ProvisioningLossQuirk, in.ipv6ProvisioningLossQuirk); + } + + private T parcelingRoundTrip(final T in) throws Exception { + final Parcel p = Parcel.obtain(); + in.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + final byte[] marshalledData = p.marshall(); + p.recycle(); + + final Parcel q = Parcel.obtain(); + q.unmarshall(marshalledData, 0, marshalledData.length); + q.setDataPosition(0); + + final Parcelable.Creator creator = (Parcelable.Creator) + in.getClass().getField("CREATOR").get(null); // static object, so null receiver + final T unmarshalled = (T) creator.createFromParcel(q); + q.recycle(); + return unmarshalled; + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java new file mode 100644 index 000000000000..b0a9b8a55322 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2017 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.nsd; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.AsyncChannel; +import com.android.testutils.HandlerUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NsdManagerTest { + + static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; + + @Mock Context mContext; + @Mock INsdManager mService; + MockServiceHandler mServiceHandler; + + NsdManager mManager; + + long mTimeoutMs = 200; // non-final so that tests can adjust the value. + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mServiceHandler = spy(MockServiceHandler.create(mContext)); + when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler)); + + mManager = makeManager(); + } + + @After + public void tearDown() throws Exception { + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); + mServiceHandler.chan.disconnect(); + mServiceHandler.stop(); + if (mManager != null) { + mManager.disconnect(); + } + } + + @Test + public void testResolveService() { + NsdManager manager = mManager; + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); + NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); + + manager.resolveService(request, listener); + int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); + int err = 33; + sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null); + verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); + + manager.resolveService(request, listener); + int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + } + + @Test + public void testParallelResolveService() { + NsdManager manager = mManager; + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); + + NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class); + NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); + + manager.resolveService(request, listener1); + int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); + + manager.resolveService(request, listener2); + int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); + + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply); + + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + } + + @Test + public void testRegisterService() { + NsdManager manager = mManager; + + NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); + request1.setPort(2201); + request2.setPort(2202); + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class); + + // Register two services + manager.registerService(request1, PROTOCOL, listener1); + int key1 = verifyRequest(NsdManager.REGISTER_SERVICE); + + manager.registerService(request2, PROTOCOL, listener2); + int key2 = verifyRequest(NsdManager.REGISTER_SERVICE); + + // First reques fails, second request succeeds + sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2); + verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); + + int err = 1; + sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1); + verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); + + // Client retries first request, it succeeds + manager.registerService(request1, PROTOCOL, listener1); + int key3 = verifyRequest(NsdManager.REGISTER_SERVICE); + + sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1); + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); + + // First request is unregistered, it succeeds + manager.unregisterService(listener1); + int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE); + assertEquals(key3, key3again); + + sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null); + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); + + // Second request is unregistered, it fails + manager.unregisterService(listener2); + int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE); + assertEquals(key2, key2again); + + sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null); + verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); + + // TODO: do not unregister listener until service is unregistered + // Client retries unregistration of second request, it succeeds + //manager.unregisterService(listener2); + //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE); + //assertEquals(key2, key2yetAgain); + + //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null); + //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); + } + + @Test + public void testDiscoverService() { + NsdManager manager = mManager; + + NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); + NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type"); + + NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class); + + // Client registers for discovery, request fails + manager.discoverServices("a_type", PROTOCOL, listener); + int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + int err = 1; + sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null); + verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); + + // Client retries, request succeeds + manager.discoverServices("a_type", PROTOCOL, listener); + int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); + + + // mdns notifies about services + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); + + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); + + sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); + + + // Client unregisters its listener + manager.stopServiceDiscovery(listener); + int key2again = verifyRequest(NsdManager.STOP_DISCOVERY); + assertEquals(key2, key2again); + + // TODO: unregister listener immediately and stop notifying it about services + // Notifications are still passed to the client's listener + sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); + + // Client is notified of complete unregistration + sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type"); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); + + // Notifications are not passed to the client anymore + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3); + verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); + + + // Client registers for service discovery + reset(listener); + manager.discoverServices("a_type", PROTOCOL, listener); + int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); + + // Client unregisters immediately, it fails + manager.stopServiceDiscovery(listener); + int key3again = verifyRequest(NsdManager.STOP_DISCOVERY); + assertEquals(key3, key3again); + + err = 2; + sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type"); + verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); + + // New notifications are not passed to the client anymore + sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1); + verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); + } + + @Test + public void testInvalidCalls() { + NsdManager manager = mManager; + + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); + NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); + + NsdServiceInfo invalidService = new NsdServiceInfo(null, null); + NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type"); + validService.setPort(2222); + + // Service registration + // - invalid arguments + mustFail(() -> { manager.unregisterService(null); }); + mustFail(() -> { manager.registerService(null, -1, null); }); + mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); }); + mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); }); + mustFail(() -> { manager.registerService(validService, -1, listener1); }); + mustFail(() -> { manager.registerService(validService, PROTOCOL, null); }); + manager.registerService(validService, PROTOCOL, listener1); + // - listener already registered + mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); }); + manager.unregisterService(listener1); + // TODO: make listener immediately reusable + //mustFail(() -> { manager.unregisterService(listener1); }); + //manager.registerService(validService, PROTOCOL, listener1); + + // Discover service + // - invalid arguments + mustFail(() -> { manager.stopServiceDiscovery(null); }); + mustFail(() -> { manager.discoverServices(null, -1, null); }); + mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); }); + mustFail(() -> { manager.discoverServices("a_service", -1, listener2); }); + mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); }); + manager.discoverServices("a_service", PROTOCOL, listener2); + // - listener already registered + mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); }); + manager.stopServiceDiscovery(listener2); + // TODO: make listener immediately reusable + //mustFail(() -> { manager.stopServiceDiscovery(listener2); }); + //manager.discoverServices("another_service", PROTOCOL, listener2); + + // Resolver service + // - invalid arguments + mustFail(() -> { manager.resolveService(null, null); }); + mustFail(() -> { manager.resolveService(null, listener3); }); + mustFail(() -> { manager.resolveService(invalidService, listener3); }); + mustFail(() -> { manager.resolveService(validService, null); }); + manager.resolveService(validService, listener3); + // - listener already registered:w + mustFail(() -> { manager.resolveService(validService, listener3); }); + } + + public void mustFail(Runnable fn) { + try { + fn.run(); + fail(); + } catch (Exception expected) { + } + } + + NsdManager makeManager() { + NsdManager manager = new NsdManager(mContext, mService); + // Acknowledge first two messages connecting the AsyncChannel. + verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any()); + reset(mServiceHandler); + assertNotNull(mServiceHandler.chan); + return manager; + } + + int verifyRequest(int expectedMessageType) { + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); + verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); + reset(mServiceHandler); + Message received = mServiceHandler.getLastMessage(); + assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what)); + return received.arg2; + } + + void sendResponse(int replyType, int arg, int key, Object obj) { + mServiceHandler.chan.sendMessage(replyType, arg, key, obj); + } + + // Implements the server side of AsyncChannel connection protocol + public static class MockServiceHandler extends Handler { + public final Context context; + public AsyncChannel chan; + public Message lastMessage; + + MockServiceHandler(Looper l, Context c) { + super(l); + context = c; + } + + synchronized Message getLastMessage() { + return lastMessage; + } + + synchronized void setLastMessage(Message msg) { + lastMessage = obtainMessage(); + lastMessage.copyFrom(msg); + } + + @Override + public void handleMessage(Message msg) { + setLastMessage(msg); + if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) { + chan = new AsyncChannel(); + chan.connect(context, this, msg.replyTo); + chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); + } + } + + void stop() { + getLooper().quitSafely(); + } + + static MockServiceHandler create(Context context) { + HandlerThread t = new HandlerThread("mock-service-handler"); + t.start(); + return new MockServiceHandler(t.getLooper(), context); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java new file mode 100644 index 000000000000..94dfc7515c67 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2014 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.nsd; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.StrictMode; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Map; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NsdServiceInfoTest { + + public final static InetAddress LOCALHOST; + static { + // Because test. + StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(policy); + + InetAddress _host = null; + try { + _host = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { } + LOCALHOST = _host; + } + + @Test + public void testLimits() throws Exception { + NsdServiceInfo info = new NsdServiceInfo(); + + // Non-ASCII keys. + boolean exceptionThrown = false; + try { + info.setAttribute("猫", "meow"); + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + assertEmptyServiceInfo(info); + + // ASCII keys with '=' character. + exceptionThrown = false; + try { + info.setAttribute("kitten=", "meow"); + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + assertEmptyServiceInfo(info); + + // Single key + value length too long. + exceptionThrown = false; + try { + String longValue = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + + "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + + "ooooooooooooooooooooooooooooong"; // 248 characters. + info.setAttribute("longcat", longValue); // Key + value == 255 characters. + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + assertEmptyServiceInfo(info); + + // Total TXT record length too long. + exceptionThrown = false; + int recordsAdded = 0; + try { + for (int i = 100; i < 300; ++i) { + // 6 char key + 5 char value + 2 bytes overhead = 13 byte record length. + String key = String.format("key%d", i); + info.setAttribute(key, "12345"); + recordsAdded++; + } + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + assertTrue(exceptionThrown); + assertTrue(100 == recordsAdded); + assertTrue(info.getTxtRecord().length == 1300); + } + + @Test + public void testParcel() throws Exception { + NsdServiceInfo emptyInfo = new NsdServiceInfo(); + checkParcelable(emptyInfo); + + NsdServiceInfo fullInfo = new NsdServiceInfo(); + fullInfo.setServiceName("kitten"); + fullInfo.setServiceType("_kitten._tcp"); + fullInfo.setPort(4242); + fullInfo.setHost(LOCALHOST); + checkParcelable(fullInfo); + + NsdServiceInfo noHostInfo = new NsdServiceInfo(); + noHostInfo.setServiceName("kitten"); + noHostInfo.setServiceType("_kitten._tcp"); + noHostInfo.setPort(4242); + checkParcelable(noHostInfo); + + NsdServiceInfo attributedInfo = new NsdServiceInfo(); + attributedInfo.setServiceName("kitten"); + attributedInfo.setServiceType("_kitten._tcp"); + attributedInfo.setPort(4242); + attributedInfo.setHost(LOCALHOST); + attributedInfo.setAttribute("color", "pink"); + attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8")); + attributedInfo.setAttribute("adorable", (String) null); + attributedInfo.setAttribute("sticky", "yes"); + attributedInfo.setAttribute("siblings", new byte[] {}); + attributedInfo.setAttribute("edge cases", new byte[] {0, -1, 127, -128}); + attributedInfo.removeAttribute("sticky"); + checkParcelable(attributedInfo); + + // Sanity check that we actually wrote attributes to attributedInfo. + assertTrue(attributedInfo.getAttributes().keySet().contains("adorable")); + String sound = new String(attributedInfo.getAttributes().get("sound"), "UTF-8"); + assertTrue(sound.equals("にゃあ")); + byte[] edgeCases = attributedInfo.getAttributes().get("edge cases"); + assertTrue(Arrays.equals(edgeCases, new byte[] {0, -1, 127, -128})); + assertFalse(attributedInfo.getAttributes().keySet().contains("sticky")); + } + + public void checkParcelable(NsdServiceInfo original) { + // Write to parcel. + Parcel p = Parcel.obtain(); + Bundle writer = new Bundle(); + writer.putParcelable("test_info", original); + writer.writeToParcel(p, 0); + + // Extract from parcel. + p.setDataPosition(0); + Bundle reader = p.readBundle(); + reader.setClassLoader(NsdServiceInfo.class.getClassLoader()); + NsdServiceInfo result = reader.getParcelable("test_info"); + + // Assert equality of base fields. + assertEquals(original.getServiceName(), result.getServiceName()); + assertEquals(original.getServiceType(), result.getServiceType()); + assertEquals(original.getHost(), result.getHost()); + assertTrue(original.getPort() == result.getPort()); + + // Assert equality of attribute map. + Map originalMap = original.getAttributes(); + Map resultMap = result.getAttributes(); + assertEquals(originalMap.keySet(), resultMap.keySet()); + for (String key : originalMap.keySet()) { + assertTrue(Arrays.equals(originalMap.get(key), resultMap.get(key))); + } + } + + public void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) { + byte[] txtRecord = shouldBeEmpty.getTxtRecord(); + if (txtRecord == null || txtRecord.length == 0) { + return; + } + fail("NsdServiceInfo.getTxtRecord did not return null but " + Arrays.toString(txtRecord)); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java new file mode 100644 index 000000000000..b626db8d89e4 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2019 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.util; + +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_GLOBAL; +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_LINKLOCAL; +import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_SITELOCAL; + +import static org.junit.Assert.assertEquals; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.InetAddresses; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DnsUtilsTest { + private InetAddress stringToAddress(@NonNull String addr) { + return InetAddresses.parseNumericAddress(addr); + } + + private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr) { + return makeSortableAddress(addr, null); + } + + private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr, + @Nullable String srcAddr) { + return new DnsUtils.SortableAddress(stringToAddress(addr), + srcAddr != null ? stringToAddress(srcAddr) : null); + } + + @Test + public void testRfc6724Comparator() { + final List test = Arrays.asList( + // Ipv4 + makeSortableAddress("216.58.200.36", "192.168.1.1"), + // global with different scope src + makeSortableAddress("2404:6800:4008:801::2004", "fe80::1111:2222"), + // global without src addr + makeSortableAddress("2404:6800:cafe:801::1"), + // loop back + makeSortableAddress("::1", "::1"), + // link local + makeSortableAddress("fe80::c46f:1cff:fe04:39b4", "fe80::1"), + // teredo tunneling + makeSortableAddress("2001::47c1", "2001::2"), + // 6bone without src addr + makeSortableAddress("3ffe::1234:5678"), + // IPv4-compatible + makeSortableAddress("::216.58.200.36", "::216.58.200.9"), + // 6bone + makeSortableAddress("3ffe::1234:5678", "3ffe::1234:1"), + // IPv4-mapped IPv6 + makeSortableAddress("::ffff:192.168.95.7", "::ffff:192.168.95.1")); + + final List expected = Arrays.asList( + stringToAddress("::1"), // loop back + stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local + stringToAddress("216.58.200.36"), // Ipv4 + stringToAddress("::ffff:192.168.95.7"), // IPv4-mapped IPv6 + stringToAddress("2001::47c1"), // teredo tunneling + stringToAddress("::216.58.200.36"), // IPv4-compatible + stringToAddress("3ffe::1234:5678"), // 6bone + stringToAddress("2404:6800:4008:801::2004"), // global with different scope src + stringToAddress("2404:6800:cafe:801::1"), // global without src addr + stringToAddress("3ffe::1234:5678")); // 6bone without src addr + + Collections.sort(test, new DnsUtils.Rfc6724Comparator()); + + for (int i = 0; i < test.size(); ++i) { + assertEquals(test.get(i).address, expected.get(i)); + } + + // TODO: add more combinations + } + + @Test + public void testV4SortableAddress() { + // Test V4 address + DnsUtils.SortableAddress test = makeSortableAddress("216.58.200.36"); + assertEquals(test.hasSrcAddr, 0); + assertEquals(test.prefixMatchLen, 0); + assertEquals(test.address, stringToAddress("216.58.200.36")); + assertEquals(test.labelMatch, 0); + assertEquals(test.scopeMatch, 0); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 4); + assertEquals(test.precedence, 35); + + // Test V4 loopback address with the same source address + test = makeSortableAddress("127.1.2.3", "127.1.2.3"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.prefixMatchLen, 0); + assertEquals(test.address, stringToAddress("127.1.2.3")); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 4); + assertEquals(test.precedence, 35); + } + + @Test + public void testV6SortableAddress() { + // Test global address + DnsUtils.SortableAddress test = makeSortableAddress("2404:6800:4008:801::2004"); + assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test global address with global source address + test = makeSortableAddress("2404:6800:4008:801::2004", + "2401:fa00:fc:fd00:6d6c:7199:b8e7:41d6"); + assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + assertEquals(test.prefixMatchLen, 13); + + // Test global address with linklocal source address + test = makeSortableAddress("2404:6800:4008:801::2004", "fe80::c46f:1cff:fe04:39b4"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 0); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + assertEquals(test.prefixMatchLen, 0); + + // Test loopback address with the same source address + test = makeSortableAddress("::1", "::1"); + assertEquals(test.hasSrcAddr, 1); + assertEquals(test.prefixMatchLen, 16 * 8); + assertEquals(test.labelMatch, 1); + assertEquals(test.scopeMatch, 1); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 0); + assertEquals(test.precedence, 50); + + // Test linklocal address + test = makeSortableAddress("fe80::c46f:1cff:fe04:39b4"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test linklocal address + test = makeSortableAddress("fe80::"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); + assertEquals(test.label, 1); + assertEquals(test.precedence, 40); + + // Test 6to4 address + test = makeSortableAddress("2002:c000:0204::"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 2); + assertEquals(test.precedence, 30); + + // Test unique local address + test = makeSortableAddress("fc00::c000:13ab"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 13); + assertEquals(test.precedence, 3); + + // Test teredo tunneling address + test = makeSortableAddress("2001::47c1"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 5); + assertEquals(test.precedence, 5); + + // Test IPv4-compatible addresses + test = makeSortableAddress("::216.58.200.36"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 3); + assertEquals(test.precedence, 1); + + // Test site-local address + test = makeSortableAddress("fec0::cafe:3ab2"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_SITELOCAL); + assertEquals(test.label, 11); + assertEquals(test.precedence, 1); + + // Test 6bone address + test = makeSortableAddress("3ffe::1234:5678"); + assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); + assertEquals(test.label, 12); + assertEquals(test.precedence, 1); + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt new file mode 100644 index 000000000000..5006d5345f5f --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2019 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.util + +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityResources +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.MAX_TRANSPORT +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkCapabilities.TRANSPORT_ETHERNET +import android.net.NetworkCapabilities.TRANSPORT_VPN +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import androidx.test.filters.SmallTest +import com.android.internal.R +import org.junit.After +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.any +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock + +/** + * Tests for [KeepaliveUtils]. + * + * Build, install and run with: + * atest android.net.util.KeepaliveUtilsTest + */ +@RunWith(JUnit4::class) +@SmallTest +class KeepaliveUtilsTest { + + // Prepare mocked context with given resource strings. + private fun getMockedContextWithStringArrayRes( + id: Int, + name: String, + res: Array? + ): Context { + val mockRes = mock(Resources::class.java) + doReturn(res).`when`(mockRes).getStringArray(eq(id)) + doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any()) + + return mock(Context::class.java).apply { + doReturn(mockRes).`when`(this).getResources() + ConnectivityResources.setResourcesContextForTest(this) + } + } + + @After + fun tearDown() { + ConnectivityResources.setResourcesContextForTest(null) + } + + @Test + fun testGetSupportedKeepalives() { + fun assertRunWithException(res: Array?) { + try { + val mockContext = getMockedContextWithStringArrayRes( + R.array.config_networkSupportedKeepaliveCount, + "config_networkSupportedKeepaliveCount", res) + KeepaliveUtils.getSupportedKeepalives(mockContext) + fail("Expected KeepaliveDeviceConfigurationException") + } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) { + } + } + + // Check resource with various invalid format. + assertRunWithException(null) + assertRunWithException(arrayOf(null)) + assertRunWithException(arrayOfNulls(10)) + assertRunWithException(arrayOf("")) + assertRunWithException(arrayOf("3,ABC")) + assertRunWithException(arrayOf("6,3,3")) + assertRunWithException(arrayOf("5")) + + // Check resource with invalid slots value. + assertRunWithException(arrayOf("3,-1")) + + // Check resource with invalid transport type. + assertRunWithException(arrayOf("-1,3")) + assertRunWithException(arrayOf("10,3")) + + // Check valid customization generates expected array. + val validRes = arrayOf("0,3", "1,0", "4,4") + val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0) + + val mockContext = getMockedContextWithStringArrayRes( + R.array.config_networkSupportedKeepaliveCount, + "config_networkSupportedKeepaliveCount", validRes) + val actual = KeepaliveUtils.getSupportedKeepalives(mockContext) + assertArrayEquals(expectedValidRes, actual) + } + + @Test + fun testGetSupportedKeepalivesForNetworkCapabilities() { + // Mock customized supported keepalives for each transport type, and assuming: + // 3 for cellular, + // 6 for wifi, + // 0 for others. + val cust = IntArray(MAX_TRANSPORT + 1).apply { + this[TRANSPORT_CELLULAR] = 3 + this[TRANSPORT_WIFI] = 6 + } + + val nc = NetworkCapabilities() + // Check supported keepalives with single transport type. + nc.transportTypes = intArrayOf(TRANSPORT_CELLULAR) + assertEquals(3, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) + + // Check supported keepalives with multiple transport types. + nc.transportTypes = intArrayOf(TRANSPORT_WIFI, TRANSPORT_VPN) + assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) + + // Check supported keepalives with non-customized transport type. + nc.transportTypes = intArrayOf(TRANSPORT_ETHERNET) + assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) + + // Check supported keepalives with undefined transport type. + nc.transportTypes = intArrayOf(MAX_TRANSPORT + 1) + try { + KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc) + fail("Expected ArrayIndexOutOfBoundsException") + } catch (expected: ArrayIndexOutOfBoundsException) { + } + } +} diff --git a/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt new file mode 100644 index 000000000000..25aa6266577e --- /dev/null +++ b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2021 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.util + +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY +import android.net.ConnectivityResources +import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI +import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE +import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener +import android.provider.Settings +import android.telephony.SubscriptionInfo +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.test.mock.MockContentResolver +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.connectivity.resources.R +import com.android.internal.util.test.FakeSettingsProvider +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.argThat +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.any +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +/** + * Tests for [MultinetworkPolicyTracker]. + * + * Build, install and run with: + * atest android.net.util.MultinetworkPolicyTrackerTest + */ +@RunWith(AndroidJUnit4::class) +@SmallTest +class MultinetworkPolicyTrackerTest { + private val resources = mock(Resources::class.java).also { + doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier( + eq("config_networkAvoidBadWifi"), eq("integer"), any()) + doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi) + } + private val telephonyManager = mock(TelephonyManager::class.java) + private val subscriptionManager = mock(SubscriptionManager::class.java).also { + doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt()) + } + private val resolver = MockContentResolver().apply { + addProvider(Settings.AUTHORITY, FakeSettingsProvider()) } + private val context = mock(Context::class.java).also { + doReturn(Context.TELEPHONY_SERVICE).`when`(it) + .getSystemServiceName(TelephonyManager::class.java) + doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE) + doReturn(subscriptionManager).`when`(it) + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) + doReturn(resolver).`when`(it).contentResolver + doReturn(resources).`when`(it).resources + doReturn(it).`when`(it).createConfigurationContext(any()) + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1") + ConnectivityResources.setResourcesContextForTest(it) + } + private val tracker = MultinetworkPolicyTracker(context, null /* handler */) + + private fun assertMultipathPreference(preference: Int) { + Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, + preference.toString()) + tracker.updateMeteredMultipathPreference() + assertEquals(preference, tracker.meteredMultipathPreference) + } + + @After + fun tearDown() { + ConnectivityResources.setResourcesContextForTest(null) + } + + @Test + fun testUpdateMeteredMultipathPreference() { + assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER) + assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY) + assertMultipathPreference(MULTIPATH_PREFERENCE_PERFORMANCE) + } + + @Test + fun testUpdateAvoidBadWifi() { + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") + assertTrue(tracker.updateAvoidBadWifi()) + assertFalse(tracker.avoidBadWifi) + + doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi) + assertTrue(tracker.updateAvoidBadWifi()) + assertTrue(tracker.avoidBadWifi) + } + + @Test + fun testOnActiveDataSubscriptionIdChanged() { + val testSubId = 1000 + val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */, + "TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */, + "123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */, + ""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */, + "1"/* cardString */) + doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId) + + // Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in + // MultinetworkPolicyTracker should be also updated after subId changed. + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") + Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, + MULTIPATH_PREFERENCE_PERFORMANCE.toString()) + + val listenerCaptor = ArgumentCaptor.forClass( + ActiveDataSubscriptionIdListener::class.java) + verify(telephonyManager, times(1)) + .registerTelephonyCallback(any(), listenerCaptor.capture()) + val listener = listenerCaptor.value + listener.onActiveDataSubscriptionIdChanged(testSubId) + + // Check it get resource value with test sub id. + verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId) + verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 }) + + // Check if avoidBadWifi and meteredMultipathPreference values have been updated. + assertFalse(tracker.avoidBadWifi) + assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference) + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java new file mode 100644 index 000000000000..3cfecd552967 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 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.internal.net; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.EPERM; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOCK_STREAM; + +import static junit.framework.Assert.assertEquals; + +import static org.junit.Assert.fail; + +import android.system.ErrnoException; +import android.system.Os; + +import androidx.test.runner.AndroidJUnit4; + +import libcore.io.IoUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@androidx.test.filters.SmallTest +public class NetworkUtilsInternalTest { + + private static void expectSocketSuccess(String msg, int domain, int type) { + try { + IoUtils.closeQuietly(Os.socket(domain, type, 0)); + } catch (ErrnoException e) { + fail(msg + e.getMessage()); + } + } + + private static void expectSocketPemissionError(String msg, int domain, int type) { + try { + IoUtils.closeQuietly(Os.socket(domain, type, 0)); + fail(msg); + } catch (ErrnoException e) { + assertEquals(msg, e.errno, EPERM); + } + } + + private static void expectHasNetworking() { + expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", + AF_UNIX, SOCK_STREAM); + expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException", + AF_INET, SOCK_DGRAM); + expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException", + AF_INET6, SOCK_DGRAM); + } + + private static void expectNoNetworking() { + expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", + AF_UNIX, SOCK_STREAM); + expectSocketPemissionError( + "Creating a AF_INET socket should have thrown ErrnoException(EPERM)", + AF_INET, SOCK_DGRAM); + expectSocketPemissionError( + "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)", + AF_INET6, SOCK_DGRAM); + } + + @Test + public void testSetAllowNetworkingForProcess() { + expectHasNetworking(); + NetworkUtilsInternal.setAllowNetworkingForProcess(false); + expectNoNetworking(); + NetworkUtilsInternal.setAllowNetworkingForProcess(true); + expectHasNetworking(); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java new file mode 100644 index 000000000000..46597d19ef1b --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2019 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.internal.net; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.IpSecAlgorithm; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** Unit tests for {@link VpnProfile}. */ +@SmallTest +@RunWith(JUnit4.class) +public class VpnProfileTest { + private static final String DUMMY_PROFILE_KEY = "Test"; + + private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23; + private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24; + + @Test + public void testDefaults() throws Exception { + final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY); + + assertEquals(DUMMY_PROFILE_KEY, p.key); + assertEquals("", p.name); + assertEquals(VpnProfile.TYPE_PPTP, p.type); + assertEquals("", p.server); + assertEquals("", p.username); + assertEquals("", p.password); + assertEquals("", p.dnsServers); + assertEquals("", p.searchDomains); + assertEquals("", p.routes); + assertTrue(p.mppe); + assertEquals("", p.l2tpSecret); + assertEquals("", p.ipsecIdentifier); + assertEquals("", p.ipsecSecret); + assertEquals("", p.ipsecUserCert); + assertEquals("", p.ipsecCaCert); + assertEquals("", p.ipsecServerCert); + assertEquals(null, p.proxy); + assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty()); + assertFalse(p.isBypassable); + assertFalse(p.isMetered); + assertEquals(1360, p.maxMtu); + assertFalse(p.areAuthParamsInline); + assertFalse(p.isRestrictedToTestNetworks); + } + + private VpnProfile getSampleIkev2Profile(String key) { + final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */); + + p.name = "foo"; + p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + p.server = "bar"; + p.username = "baz"; + p.password = "qux"; + p.dnsServers = "8.8.8.8"; + p.searchDomains = ""; + p.routes = "0.0.0.0/0"; + p.mppe = false; + p.l2tpSecret = ""; + p.ipsecIdentifier = "quux"; + p.ipsecSecret = "quuz"; + p.ipsecUserCert = "corge"; + p.ipsecCaCert = "grault"; + p.ipsecServerCert = "garply"; + p.proxy = null; + p.setAllowedAlgorithms( + Arrays.asList( + IpSecAlgorithm.AUTH_CRYPT_AES_GCM, + IpSecAlgorithm.AUTH_HMAC_SHA512, + IpSecAlgorithm.CRYPT_AES_CBC)); + p.isBypassable = true; + p.isMetered = true; + p.maxMtu = 1350; + p.areAuthParamsInline = true; + + // Not saved, but also not compared. + p.saveLogin = true; + + return p; + } + + @Test + public void testEquals() { + assertEquals( + getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY)); + + final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + modified.maxMtu--; + assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23); + } + + @Test + public void testSetInvalidAlgorithmValueDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetInvalidAlgorithmListDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testEncodeDecode() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertEquals(profile, decoded); + } + + @Test + public void testEncodeDecodeTooManyValues() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final byte[] tooManyValues = + (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes(); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues)); + } + + private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) { + // Sort to ensure when we remove, we can do it from greatest first. + Arrays.sort(missingIndices); + + final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode()); + final List parts = + new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER))); + + // Remove from back first to ensure indexing is consistent. + for (int i = missingIndices.length - 1; i >= 0; i--) { + parts.remove(missingIndices[i]); + } + + return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0])); + } + + @Test + public void testEncodeDecodeInvalidNumberOfValues() { + final String tooFewValues = + getEncodedDecodedIkev2ProfileMissingValues( + ENCODED_INDEX_AUTH_PARAMS_INLINE, + ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes())); + } + + @Test + public void testEncodeDecodeMissingIsRestrictedToTestNetworks() { + final String tooFewValues = + getEncodedDecodedIkev2ProfileMissingValues( + ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); + + // Verify decoding without isRestrictedToTestNetworks defaults to false + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()); + assertFalse(decoded.isRestrictedToTestNetworks); + } + + @Test + public void testEncodeDecodeLoginsNotSaved() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + profile.saveLogin = false; + + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertNotEquals(profile, decoded); + + // Add the username/password back, everything else must be equal. + decoded.username = profile.username; + decoded.password = profile.password; + assertEquals(profile, decoded); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java new file mode 100644 index 000000000000..d2fbdce9771a --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 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.internal.util; + +import static com.android.internal.util.BitUtils.bytesToBEInt; +import static com.android.internal.util.BitUtils.bytesToLEInt; +import static com.android.internal.util.BitUtils.getUint16; +import static com.android.internal.util.BitUtils.getUint32; +import static com.android.internal.util.BitUtils.getUint8; +import static com.android.internal.util.BitUtils.packBits; +import static com.android.internal.util.BitUtils.uint16; +import static com.android.internal.util.BitUtils.uint32; +import static com.android.internal.util.BitUtils.uint8; +import static com.android.internal.util.BitUtils.unpackBits; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Random; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BitUtilsTest { + + @Test + public void testUnsignedByteWideningConversions() { + byte b0 = 0; + byte b1 = 1; + byte bm1 = -1; + assertEquals(0, uint8(b0)); + assertEquals(1, uint8(b1)); + assertEquals(127, uint8(Byte.MAX_VALUE)); + assertEquals(128, uint8(Byte.MIN_VALUE)); + assertEquals(255, uint8(bm1)); + assertEquals(255, uint8((byte)255)); + } + + @Test + public void testUnsignedShortWideningConversions() { + short s0 = 0; + short s1 = 1; + short sm1 = -1; + assertEquals(0, uint16(s0)); + assertEquals(1, uint16(s1)); + assertEquals(32767, uint16(Short.MAX_VALUE)); + assertEquals(32768, uint16(Short.MIN_VALUE)); + assertEquals(65535, uint16(sm1)); + assertEquals(65535, uint16((short)65535)); + } + + @Test + public void testUnsignedShortComposition() { + byte b0 = 0; + byte b1 = 1; + byte b2 = 2; + byte b10 = 10; + byte b16 = 16; + byte b128 = -128; + byte b224 = -32; + byte b255 = -1; + assertEquals(0x0000, uint16(b0, b0)); + assertEquals(0xffff, uint16(b255, b255)); + assertEquals(0x0a01, uint16(b10, b1)); + assertEquals(0x8002, uint16(b128, b2)); + assertEquals(0x01ff, uint16(b1, b255)); + assertEquals(0x80ff, uint16(b128, b255)); + assertEquals(0xe010, uint16(b224, b16)); + } + + @Test + public void testUnsignedIntWideningConversions() { + assertEquals(0, uint32(0)); + assertEquals(1, uint32(1)); + assertEquals(2147483647L, uint32(Integer.MAX_VALUE)); + assertEquals(2147483648L, uint32(Integer.MIN_VALUE)); + assertEquals(4294967295L, uint32(-1)); + assertEquals(4294967295L, uint32((int)4294967295L)); + } + + @Test + public void testBytesToInt() { + assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0))); + assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255))); + assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1))); + assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2))); + assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255))); + assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1))); + + assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0))); + assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1))); + assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255))); + } + + @Test + public void testUnsignedGetters() { + ByteBuffer b = ByteBuffer.allocate(4); + b.putInt(0xffff); + + assertEquals(0x0, getUint8(b, 0)); + assertEquals(0x0, getUint8(b, 1)); + assertEquals(0xff, getUint8(b, 2)); + assertEquals(0xff, getUint8(b, 3)); + + assertEquals(0x0, getUint16(b, 0)); + assertEquals(0xffff, getUint16(b, 2)); + + b.rewind(); + b.putInt(0xffffffff); + assertEquals(0xffffffffL, getUint32(b, 0)); + } + + @Test + public void testBitsPacking() { + BitPackingTestCase[] testCases = { + new BitPackingTestCase(0, ints()), + new BitPackingTestCase(1, ints(0)), + new BitPackingTestCase(2, ints(1)), + new BitPackingTestCase(3, ints(0, 1)), + new BitPackingTestCase(4, ints(2)), + new BitPackingTestCase(6, ints(1, 2)), + new BitPackingTestCase(9, ints(0, 3)), + new BitPackingTestCase(~Long.MAX_VALUE, ints(63)), + new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)), + new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)), + }; + for (BitPackingTestCase tc : testCases) { + int[] got = unpackBits(tc.packedBits); + assertTrue( + "unpackBits(" + + tc.packedBits + + "): expected " + + Arrays.toString(tc.bits) + + " but got " + + Arrays.toString(got), + Arrays.equals(tc.bits, got)); + } + for (BitPackingTestCase tc : testCases) { + long got = packBits(tc.bits); + assertEquals( + "packBits(" + + Arrays.toString(tc.bits) + + "): expected " + + tc.packedBits + + " but got " + + got, + tc.packedBits, + got); + } + + long[] moreTestCases = { + 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(), + }; + for (long l : moreTestCases) { + assertEquals(l, packBits(unpackBits(l))); + } + } + + static byte[] bytes(int b1, int b2, int b3, int b4) { + return new byte[] {b(b1), b(b2), b(b3), b(b4)}; + } + + static byte b(int i) { + return (byte) i; + } + + static int[] ints(int... array) { + return array; + } + + static class BitPackingTestCase { + final int[] bits; + final long packedBits; + + BitPackingTestCase(long packedBits, int[] bits) { + this.bits = bits; + this.packedBits = packedBits; + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java new file mode 100644 index 000000000000..d06095a690cf --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2017 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.internal.util; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RingBufferTest { + + @Test + public void testEmptyRingBuffer() { + RingBuffer buffer = new RingBuffer<>(String.class, 100); + + assertArrayEquals(new String[0], buffer.toArray()); + } + + @Test + public void testIncorrectConstructorArguments() { + try { + RingBuffer buffer = new RingBuffer<>(String.class, -10); + fail("Should not be able to create a negative capacity RingBuffer"); + } catch (IllegalArgumentException expected) { + } + + try { + RingBuffer buffer = new RingBuffer<>(String.class, 0); + fail("Should not be able to create a 0 capacity RingBuffer"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testRingBufferWithNoWrapping() { + RingBuffer buffer = new RingBuffer<>(String.class, 100); + + buffer.append("a"); + buffer.append("b"); + buffer.append("c"); + buffer.append("d"); + buffer.append("e"); + + String[] expected = {"a", "b", "c", "d", "e"}; + assertArrayEquals(expected, buffer.toArray()); + } + + @Test + public void testRingBufferWithCapacity1() { + RingBuffer buffer = new RingBuffer<>(String.class, 1); + + buffer.append("a"); + assertArrayEquals(new String[]{"a"}, buffer.toArray()); + + buffer.append("b"); + assertArrayEquals(new String[]{"b"}, buffer.toArray()); + + buffer.append("c"); + assertArrayEquals(new String[]{"c"}, buffer.toArray()); + + buffer.append("d"); + assertArrayEquals(new String[]{"d"}, buffer.toArray()); + + buffer.append("e"); + assertArrayEquals(new String[]{"e"}, buffer.toArray()); + } + + @Test + public void testRingBufferWithWrapping() { + int capacity = 100; + RingBuffer buffer = new RingBuffer<>(String.class, capacity); + + buffer.append("a"); + buffer.append("b"); + buffer.append("c"); + buffer.append("d"); + buffer.append("e"); + + String[] expected1 = {"a", "b", "c", "d", "e"}; + assertArrayEquals(expected1, buffer.toArray()); + + String[] expected2 = new String[capacity]; + int firstIndex = 0; + int lastIndex = capacity - 1; + + expected2[firstIndex] = "e"; + for (int i = 1; i < capacity; i++) { + buffer.append("x"); + expected2[i] = "x"; + } + assertArrayEquals(expected2, buffer.toArray()); + + buffer.append("x"); + expected2[firstIndex] = "x"; + assertArrayEquals(expected2, buffer.toArray()); + + for (int i = 0; i < 10; i++) { + for (String s : expected2) { + buffer.append(s); + } + } + assertArrayEquals(expected2, buffer.toArray()); + + buffer.append("a"); + expected2[lastIndex] = "a"; + assertArrayEquals(expected2, buffer.toArray()); + } + + @Test + public void testGetNextSlot() { + int capacity = 100; + RingBuffer buffer = new RingBuffer<>(DummyClass1.class, capacity); + + final DummyClass1[] actual = new DummyClass1[capacity]; + final DummyClass1[] expected = new DummyClass1[capacity]; + for (int i = 0; i < capacity; ++i) { + final DummyClass1 obj = buffer.getNextSlot(); + obj.x = capacity * i; + actual[i] = obj; + expected[i] = new DummyClass1(); + expected[i].x = capacity * i; + } + assertArrayEquals(expected, buffer.toArray()); + + for (int i = 0; i < capacity; ++i) { + if (actual[i] != buffer.getNextSlot()) { + fail("getNextSlot() should re-use objects if available"); + } + } + + RingBuffer buffer2 = new RingBuffer<>(DummyClass2.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(No nullary constructor)", buffer2.getNextSlot()); + + RingBuffer buffer3 = new RingBuffer<>(DummyClass3.class, capacity); + assertNull("getNextSlot() should return null if the object can't be initiated " + + "(Inaccessible class)", buffer3.getNextSlot()); + } + + public static final class DummyClass1 { + int x; + + public boolean equals(Object o) { + if (o instanceof DummyClass1) { + final DummyClass1 other = (DummyClass1) o; + return other.x == this.x; + } + return false; + } + } + + public static final class DummyClass2 { + public DummyClass2(int x) {} + } + + private static final class DummyClass3 {} +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java new file mode 100644 index 000000000000..63501d7662ed --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -0,0 +1,12792 @@ +/* + * Copyright (C) 2012 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 static android.Manifest.permission.CHANGE_NETWORK_STATE; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.LOCAL_MAC_ADDRESS; +import static android.Manifest.permission.NETWORK_FACTORY; +import static android.Manifest.permission.NETWORK_SETTINGS; +import static android.app.PendingIntent.FLAG_IMMUTABLE; +import static android.content.Intent.ACTION_PACKAGE_ADDED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static android.content.Intent.ACTION_PACKAGE_REPLACED; +import static android.content.Intent.ACTION_USER_ADDED; +import static android.content.Intent.ACTION_USER_REMOVED; +import static android.content.Intent.ACTION_USER_UNLOCKED; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; +import static android.content.pm.PackageManager.FEATURE_WIFI; +import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; +import static android.content.pm.PackageManager.GET_PERMISSIONS; +import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; +import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE; +import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT; +import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; +import static android.net.ConnectivityManager.TYPE_ETHERNET; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; +import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; +import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; +import static android.net.ConnectivityManager.TYPE_PROXY; +import static android.net.ConnectivityManager.TYPE_VPN; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_BIP; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; +import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VSIM; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; +import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; +import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; +import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_NONE; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; +import static android.net.NetworkScore.KEEP_CONNECTED_FOR_HANDOVER; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; +import static android.net.RouteInfo.RTN_UNREACHABLE; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_REMOVED; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; +import static android.os.Process.INVALID_UID; +import static android.system.OsConstants.IPPROTO_TCP; + +import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; +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; +import static com.android.testutils.MiscAsserts.assertRunsInAtMost; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.AdditionalMatchers.aryEq; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AlarmManager; +import android.app.AppOpsManager; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.usage.NetworkStatsManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.location.LocationManager; +import android.net.CaptivePortalData; +import android.net.ConnectionInfo; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.ConnectivityManager.PacketKeepalive; +import android.net.ConnectivityManager.PacketKeepaliveCallback; +import android.net.ConnectivityManager.TooManyRequestsException; +import android.net.ConnectivityResources; +import android.net.ConnectivitySettingsManager; +import android.net.ConnectivityThread; +import android.net.DataStallReportParcelable; +import android.net.EthernetManager; +import android.net.IConnectivityDiagnosticsCallback; +import android.net.IDnsResolver; +import android.net.INetd; +import android.net.INetworkMonitor; +import android.net.INetworkMonitorCallbacks; +import android.net.IOnCompleteListener; +import android.net.IQosCallback; +import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; +import android.net.IpPrefix; +import android.net.IpSecManager; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.MatchAllNetworkSpecifier; +import android.net.NativeNetworkConfig; +import android.net.NativeNetworkType; +import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; +import android.net.NetworkCapabilities; +import android.net.NetworkFactory; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.NetworkPolicyManager; +import android.net.NetworkPolicyManager.NetworkPolicyCallback; +import android.net.NetworkRequest; +import android.net.NetworkScore; +import android.net.NetworkSpecifier; +import android.net.NetworkStack; +import android.net.NetworkStateSnapshot; +import android.net.NetworkTestResultParcelable; +import android.net.OemNetworkPreferences; +import android.net.ProxyInfo; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosSession; +import android.net.ResolverParamsParcel; +import android.net.RouteInfo; +import android.net.RouteInfoParcel; +import android.net.SocketKeepalive; +import android.net.TransportInfo; +import android.net.UidRange; +import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; +import android.net.Uri; +import android.net.VpnManager; +import android.net.VpnTransportInfo; +import android.net.metrics.IpConnectivityLog; +import android.net.networkstack.NetworkStackClientBase; +import android.net.resolv.aidl.Nat64PrefixEventParcel; +import android.net.resolv.aidl.PrivateDnsValidationEventParcel; +import android.net.shared.NetworkMonitorUtils; +import android.net.shared.PrivateDnsConfig; +import android.net.util.MultinetworkPolicyTracker; +import android.os.BadParcelableException; +import android.os.Binder; +import android.os.Build; +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; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.os.SystemClock; +import android.os.SystemConfigManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.security.Credentials; +import android.system.Os; +import android.telephony.TelephonyManager; +import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; +import android.test.mock.MockContentResolver; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Log; +import android.util.Pair; +import android.util.Range; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.connectivity.resources.R; +import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +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.net.module.util.ArrayTrackRecord; +import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; +import com.android.server.connectivity.MockableSystemProperties; +import com.android.server.connectivity.Nat464Xlat; +import com.android.server.connectivity.NetworkAgentInfo; +import com.android.server.connectivity.NetworkNotificationManager.NotificationType; +import com.android.server.connectivity.ProxyTracker; +import com.android.server.connectivity.QosCallbackTracker; +import com.android.server.connectivity.Vpn; +import com.android.server.connectivity.VpnProfileStore; +import com.android.server.net.NetworkPinner; +import com.android.testutils.ExceptionUtils; +import com.android.testutils.HandlerUtils; +import com.android.testutils.RecorderCallback.CallbackEntry; +import com.android.testutils.TestableNetworkCallback; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.stubbing.Answer; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.DatagramSocket; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import kotlin.reflect.KClass; + +/** + * Tests for {@link ConnectivityService}. + * + * Build, install and run with: + * runtest frameworks-net -c com.android.server.ConnectivityServiceTest + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ConnectivityServiceTest { + private static final String TAG = "ConnectivityServiceTest"; + + private static final int TIMEOUT_MS = 500; + // Broadcasts can take a long time to be delivered. The test will not wait for that long unless + // there is a failure, so use a long timeout. + private static final int BROADCAST_TIMEOUT_MS = 30_000; + private static final int TEST_LINGER_DELAY_MS = 400; + private static final int TEST_NASCENT_DELAY_MS = 300; + // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish + // between a LOST callback that arrives immediately and a LOST callback that arrives after + // the linger/nascent timeout. For this, our assertions should run fast enough to leave + // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are + // supposedly fired, and the time we call expectCallback. + private static final int TEST_CALLBACK_TIMEOUT_MS = 250; + // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to + // complete before callbacks are verified. + private static final int TEST_REQUEST_TIMEOUT_MS = 150; + + private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000; + + private static final long TIMESTAMP = 1234L; + + private static final int NET_ID = 110; + private static final int OEM_PREF_ANY_NET_ID = -1; + // Set a non-zero value to verify the flow to set tcp init rwnd value. + private static final int TEST_TCP_INIT_RWND = 60; + + // Used for testing the per-work-profile default network. + private static final int TEST_APP_ID = 103; + private static final int TEST_WORK_PROFILE_USER_ID = 2; + private static final int TEST_WORK_PROFILE_APP_UID = + UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID); + private static final String CLAT_PREFIX = "v4-"; + private static final String MOBILE_IFNAME = "test_rmnet_data0"; + private static final String WIFI_IFNAME = "test_wlan0"; + private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; + private static final String VPN_IFNAME = "tun10042"; + private static final String TEST_PACKAGE_NAME = "com.android.test.package"; + private static final int TEST_PACKAGE_UID = 123; + private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn"; + + private static final String INTERFACE_NAME = "interface"; + + private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/"; + private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT = + "https://android.com/terms/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER = + "https://example.com/terms/"; + private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; + private static final String TEST_USER_PORTAL_API_URL_CAPPORT = + "https://android.com/user/api/capport/"; + private static final String TEST_FRIENDLY_NAME = "Network friendly name"; + private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; + + private MockContext mServiceContext; + private HandlerThread mCsHandlerThread; + private HandlerThread mVMSHandlerThread; + private ConnectivityService.Dependencies mDeps; + private ConnectivityService mService; + private WrappedConnectivityManager mCm; + private TestNetworkAgentWrapper mWiFiNetworkAgent; + private TestNetworkAgentWrapper mCellNetworkAgent; + private TestNetworkAgentWrapper mEthernetNetworkAgent; + private MockVpn mMockVpn; + private Context mContext; + private NetworkPolicyCallback mPolicyCallback; + private WrappedMultinetworkPolicyTracker mPolicyTracker; + private HandlerThread mAlarmManagerThread; + private TestNetIdManager mNetIdManager; + private QosCallbackMockHelper mQosCallbackMockHelper; + private QosCallbackTracker mQosCallbackTracker; + private VpnManagerService mVpnManagerService; + private TestNetworkCallback mDefaultNetworkCallback; + private TestNetworkCallback mSystemDefaultNetworkCallback; + private TestNetworkCallback mProfileDefaultNetworkCallback; + + // State variables required to emulate NetworkPolicyManagerService behaviour. + private int mBlockedReasons = BLOCKED_REASON_NONE; + + @Mock DeviceIdleInternal mDeviceIdleInternal; + @Mock INetworkManagementService mNetworkManagementService; + @Mock NetworkStatsManager mStatsManager; + @Mock IDnsResolver mMockDnsResolver; + @Mock INetd mMockNetd; + @Mock NetworkStackClientBase mNetworkStack; + @Mock PackageManager mPackageManager; + @Mock UserManager mUserManager; + @Mock NotificationManager mNotificationManager; + @Mock AlarmManager mAlarmManager; + @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback; + @Mock IBinder mIBinder; + @Mock LocationManager mLocationManager; + @Mock AppOpsManager mAppOpsManager; + @Mock TelephonyManager mTelephonyManager; + @Mock MockableSystemProperties mSystemProperties; + @Mock EthernetManager mEthernetManager; + @Mock NetworkPolicyManager mNetworkPolicyManager; + @Mock VpnProfileStore mVpnProfileStore; + @Mock SystemConfigManager mSystemConfigManager; + @Mock Resources mResources; + + private ArgumentCaptor mResolverParamsParcelCaptor = + ArgumentCaptor.forClass(ResolverParamsParcel.class); + + // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods + // do not go through ConnectivityService but talk to netd directly, so they don't automatically + // reflect the state of our test ConnectivityService. + private class WrappedConnectivityManager extends ConnectivityManager { + private Network mFakeBoundNetwork; + + public synchronized boolean bindProcessToNetwork(Network network) { + mFakeBoundNetwork = network; + return true; + } + + public synchronized Network getBoundNetworkForProcess() { + return mFakeBoundNetwork; + } + + public WrappedConnectivityManager(Context context, ConnectivityService service) { + super(context, service); + } + } + + private class MockContext extends BroadcastInterceptingContext { + private final MockContentResolver mContentResolver; + + @Spy private Resources mInternalResources; + private final LinkedBlockingQueue mStartedActivities = new LinkedBlockingQueue<>(); + + // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant + private final HashMap mMockedPermissions = new HashMap<>(); + + MockContext(Context base, ContentProvider settingsProvider) { + super(base); + + mInternalResources = spy(base.getResources()); + when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes)) + .thenReturn(new String[] { + "wifi,1,1,1,-1,true", + "mobile,0,0,0,-1,true", + "mobile_mms,2,0,2,60000,true", + "mobile_supl,3,0,2,60000,true", + }); + + mContentResolver = new MockContentResolver(); + mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); + } + + @Override + public void startActivityAsUser(Intent intent, UserHandle handle) { + mStartedActivities.offer(intent); + } + + public Intent expectStartActivityIntent(int timeoutMs) { + Intent intent = null; + try { + intent = mStartedActivities.poll(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) {} + assertNotNull("Did not receive sign-in intent after " + timeoutMs + "ms", intent); + return intent; + } + + public void expectNoStartActivityIntent(int timeoutMs) { + try { + assertNull("Received unexpected Intent to start activity", + mStartedActivities.poll(timeoutMs, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) {} + } + + @Override + public ComponentName startService(Intent service) { + final String action = service.getAction(); + if (!VpnConfig.SERVICE_INTERFACE.equals(action)) { + fail("Attempt to start unknown service, action=" + action); + } + return new ComponentName(service.getPackage(), "com.android.test.Service"); + } + + @Override + public Object getSystemService(String name) { + if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; + if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager; + if (Context.USER_SERVICE.equals(name)) return mUserManager; + if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager; + if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager; + if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager; + if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; + if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; + if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; + if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager; + if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; + return super.getSystemService(name); + } + + final HashMap mUserManagers = new HashMap<>(); + @Override + public Context createContextAsUser(UserHandle user, int flags) { + final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this)); + doReturn(user).when(asUser).getUser(); + doAnswer((inv) -> { + final UserManager um = mUserManagers.computeIfAbsent(user, + u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager))); + return um; + }).when(asUser).getSystemService(Context.USER_SERVICE); + return asUser; + } + + public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) { + // This relies on all contexts for a given user returning the same UM mock + final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */) + .getSystemService(UserManager.class); + doReturn(value).when(umMock).isManagedProfile(); + doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier())); + } + + @Override + public ContentResolver getContentResolver() { + return mContentResolver; + } + + @Override + public Resources getResources() { + return mInternalResources; + } + + @Override + public PackageManager getPackageManager() { + return mPackageManager; + } + + private int checkMockedPermission(String permission, Supplier ifAbsent) { + final Integer granted = mMockedPermissions.get(permission); + return granted != null ? granted : ifAbsent.get(); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + return checkMockedPermission( + permission, () -> super.checkPermission(permission, pid, uid)); + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + return checkMockedPermission( + permission, () -> super.checkCallingOrSelfPermission(permission)); + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + final Integer granted = mMockedPermissions.get(permission); + if (granted == null) { + super.enforceCallingOrSelfPermission(permission, message); + return; + } + + if (!granted.equals(PERMISSION_GRANTED)) { + throw new SecurityException("[Test] permission denied: " + permission); + } + } + + /** + * Mock checks for the specified permission, and have them behave as per {@code granted}. + * + *

Passing null reverts to default behavior, which does a real permission check on the + * test package. + * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or + * {@link PackageManager#PERMISSION_DENIED}. + */ + public void setPermission(String permission, Integer granted) { + mMockedPermissions.put(permission, granted); + } + } + + private void waitForIdle() { + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + waitForIdle(mCellNetworkAgent, TIMEOUT_MS); + waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS); + waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); + } + + private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) { + if (agent == null) { + return; + } + agent.waitForIdle(timeoutMs); + } + + @Test + public void testWaitForIdle() throws Exception { + final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. + + // Tests that waitForIdle returns immediately if the service is already idle. + for (int i = 0; i < attempts; i++) { + waitForIdle(); + } + + // Bring up a network that we can use to send messages to ConnectivityService. + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + Network n = mWiFiNetworkAgent.getNetwork(); + assertNotNull(n); + + // Tests that calling waitForIdle waits for messages to be processed. + for (int i = 0; i < attempts; i++) { + mWiFiNetworkAgent.setSignalStrength(i); + waitForIdle(); + assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength()); + } + } + + // This test has an inherent race condition in it, and cannot be enabled for continuous testing + // or presubmit tests. It is kept for manual runs and documentation purposes. + @Ignore + public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { + // Bring up a network that we can use to send messages to ConnectivityService. + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + Network n = mWiFiNetworkAgent.getNetwork(); + assertNotNull(n); + + // Ensure that not calling waitForIdle causes a race condition. + final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. + for (int i = 0; i < attempts; i++) { + mWiFiNetworkAgent.setSignalStrength(i); + if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) { + // We hit a race condition, as expected. Pass the test. + return; + } + } + + // No race? There is a bug in this test. + fail("expected race condition at least once in " + attempts + " attempts"); + } + + private class TestNetworkAgentWrapper extends NetworkAgentWrapper { + private static final int VALIDATION_RESULT_INVALID = 0; + + private static final long DATA_STALL_TIMESTAMP = 10L; + private static final int DATA_STALL_DETECTION_METHOD = 1; + + private INetworkMonitor mNetworkMonitor; + private INetworkMonitorCallbacks mNmCallbacks; + private int mNmValidationResult = VALIDATION_RESULT_INVALID; + private int mProbesCompleted; + private int mProbesSucceeded; + private String mNmValidationRedirectUrl = null; + private boolean mNmProvNotificationRequested = false; + private Runnable mCreatedCallback; + private Runnable mUnwantedCallback; + private Runnable mDisconnectedCallback; + + private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); + // Contains the redirectUrl from networkStatus(). Before reading, wait for + // mNetworkStatusReceived. + private String mRedirectUrl; + + TestNetworkAgentWrapper(int transport) throws Exception { + this(transport, new LinkProperties(), null); + } + + TestNetworkAgentWrapper(int transport, LinkProperties linkProperties) + throws Exception { + this(transport, linkProperties, null); + } + + private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate) throws Exception { + super(transport, linkProperties, ncTemplate, mServiceContext); + + // Waits for the NetworkAgent to be registered, which includes the creation of the + // NetworkMonitor. + waitForIdle(TIMEOUT_MS); + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); + } + + @Override + protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties, + NetworkAgentConfig nac) throws Exception { + mNetworkMonitor = mock(INetworkMonitor.class); + + final Answer validateAnswer = inv -> { + new Thread(ignoreExceptions(this::onValidationRequested)).start(); + return null; + }; + + doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any()); + doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt()); + + final ArgumentCaptor nmNetworkCaptor = ArgumentCaptor.forClass(Network.class); + final ArgumentCaptor nmCbCaptor = + ArgumentCaptor.forClass(INetworkMonitorCallbacks.class); + doNothing().when(mNetworkStack).makeNetworkMonitor( + nmNetworkCaptor.capture(), + any() /* name */, + nmCbCaptor.capture()); + + final InstrumentedNetworkAgent na = + new InstrumentedNetworkAgent(this, linkProperties, nac) { + @Override + public void networkStatus(int status, String redirectUrl) { + mRedirectUrl = redirectUrl; + mNetworkStatusReceived.open(); + } + + @Override + public void onNetworkCreated() { + super.onNetworkCreated(); + if (mCreatedCallback != null) mCreatedCallback.run(); + } + + @Override + public void onNetworkUnwanted() { + super.onNetworkUnwanted(); + if (mUnwantedCallback != null) mUnwantedCallback.run(); + } + + @Override + public void onNetworkDestroyed() { + super.onNetworkDestroyed(); + if (mDisconnectedCallback != null) mDisconnectedCallback.run(); + } + }; + + assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId); + mNmCallbacks = nmCbCaptor.getValue(); + + mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor); + + return na; + } + + private void onValidationRequested() throws Exception { + if (mNmProvNotificationRequested + && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) { + mNmCallbacks.hideProvisioningNotification(); + mNmProvNotificationRequested = false; + } + + mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded); + final NetworkTestResultParcelable p = new NetworkTestResultParcelable(); + p.result = mNmValidationResult; + p.probesAttempted = mProbesCompleted; + p.probesSucceeded = mProbesSucceeded; + p.redirectUrl = mNmValidationRedirectUrl; + p.timestampMillis = TIMESTAMP; + mNmCallbacks.notifyNetworkTestedWithExtras(p); + + if (mNmValidationRedirectUrl != null) { + mNmCallbacks.showProvisioningNotification( + "test_provisioning_notif_action", TEST_PACKAGE_NAME); + mNmProvNotificationRequested = true; + } + } + + /** + * Connect without adding any internet capability. + */ + public void connectWithoutInternet() { + super.connect(); + } + + /** + * Transition this NetworkAgent to CONNECTED state with NET_CAPABILITY_INTERNET. + * @param validated Indicate if network should pretend to be validated. + */ + public void connect(boolean validated) { + connect(validated, true, false /* isStrictMode */); + } + + /** + * Transition this NetworkAgent to CONNECTED state. + * @param validated Indicate if network should pretend to be validated. + * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET. + */ + public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { + assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET)); + + ConnectivityManager.NetworkCallback callback = null; + final ConditionVariable validatedCv = new ConditionVariable(); + if (validated) { + setNetworkValid(isStrictMode); + NetworkRequest request = new NetworkRequest.Builder() + .addTransportType(getNetworkCapabilities().getTransportTypes()[0]) + .clearCapabilities() + .build(); + callback = new ConnectivityManager.NetworkCallback() { + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + if (network.equals(getNetwork()) && + networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { + validatedCv.open(); + } + } + }; + mCm.registerNetworkCallback(request, callback); + } + if (hasInternet) { + addCapability(NET_CAPABILITY_INTERNET); + } + + connectWithoutInternet(); + + if (validated) { + // Wait for network to validate. + waitFor(validatedCv); + setNetworkInvalid(isStrictMode); + } + + if (callback != null) mCm.unregisterNetworkCallback(callback); + } + + public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) { + setNetworkPortal(redirectUrl, isStrictMode); + connect(false, true /* hasInternet */, isStrictMode); + } + + public void connectWithPartialConnectivity() { + setNetworkPartial(); + connect(false); + } + + public void connectWithPartialValidConnectivity(boolean isStrictMode) { + setNetworkPartialValid(isStrictMode); + connect(false, true /* hasInternet */, isStrictMode); + } + + void setNetworkValid(boolean isStrictMode) { + mNmValidationResult = NETWORK_VALIDATION_RESULT_VALID; + mNmValidationRedirectUrl = null; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS; + if (isStrictMode) { + probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + // The probesCompleted equals to probesSucceeded for the case of valid network, so put + // the same value into two different parameter of the method. + setProbesStatus(probesSucceeded, probesSucceeded); + } + + void setNetworkInvalid(boolean isStrictMode) { + mNmValidationResult = VALIDATION_RESULT_INVALID; + mNmValidationRedirectUrl = null; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = 0; + // If the isStrictMode is true, it means the network is invalid when NetworkMonitor + // tried to validate the private DNS but failed. + if (isStrictMode) { + probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP; + probesSucceeded = probesCompleted; + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); + } + + void setNetworkPortal(String redirectUrl, boolean isStrictMode) { + setNetworkInvalid(isStrictMode); + mNmValidationRedirectUrl = redirectUrl; + // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP + // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet. + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = VALIDATION_RESULT_INVALID; + if (isStrictMode) { + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); + } + + void setNetworkPartial() { + mNmValidationResult = NETWORK_VALIDATION_RESULT_PARTIAL; + mNmValidationRedirectUrl = null; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_FALLBACK; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK; + setProbesStatus(probesCompleted, probesSucceeded); + } + + void setNetworkPartialValid(boolean isStrictMode) { + setNetworkPartial(); + mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID; + int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS + | NETWORK_VALIDATION_PROBE_HTTP; + int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; + // Suppose the partial network cannot pass the private DNS validation as well, so only + // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded. + if (isStrictMode) { + probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; + } + setProbesStatus(probesCompleted, probesSucceeded); + } + + void setProbesStatus(int probesCompleted, int probesSucceeded) { + mProbesCompleted = probesCompleted; + mProbesSucceeded = probesSucceeded; + } + + void notifyCapportApiDataChanged(CaptivePortalData data) { + try { + mNmCallbacks.notifyCaptivePortalDataChanged(data); + } catch (RemoteException e) { + throw new AssertionError("This cannot happen", e); + } + } + + public String waitForRedirectUrl() { + assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS)); + return mRedirectUrl; + } + + public void expectDisconnected() { + expectDisconnected(TIMEOUT_MS); + } + + public void expectPreventReconnectReceived() { + expectPreventReconnectReceived(TIMEOUT_MS); + } + + void notifyDataStallSuspected() throws Exception { + final DataStallReportParcelable p = new DataStallReportParcelable(); + p.detectionMethod = DATA_STALL_DETECTION_METHOD; + p.timestampMillis = DATA_STALL_TIMESTAMP; + mNmCallbacks.notifyDataStallSuspected(p); + } + + public void setCreatedCallback(Runnable r) { + mCreatedCallback = r; + } + + public void setUnwantedCallback(Runnable r) { + mUnwantedCallback = r; + } + + public void setDisconnectedCallback(Runnable r) { + mDisconnectedCallback = r; + } + } + + /** + * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove + * operations have been processed and test for them. + */ + private static class MockNetworkFactory extends NetworkFactory { + private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); + + static class RequestEntry { + @NonNull + public final NetworkRequest request; + + RequestEntry(@NonNull final NetworkRequest request) { + this.request = request; + } + + static final class Add extends RequestEntry { + Add(@NonNull final NetworkRequest request) { + super(request); + } + } + + static final class Remove extends RequestEntry { + Remove(@NonNull final NetworkRequest request) { + super(request); + } + } + + @Override + public String toString() { + return "RequestEntry [ " + getClass().getName() + " : " + request + " ]"; + } + } + + // History of received requests adds and removes. + private final ArrayTrackRecord.ReadHead mRequestHistory = + new ArrayTrackRecord().newReadHead(); + + private static T failIfNull(@Nullable final T obj, @Nullable final String message) { + if (null == obj) fail(null != message ? message : "Must not be null"); + return obj; + } + + public RequestEntry.Add expectRequestAdd() { + return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS, + it -> it instanceof RequestEntry.Add), "Expected request add"); + } + + public void expectRequestAdds(final int count) { + for (int i = count; i > 0; --i) { + expectRequestAdd(); + } + } + + public RequestEntry.Remove expectRequestRemove() { + return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS, + it -> it instanceof RequestEntry.Remove), "Expected request remove"); + } + + public void expectRequestRemoves(final int count) { + for (int i = count; i > 0; --i) { + expectRequestRemove(); + } + } + + // Used to collect the networks requests managed by this factory. This is a duplicate of + // the internal information stored in the NetworkFactory (which is private). + private SparseArray mNetworkRequests = new SparseArray<>(); + private final HandlerThread mHandlerSendingRequests; + + public MockNetworkFactory(Looper looper, Context context, String logTag, + NetworkCapabilities filter, HandlerThread threadSendingRequests) { + super(looper, context, logTag, filter); + mHandlerSendingRequests = threadSendingRequests; + } + + public int getMyRequestCount() { + return getRequestCount(); + } + + protected void startNetwork() { + mNetworkStarted.set(true); + } + + protected void stopNetwork() { + mNetworkStarted.set(false); + } + + public boolean getMyStartRequested() { + return mNetworkStarted.get(); + } + + + @Override + protected void needNetworkFor(NetworkRequest request) { + mNetworkRequests.put(request.requestId, request); + super.needNetworkFor(request); + mRequestHistory.add(new RequestEntry.Add(request)); + } + + @Override + protected void releaseNetworkFor(NetworkRequest request) { + mNetworkRequests.remove(request.requestId); + super.releaseNetworkFor(request); + mRequestHistory.add(new RequestEntry.Remove(request)); + } + + public void assertRequestCountEquals(final int count) { + assertEquals(count, getMyRequestCount()); + } + + @Override + public void terminate() { + super.terminate(); + // Make sure there are no remaining requests unaccounted for. + HandlerUtils.waitForIdle(mHandlerSendingRequests, TIMEOUT_MS); + assertNull(mRequestHistory.poll(0, r -> true)); + } + + // Trigger releasing the request as unfulfillable + public void triggerUnfulfillable(NetworkRequest r) { + super.releaseRequestAsUnfulfillableByAnyFactory(r); + } + + public void assertNoRequestChanged() { + assertNull(mRequestHistory.poll(0, r -> true)); + } + } + + private Set uidRangesForUids(int... uids) { + final ArraySet ranges = new ArraySet<>(); + for (final int uid : uids) { + ranges.add(new UidRange(uid, uid)); + } + return ranges; + } + + private static Looper startHandlerThreadAndReturnLooper() { + final HandlerThread handlerThread = new HandlerThread("MockVpnThread"); + handlerThread.start(); + return handlerThread.getLooper(); + } + + private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork { + // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does + // not inherit from NetworkAgent. + private TestNetworkAgentWrapper mMockNetworkAgent; + private boolean mAgentRegistered = false; + + private int mVpnType = VpnManager.TYPE_VPN_SERVICE; + private UnderlyingNetworkInfo mUnderlyingNetworkInfo; + + // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started. + // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the + // test expects two starts in a row, or even if the production code calls start twice in a + // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into + // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has + // extensive access into the internals of Vpn. + private ConditionVariable mStartLegacyVpnCv = new ConditionVariable(); + private ConditionVariable mStopVpnRunnerCv = new ConditionVariable(); + + public MockVpn(int userId) { + super(startHandlerThreadAndReturnLooper(), mServiceContext, + new Dependencies() { + @Override + public boolean isCallerSystem() { + return true; + } + + @Override + public DeviceIdleInternal getDeviceIdleInternal() { + return mDeviceIdleInternal; + } + }, + mNetworkManagementService, mMockNetd, userId, mVpnProfileStore); + } + + public void setUids(Set uids) { + mNetworkCapabilities.setUids(UidRange.toIntRanges(uids)); + if (mAgentRegistered) { + mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true); + } + } + + public void setVpnType(int vpnType) { + mVpnType = vpnType; + } + + @Override + public Network getNetwork() { + return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork(); + } + + @Override + public int getActiveVpnType() { + return mVpnType; + } + + private LinkProperties makeLinkProperties() { + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(VPN_IFNAME); + return lp; + } + + private void registerAgent(boolean isAlwaysMetered, Set uids, LinkProperties lp) + throws Exception { + if (mAgentRegistered) throw new IllegalStateException("already registered"); + updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent"); + mConfig = new VpnConfig(); + mConfig.session = "MySession12345"; + setUids(uids); + if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); + mInterface = VPN_IFNAME; + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), + mConfig.session)); + mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, + mNetworkCapabilities); + mMockNetworkAgent.waitForIdle(TIMEOUT_MS); + + verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), + eq(toUidRangeStableParcels(uids))); + verify(mMockNetd, never()) + .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any()); + mAgentRegistered = true; + verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId, + !mMockNetworkAgent.isBypassableVpn(), mVpnType)); + updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent"); + mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); + mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); + } + + private void registerAgent(Set uids) throws Exception { + registerAgent(false /* isAlwaysMetered */, uids, makeLinkProperties()); + } + + private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { + mMockNetworkAgent.connect(validated, hasInternet, isStrictMode); + } + + private void connect(boolean validated) { + mMockNetworkAgent.connect(validated); + } + + private TestNetworkAgentWrapper getAgent() { + return mMockNetworkAgent; + } + + public void establish(LinkProperties lp, int uid, Set ranges, boolean validated, + boolean hasInternet, boolean isStrictMode) throws Exception { + mNetworkCapabilities.setOwnerUid(uid); + mNetworkCapabilities.setAdministratorUids(new int[]{uid}); + registerAgent(false, ranges, lp); + connect(validated, hasInternet, isStrictMode); + waitForIdle(); + } + + public void establish(LinkProperties lp, int uid, Set ranges) throws Exception { + establish(lp, uid, ranges, true, true, false); + } + + public void establishForMyUid(LinkProperties lp) throws Exception { + final int uid = Process.myUid(); + establish(lp, uid, uidRangesForUids(uid), true, true, false); + } + + public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode) + throws Exception { + final int uid = Process.myUid(); + establish(makeLinkProperties(), uid, uidRangesForUids(uid), validated, hasInternet, + isStrictMode); + } + + public void establishForMyUid() throws Exception { + establishForMyUid(makeLinkProperties()); + } + + public void sendLinkProperties(LinkProperties lp) { + mMockNetworkAgent.sendLinkProperties(lp); + } + + public void disconnect() { + if (mMockNetworkAgent != null) { + mMockNetworkAgent.disconnect(); + updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect"); + } + mAgentRegistered = false; + setUids(null); + // Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on. + mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); + mInterface = null; + } + + @Override + public void startLegacyVpnRunner() { + mStartLegacyVpnCv.open(); + } + + public void expectStartLegacyVpnRunner() { + assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms", + mStartLegacyVpnCv.block(TIMEOUT_MS)); + + // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just + // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect + // that the VpnRunner is stopped and immediately restarted by calling + // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back. + mStopVpnRunnerCv = new ConditionVariable(); + } + + @Override + public void stopVpnRunnerPrivileged() { + if (mVpnRunner != null) { + super.stopVpnRunnerPrivileged(); + disconnect(); + mStartLegacyVpnCv = new ConditionVariable(); + } + mVpnRunner = null; + mStopVpnRunnerCv.open(); + } + + public void expectStopVpnRunnerPrivileged() { + assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms", + mStopVpnRunnerCv.block(TIMEOUT_MS)); + } + + @Override + public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { + if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo; + + return super.getUnderlyingNetworkInfo(); + } + + private synchronized void setUnderlyingNetworkInfo( + UnderlyingNetworkInfo underlyingNetworkInfo) { + mUnderlyingNetworkInfo = underlyingNetworkInfo; + } + } + + private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set ranges) { + return ranges.stream().map( + r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new); + } + + private VpnManagerService makeVpnManagerService() { + final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() { + public int getCallingUid() { + return mDeps.getCallingUid(); + } + + public HandlerThread makeHandlerThread() { + return mVMSHandlerThread; + } + + @Override + public VpnProfileStore getVpnProfileStore() { + return mVpnProfileStore; + } + + public INetd getNetd() { + return mMockNetd; + } + + public INetworkManagementService getINetworkManagementService() { + return mNetworkManagementService; + } + }; + return new VpnManagerService(mServiceContext, deps); + } + + private void assertVpnTransportInfo(NetworkCapabilities nc, int type) { + assertNotNull(nc); + final TransportInfo ti = nc.getTransportInfo(); + assertTrue("VPN TransportInfo is not a VpnTransportInfo: " + ti, + ti instanceof VpnTransportInfo); + assertEquals(type, ((VpnTransportInfo) ti).getType()); + + } + + private void processBroadcast(Intent intent) { + mServiceContext.sendBroadcast(intent); + HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS); + waitForIdle(); + } + + private void mockVpn(int uid) { + synchronized (mVpnManagerService.mVpns) { + int userId = UserHandle.getUserId(uid); + mMockVpn = new MockVpn(userId); + // Every running user always has a Vpn in the mVpns array, even if no VPN is running. + mVpnManagerService.mVpns.put(userId, mMockVpn); + } + } + + private void mockUidNetworkingBlocked() { + doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1)) + ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); + } + + private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) { + final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK); + if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) { + return true; + } + if (meteredNetwork) { + return blockedReasons != BLOCKED_REASON_NONE; + } + return false; + } + + private void setBlockedReasonChanged(int blockedReasons) { + mBlockedReasons = blockedReasons; + mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons); + } + + private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) { + return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd; + } + + private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { + volatile boolean mConfigRestrictsAvoidBadWifi; + volatile int mConfigMeteredMultipathPreference; + + WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { + super(c, h, r); + } + + @Override + public boolean configRestrictsAvoidBadWifi() { + return mConfigRestrictsAvoidBadWifi; + } + + @Override + public int configMeteredMultipathPreference() { + return mConfigMeteredMultipathPreference; + } + } + + /** + * Wait up to TIMEOUT_MS for {@code conditionVariable} to open. + * Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens. + */ + static private void waitFor(ConditionVariable conditionVariable) { + if (conditionVariable.block(TIMEOUT_MS)) { + return; + } + fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms"); + } + + private T doAsUid(final int uid, @NonNull final Supplier what) { + when(mDeps.getCallingUid()).thenReturn(uid); + try { + return what.get(); + } finally { + returnRealCallingUid(); + } + } + + private void doAsUid(final int uid, @NonNull final Runnable what) { + doAsUid(uid, () -> { + what.run(); return Void.TYPE; + }); + } + + private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback, + int uid) { + doAsUid(uid, () -> { + mCm.registerNetworkCallback(request, callback); + }); + } + + private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback, + final int uid) { + doAsUid(uid, () -> { + mCm.registerDefaultNetworkCallback(callback); + waitForIdle(); + }); + } + + private interface ExceptionalRunnable { + void run() throws Exception; + } + + private void withPermission(String permission, ExceptionalRunnable r) throws Exception { + if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + r.run(); + return; + } + try { + mServiceContext.setPermission(permission, PERMISSION_GRANTED); + r.run(); + } finally { + mServiceContext.setPermission(permission, PERMISSION_DENIED); + } + } + + private static final int PRIMARY_USER = 0; + private static final UidRange PRIMARY_UIDRANGE = + UidRange.createForUser(UserHandle.of(PRIMARY_USER)); + private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100); + private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101); + private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043); + private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "", + UserInfo.FLAG_PRIMARY); + private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER); + + private static final int RESTRICTED_USER = 1; + private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "", + UserInfo.FLAG_RESTRICTED); + static { + RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER; + } + + @Before + public void setUp() throws Exception { + mNetIdManager = new TestNetIdManager(); + + mContext = InstrumentationRegistry.getContext(); + + MockitoAnnotations.initMocks(this); + + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); + when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO); + // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context + // it was started from, i.e., PRIMARY_USER. + when(mUserManager.canHaveRestrictedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO); + + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; + when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) + .thenReturn(applicationInfo); + when(mPackageManager.getTargetSdkVersion(anyString())) + .thenReturn(applicationInfo.targetSdkVersion); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); + + // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. + // http://b/25897652 . + if (Looper.myLooper() == null) { + Looper.prepare(); + } + mockDefaultPackages(); + mockHasSystemFeature(FEATURE_WIFI, true); + mockHasSystemFeature(FEATURE_WIFI_DIRECT, true); + doReturn(true).when(mTelephonyManager).isDataCapable(); + + FakeSettingsProvider.clearSettingsProvider(); + mServiceContext = new MockContext(InstrumentationRegistry.getContext(), + new FakeSettingsProvider()); + mServiceContext.setUseRegisteredHandlers(true); + + mAlarmManagerThread = new HandlerThread("TestAlarmManager"); + mAlarmManagerThread.start(); + initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler()); + + mCsHandlerThread = new HandlerThread("TestConnectivityService"); + mVMSHandlerThread = new HandlerThread("TestVpnManagerService"); + mDeps = makeDependencies(); + returnRealCallingUid(); + mService = new ConnectivityService(mServiceContext, + mMockDnsResolver, + mock(IpConnectivityLog.class), + mMockNetd, + mDeps); + mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; + mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; + verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); + + final ArgumentCaptor policyCallbackCaptor = + ArgumentCaptor.forClass(NetworkPolicyCallback.class); + verify(mNetworkPolicyManager).registerNetworkPolicyCallback(any(), + policyCallbackCaptor.capture()); + mPolicyCallback = policyCallbackCaptor.getValue(); + + // Create local CM before sending system ready so that we can answer + // getSystemService() correctly. + mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); + mService.systemReadyInternal(); + mVpnManagerService = makeVpnManagerService(); + mVpnManagerService.systemReady(); + mockVpn(Process.myUid()); + mCm.bindProcessToNetwork(null); + mQosCallbackTracker = mock(QosCallbackTracker.class); + + // Ensure that the default setting for Captive Portals is used for most tests + setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT); + setAlwaysOnNetworks(false); + setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); + } + + private void returnRealCallingUid() { + doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid(); + } + + private ConnectivityService.Dependencies makeDependencies() { + doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false); + final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); + doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); + doReturn(mNetIdManager).when(deps).makeNetIdManager(); + doReturn(mNetworkStack).when(deps).getNetworkStack(); + doReturn(mSystemProperties).when(deps).getSystemProperties(); + doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); + doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any()); + doAnswer(inv -> { + mPolicyTracker = new WrappedMultinetworkPolicyTracker( + inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); + return mPolicyTracker; + }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any()); + doReturn(true).when(deps).getCellular464XlatEnabled(); + + doReturn(60000).when(mResources).getInteger(R.integer.config_networkTransitionTimeout); + doReturn("").when(mResources).getString(R.string.config_networkCaptivePortalServerUrl); + doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray( + R.array.config_wakeonlan_supported_interfaces); + doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray( + R.array.config_networkSupportedKeepaliveCount); + doReturn(new String[0]).when(mResources).getStringArray( + R.array.config_networkNotifySwitches); + doReturn(new int[]{10, 11, 12, 14, 15}).when(mResources).getIntArray( + R.array.config_protectedNetworks); + // We don't test the actual notification value strings, so just return an empty array. + // It doesn't matter what the values are as long as it's not null. + doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name); + + doReturn(R.array.config_networkSupportedKeepaliveCount).when(mResources) + .getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any()); + doReturn(R.array.network_switch_type_name).when(mResources) + .getIdentifier(eq("network_switch_type_name"), eq("array"), any()); + + + final ConnectivityResources connRes = mock(ConnectivityResources.class); + doReturn(mResources).when(connRes).get(); + doReturn(connRes).when(deps).getResources(any()); + + final Context mockResContext = mock(Context.class); + doReturn(mResources).when(mockResContext).getResources(); + ConnectivityResources.setResourcesContextForTest(mockResContext); + + return deps; + } + + private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) { + doAnswer(inv -> { + final long when = inv.getArgument(1); + final WakeupMessage wakeupMsg = inv.getArgument(3); + final Handler handler = inv.getArgument(4); + + long delayMs = when - SystemClock.elapsedRealtime(); + if (delayMs < 0) delayMs = 0; + if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) { + fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS + + "ms into the future: " + delayMs); + } + alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */, + delayMs); + + return null; + }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), + any(WakeupMessage.class), any()); + + doAnswer(inv -> { + final WakeupMessage wakeupMsg = inv.getArgument(0); + alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */); + return null; + }).when(am).cancel(any(WakeupMessage.class)); + } + + @After + public void tearDown() throws Exception { + unregisterDefaultNetworkCallbacks(); + maybeTearDownEnterpriseNetwork(); + setAlwaysOnNetworks(false); + if (mCellNetworkAgent != null) { + mCellNetworkAgent.disconnect(); + mCellNetworkAgent = null; + } + if (mWiFiNetworkAgent != null) { + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent = null; + } + if (mEthernetNetworkAgent != null) { + mEthernetNetworkAgent.disconnect(); + mEthernetNetworkAgent = null; + } + + if (mQosCallbackMockHelper != null) { + mQosCallbackMockHelper.tearDown(); + mQosCallbackMockHelper = null; + } + mMockVpn.disconnect(); + waitForIdle(); + + FakeSettingsProvider.clearSettingsProvider(); + ConnectivityResources.setResourcesContextForTest(null); + + mCsHandlerThread.quitSafely(); + mAlarmManagerThread.quitSafely(); + } + + private void mockDefaultPackages() throws Exception { + final String myPackageName = mContext.getPackageName(); + final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo( + myPackageName, PackageManager.GET_PERMISSIONS); + when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn( + new String[] {myPackageName}); + when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(), + eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo); + + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + Arrays.asList(new PackageInfo[] { + buildPackageInfo(/* SYSTEM */ false, APP1_UID), + buildPackageInfo(/* SYSTEM */ false, APP2_UID), + buildPackageInfo(/* SYSTEM */ false, VPN_UID) + })); + + // Create a fake always-on VPN package. + final int userId = UserHandle.getCallingUserId(); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; // Always-on supported in N+. + when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(), + eq(userId))).thenReturn(applicationInfo); + + // Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy. + ResolveInfo rInfo = new ResolveInfo(); + rInfo.serviceInfo = new ServiceInfo(); + rInfo.serviceInfo.metaData = new Bundle(); + final List services = Arrays.asList(new ResolveInfo[]{rInfo}); + when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA), + eq(userId))).thenReturn(services); + when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId)) + .thenReturn(Process.myUid()); + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId)) + .thenReturn(VPN_UID); + } + + private void verifyActiveNetwork(int transport) { + // Test getActiveNetworkInfo() + assertNotNull(mCm.getActiveNetworkInfo()); + assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType()); + // Test getActiveNetwork() + assertNotNull(mCm.getActiveNetwork()); + assertEquals(mCm.getActiveNetwork(), mCm.getActiveNetworkForUid(Process.myUid())); + if (!NetworkCapabilities.isValidTransport(transport)) { + throw new IllegalStateException("Unknown transport " + transport); + } + switch (transport) { + case TRANSPORT_WIFI: + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + break; + case TRANSPORT_CELLULAR: + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + break; + case TRANSPORT_ETHERNET: + assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + break; + default: + break; + } + // Test getNetworkInfo(Network) + assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork())); + assertEquals(transportToLegacyType(transport), + mCm.getNetworkInfo(mCm.getActiveNetwork()).getType()); + assertNotNull(mCm.getActiveNetworkInfoForUid(Process.myUid())); + // Test getNetworkCapabilities(Network) + assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork())); + assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport)); + } + + private void verifyNoNetwork() { + waitForIdle(); + // Test getActiveNetworkInfo() + assertNull(mCm.getActiveNetworkInfo()); + // Test getActiveNetwork() + assertNull(mCm.getActiveNetwork()); + assertNull(mCm.getActiveNetworkForUid(Process.myUid())); + // Test getAllNetworks() + assertEmpty(mCm.getAllNetworks()); + assertEmpty(mCm.getAllNetworkStateSnapshots()); + } + + /** + * Class to simplify expecting broadcasts using BroadcastInterceptingContext. + * Ensures that the receiver is unregistered after the expected broadcast is received. This + * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs + * the receivers' receive method while iterating over the list of receivers, and unregistering + * the receiver during iteration throws ConcurrentModificationException. + */ + private class ExpectedBroadcast extends CompletableFuture { + private final BroadcastReceiver mReceiver; + + ExpectedBroadcast(BroadcastReceiver receiver) { + mReceiver = receiver; + } + + public Intent expectBroadcast(int timeoutMs) throws Exception { + try { + return get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + fail("Expected broadcast not received after " + timeoutMs + " ms"); + return null; + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + + public Intent expectBroadcast() throws Exception { + return expectBroadcast(BROADCAST_TIMEOUT_MS); + } + + public void expectNoBroadcast(int timeoutMs) throws Exception { + waitForIdle(); + try { + final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS); + fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras()); + } catch (TimeoutException expected) { + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + } + + /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */ + private ExpectedBroadcast registerConnectivityBroadcast(final int count) { + return registerConnectivityBroadcastThat(count, intent -> true); + } + + private ExpectedBroadcast registerConnectivityBroadcastThat(final int count, + @NonNull final Predicate filter) { + final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION); + // AtomicReference allows receiver to access expected even though it is constructed later. + final AtomicReference expectedRef = new AtomicReference<>(); + final BroadcastReceiver receiver = new BroadcastReceiver() { + private int mRemaining = count; + public void onReceive(Context context, Intent intent) { + final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); + final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); + Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni); + if (!filter.test(intent)) return; + if (--mRemaining == 0) { + expectedRef.get().complete(intent); + } + } + }; + final ExpectedBroadcast expected = new ExpectedBroadcast(receiver); + expectedRef.set(expected); + mServiceContext.registerReceiver(receiver, intentFilter); + return expected; + } + + private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) { + final DetailedState state = ni.getDetailedState(); + if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false; + // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION + // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also + // nulls out extraInfo. + if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false; + // Can't make any assertions about DISCONNECTED broadcasts. When a network actually + // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo + // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to + // a network switch, extraInfo will likely be populated. + // This is likely a bug in CS, but likely not one we can fix without impacting apps. + return true; + } + + private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) { + return registerConnectivityBroadcastThat(1, intent -> { + final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); + final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); + return type == actualType + && state == ni.getDetailedState() + && extraInfoInBroadcastHasExpectedNullness(ni); + }); + } + + @Test + public void testNetworkTypes() { + // Ensure that our mocks for the networkAttributes config variable work as expected. If they + // don't, then tests that depend on CONNECTIVITY_ACTION broadcasts for these network types + // will fail. Failing here is much easier to debug. + assertTrue(mCm.isNetworkSupported(TYPE_WIFI)); + assertTrue(mCm.isNetworkSupported(TYPE_MOBILE)); + assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS)); + assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); + assertFalse(mCm.isNetworkSupported(TYPE_PROXY)); + + // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our + // mocks, this assert exercises the ConnectivityService code path that ensures that + // TYPE_ETHERNET is supported if the ethernet service is running. + assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET)); + } + + @Test + public void testNetworkFeature() throws Exception { + // Connect the cell agent and wait for the connected broadcast. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + + // Build legacy request for SUPL. + final NetworkCapabilities legacyCaps = new NetworkCapabilities(); + legacyCaps.addTransportType(TRANSPORT_CELLULAR); + legacyCaps.addCapability(NET_CAPABILITY_SUPL); + final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL, + ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST); + + // File request, withdraw it and make sure no broadcast is sent + b = registerConnectivityBroadcast(1); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.requestNetwork(legacyRequest, callback); + callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback); + b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent + + // Disconnect the network and expect mobile disconnected broadcast. + b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + mCellNetworkAgent.disconnect(); + b.expectBroadcast(); + } + + @Test + public void testLingering() throws Exception { + verifyNoNetwork(); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + assertNull(mCm.getActiveNetworkInfo()); + assertNull(mCm.getActiveNetwork()); + // Test bringing up validated cellular. + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + assertLength(2, mCm.getAllNetworks()); + assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || + mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork())); + assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) || + mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork())); + // Test bringing up validated WiFi. + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + assertLength(2, mCm.getAllNetworks()); + assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || + mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork())); + assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) || + mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork())); + // Test cellular linger timeout. + mCellNetworkAgent.expectDisconnected(); + waitForIdle(); + assertLength(1, mCm.getAllNetworks()); + verifyActiveNetwork(TRANSPORT_WIFI); + assertLength(1, mCm.getAllNetworks()); + assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); + // Test WiFi disconnect. + b = registerConnectivityBroadcast(1); + mWiFiNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyNoNetwork(); + } + + /** + * Verify a newly created network will be inactive instead of torn down even if no one is + * requesting. + */ + @Test + public void testNewNetworkInactive() throws Exception { + // Create a callback that monitoring the testing network. + final TestNetworkCallback listenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback); + + // 1. Create a network that is not requested by anyone, and does not satisfy any of the + // default requests. Verify that the network will be inactive instead of torn down. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + listenCallback.assertNoCallback(); + + // Verify that the network will be torn down after nascent expiry. A small period of time + // is added in case of flakiness. + final int nascentTimeoutMs = + mService.mNascentDelayMs + mService.mNascentDelayMs / 4; + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs); + + // 2. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback wifiCallback = new TestNetworkCallback(); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will be kept since the request is still satisfied. And is able + // to get disconnected as usual if the request is released after the nascent timer expires. + listenCallback.assertNoCallback(nascentTimeoutMs); + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // 3. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will still be torn down after the request gets removed. + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // There is no need to ensure that LOSING is never sent in the common case that the + // network immediately satisfies a request that was already present, because it is already + // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called. + + mCm.unregisterNetworkCallback(listenCallback); + } + + /** + * Verify a newly created network will be inactive and switch to background if only background + * request is satisfied. + */ + @Test + public void testNewNetworkInactive_bgNetwork() throws Exception { + // Create a callback that monitoring the wifi network. + final TestNetworkCallback wifiListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback); + + // Create callbacks that can monitor background and foreground mobile networks. + // This is done by granting using background networks permission before registration. Thus, + // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default. + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback(); + final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback); + + // Connect wifi, which satisfies default request. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + + // Connect a cellular network, verify that satisfies only the background callback. + setAlwaysOnNetworks(true); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + assertFalse(isForegroundNetwork(mCellNetworkAgent)); + + mCellNetworkAgent.disconnect(); + bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(wifiListenCallback); + mCm.unregisterNetworkCallback(bgMobileListenCallback); + mCm.unregisterNetworkCallback(fgMobileListenCallback); + } + + @Test + public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { + // Test bringing up unvalidated WiFi + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up unvalidated cellular + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); + waitForIdle(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test cellular disconnect. + mCellNetworkAgent.disconnect(); + waitForIdle(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up validated cellular + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + b = registerConnectivityBroadcast(2); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test cellular disconnect. + b = registerConnectivityBroadcast(2); + mCellNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test WiFi disconnect. + b = registerConnectivityBroadcast(1); + mWiFiNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyNoNetwork(); + } + + @Test + public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { + // Test bringing up unvalidated cellular. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mCellNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test bringing up unvalidated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test WiFi disconnect. + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test cellular disconnect. + b = registerConnectivityBroadcast(1); + mCellNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyNoNetwork(); + } + + @Test + public void testUnlingeringDoesNotValidate() throws Exception { + // Test bringing up unvalidated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + // Test bringing up validated cellular. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + b = registerConnectivityBroadcast(2); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + // Test cellular disconnect. + b = registerConnectivityBroadcast(2); + mCellNetworkAgent.disconnect(); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Unlingering a network should not cause it to be marked as validated. + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + } + + @Test + public void testCellularOutscoresWeakWifi() throws Exception { + // Test bringing up validated cellular. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test WiFi getting really weak. + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.adjustScore(-11); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test WiFi restoring signal strength. + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.adjustScore(11); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + } + + @Test + public void testReapingNetwork() throws Exception { + // Test bringing up WiFi without NET_CAPABILITY_INTERNET. + // Expect it to be torn down immediately because it satisfies no requests. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + mWiFiNetworkAgent.expectDisconnected(); + // Test bringing up cellular without NET_CAPABILITY_INTERNET. + // Expect it to be torn down immediately because it satisfies no requests. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mCellNetworkAgent.connectWithoutInternet(); + mCellNetworkAgent.expectDisconnected(); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up unvalidated cellular. + // Expect it to be torn down because it could never be the highest scoring network + // satisfying the default request even if it validated. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); + mCellNetworkAgent.expectDisconnected(); + verifyActiveNetwork(TRANSPORT_WIFI); + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + } + + @Test + public void testCellularFallback() throws Exception { + // Test bringing up validated cellular. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Reevaluate WiFi (it'll instantly fail DNS). + b = registerConnectivityBroadcast(2); + assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); + // Should quickly fall back to Cellular. + b.expectBroadcast(); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Reevaluate cellular (it'll instantly fail DNS). + b = registerConnectivityBroadcast(2); + assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); + // Should quickly fall back to WiFi. + b.expectBroadcast(); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_WIFI); + } + + @Test + public void testWiFiFallback() throws Exception { + // Test bringing up unvalidated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mWiFiNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + // Test bringing up validated cellular. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + b = registerConnectivityBroadcast(2); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + // Reevaluate cellular (it'll instantly fail DNS). + b = registerConnectivityBroadcast(2); + assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); + // Should quickly fall back to WiFi. + b.expectBroadcast(); + assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( + NET_CAPABILITY_VALIDATED)); + verifyActiveNetwork(TRANSPORT_WIFI); + } + + @Test + public void testRequiresValidation() { + assertTrue(NetworkMonitorUtils.isValidationRequired( + mCm.getDefaultRequest().networkCapabilities)); + } + + /** + * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks + * this class receives, by calling expectCallback() exactly once each time a callback is + * received. assertNoCallback may be called at any time. + */ + private class TestNetworkCallback extends TestableNetworkCallback { + TestNetworkCallback() { + super(TEST_CALLBACK_TIMEOUT_MS); + } + + @Override + public void assertNoCallback() { + // TODO: better support this use case in TestableNetworkCallback + waitForIdle(); + assertNoCallback(0 /* timeout */); + } + + @Override + public T expectCallback(final KClass type, final HasNetwork n, + final long timeoutMs) { + final T callback = super.expectCallback(type, n, timeoutMs); + if (callback instanceof CallbackEntry.Losing) { + // TODO : move this to the specific test(s) needing this rather than here. + final CallbackEntry.Losing losing = (CallbackEntry.Losing) callback; + final int maxMsToLive = losing.getMaxMsToLive(); + String msg = String.format( + "Invalid linger time value %d, must be between %d and %d", + maxMsToLive, 0, mService.mLingerDelayMs); + assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs); + } + return callback; + } + } + + // Can't be part of TestNetworkCallback because "cannot be declared static; static methods can + // only be declared in a static or top level type". + static void assertNoCallbacks(TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.assertNoCallback(); + } + } + + static void expectOnLost(TestNetworkAgentWrapper network, TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.expectCallback(CallbackEntry.LOST, network); + } + } + + static void expectAvailableCallbacksUnvalidatedWithSpecifier(TestNetworkAgentWrapper network, + NetworkSpecifier specifier, TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.expectCallback(CallbackEntry.AVAILABLE, network); + c.expectCapabilitiesThat(network, (nc) -> + !nc.hasCapability(NET_CAPABILITY_VALIDATED) + && Objects.equals(specifier, nc.getNetworkSpecifier())); + c.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, network); + c.expectCallback(CallbackEntry.BLOCKED_STATUS, network); + } + } + + @Test + public void testStateChangeNetworkCallbacks() throws Exception { + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + + // Test unvalidated networks + ExpectedBroadcast b = registerConnectivityBroadcast(1); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + b.expectBroadcast(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + // This should not trigger spurious onAvailable() callbacks, b/21762680. + mCellNetworkAgent.adjustScore(-1); + waitForIdle(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + b.expectBroadcast(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + b = registerConnectivityBroadcast(2); + mWiFiNetworkAgent.disconnect(); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + cellNetworkCallback.assertNoCallback(); + b.expectBroadcast(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + b = registerConnectivityBroadcast(1); + mCellNetworkAgent.disconnect(); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + b.expectBroadcast(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + // Test validated networks + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + // This should not trigger spurious onAvailable() callbacks, b/21762680. + mCellNetworkAgent.adjustScore(-1); + waitForIdle(); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + mWiFiNetworkAgent.disconnect(); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + + mCellNetworkAgent.disconnect(); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); + } + + private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + mCm.registerNetworkCallback(wifiRequest, callback); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + final LinkProperties newLp = new LinkProperties(); + final Uri capportUrl = Uri.parse("https://capport.example.com/api"); + final CaptivePortalData capportData = new CaptivePortalData.Builder() + .setCaptive(true).build(); + + final Uri expectedCapportUrl = sanitized ? null : capportUrl; + newLp.setCaptivePortalApiUrl(capportUrl); + mWiFiNetworkAgent.sendLinkProperties(newLp); + callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); + defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); + + final CaptivePortalData expectedCapportData = sanitized ? null : capportData; + mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData); + callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportData, lp.getCaptivePortalData())); + defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportData, lp.getCaptivePortalData())); + + final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork()); + assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl()); + assertEquals(expectedCapportData, lp.getCaptivePortalData()); + } + + @Test + public void networkCallbacksSanitizationTest_Sanitize() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); + doNetworkCallbacksSanitizationTest(true /* sanitized */); + } + + @Test + public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); + doNetworkCallbacksSanitizationTest(false /* sanitized */); + } + + @Test + public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + doNetworkCallbacksSanitizationTest(false /* sanitized */); + } + + @Test + public void testOwnerUidCannotChange() throws Exception { + final NetworkCapabilities ncTemplate = new NetworkCapabilities(); + final int originalOwnerUid = Process.myUid(); + ncTemplate.setOwnerUid(originalOwnerUid); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), + ncTemplate); + mWiFiNetworkAgent.connect(false); + waitForIdle(); + + // Send ConnectivityService an update to the mWiFiNetworkAgent's capabilities that changes + // the owner UID and an unrelated capability. + NetworkCapabilities agentCapabilities = mWiFiNetworkAgent.getNetworkCapabilities(); + assertEquals(originalOwnerUid, agentCapabilities.getOwnerUid()); + agentCapabilities.setOwnerUid(42); + assertFalse(agentCapabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); + agentCapabilities.addCapability(NET_CAPABILITY_NOT_CONGESTED); + mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true); + waitForIdle(); + + // Owner UIDs are not visible without location permission. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + // Check that the capability change has been applied but the owner UID is not modified. + NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); + assertEquals(originalOwnerUid, nc.getOwnerUid()); + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); + } + + @Test + public void testMultipleLingering() throws Exception { + // This test would be flaky with the default 120ms timer: that is short enough that + // lingered networks are torn down before assertions can be run. We don't want to mock the + // lingering timer to keep the WakeupMessage logic realistic: this has already proven useful + // in detecting races. + mService.mLingerDelayMs = 300; + + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.connect(true); + // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request. + // We then get LOSING when wifi validates and cell is outscored. + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mEthernetNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); + // TODO: Investigate sending validated before losing. + callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mEthernetNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + for (int i = 0; i < 4; i++) { + TestNetworkAgentWrapper oldNetwork, newNetwork; + if (i % 2 == 0) { + mWiFiNetworkAgent.adjustScore(-15); + oldNetwork = mWiFiNetworkAgent; + newNetwork = mCellNetworkAgent; + } else { + mWiFiNetworkAgent.adjustScore(15); + oldNetwork = mCellNetworkAgent; + newNetwork = mWiFiNetworkAgent; + + } + callback.expectCallback(CallbackEntry.LOSING, oldNetwork); + // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no + // longer lingering? + defaultCallback.expectAvailableCallbacksValidated(newNetwork); + assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork()); + } + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even + // if the network is still up. + mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + // We expect a notification about the capabilities change, and nothing else. + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); + defaultCallback.assertNoCallback(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Wifi no longer satisfies our listen, which is for an unmetered network. + // But because its score is 55, it's still up (and the default network). + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Disconnect our test networks. + mWiFiNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + mCellNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + waitForIdle(); + assertEquals(null, mCm.getActiveNetwork()); + + mCm.unregisterNetworkCallback(callback); + waitForIdle(); + + // Check that a network is only lingered or torn down if it would not satisfy a request even + // if it validated. + request = new NetworkRequest.Builder().clearCapabilities().build(); + callback = new TestNetworkCallback(); + + mCm.registerNetworkCallback(request, callback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); // Score: 10 + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi with a score of 20. + // Cell stays up because it would satisfy the default request if it validated. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); // Score: 20 + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but + // it's arguably correct to linger it, since it was the default network before it validated. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + mCellNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + waitForIdle(); + assertEquals(null, mCm.getActiveNetwork()); + + // If a network is lingering, and we add and remove a request from it, resume lingering. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + // TODO: Investigate sending validated before losing. + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + NetworkCallback noopCallback = new NetworkCallback(); + mCm.requestNetwork(cellRequest, noopCallback); + // TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer + // lingering? + mCm.unregisterNetworkCallback(noopCallback); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + + // Similar to the above: lingering can start even after the lingered request is removed. + // Disconnect wifi and switch to cell. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Cell is now the default network. Pin it with a cell-specific request. + noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525 + mCm.requestNetwork(cellRequest, noopCallback); + + // Now connect wifi, and expect it to become the default network. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + // The default request is lingering on cell, but nothing happens to cell, and we send no + // callbacks for it, because it's kept up by cellRequest. + callback.assertNoCallback(); + // Now unregister cellRequest and expect cell to start lingering. + mCm.unregisterNetworkCallback(noopCallback); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + + // Let linger run its course. + callback.assertNoCallback(); + final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4; + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs); + + // Register a TRACK_DEFAULT request and check that it does not affect lingering. + TestNetworkCallback trackDefaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(trackDefaultCallback); + trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); + trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Let linger run its course. + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs); + + // Clean up. + mEthernetNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + trackDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + + mCm.unregisterNetworkCallback(callback); + mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(trackDefaultCallback); + } + + private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception { + grantUsingBackgroundNetworksPermissionForUid(uid, mContext.getPackageName()); + } + + private void grantUsingBackgroundNetworksPermissionForUid( + final int uid, final String packageName) throws Exception { + when(mPackageManager.getPackageInfo( + eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER))) + .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid)); + mService.mPermissionMonitor.onPackageAdded(packageName, uid); + } + + @Test + public void testNetworkGoesIntoBackgroundAfterLinger() throws Exception { + setAlwaysOnNetworks(true); + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities() + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + // Wifi comes up and cell lingers. + mWiFiNetworkAgent.connect(true); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + + // File a request for cellular, then release it. + NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + NetworkCallback noopCallback = new NetworkCallback(); + mCm.requestNetwork(cellRequest, noopCallback); + mCm.unregisterNetworkCallback(noopCallback); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + + // Let linger run its course. + callback.assertNoCallback(); + final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; + callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent, + lingerTimeoutMs); + + // Clean up. + mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(callback); + } + + private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) { + return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission, + /*secure=*/ false, VpnManager.TYPE_VPN_NONE); + } + + private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) { + return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE, + secure, vpnType); + } + + @Test + public void testNetworkAgentCallbacks() throws Exception { + // Keeps track of the order of events that happen in this test. + final LinkedBlockingQueue eventOrder = new LinkedBlockingQueue<>(); + + final NetworkRequest request = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + final AtomicReference wifiNetwork = new AtomicReference<>(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + // Expectations for state when various callbacks fire. These expectations run on the handler + // thread and not on the test thread because they need to prevent the handler thread from + // advancing while they examine state. + + // 1. When onCreated fires, netd has been told to create the network. + mWiFiNetworkAgent.setCreatedCallback(() -> { + eventOrder.offer("onNetworkCreated"); + wifiNetwork.set(mWiFiNetworkAgent.getNetwork()); + assertNotNull(wifiNetwork.get()); + try { + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE)); + } catch (RemoteException impossible) { + fail(); + } + }); + + // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just + // check that it is fired at some point after disconnect. + mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted")); + + // 3. While the teardown timer is running, connectivity APIs report the network is gone, but + // netd has not yet been told to destroy it. + final Runnable duringTeardown = () -> { + eventOrder.offer("timePasses"); + assertNull(mCm.getLinkProperties(wifiNetwork.get())); + try { + verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId()); + } catch (RemoteException impossible) { + fail(); + } + }; + + // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone, + // and netd has been told to destroy it. + mWiFiNetworkAgent.setDisconnectedCallback(() -> { + eventOrder.offer("onNetworkDisconnected"); + assertNull(mCm.getLinkProperties(wifiNetwork.get())); + try { + verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId()); + } catch (RemoteException impossible) { + fail(); + } + }); + + // Connect a network, and file a request for it after it has come up, to ensure the nascent + // timer is cleared and the test does not have to wait for it. Filing the request after the + // network has come up is necessary because ConnectivityService does not appear to clear the + // nascent timer if the first request satisfied by the network was filed before the network + // connected. + // TODO: fix this bug, file the request before connecting, and remove the waitForIdle. + mWiFiNetworkAgent.connectWithoutInternet(); + waitForIdle(); + mCm.requestNetwork(request, callback); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Set teardown delay and make sure CS has processed it. + mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMillis(300); + waitForIdle(); + + // Post the duringTeardown lambda to the handler so it fires while teardown is in progress. + // The delay must be long enough it will run after the unregisterNetworkCallback has torn + // down the network and started the teardown timer, and short enough that the lambda is + // scheduled to run before the teardown timer. + final Handler h = new Handler(mCsHandlerThread.getLooper()); + h.postDelayed(duringTeardown, 150); + + // Disconnect the network and check that events happened in the right order. + mCm.unregisterNetworkCallback(callback); + assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + mCm.unregisterNetworkCallback(callback); + } + + @Test + public void testExplicitlySelected() throws Exception { + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + // Bring up validated cell. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + // Bring up unvalidated wifi with explicitlySelected=true. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(true, false); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Cell Remains the default. + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Lower wifi's score to below than cell, and check that it doesn't disconnect because + // it's explicitly selected. + mWiFiNetworkAgent.adjustScore(-40); + mWiFiNetworkAgent.adjustScore(40); + callback.assertNoCallback(); + + // If the user chooses yes on the "No Internet access, stay connected?" dialog, we switch to + // wifi even though it's unvalidated. + mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), true, false); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Disconnect wifi, and then reconnect, again with explicitlySelected=true. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(true, false); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the + // network to disconnect. + mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), false, false); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Reconnect, again with explicitlySelected=true, but this time validate. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(true, false); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); + assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + callback.assertNoCallback(); + + // Disconnect wifi, and then reconnect as if the user had selected "yes, don't ask again" + // (i.e., with explicitlySelected=true and acceptUnvalidated=true). Expect to switch to + // wifi immediately. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(true, true); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mEthernetNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + mEthernetNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + + // Disconnect and reconnect with explicitlySelected=false and acceptUnvalidated=true. + // Check that the network is not scored specially and that the device prefers cell data. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(false, true); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Clean up. + mWiFiNetworkAgent.disconnect(); + mCellNetworkAgent.disconnect(); + + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + } + + private void tryNetworkFactoryRequests(int capability) throws Exception { + // Verify NOT_RESTRICTED is set appropriately + final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability) + .build().networkCapabilities; + if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN + || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA + || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS + || capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP + || capability == NET_CAPABILITY_VSIM || capability == NET_CAPABILITY_BIP + || capability == NET_CAPABILITY_ENTERPRISE) { + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } else { + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } + + NetworkCapabilities filter = new NetworkCapabilities(); + filter.addTransportType(TRANSPORT_CELLULAR); + filter.addCapability(capability); + // Add NOT_VCN_MANAGED capability into filter unconditionally since some requests will add + // NOT_VCN_MANAGED automatically but not for NetworkCapabilities, + // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details. + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); + handlerThread.start(); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.setScoreFilter(45); + testFactory.register(); + + final NetworkCallback networkCallback; + if (capability != NET_CAPABILITY_INTERNET) { + // If the capability passed in argument is part of the default request, then the + // factory will see the default request. Otherwise the filter will prevent the + // factory from seeing it. In that case, add a request so it can be tested. + assertFalse(testFactory.getMyStartRequested()); + NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build(); + networkCallback = new NetworkCallback(); + mCm.requestNetwork(request, networkCallback); + } else { + networkCallback = null; + } + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + assertTrue(testFactory.getMyStartRequested()); + + // Now bring in a higher scored network. + TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + // When testAgent connects, because of its score (50 legacy int / cell transport) + // it will beat or equal the testFactory's offer, so the request will be removed. + // Note the agent as validated only if the capability is INTERNET, as it's the only case + // where it makes sense. + testAgent.connect(NET_CAPABILITY_INTERNET == capability /* validated */); + testAgent.addCapability(capability); + testFactory.expectRequestRemove(); + testFactory.assertRequestCountEquals(0); + assertFalse(testFactory.getMyStartRequested()); + + // Add a request and make sure it's not sent to the factory, because the agent + // is satisfying it better. + final NetworkCallback cb = new ConnectivityManager.NetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(capability).build(), cb); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(0); + assertFalse(testFactory.getMyStartRequested()); + + // If using legacy scores, make the test agent weak enough to have the exact same score as + // the factory (50 for cell - 5 adjustment). Make sure the factory doesn't see the request. + // If not using legacy score, this is a no-op and the "same score removes request" behavior + // has already been tested above. + testAgent.adjustScore(-5); + expectNoRequestChanged(testFactory); + assertFalse(testFactory.getMyStartRequested()); + + // Make the test agent weak enough that the factory will see the two requests (the one that + // was just sent, and either the default one or the one sent at the top of this test if + // the default won't be seen). + testAgent.setScore(new NetworkScore.Builder().setLegacyInt(2).setExiting(true).build()); + testFactory.expectRequestAdds(2); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); + + // Now unregister and make sure the request is removed. + mCm.unregisterNetworkCallback(cb); + testFactory.expectRequestRemove(); + + // Bring in a bunch of requests. + assertEquals(1, testFactory.getMyRequestCount()); + ConnectivityManager.NetworkCallback[] networkCallbacks = + new ConnectivityManager.NetworkCallback[10]; + for (int i = 0; i< networkCallbacks.length; i++) { + networkCallbacks[i] = new ConnectivityManager.NetworkCallback(); + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(capability); + mCm.requestNetwork(builder.build(), networkCallbacks[i]); + } + testFactory.expectRequestAdds(10); + testFactory.assertRequestCountEquals(11); // +1 for the default/test specific request + assertTrue(testFactory.getMyStartRequested()); + + // Remove the requests. + for (int i = 0; i < networkCallbacks.length; i++) { + mCm.unregisterNetworkCallback(networkCallbacks[i]); + } + testFactory.expectRequestRemoves(10); + testFactory.assertRequestCountEquals(1); + assertTrue(testFactory.getMyStartRequested()); + + // Adjust the agent score up again. Expect the request to be withdrawn. + testAgent.setScore(new NetworkScore.Builder().setLegacyInt(50).build()); + testFactory.expectRequestRemove(); + testFactory.assertRequestCountEquals(0); + assertFalse(testFactory.getMyStartRequested()); + + // Drop the higher scored network. + testAgent.disconnect(); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + assertEquals(1, testFactory.getMyRequestCount()); + assertTrue(testFactory.getMyStartRequested()); + + testFactory.terminate(); + if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback); + handlerThread.quit(); + } + + @Test + public void testNetworkFactoryRequests() throws Exception { + tryNetworkFactoryRequests(NET_CAPABILITY_MMS); + tryNetworkFactoryRequests(NET_CAPABILITY_SUPL); + tryNetworkFactoryRequests(NET_CAPABILITY_DUN); + tryNetworkFactoryRequests(NET_CAPABILITY_FOTA); + tryNetworkFactoryRequests(NET_CAPABILITY_IMS); + tryNetworkFactoryRequests(NET_CAPABILITY_CBS); + tryNetworkFactoryRequests(NET_CAPABILITY_WIFI_P2P); + tryNetworkFactoryRequests(NET_CAPABILITY_IA); + tryNetworkFactoryRequests(NET_CAPABILITY_RCS); + tryNetworkFactoryRequests(NET_CAPABILITY_XCAP); + tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE); + tryNetworkFactoryRequests(NET_CAPABILITY_EIMS); + tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED); + tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET); + tryNetworkFactoryRequests(NET_CAPABILITY_TRUSTED); + tryNetworkFactoryRequests(NET_CAPABILITY_NOT_VPN); + tryNetworkFactoryRequests(NET_CAPABILITY_VSIM); + tryNetworkFactoryRequests(NET_CAPABILITY_BIP); + // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed. + } + + @Test + public void testRegisterIgnoringScore() throws Exception { + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(90).build()); + mWiFiNetworkAgent.connect(true /* validated */); + + // Make sure the factory sees the default network + final NetworkCapabilities filter = new NetworkCapabilities(); + filter.addTransportType(TRANSPORT_CELLULAR); + filter.addCapability(NET_CAPABILITY_INTERNET); + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); + handlerThread.start(); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.register(); + + final MockNetworkFactory testFactoryAll = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactoryAll", filter, mCsHandlerThread); + testFactoryAll.registerIgnoringScore(); + + // The regular test factory should not see the request, because WiFi is stronger than cell. + expectNoRequestChanged(testFactory); + // With ignoringScore though the request is seen. + testFactoryAll.expectRequestAdd(); + + // The legacy int will be ignored anyway, set the only other knob to true + mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(110) + .setTransportPrimary(true).build()); + + expectNoRequestChanged(testFactory); // still not seeing the request + expectNoRequestChanged(testFactoryAll); // still seeing the request + + mWiFiNetworkAgent.disconnect(); + } + + @Test + public void testNetworkFactoryUnregister() throws Exception { + // Make sure the factory sees the default network + final NetworkCapabilities filter = new NetworkCapabilities(); + filter.addCapability(NET_CAPABILITY_INTERNET); + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + + final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); + handlerThread.start(); + + // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it + // does not crash. + for (int i = 0; i < 100; i++) { + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + // Register the factory and don't be surprised when the default request arrives. + testFactory.register(); + testFactory.expectRequestAdd(); + + testFactory.setScoreFilter(42); + testFactory.terminate(); + + if (i % 2 == 0) { + try { + testFactory.register(); + fail("Re-registering terminated NetworkFactory should throw"); + } catch (IllegalStateException expected) { + } + } + } + handlerThread.quit(); + } + + @Test + public void testNoMutableNetworkRequests() throws Exception { + final PendingIntent pendingIntent = PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); + NetworkRequest request1 = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED) + .build(); + NetworkRequest request2 = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL) + .build(); + + Class expected = IllegalArgumentException.class; + assertThrows(expected, () -> mCm.requestNetwork(request1, new NetworkCallback())); + assertThrows(expected, () -> mCm.requestNetwork(request1, pendingIntent)); + assertThrows(expected, () -> mCm.requestNetwork(request2, new NetworkCallback())); + assertThrows(expected, () -> mCm.requestNetwork(request2, pendingIntent)); + } + + @Test + public void testMMSonWiFi() throws Exception { + // Test bringing up cellular without MMS NetworkRequest gets reaped + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); + mCellNetworkAgent.connectWithoutInternet(); + mCellNetworkAgent.expectDisconnected(); + waitForIdle(); + assertEmpty(mCm.getAllNetworks()); + verifyNoNetwork(); + + // Test bringing up validated WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + + // Register MMS NetworkRequest + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(builder.build(), networkCallback); + + // Test bringing up unvalidated cellular with MMS + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); + mCellNetworkAgent.connectWithoutInternet(); + networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + verifyActiveNetwork(TRANSPORT_WIFI); + + // Test releasing NetworkRequest disconnects cellular with MMS + mCm.unregisterNetworkCallback(networkCallback); + mCellNetworkAgent.expectDisconnected(); + verifyActiveNetwork(TRANSPORT_WIFI); + } + + @Test + public void testMMSonCell() throws Exception { + // Test bringing up cellular without MMS + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + mCellNetworkAgent.connect(false); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + + // Register MMS NetworkRequest + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(builder.build(), networkCallback); + + // Test bringing up MMS cellular network + TestNetworkAgentWrapper + mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); + mmsNetworkAgent.connectWithoutInternet(); + networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent); + verifyActiveNetwork(TRANSPORT_CELLULAR); + + // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent + mCm.unregisterNetworkCallback(networkCallback); + mmsNetworkAgent.expectDisconnected(); + verifyActiveNetwork(TRANSPORT_CELLULAR); + } + + @Test + public void testPartialConnectivity() throws Exception { + // Register network callback. + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + // Bring up validated mobile data. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + // Bring up wifi with partial connectivity. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithPartialConnectivity(); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); + + // Mobile data should be the default network. + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + callback.assertNoCallback(); + + // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http + // probe. + mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); + // If the user chooses yes to use this partial connectivity wifi, switch the default + // network to wifi and check if wifi becomes valid or not. + mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, + false /* always */); + // If user accepts partial connectivity network, + // NetworkMonitor#setAcceptPartialConnectivity() should be called too. + waitForIdle(); + verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); + + // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is + // validated. + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, + mWiFiNetworkAgent); + assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // Disconnect and reconnect wifi with partial connectivity again. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithPartialConnectivity(); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); + + // Mobile data should be the default network. + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + + // If the user chooses no, disconnect wifi immediately. + mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */, + false /* always */); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // If user accepted partial connectivity before, and device reconnects to that network + // again, but now the network has full connectivity. The network shouldn't contain + // NET_CAPABILITY_PARTIAL_CONNECTIVITY. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + // acceptUnvalidated is also used as setting for accepting partial networks. + mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, + true /* acceptUnvalidated */); + mWiFiNetworkAgent.connect(true); + + // If user accepted partial connectivity network before, + // NetworkMonitor#setAcceptPartialConnectivity() will be called in + // ConnectivityService#updateNetworkInfo(). + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)); + + // Wifi should be the default network. + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // The user accepted partial connectivity and selected "don't ask again". Now the user + // reconnects to the partial connectivity network. Switch to wifi as soon as partial + // connectivity is detected. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, + true /* acceptUnvalidated */); + mWiFiNetworkAgent.connectWithPartialConnectivity(); + // If user accepted partial connectivity network before, + // NetworkMonitor#setAcceptPartialConnectivity() will be called in + // ConnectivityService#updateNetworkInfo(). + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); + + // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is + // validated. + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // If the user accepted partial connectivity, and the device auto-reconnects to the partial + // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */, + true /* acceptUnvalidated */); + + // NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as + // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls + // notifyNetworkConnected. + mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith( + NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + } + + @Test + public void testCaptivePortalOnPartialConnectivity() throws Exception { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + final TestNetworkCallback validatedCallback = new TestNetworkCallback(); + final NetworkRequest validatedRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED).build(); + mCm.registerNetworkCallback(validatedRequest, validatedCallback); + + // Bring up a network with a captive portal. + // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + String redirectUrl = "http://android.com/path"; + mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl); + + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. + mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork()); + verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) + .launchCaptivePortalApp(); + + // Report that the captive portal is dismissed with partial connectivity, and check that + // callbacks are fired. + mWiFiNetworkAgent.setNetworkPartial(); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + waitForIdle(); + captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + // Report partial connectivity is accepted. + mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); + mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, + false /* always */); + waitForIdle(); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + NetworkCapabilities nc = + validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(captivePortalCallback); + mCm.unregisterNetworkCallback(validatedCallback); + } + + @Test + public void testCaptivePortal() throws Exception { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + final TestNetworkCallback validatedCallback = new TestNetworkCallback(); + final NetworkRequest validatedRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED).build(); + mCm.registerNetworkCallback(validatedRequest, validatedCallback); + + // Bring up a network with a captive portal. + // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + String firstRedirectUrl = "http://example.com/firstPath"; + mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); + + // Take down network. + // Expect onLost callback. + mWiFiNetworkAgent.disconnect(); + captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Bring up a network with a captive portal. + // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + String secondRedirectUrl = "http://example.com/secondPath"; + mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl); + + // Make captive portal disappear then revalidate. + // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Expect NET_CAPABILITY_VALIDATED onAvailable callback. + validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + + // Break network connectivity. + // Expect NET_CAPABILITY_VALIDATED onLost callback. + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); + validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + } + + @Test + public void testCaptivePortalApp() throws Exception { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + final TestNetworkCallback validatedCallback = new TestNetworkCallback(); + final NetworkRequest validatedRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED).build(); + mCm.registerNetworkCallback(validatedRequest, validatedCallback); + + // Bring up wifi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); + + // Check that calling startCaptivePortalApp does nothing. + final int fastTimeoutMs = 100; + mCm.startCaptivePortalApp(wifiNetwork); + waitForIdle(); + verify(mWiFiNetworkAgent.mNetworkMonitor, never()).launchCaptivePortalApp(); + mServiceContext.expectNoStartActivityIntent(fastTimeoutMs); + + // Turn into a captive portal. + mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */); + mCm.reportNetworkConnectivity(wifiNetwork, false); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. + mCm.startCaptivePortalApp(wifiNetwork); + waitForIdle(); + verify(mWiFiNetworkAgent.mNetworkMonitor).launchCaptivePortalApp(); + + // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal) + final Bundle testBundle = new Bundle(); + final String testKey = "testkey"; + final String testValue = "testvalue"; + testBundle.putString(testKey, testValue); + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_GRANTED); + mCm.startCaptivePortalApp(wifiNetwork, testBundle); + final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); + assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); + assertEquals(testValue, signInIntent.getStringExtra(testKey)); + + // Report that the captive portal is dismissed, and check that callbacks are fired + mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(validatedCallback); + mCm.unregisterNetworkCallback(captivePortalCallback); + } + + @Test + public void testAvoidOrIgnoreCaptivePortals() throws Exception { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + final TestNetworkCallback validatedCallback = new TestNetworkCallback(); + final NetworkRequest validatedRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED).build(); + mCm.registerNetworkCallback(validatedRequest, validatedCallback); + + setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID); + // Bring up a network with a captive portal. + // Expect it to fail to connect and not result in any callbacks. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + String firstRedirectUrl = "http://example.com/firstPath"; + + mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent.expectPreventReconnectReceived(); + + assertNoCallbacks(captivePortalCallback, validatedCallback); + } + + @Test + public void testCaptivePortalApi() throws Exception { + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final String redirectUrl = "http://example.com/firstPath"; + + mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + final CaptivePortalData testData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(redirectUrl)) + .setBytesRemaining(12345L) + .build(); + + mWiFiNetworkAgent.notifyCapportApiDataChanged(testData); + + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> testData.equals(lp.getCaptivePortalData())); + + final LinkProperties newLps = new LinkProperties(); + newLps.setMtu(1234); + mWiFiNetworkAgent.sendLinkProperties(newLps); + // CaptivePortalData is not lost and unchanged when LPs are received from the NetworkAgent + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234); + } + + private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception { + // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks + // with sensitive (captive portal) data + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + return captivePortalCallback; + } + + private class CaptivePortalTestData { + CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData, + CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData, + CaptivePortalData expectedMergedOtherData) { + mNaPasspointData = naPasspointData; + mCapportData = capportData; + mNaOtherData = naOtherData; + mExpectedMergedPasspointData = expectedMergedPasspointData; + mExpectedMergedOtherData = expectedMergedOtherData; + } + + public final CaptivePortalData mNaPasspointData; + public final CaptivePortalData mCapportData; + public final CaptivePortalData mNaOtherData; + public final CaptivePortalData mExpectedMergedPasspointData; + public final CaptivePortalData mExpectedMergedOtherData; + + } + + private CaptivePortalTestData setupCaptivePortalData() { + final CaptivePortalData capportData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) + .setExpiryTime(1000000L) + .setBytesRemaining(12345L) + .build(); + + final CaptivePortalData naPasspointData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData naOtherData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + return new CaptivePortalTestData(naPasspointData, capportData, naOtherData, + expectedMergedPasspointData, expectedMergedOtherData); + } + + @Test + public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Baseline capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + + // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm + // that API data gets precedence on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedPasspointData + .equals(lp.getCaptivePortalData())); + + // Now send this information from non-Passpoint source, confirm that Capport data takes + // precedence + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedOtherData + .equals(lp.getCaptivePortalData())); + + // Create a new LP with no Network agent capport data + final LinkProperties newLps = new LinkProperties(); + newLps.setMtu(1234); + mWiFiNetworkAgent.sendLinkProperties(newLps); + // CaptivePortalData is not lost and has the original values when LPs are received from the + // NetworkAgent + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()) + && lp.getMtu() == 1234); + + // Now send capport data only from the Network agent + mWiFiNetworkAgent.notifyCapportApiDataChanged(null); + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> lp.getCaptivePortalData() == null); + + newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData); + mWiFiNetworkAgent.sendLinkProperties(newLps); + + // Make sure that only the network agent capport data is available + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); + } + + @Test + public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedPasspointData.equals( + lp.getCaptivePortalData())); + + // Now set the naData to null + linkProperties.setCaptivePortalData(null); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the Capport data is retained correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + } + + @Test + public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport() + throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedOtherData.equals( + lp.getCaptivePortalData())); + } + + private NetworkRequest.Builder newWifiRequestBuilder() { + return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); + } + + /** + * Verify request matching behavior with network specifiers. + * + * This test does not check updating the specifier on a live network because the specifier is + * immutable and this triggers a WTF in + * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)}. + */ + @Test + public void testNetworkSpecifier() throws Exception { + // A NetworkSpecifier subclass that matches all networks but must not be visible to apps. + class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements + Parcelable { + @Override + public boolean canBeSatisfiedBy(NetworkSpecifier other) { + return true; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) {} + + @Override + public NetworkSpecifier redact() { + return null; + } + } + + // A network specifier that matches either another LocalNetworkSpecifier with the same + // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is. + class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable { + private String mString; + + LocalStringNetworkSpecifier(String string) { + mString = string; + } + + @Override + public boolean canBeSatisfiedBy(NetworkSpecifier other) { + if (other instanceof LocalStringNetworkSpecifier) { + return TextUtils.equals(mString, + ((LocalStringNetworkSpecifier) other).mString); + } + if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true; + return false; + } + + @Override + public int describeContents() { + return 0; + } + @Override + public void writeToParcel(Parcel dest, int flags) {} + } + + + NetworkRequest rEmpty1 = newWifiRequestBuilder().build(); + NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build(); + NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build(); + NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier( + (NetworkSpecifier) null).build(); + NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier( + new LocalStringNetworkSpecifier("foo")).build(); + NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier( + new LocalStringNetworkSpecifier("bar")).build(); + + TestNetworkCallback cEmpty1 = new TestNetworkCallback(); + TestNetworkCallback cEmpty2 = new TestNetworkCallback(); + TestNetworkCallback cEmpty3 = new TestNetworkCallback(); + TestNetworkCallback cEmpty4 = new TestNetworkCallback(); + TestNetworkCallback cFoo = new TestNetworkCallback(); + TestNetworkCallback cBar = new TestNetworkCallback(); + TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] { + cEmpty1, cEmpty2, cEmpty3, cEmpty4 }; + + mCm.registerNetworkCallback(rEmpty1, cEmpty1); + mCm.registerNetworkCallback(rEmpty2, cEmpty2); + mCm.registerNetworkCallback(rEmpty3, cEmpty3); + mCm.registerNetworkCallback(rEmpty4, cEmpty4); + mCm.registerNetworkCallback(rFoo, cFoo); + mCm.registerNetworkCallback(rBar, cBar); + + LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo"); + LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar"); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, + cEmpty1, cEmpty2, cEmpty3, cEmpty4); + assertNoCallbacks(cFoo, cBar); + + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.setNetworkSpecifier(nsFoo); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsFoo, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + cBar.assertNoCallback(); + assertEquals(nsFoo, + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); + assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.setNetworkSpecifier(nsBar); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsBar, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); + cFoo.assertNoCallback(); + assertEquals(nsBar, + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); + + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); + cFoo.assertNoCallback(); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier()); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); + assertNull( + mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); + + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); + } + + /** + * @return the context's attribution tag + */ + private String getAttributionTag() { + return mContext.getAttributionTag(); + } + + @Test + public void testInvalidNetworkSpecifier() { + assertThrows(IllegalArgumentException.class, () -> { + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.setNetworkSpecifier(new MatchAllNetworkSpecifier()); + }); + + assertThrows(IllegalArgumentException.class, () -> { + NetworkCapabilities networkCapabilities = new NetworkCapabilities(); + networkCapabilities.addTransportType(TRANSPORT_WIFI) + .setNetworkSpecifier(new MatchAllNetworkSpecifier()); + mService.requestNetwork(Process.INVALID_UID, networkCapabilities, + NetworkRequest.Type.REQUEST.ordinal(), null, 0, null, + ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, + mContext.getPackageName(), getAttributionTag()); + }); + + class NonParcelableSpecifier extends NetworkSpecifier { + @Override + public boolean canBeSatisfiedBy(NetworkSpecifier other) { + return false; + } + }; + class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable { + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel p, int flags) {} + } + + final NetworkRequest.Builder builder = + new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET); + assertThrows(ClassCastException.class, () -> { + builder.setNetworkSpecifier(new NonParcelableSpecifier()); + Parcel parcelW = Parcel.obtain(); + builder.build().writeToParcel(parcelW, 0); + }); + + final NetworkRequest nr = + new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET) + .setNetworkSpecifier(new ParcelableSpecifier()) + .build(); + assertNotNull(nr); + + assertThrows(BadParcelableException.class, () -> { + Parcel parcelW = Parcel.obtain(); + nr.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR); + }); + } + + @Test + public void testNetworkRequestUidSpoofSecurityException() throws Exception { + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + NetworkRequest networkRequest = newWifiRequestBuilder().build(); + TestNetworkCallback networkCallback = new TestNetworkCallback(); + doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString()); + assertThrows(SecurityException.class, () -> { + mCm.requestNetwork(networkRequest, networkCallback); + }); + } + + @Test + public void testInvalidSignalStrength() { + NetworkRequest r = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .addTransportType(TRANSPORT_WIFI) + .setSignalStrength(-75) + .build(); + // Registering a NetworkCallback with signal strength but w/o NETWORK_SIGNAL_STRENGTH_WAKEUP + // permission should get SecurityException. + assertThrows(SecurityException.class, () -> + mCm.registerNetworkCallback(r, new NetworkCallback())); + + assertThrows(SecurityException.class, () -> + mCm.registerNetworkCallback(r, PendingIntent.getService( + mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))); + + // Requesting a Network with signal strength should get IllegalArgumentException. + assertThrows(IllegalArgumentException.class, () -> + mCm.requestNetwork(r, new NetworkCallback())); + + assertThrows(IllegalArgumentException.class, () -> + mCm.requestNetwork(r, PendingIntent.getService( + mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))); + } + + @Test + public void testRegisterDefaultNetworkCallback() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler); + systemDefaultCallback.assertNoCallback(); + + // Create a TRANSPORT_CELLULAR request to keep the mobile interface up + // whenever Wi-Fi is up. Without this, the mobile network agent is + // reaped before any other activity can take place. + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + cellNetworkCallback.assertNoCallback(); + + // Bring up cell and expect CALLBACK_AVAILABLE. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring up wifi and expect CALLBACK_AVAILABLE. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + cellNetworkCallback.assertNoCallback(); + defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring down cell. Expect no default network callback, since it wasn't the default. + mCellNetworkAgent.disconnect(); + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring up cell. Expect no default network callback, since it won't be the default. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + // Bring down wifi. Expect the default network callback to notified of LOST wifi + // followed by AVAILABLE cell. + mWiFiNetworkAgent.disconnect(); + cellNetworkCallback.assertNoCallback(); + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + mCellNetworkAgent.disconnect(); + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + waitForIdle(); + assertEquals(null, mCm.getActiveNetwork()); + + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(null, systemDefaultCallback.getLastAvailableNetwork()); + + mMockVpn.disconnect(); + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); + waitForIdle(); + assertEquals(null, mCm.getActiveNetwork()); + } + + @Test + public void testAdditionalStateCallbacks() throws Exception { + // File a network request for mobile. + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + + // Bring up the mobile network. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + // We should get onAvailable(), onCapabilitiesChanged(), and + // onLinkPropertiesChanged() in rapid succession. Additionally, we + // should get onCapabilitiesChanged() when the mobile network validates. + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + + // Update LinkProperties. + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("foonet_data0"); + mCellNetworkAgent.sendLinkProperties(lp); + // We should get onLinkPropertiesChanged(). + cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + + // Suspend the network. + mCellNetworkAgent.suspend(); + cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED, + mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState()); + + // Register a garden variety default network request. + TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(dfltNetworkCallback); + // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(), + // as well as onNetworkSuspended() in rapid succession. + dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true); + dfltNetworkCallback.assertNoCallback(); + mCm.unregisterNetworkCallback(dfltNetworkCallback); + + mCellNetworkAgent.resume(); + cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED, + mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState()); + + dfltNetworkCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(dfltNetworkCallback); + // This time onNetworkSuspended should not be called. + dfltNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + dfltNetworkCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(dfltNetworkCallback); + mCm.unregisterNetworkCallback(cellNetworkCallback); + } + + @Test + public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception { + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false /* validated */); + + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + final TestNetworkCallback callback = new TestNetworkCallback(); + assertThrows(SecurityException.class, + () -> mCm.registerSystemDefaultNetworkCallback(callback, handler)); + callback.assertNoCallback(); + assertThrows(SecurityException.class, + () -> mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler)); + callback.assertNoCallback(); + + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + mCm.registerSystemDefaultNetworkCallback(callback, handler); + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback); + + mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler); + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback); + } + + private void setCaptivePortalMode(int mode) { + ContentResolver cr = mServiceContext.getContentResolver(); + Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode); + } + + private void setAlwaysOnNetworks(boolean enable) { + ContentResolver cr = mServiceContext.getContentResolver(); + Settings.Global.putInt(cr, ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, + enable ? 1 : 0); + mService.updateAlwaysOnNetworks(); + waitForIdle(); + } + + private void setPrivateDnsSettings(int mode, String specifier) { + ConnectivitySettingsManager.setPrivateDnsMode(mServiceContext, mode); + ConnectivitySettingsManager.setPrivateDnsHostname(mServiceContext, specifier); + mService.updatePrivateDnsSettings(); + waitForIdle(); + } + + private boolean isForegroundNetwork(TestNetworkAgentWrapper network) { + NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork()); + assertNotNull(nc); + return nc.hasCapability(NET_CAPABILITY_FOREGROUND); + } + + @Test + public void testBackgroundNetworks() throws Exception { + // Create a cellular background request. + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback cellBgCallback = new TestNetworkCallback(); + mCm.requestBackgroundNetwork(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), + cellBgCallback, mCsHandlerThread.getThreadHandler()); + + // Make callbacks for monitoring. + final NetworkRequest request = new NetworkRequest.Builder().build(); + final NetworkRequest fgRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_FOREGROUND).build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + final TestNetworkCallback fgCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + mCm.registerNetworkCallback(fgRequest, fgCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertTrue(isForegroundNetwork(mCellNetworkAgent)); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + + // When wifi connects, cell lingers. + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + fgCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + assertTrue(isForegroundNetwork(mCellNetworkAgent)); + assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); + + // When lingering is complete, cell is still there but is now in the background. + waitForIdle(); + int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; + fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs); + // Expect a network capabilities update sans FOREGROUND. + callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); + assertFalse(isForegroundNetwork(mCellNetworkAgent)); + assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); + + // File a cell request and check that cell comes into the foreground. + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + final TestNetworkCallback cellCallback = new TestNetworkCallback(); + mCm.requestNetwork(cellRequest, cellCallback); + cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + // Expect a network capabilities update with FOREGROUND, because the most recent + // request causes its state to change. + cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); + assertTrue(isForegroundNetwork(mCellNetworkAgent)); + assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); + + // Release the request. The network immediately goes into the background, since it was not + // lingering. + mCm.unregisterNetworkCallback(cellCallback); + fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + // Expect a network capabilities update sans FOREGROUND. + callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); + assertFalse(isForegroundNetwork(mCellNetworkAgent)); + assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); + + // Disconnect wifi and check that cell is foreground again. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + fgCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertTrue(isForegroundNetwork(mCellNetworkAgent)); + + mCm.unregisterNetworkCallback(callback); + mCm.unregisterNetworkCallback(fgCallback); + mCm.unregisterNetworkCallback(cellBgCallback); + } + + @Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing. + public void benchmarkRequestRegistrationAndCallbackDispatch() throws Exception { + // TODO: turn this unit test into a real benchmarking test. + // Benchmarks connecting and switching performance in the presence of a large number of + // NetworkRequests. + // 1. File NUM_REQUESTS requests. + // 2. Have a network connect. Wait for NUM_REQUESTS onAvailable callbacks to fire. + // 3. Have a new network connect and outscore the previous. Wait for NUM_REQUESTS onLosing + // and NUM_REQUESTS onAvailable callbacks to fire. + // See how long it took. + final int NUM_REQUESTS = 90; + final int REGISTER_TIME_LIMIT_MS = 200; + final int CONNECT_TIME_LIMIT_MS = 60; + final int SWITCH_TIME_LIMIT_MS = 60; + final int UNREGISTER_TIME_LIMIT_MS = 20; + + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + final NetworkCallback[] callbacks = new NetworkCallback[NUM_REQUESTS]; + final CountDownLatch availableLatch = new CountDownLatch(NUM_REQUESTS); + final CountDownLatch losingLatch = new CountDownLatch(NUM_REQUESTS); + + for (int i = 0; i < NUM_REQUESTS; i++) { + callbacks[i] = new NetworkCallback() { + @Override public void onAvailable(Network n) { availableLatch.countDown(); } + @Override public void onLosing(Network n, int t) { losingLatch.countDown(); } + }; + } + + assertRunsInAtMost("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> { + for (NetworkCallback cb : callbacks) { + mCm.registerNetworkCallback(request, cb); + } + }); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + // Don't request that the network validate, because otherwise connect() will block until + // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired, + // and we won't actually measure anything. + mCellNetworkAgent.connect(false); + + long onAvailableDispatchingDuration = durationOf(() -> { + await(availableLatch, 10 * CONNECT_TIME_LIMIT_MS); + }); + Log.d(TAG, String.format("Dispatched %d of %d onAvailable callbacks in %dms", + NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS, + onAvailableDispatchingDuration)); + assertTrue(String.format("Dispatching %d onAvailable callbacks in %dms, expected %dms", + NUM_REQUESTS, onAvailableDispatchingDuration, CONNECT_TIME_LIMIT_MS), + onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS); + + // Give wifi a high enough score that we'll linger cell when wifi comes up. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.adjustScore(40); + mWiFiNetworkAgent.connect(false); + + long onLostDispatchingDuration = durationOf(() -> { + await(losingLatch, 10 * SWITCH_TIME_LIMIT_MS); + }); + Log.d(TAG, String.format("Dispatched %d of %d onLosing callbacks in %dms", + NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, onLostDispatchingDuration)); + assertTrue(String.format("Dispatching %d onLosing callbacks in %dms, expected %dms", + NUM_REQUESTS, onLostDispatchingDuration, SWITCH_TIME_LIMIT_MS), + onLostDispatchingDuration <= SWITCH_TIME_LIMIT_MS); + + assertRunsInAtMost("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> { + for (NetworkCallback cb : callbacks) { + mCm.unregisterNetworkCallback(cb); + } + }); + } + + @Test + public void testMobileDataAlwaysOn() throws Exception { + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + + final HandlerThread handlerThread = new HandlerThread("MobileDataAlwaysOnFactory"); + handlerThread.start(); + NetworkCapabilities filter = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .addCapability(NET_CAPABILITY_INTERNET); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.setScoreFilter(40); + + // Register the factory and expect it to start looking for a network. + testFactory.register(); + + try { + // Expect the factory to receive the default network request. + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + assertTrue(testFactory.getMyStartRequested()); + + // Bring up wifi. The factory stops looking for a network. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + // Score 60 - 40 penalty for not validated yet, then 60 when it validates + mWiFiNetworkAgent.connect(true); + // The network connects with a low score, so the offer can still beat it and + // nothing happens. Then the network validates, and the offer with its filter score + // of 40 can no longer beat it and the request is removed. + testFactory.expectRequestRemove(); + testFactory.assertRequestCountEquals(0); + + assertFalse(testFactory.getMyStartRequested()); + + // Turn on mobile data always on. This request will not match the wifi request, so + // it will be sent to the test factory whose filters allow to see it. + setAlwaysOnNetworks(true); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + + assertTrue(testFactory.getMyStartRequested()); + + // Bring up cell data and check that the factory stops looking. + assertLength(1, mCm.getAllNetworks()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false); + cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent, false, false, false, + TEST_CALLBACK_TIMEOUT_MS); + // When cell connects, it will satisfy the "mobile always on request" right away + // by virtue of being the only network that can satisfy the request. However, its + // score is low (50 - 40 = 10) so the test factory can still hope to beat it. + expectNoRequestChanged(testFactory); + + // Next, cell validates. This gives it a score of 50 and the test factory can't + // hope to beat that according to its filters. It will see the message that its + // offer is now unnecessary. + mCellNetworkAgent.setNetworkValid(true); + // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is + // validated – see testPartialConnectivity. + mCm.reportNetworkConnectivity(mCellNetworkAgent.getNetwork(), true); + cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent); + testFactory.expectRequestRemove(); + testFactory.assertRequestCountEquals(0); + // Accordingly, the factory shouldn't be started. + assertFalse(testFactory.getMyStartRequested()); + + // Check that cell data stays up. + waitForIdle(); + verifyActiveNetwork(TRANSPORT_WIFI); + assertLength(2, mCm.getAllNetworks()); + + // Cell disconnects. There is still the "mobile data always on" request outstanding, + // and the test factory should see it now that it isn't hopelessly outscored. + mCellNetworkAgent.disconnect(); + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + assertLength(1, mCm.getAllNetworks()); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + + // Reconnect cell validated, see the request disappear again. Then withdraw the + // mobile always on request. This will tear down cell, and there shouldn't be a + // blip where the test factory briefly sees the request or anything. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertLength(2, mCm.getAllNetworks()); + testFactory.expectRequestRemove(); + testFactory.assertRequestCountEquals(0); + setAlwaysOnNetworks(false); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(0); + assertFalse(testFactory.getMyStartRequested()); + // ... and cell data to be torn down immediately since it is no longer nascent. + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + waitForIdle(); + assertLength(1, mCm.getAllNetworks()); + } finally { + testFactory.terminate(); + mCm.unregisterNetworkCallback(cellNetworkCallback); + handlerThread.quit(); + } + } + + @Test + public void testAvoidBadWifiSetting() throws Exception { + final ContentResolver cr = mServiceContext.getContentResolver(); + final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI; + + mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; + String[] values = new String[] {null, "0", "1"}; + for (int i = 0; i < values.length; i++) { + Settings.Global.putInt(cr, settingName, 1); + mPolicyTracker.reevaluate(); + waitForIdle(); + String msg = String.format("config=false, setting=%s", values[i]); + assertTrue(mService.avoidBadWifi()); + assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated()); + } + + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; + + Settings.Global.putInt(cr, settingName, 0); + mPolicyTracker.reevaluate(); + waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); + + Settings.Global.putInt(cr, settingName, 1); + mPolicyTracker.reevaluate(); + waitForIdle(); + assertTrue(mService.avoidBadWifi()); + assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); + + Settings.Global.putString(cr, settingName, null); + mPolicyTracker.reevaluate(); + waitForIdle(); + assertFalse(mService.avoidBadWifi()); + assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated()); + } + + @Ignore("Refactoring in progress b/178071397") + @Test + public void testAvoidBadWifi() throws Exception { + final ContentResolver cr = mServiceContext.getContentResolver(); + + // Pretend we're on a carrier that restricts switching away from bad wifi. + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; + + // File a request for cell to ensure it doesn't go down. + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + + TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + NetworkRequest validatedWifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_VALIDATED) + .build(); + TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); + + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 0); + mPolicyTracker.reevaluate(); + + // Bring up validated cell. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + Network cellNetwork = mCellNetworkAgent.getNetwork(); + + // Bring up validated wifi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); + + // Fail validation on wifi. + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); + mCm.reportNetworkConnectivity(wifiNetwork, false); + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Because avoid bad wifi is off, we don't switch to cellular. + defaultCallback.assertNoCallback(); + assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + + // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect + // that we switch back to cell. + mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; + mPolicyTracker.reevaluate(); + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Switch back to a restrictive carrier. + mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; + mPolicyTracker.reevaluate(); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + + // Simulate the user selecting "switch" on the dialog, and check that we switch to cell. + mCm.setAvoidUnvalidated(wifiNetwork); + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Disconnect and reconnect wifi to clear the one-time switch above. + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + wifiNetwork = mWiFiNetworkAgent.getNetwork(); + + // Fail validation on wifi and expect the dialog to appear. + mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); + mCm.reportNetworkConnectivity(wifiNetwork, false); + defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Simulate the user selecting "switch" and checking the don't ask again checkbox. + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); + mPolicyTracker.reevaluate(); + + // We now switch to cell. + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( + NET_CAPABILITY_VALIDATED)); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // Simulate the user turning the cellular fallback setting off and then on. + // We switch to wifi and then to cell. + Settings.Global.putString(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null); + mPolicyTracker.reevaluate(); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mCm.getActiveNetwork(), wifiNetwork); + Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); + mPolicyTracker.reevaluate(); + defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertEquals(mCm.getActiveNetwork(), cellNetwork); + + // If cell goes down, we switch to wifi. + mCellNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + validatedWifiCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(cellNetworkCallback); + mCm.unregisterNetworkCallback(validatedWifiCallback); + mCm.unregisterNetworkCallback(defaultCallback); + } + + @Test + public void testMeteredMultipathPreferenceSetting() throws Exception { + final ContentResolver cr = mServiceContext.getContentResolver(); + final String settingName = ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE; + + for (int config : Arrays.asList(0, 3, 2)) { + for (String setting: Arrays.asList(null, "0", "2", "1")) { + mPolicyTracker.mConfigMeteredMultipathPreference = config; + Settings.Global.putString(cr, settingName, setting); + mPolicyTracker.reevaluate(); + waitForIdle(); + + final int expected = (setting != null) ? Integer.parseInt(setting) : config; + String msg = String.format("config=%d, setting=%s", config, setting); + assertEquals(msg, expected, mCm.getMultipathPreference(null)); + } + } + } + + /** + * Validate that a satisfied network request does not trigger onUnavailable() once the + * time-out period expires. + */ + @Test + public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, + TEST_CALLBACK_TIMEOUT_MS); + + // pass timeout and validate that UNAVAILABLE is not called + networkCallback.assertNoCallback(); + } + + /** + * Validate that a satisfied network request followed by a disconnected (lost) network does + * not trigger onUnavailable() once the time-out period expires. + */ + @Test + public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, + TEST_CALLBACK_TIMEOUT_MS); + mWiFiNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // Validate that UNAVAILABLE is not called + networkCallback.assertNoCallback(); + } + + /** + * Validate that when a time-out is specified for a network request the onUnavailable() + * callback is called when time-out expires. Then validate that if network request is + * (somehow) satisfied - the callback isn't called later. + */ + @Test + public void testTimedoutNetworkRequest() throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + final int timeoutMs = 10; + mCm.requestNetwork(nr, networkCallback, timeoutMs); + + // pass timeout and validate that UNAVAILABLE is called + networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null); + + // create a network satisfying request - validate that request not triggered + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.assertNoCallback(); + } + + /** + * Validate that when a network request is unregistered (cancelled), no posterior event can + * trigger the callback. + */ + @Test + public void testNoCallbackAfterUnregisteredNetworkRequest() throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + final int timeoutMs = 10; + + mCm.requestNetwork(nr, networkCallback, timeoutMs); + mCm.unregisterNetworkCallback(networkCallback); + // Regardless of the timeout, unregistering the callback in ConnectivityManager ensures + // that this callback will not be called. + networkCallback.assertNoCallback(); + + // create a network satisfying request - validate that request not triggered + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + networkCallback.assertNoCallback(); + } + + @Test + public void testUnfulfillableNetworkRequest() throws Exception { + runUnfulfillableNetworkRequest(false); + } + + @Test + public void testUnfulfillableNetworkRequestAfterUnregister() throws Exception { + runUnfulfillableNetworkRequest(true); + } + + /** + * Validate the callback flow for a factory releasing a request as unfulfillable. + */ + private void runUnfulfillableNetworkRequest(boolean preUnregister) throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + + final HandlerThread handlerThread = new HandlerThread("testUnfulfillableNetworkRequest"); + handlerThread.start(); + NetworkCapabilities filter = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.setScoreFilter(40); + + // Register the factory and expect it to receive the default request. + testFactory.register(); + testFactory.expectRequestAdd(); + + try { + // Now file the test request and expect it. + mCm.requestNetwork(nr, networkCallback); + final NetworkRequest newRequest = testFactory.expectRequestAdd().request; + + if (preUnregister) { + mCm.unregisterNetworkCallback(networkCallback); + + // The request has been released : the factory should see it removed + // immediately. + testFactory.expectRequestRemove(); + + // Simulate the factory releasing the request as unfulfillable: no-op since + // the callback has already been unregistered (but a test that no exceptions are + // thrown). + testFactory.triggerUnfulfillable(newRequest); + } else { + // Simulate the factory releasing the request as unfulfillable and expect + // onUnavailable! + testFactory.triggerUnfulfillable(newRequest); + + networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null); + + // Declaring a request unfulfillable releases it automatically. + testFactory.expectRequestRemove(); + + // unregister network callback - a no-op (since already freed by the + // on-unavailable), but should not fail or throw exceptions. + mCm.unregisterNetworkCallback(networkCallback); + + // The factory should not see any further removal, as this request has + // already been removed. + } + } finally { + testFactory.terminate(); + handlerThread.quit(); + } + } + + private static class TestKeepaliveCallback extends PacketKeepaliveCallback { + + public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR } + + private class CallbackValue { + public CallbackType callbackType; + public int error; + + public CallbackValue(CallbackType type) { + this.callbackType = type; + this.error = PacketKeepalive.SUCCESS; + assertTrue("onError callback must have error", type != CallbackType.ON_ERROR); + } + + public CallbackValue(CallbackType type, int error) { + this.callbackType = type; + this.error = error; + assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR); + } + + @Override + public boolean equals(Object o) { + return o instanceof CallbackValue && + this.callbackType == ((CallbackValue) o).callbackType && + this.error == ((CallbackValue) o).error; + } + + @Override + public String toString() { + return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error); + } + } + + private final LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>(); + + @Override + public void onStarted() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED)); + } + + @Override + public void onStopped() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED)); + } + + @Override + public void onError(int error) { + mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error)); + } + + private void expectCallback(CallbackValue callbackValue) throws InterruptedException { + assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + public void expectStarted() throws Exception { + expectCallback(new CallbackValue(CallbackType.ON_STARTED)); + } + + public void expectStopped() throws Exception { + expectCallback(new CallbackValue(CallbackType.ON_STOPPED)); + } + + public void expectError(int error) throws Exception { + expectCallback(new CallbackValue(CallbackType.ON_ERROR, error)); + } + } + + private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback { + + public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; + + private class CallbackValue { + public CallbackType callbackType; + public int error; + + CallbackValue(CallbackType type) { + this.callbackType = type; + this.error = SocketKeepalive.SUCCESS; + assertTrue("onError callback must have error", type != CallbackType.ON_ERROR); + } + + CallbackValue(CallbackType type, int error) { + this.callbackType = type; + this.error = error; + assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR); + } + + @Override + public boolean equals(Object o) { + return o instanceof CallbackValue + && this.callbackType == ((CallbackValue) o).callbackType + && this.error == ((CallbackValue) o).error; + } + + @Override + public String toString() { + return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, + error); + } + } + + private LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>(); + private final Executor mExecutor; + + TestSocketKeepaliveCallback(@NonNull Executor executor) { + mExecutor = executor; + } + + @Override + public void onStarted() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED)); + } + + @Override + public void onStopped() { + mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED)); + } + + @Override + public void onError(int error) { + mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error)); + } + + private void expectCallback(CallbackValue callbackValue) throws InterruptedException { + assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + } + + public void expectStarted() throws InterruptedException { + expectCallback(new CallbackValue(CallbackType.ON_STARTED)); + } + + public void expectStopped() throws InterruptedException { + expectCallback(new CallbackValue(CallbackType.ON_STOPPED)); + } + + public void expectError(int error) throws InterruptedException { + expectCallback(new CallbackValue(CallbackType.ON_ERROR, error)); + } + + public void assertNoCallback() { + waitForIdleSerialExecutor(mExecutor, TIMEOUT_MS); + CallbackValue cv = mCallbacks.peek(); + assertNull("Unexpected callback: " + cv, cv); + } + } + + private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception { + // Ensure the network is disconnected before anything else occurs + if (mWiFiNetworkAgent != null) { + assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork())); + } + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mWiFiNetworkAgent.connect(true); + b.expectBroadcast(); + verifyActiveNetwork(TRANSPORT_WIFI); + mWiFiNetworkAgent.sendLinkProperties(lp); + waitForIdle(); + return mWiFiNetworkAgent.getNetwork(); + } + + @Test + public void testPacketKeepalives() throws Exception { + InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); + InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); + InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); + InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); + InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888"); + + final int validKaInterval = 15; + final int invalidKaInterval = 9; + + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan12"); + lp.addLinkAddress(new LinkAddress(myIPv6, 64)); + lp.addLinkAddress(new LinkAddress(myIPv4, 25)); + lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + + Network notMyNet = new Network(61234); + Network myNet = connectKeepaliveNetwork(lp); + + TestKeepaliveCallback callback = new TestKeepaliveCallback(); + PacketKeepalive ka; + + // Attempt to start keepalives with invalid parameters and check for errors. + ka = mCm.startNattKeepalive(notMyNet, validKaInterval, callback, myIPv4, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); + + ka = mCm.startNattKeepalive(myNet, invalidKaInterval, callback, myIPv4, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 1234, dstIPv6); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + + // NAT-T is only supported for IPv4. + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); + + // Check that a started keepalive can be stopped. + mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS); + ka.stop(); + callback.expectStopped(); + + // Check that deleting the IP address stops the keepalive. + LinkProperties bogusLp = new LinkProperties(lp); + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25)); + bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25)); + mWiFiNetworkAgent.sendLinkProperties(bogusLp); + callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); + mWiFiNetworkAgent.sendLinkProperties(lp); + + // Check that a started keepalive is stopped correctly when the network disconnects. + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); + + // ... and that stopping it after that has no adverse effects. + waitForIdle(); + final Network myNetAlias = myNet; + assertNull(mCm.getNetworkCapabilities(myNetAlias)); + ka.stop(); + + // Reconnect. + myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); + + // Check that keepalive slots start from 1 and increment. The first one gets slot 1. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); + ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); + callback.expectStarted(); + + // The second one gets slot 2. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); + TestKeepaliveCallback callback2 = new TestKeepaliveCallback(); + PacketKeepalive ka2 = mCm.startNattKeepalive( + myNet, validKaInterval, callback2, myIPv4, 6789, dstIPv4); + callback2.expectStarted(); + + // Now stop the first one and create a third. This also gets slot 1. + ka.stop(); + callback.expectStopped(); + + mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); + TestKeepaliveCallback callback3 = new TestKeepaliveCallback(); + PacketKeepalive ka3 = mCm.startNattKeepalive( + myNet, validKaInterval, callback3, myIPv4, 9876, dstIPv4); + callback3.expectStarted(); + + ka2.stop(); + callback2.expectStopped(); + + ka3.stop(); + callback3.expectStopped(); + } + + // Helper method to prepare the executor and run test + private void runTestWithSerialExecutors(ExceptionUtils.ThrowingConsumer functor) + throws Exception { + final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor(); + final Executor executorInline = (Runnable r) -> r.run(); + functor.accept(executorSingleThread); + executorSingleThread.shutdown(); + functor.accept(executorInline); + } + + @Test + public void testNattSocketKeepalives() throws Exception { + runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesWithExecutor(executor)); + runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesFdWithExecutor(executor)); + } + + private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception { + // TODO: 1. Move this outside of ConnectivityServiceTest. + // 2. Make test to verify that Nat-T keepalive socket is created by IpSecService. + // 3. Mock ipsec service. + final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); + final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); + final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); + final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); + final InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888"); + + final int validKaInterval = 15; + final int invalidKaInterval = 9; + + final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); + final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(); + final int srcPort = testSocket.getPort(); + + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan12"); + lp.addLinkAddress(new LinkAddress(myIPv6, 64)); + lp.addLinkAddress(new LinkAddress(myIPv4, 25)); + lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + + Network notMyNet = new Network(61234); + Network myNet = connectKeepaliveNetwork(lp); + + TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); + + // Attempt to start keepalives with invalid parameters and check for errors. + // Invalid network. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + notMyNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); + } + + // Invalid interval. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(invalidKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_INTERVAL); + } + + // Invalid destination. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv6, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); + } + + // Invalid source; + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv6, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); + } + + // NAT-T is only supported for IPv4. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv6, dstIPv6, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); + } + + // Basic check before testing started keepalive. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_UNSUPPORTED); + } + + // Check that a started keepalive can be stopped. + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); + ka.stop(); + callback.expectStopped(); + + // Check that keepalive could be restarted. + ka.start(validKaInterval); + callback.expectStarted(); + ka.stop(); + callback.expectStopped(); + + // Check that keepalive can be restarted without waiting for callback. + ka.start(validKaInterval); + callback.expectStarted(); + ka.stop(); + ka.start(validKaInterval); + callback.expectStopped(); + callback.expectStarted(); + ka.stop(); + callback.expectStopped(); + } + + // Check that deleting the IP address stops the keepalive. + LinkProperties bogusLp = new LinkProperties(lp); + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25)); + bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25)); + mWiFiNetworkAgent.sendLinkProperties(bogusLp); + callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); + mWiFiNetworkAgent.sendLinkProperties(lp); + } + + // Check that a started keepalive is stopped correctly when the network disconnects. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); + + // ... and that stopping it after that has no adverse effects. + waitForIdle(); + final Network myNetAlias = myNet; + assertNull(mCm.getNetworkCapabilities(myNetAlias)); + ka.stop(); + callback.assertNoCallback(); + } + + // Reconnect. + myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); + + // Check that a stop followed by network disconnects does not result in crash. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + // Delay the response of keepalive events in networkAgent long enough to make sure + // the follow-up network disconnection will be processed first. + mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS); + ka.stop(); + + // Make sure the stop has been processed. Wait for executor idle is needed to prevent + // flaky since the actual stop call to the service is delegated to executor thread. + waitForIdleSerialExecutor(executor, TIMEOUT_MS); + waitForIdle(); + + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + callback.expectStopped(); + callback.assertNoCallback(); + } + + // Reconnect. + waitForIdle(); + myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); + + // Check that keepalive slots start from 1 and increment. The first one gets slot 1. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); + int srcPort2 = 0; + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + + // The second one gets slot 2. + mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); + final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(); + srcPort2 = testSocket2.getPort(); + TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback(executor); + try (SocketKeepalive ka2 = mCm.createSocketKeepalive( + myNet, testSocket2, myIPv4, dstIPv4, executor, callback2)) { + ka2.start(validKaInterval); + callback2.expectStarted(); + + ka.stop(); + callback.expectStopped(); + + ka2.stop(); + callback2.expectStopped(); + + testSocket.close(); + testSocket2.close(); + } + } + + // Check that there is no port leaked after all keepalives and sockets are closed. + // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7. + // assertFalse(isUdpPortInUse(srcPort)); + // assertFalse(isUdpPortInUse(srcPort2)); + + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent = null; + } + + @Test + public void testTcpSocketKeepalives() throws Exception { + runTestWithSerialExecutors(executor -> doTestTcpSocketKeepalivesWithExecutor(executor)); + } + + private void doTestTcpSocketKeepalivesWithExecutor(Executor executor) throws Exception { + final int srcPortV4 = 12345; + final int srcPortV6 = 23456; + final InetAddress myIPv4 = InetAddress.getByName("127.0.0.1"); + final InetAddress myIPv6 = InetAddress.getByName("::1"); + + final int validKaInterval = 15; + + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan12"); + lp.addLinkAddress(new LinkAddress(myIPv6, 64)); + lp.addLinkAddress(new LinkAddress(myIPv4, 25)); + lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); + lp.addRoute(new RouteInfo(InetAddress.getByName("127.0.0.254"))); + + final Network notMyNet = new Network(61234); + final Network myNet = connectKeepaliveNetwork(lp); + + final Socket testSocketV4 = new Socket(); + final Socket testSocketV6 = new Socket(); + + TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); + + // Attempt to start Tcp keepalives with invalid parameters and check for errors. + // Invalid network. + try (SocketKeepalive ka = mCm.createSocketKeepalive( + notMyNet, testSocketV4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); + } + + // Invalid Socket (socket is not bound with IPv4 address). + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocketV4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); + } + + // Invalid Socket (socket is not bound with IPv6 address). + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocketV6, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); + } + + // Bind the socket address + testSocketV4.bind(new InetSocketAddress(myIPv4, srcPortV4)); + testSocketV6.bind(new InetSocketAddress(myIPv6, srcPortV6)); + + // Invalid Socket (socket is bound with IPv4 address). + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocketV4, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); + } + + // Invalid Socket (socket is bound with IPv6 address). + try (SocketKeepalive ka = mCm.createSocketKeepalive( + myNet, testSocketV6, executor, callback)) { + ka.start(validKaInterval); + callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); + } + + testSocketV4.close(); + testSocketV6.close(); + + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent = null; + } + + private void doTestNattSocketKeepalivesFdWithExecutor(Executor executor) throws Exception { + final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); + final InetAddress anyIPv4 = InetAddress.getByName("0.0.0.0"); + final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); + final int validKaInterval = 15; + + // Prepare the target network. + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("wlan12"); + lp.addLinkAddress(new LinkAddress(myIPv4, 25)); + lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); + Network myNet = connectKeepaliveNetwork(lp); + mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); + mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); + + TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); + + // Prepare the target file descriptor, keep only one instance. + final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); + final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(); + final int srcPort = testSocket.getPort(); + final ParcelFileDescriptor testPfd = + ParcelFileDescriptor.dup(testSocket.getFileDescriptor()); + testSocket.close(); + assertTrue(isUdpPortInUse(srcPort)); + + // Start keepalive and explicit make the variable goes out of scope with try-with-resources + // block. + try (SocketKeepalive ka = mCm.createNattKeepalive( + myNet, testPfd, myIPv4, dstIPv4, executor, callback)) { + ka.start(validKaInterval); + callback.expectStarted(); + ka.stop(); + callback.expectStopped(); + } + + // Check that the ParcelFileDescriptor is still valid after keepalive stopped, + // ErrnoException with EBADF will be thrown if the socket is closed when checking local + // address. + assertTrue(isUdpPortInUse(srcPort)); + final InetSocketAddress sa = + (InetSocketAddress) Os.getsockname(testPfd.getFileDescriptor()); + assertEquals(anyIPv4, sa.getAddress()); + + testPfd.close(); + // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7. + // assertFalse(isUdpPortInUse(srcPort)); + + mWiFiNetworkAgent.disconnect(); + mWiFiNetworkAgent.expectDisconnected(); + mWiFiNetworkAgent = null; + } + + private static boolean isUdpPortInUse(int port) { + try (DatagramSocket ignored = new DatagramSocket(port)) { + return false; + } catch (IOException alreadyInUse) { + return true; + } + } + + @Test + public void testGetCaptivePortalServerUrl() throws Exception { + String url = mCm.getCaptivePortalServerUrl(); + assertEquals("http://connectivitycheck.gstatic.com/generate_204", url); + } + + private static class TestNetworkPinner extends NetworkPinner { + public static boolean awaitPin(int timeoutMs) throws InterruptedException { + synchronized(sLock) { + if (sNetwork == null) { + sLock.wait(timeoutMs); + } + return sNetwork != null; + } + } + + public static boolean awaitUnpin(int timeoutMs) throws InterruptedException { + synchronized(sLock) { + if (sNetwork != null) { + sLock.wait(timeoutMs); + } + return sNetwork == null; + } + } + } + + private void assertPinnedToWifiWithCellDefault() { + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getBoundNetworkForProcess()); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + } + + private void assertPinnedToWifiWithWifiDefault() { + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getBoundNetworkForProcess()); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + } + + private void assertNotPinnedToWifi() { + assertNull(mCm.getBoundNetworkForProcess()); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + } + + @Test + public void testNetworkPinner() throws Exception { + NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI) + .build(); + assertNull(mCm.getBoundNetworkForProcess()); + + TestNetworkPinner.pin(mServiceContext, wifiRequest); + assertNull(mCm.getBoundNetworkForProcess()); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + + // When wi-fi connects, expect to be pinned. + assertTrue(TestNetworkPinner.awaitPin(100)); + assertPinnedToWifiWithCellDefault(); + + // Disconnect and expect the pin to drop. + mWiFiNetworkAgent.disconnect(); + assertTrue(TestNetworkPinner.awaitUnpin(100)); + assertNotPinnedToWifi(); + + // Reconnecting does not cause the pin to come back. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + assertFalse(TestNetworkPinner.awaitPin(100)); + assertNotPinnedToWifi(); + + // Pinning while connected causes the pin to take effect immediately. + TestNetworkPinner.pin(mServiceContext, wifiRequest); + assertTrue(TestNetworkPinner.awaitPin(100)); + assertPinnedToWifiWithCellDefault(); + + // Explicitly unpin and expect to use the default network again. + TestNetworkPinner.unpin(); + assertNotPinnedToWifi(); + + // Disconnect cell and wifi. + ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. + mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); + b.expectBroadcast(); + + // Pinning takes effect even if the pinned network is the default when the pin is set... + TestNetworkPinner.pin(mServiceContext, wifiRequest); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + assertTrue(TestNetworkPinner.awaitPin(100)); + assertPinnedToWifiWithWifiDefault(); + + // ... and is maintained even when that network is no longer the default. + b = registerConnectivityBroadcast(1); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mCellNetworkAgent.connect(true); + b.expectBroadcast(); + assertPinnedToWifiWithCellDefault(); + } + + @Test + public void testNetworkCallbackMaximum() throws Exception { + final int MAX_REQUESTS = 100; + final int CALLBACKS = 89; + final int INTENTS = 11; + final int SYSTEM_ONLY_MAX_REQUESTS = 250; + assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS); + + NetworkRequest networkRequest = new NetworkRequest.Builder().build(); + ArrayList registered = new ArrayList<>(); + + int j = 0; + while (j++ < CALLBACKS / 2) { + NetworkCallback cb = new NetworkCallback(); + mCm.requestNetwork(networkRequest, cb); + registered.add(cb); + } + while (j++ < CALLBACKS) { + NetworkCallback cb = new NetworkCallback(); + mCm.registerNetworkCallback(networkRequest, cb); + registered.add(cb); + } + j = 0; + while (j++ < INTENTS / 2) { + final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, + new Intent("a" + j), FLAG_IMMUTABLE); + mCm.requestNetwork(networkRequest, pi); + registered.add(pi); + } + while (j++ < INTENTS) { + final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, + new Intent("b" + j), FLAG_IMMUTABLE); + mCm.registerNetworkCallback(networkRequest, pi); + registered.add(pi); + } + + // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added. + assertThrows(TooManyRequestsException.class, () -> + mCm.requestNetwork(networkRequest, new NetworkCallback()) + ); + assertThrows(TooManyRequestsException.class, () -> + mCm.registerNetworkCallback(networkRequest, new NetworkCallback()) + ); + assertThrows(TooManyRequestsException.class, () -> + mCm.requestNetwork(networkRequest, + PendingIntent.getBroadcast(mContext, 0 /* requestCode */, + new Intent("c"), FLAG_IMMUTABLE)) + ); + assertThrows(TooManyRequestsException.class, () -> + mCm.registerNetworkCallback(networkRequest, + PendingIntent.getBroadcast(mContext, 0 /* requestCode */, + new Intent("d"), FLAG_IMMUTABLE)) + ); + + // The system gets another SYSTEM_ONLY_MAX_REQUESTS slots. + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { + ArrayList systemRegistered = new ArrayList<>(); + for (int i = 0; i < SYSTEM_ONLY_MAX_REQUESTS - 1; i++) { + NetworkCallback cb = new NetworkCallback(); + if (i % 2 == 0) { + mCm.registerDefaultNetworkCallbackForUid(1000000 + i, cb, handler); + } else { + mCm.registerNetworkCallback(networkRequest, cb); + } + systemRegistered.add(cb); + } + waitForIdle(); + + assertThrows(TooManyRequestsException.class, () -> + mCm.registerDefaultNetworkCallbackForUid(1001042, new NetworkCallback(), + handler)); + assertThrows(TooManyRequestsException.class, () -> + mCm.registerNetworkCallback(networkRequest, new NetworkCallback())); + + for (NetworkCallback callback : systemRegistered) { + mCm.unregisterNetworkCallback(callback); + } + waitForIdle(); // Wait for requests to be unregistered before giving up the permission. + }); + + for (Object o : registered) { + if (o instanceof NetworkCallback) { + mCm.unregisterNetworkCallback((NetworkCallback)o); + } + if (o instanceof PendingIntent) { + mCm.unregisterNetworkCallback((PendingIntent)o); + } + } + waitForIdle(); + + // Test that the limit is not hit when MAX_REQUESTS requests are added and removed. + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.requestNetwork(networkRequest, networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallback(networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallback(networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallbackForUid(1000000 + i, networkCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + mCm.unregisterNetworkCallback(networkCallback); + } + }); + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + final PendingIntent pendingIntent = PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE); + mCm.requestNetwork(networkRequest, pendingIntent); + mCm.unregisterNetworkCallback(pendingIntent); + } + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + final PendingIntent pendingIntent = PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent("f" + i), FLAG_IMMUTABLE); + mCm.registerNetworkCallback(networkRequest, pendingIntent); + mCm.unregisterNetworkCallback(pendingIntent); + } + } + + @Test + public void testNetworkInfoOfTypeNone() throws Exception { + ExpectedBroadcast b = registerConnectivityBroadcast(1); + + verifyNoNetwork(); + TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); + assertNull(mCm.getActiveNetworkInfo()); + + Network[] allNetworks = mCm.getAllNetworks(); + assertLength(1, allNetworks); + Network network = allNetworks[0]; + NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network); + assertTrue(capabilities.hasTransport(TRANSPORT_WIFI_AWARE)); + + final NetworkRequest request = + new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI_AWARE).build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + // Bring up wifi aware network. + wifiAware.connect(false, false, false /* isStrictMode */); + callback.expectAvailableCallbacksUnvalidated(wifiAware); + + assertNull(mCm.getActiveNetworkInfo()); + assertNull(mCm.getActiveNetwork()); + // TODO: getAllNetworkInfo is dirty and returns a non-empty array right from the start + // of this test. Fix it and uncomment the assert below. + //assertEmpty(mCm.getAllNetworkInfo()); + + // Disconnect wifi aware network. + wifiAware.disconnect(); + callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackEntry.Lost); + mCm.unregisterNetworkCallback(callback); + + verifyNoNetwork(); + b.expectNoBroadcast(10); + } + + @Test + public void testDeprecatedAndUnsupportedOperations() throws Exception { + final int TYPE_NONE = ConnectivityManager.TYPE_NONE; + assertNull(mCm.getNetworkInfo(TYPE_NONE)); + assertNull(mCm.getNetworkForType(TYPE_NONE)); + assertNull(mCm.getLinkProperties(TYPE_NONE)); + assertFalse(mCm.isNetworkSupported(TYPE_NONE)); + + assertThrows(IllegalArgumentException.class, + () -> mCm.networkCapabilitiesForType(TYPE_NONE)); + + Class unsupported = UnsupportedOperationException.class; + assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_WIFI, "")); + assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_WIFI, "")); + // TODO: let test context have configuration application target sdk version + // and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED + assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_NONE, "")); + assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_NONE, "")); + assertThrows(unsupported, () -> mCm.requestRouteToHostAddress(TYPE_NONE, null)); + } + + @Test + public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() throws Exception { + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(WIFI_IFNAME); + LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24"); + RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null, + InetAddresses.parseNumericAddress("192.168.12.1"), lp.getInterfaceName()); + lp.addLinkAddress(myIpv4Address); + lp.addRoute(myIpv4DefaultRoute); + + // Verify direct routes are added when network agent is first registered in + // ConnectivityService. + TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); + networkAgent.connect(true); + networkCallback.expectCallback(CallbackEntry.AVAILABLE, networkAgent); + networkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent); + CallbackEntry.LinkPropertiesChanged cbi = + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + networkAgent); + networkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, networkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent); + networkCallback.assertNoCallback(); + checkDirectlyConnectedRoutes(cbi.getLp(), Arrays.asList(myIpv4Address), + Arrays.asList(myIpv4DefaultRoute)); + checkDirectlyConnectedRoutes(mCm.getLinkProperties(networkAgent.getNetwork()), + Arrays.asList(myIpv4Address), Arrays.asList(myIpv4DefaultRoute)); + + // Verify direct routes are added during subsequent link properties updates. + LinkProperties newLp = new LinkProperties(lp); + LinkAddress myIpv6Address1 = new LinkAddress("fe80::cafe/64"); + LinkAddress myIpv6Address2 = new LinkAddress("2001:db8::2/64"); + newLp.addLinkAddress(myIpv6Address1); + newLp.addLinkAddress(myIpv6Address2); + networkAgent.sendLinkProperties(newLp); + cbi = networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent); + networkCallback.assertNoCallback(); + checkDirectlyConnectedRoutes(cbi.getLp(), + Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2), + Arrays.asList(myIpv4DefaultRoute)); + mCm.unregisterNetworkCallback(networkCallback); + } + + private void assertSameElementsNoDuplicates(T[] expected, T[] actual) { + // Easier to implement than a proper "assertSameElements" method that also correctly deals + // with duplicates. + final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual); + assertEquals(msg, expected.length, actual.length); + Set expectedSet = new ArraySet<>(Arrays.asList(expected)); + assertEquals("expected contains duplicates", expectedSet.size(), expected.length); + // actual cannot have duplicates because it's the same length and has the same elements. + Set actualSet = new ArraySet<>(Arrays.asList(actual)); + assertEquals(expectedSet, actualSet); + } + + private void expectNetworkStatus(Network[] networks, String defaultIface, + Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { + ArgumentCaptor> networksCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> vpnInfosCaptor = + ArgumentCaptor.forClass(List.class); + + verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(), + any(List.class), eq(defaultIface), vpnInfosCaptor.capture()); + + assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks); + + UnderlyingNetworkInfo[] infos = + vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]); + if (vpnUid != null) { + assertEquals("Should have exactly one VPN:", 1, infos.length); + UnderlyingNetworkInfo info = infos[0]; + assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid()); + assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface()); + assertSameElementsNoDuplicates(underlyingIfaces, + info.getUnderlyingInterfaces().toArray(new String[0])); + } else { + assertEquals(0, infos.length); + return; + } + } + + private void expectNetworkStatus( + Network[] networks, String defaultIface) throws Exception { + expectNetworkStatus(networks, defaultIface, null, null, new String[0]); + } + + @Test + public void testStatsIfacesChanged() throws Exception { + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; + final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; + + LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_IFNAME); + + // Simple connection should have updated ifaces + mCellNetworkAgent.connect(false); + mCellNetworkAgent.sendLinkProperties(cellLp); + waitForIdle(); + expectNetworkStatus(onlyCell, MOBILE_IFNAME); + reset(mStatsManager); + + // Default network switch should update ifaces. + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + waitForIdle(); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + expectNetworkStatus(onlyWifi, WIFI_IFNAME); + reset(mStatsManager); + + // Disconnect should update ifaces. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + expectNetworkStatus(onlyCell, MOBILE_IFNAME); + reset(mStatsManager); + + // Metered change should update ifaces + mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + waitForIdle(); + expectNetworkStatus(onlyCell, MOBILE_IFNAME); + reset(mStatsManager); + + mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + waitForIdle(); + expectNetworkStatus(onlyCell, MOBILE_IFNAME); + reset(mStatsManager); + + // Temp metered change shouldn't update ifaces + mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + waitForIdle(); + verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)), + any(List.class), eq(MOBILE_IFNAME), any(List.class)); + reset(mStatsManager); + + // Roaming change should update ifaces + mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); + waitForIdle(); + expectNetworkStatus(onlyCell, MOBILE_IFNAME); + reset(mStatsManager); + + // Test VPNs. + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(VPN_IFNAME); + + mMockVpn.establishForMyUid(lp); + assertUidRangesUpdatedForMyUid(true); + + final Network[] cellAndVpn = new Network[] { + mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + + // A VPN with default (null) underlying networks sets the underlying network's interfaces... + expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + + // ...and updates them as the default network switches. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + final Network[] onlyNull = new Network[]{null}; + final Network[] wifiAndVpn = new Network[] { + mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + final Network[] cellAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; + final Network[] cellNullAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()}; + + waitForIdle(); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{WIFI_IFNAME}); + reset(mStatsManager); + + // A VPN that sets its underlying networks passes the underlying interfaces, and influences + // the default interface sent to NetworkStatsService by virtue of applying to the system + // server UID (or, in this test, to the test's UID). This is the reason for sending + // MOBILE_IFNAME even though the default network is wifi. + // TODO: fix this to pass in the actual default network interface. Whether or not the VPN + // applies to the system server UID should not have any bearing on network stats. + mMockVpn.setUnderlyingNetworks(onlyCell); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + reset(mStatsManager); + + mMockVpn.setUnderlyingNetworks(cellAndWifi); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + reset(mStatsManager); + + // Null underlying networks are ignored. + mMockVpn.setUnderlyingNetworks(cellNullAndWifi); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + reset(mStatsManager); + + // If an underlying network disconnects, that interface should no longer be underlying. + // This doesn't actually work because disconnectAndDestroyNetwork only notifies + // NetworkStatsService before the underlying network is actually removed. So the underlying + // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This + // could result in incorrect data usage measurements if the interface used by the + // disconnected network is reused by a system component that does not register an agent for + // it (e.g., tethering). + mCellNetworkAgent.disconnect(); + waitForIdle(); + assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork())); + expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + + // Confirm that we never tell NetworkStatsService that cell is no longer the underlying + // network for the VPN... + verify(mStatsManager, never()).notifyNetworkStatus(any(List.class), + any(List.class), any() /* anyString() doesn't match null */, + argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1 + && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0)))); + verifyNoMoreInteractions(mStatsManager); + reset(mStatsManager); + + // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be + // called again, it does. For example, connect Ethernet, but with a low score, such that it + // does not become the default network. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.setScore( + new NetworkScore.Builder().setLegacyInt(30).setExiting(true).build()); + mEthernetNetworkAgent.connect(false); + waitForIdle(); + verify(mStatsManager).notifyNetworkStatus(any(List.class), + any(List.class), any() /* anyString() doesn't match null */, + argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1 + && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0)))); + mEthernetNetworkAgent.disconnect(); + waitForIdle(); + reset(mStatsManager); + + // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo + // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes + // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which + // is probably a performance improvement (though it's very unlikely that a VPN would declare + // no underlying networks). + // Also, for the same reason as above, the active interface passed in is null. + mMockVpn.setUnderlyingNetworks(new Network[0]); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, null); + reset(mStatsManager); + + // Specifying only a null underlying network is the same as no networks. + mMockVpn.setUnderlyingNetworks(onlyNull); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, null); + reset(mStatsManager); + + // Specifying networks that are all disconnected is the same as specifying no networks. + mMockVpn.setUnderlyingNetworks(onlyCell); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, null); + reset(mStatsManager); + + // Passing in null again means follow the default network again. + mMockVpn.setUnderlyingNetworks(null); + waitForIdle(); + expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{WIFI_IFNAME}); + reset(mStatsManager); + } + + @Test + public void testBasicDnsConfigurationPushed() throws Exception { + setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); + + // Clear any interactions that occur as a result of CS starting up. + reset(mMockDnsResolver); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + waitForIdle(); + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); + verifyNoMoreInteractions(mMockDnsResolver); + + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does + // "is-reachable" testing in order to not program netd with unreachable + // nameservers that it might try repeated to validate. + cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24")); + cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), + MOBILE_IFNAME)); + cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); + cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), + MOBILE_IFNAME)); + mCellNetworkAgent.sendLinkProperties(cellLp); + mCellNetworkAgent.connect(false); + waitForIdle(); + + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mCellNetworkAgent.getNetwork().netId)); + // CS tells dnsresolver about the empty DNS config for this network. + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); + reset(mMockDnsResolver); + + cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); + mCellNetworkAgent.sendLinkProperties(cellLp); + waitForIdle(); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "2001:db8::1")); + // Opportunistic mode. + assertTrue(ArrayUtils.contains(resolvrParams.tlsServers, "2001:db8::1")); + reset(mMockDnsResolver); + + cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); + mCellNetworkAgent.sendLinkProperties(cellLp); + waitForIdle(); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, + new String[]{"2001:db8::1", "192.0.2.1"})); + // Opportunistic mode. + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, + new String[]{"2001:db8::1", "192.0.2.1"})); + reset(mMockDnsResolver); + + final String TLS_SPECIFIER = "tls.example.com"; + final String TLS_SERVER6 = "2001:db8:53::53"; + final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) }; + final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 }; + mCellNetworkAgent.mNmCallbacks.notifyPrivateDnsConfigResolved( + new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel()); + + waitForIdle(); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, + new String[]{"2001:db8::1", "192.0.2.1"})); + reset(mMockDnsResolver); + } + + @Test + public void testDnsConfigurationTransTypesPushed() throws Exception { + // Clear any interactions that occur as a result of CS starting up. + reset(mMockDnsResolver); + + final NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mWiFiNetworkAgent.getNetwork().netId)); + verify(mMockDnsResolver, times(2)).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + final ResolverParamsParcel resolverParams = mResolverParamsParcelCaptor.getValue(); + assertContainsExactly(resolverParams.transportTypes, TRANSPORT_WIFI); + reset(mMockDnsResolver); + } + + @Test + public void testPrivateDnsNotification() throws Exception { + NetworkRequest request = new NetworkRequest.Builder() + .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .build(); + TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + // Bring up wifi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + // Private DNS resolution failed, checking if the notification will be shown or not. + mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + // If network validation failed, NetworkMonitor will re-evaluate the network. + // ConnectivityService should filter the redundant notification. This part is trying to + // simulate that situation and check if ConnectivityService could filter that case. + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notify(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); + // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be + // shown. + mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancel(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId)); + // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be + // shown again. + mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + waitForIdle(); + verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notify(anyString(), + eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); + } + + @Test + public void testPrivateDnsSettingsChange() throws Exception { + // Clear any interactions that occur as a result of CS starting up. + reset(mMockDnsResolver); + + // The default on Android is opportunistic mode ("Automatic"). + setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); + + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + waitForIdle(); + // CS tells netd about the empty DNS config for this network. + verify(mMockDnsResolver, never()).setResolverConfiguration(any()); + verifyNoMoreInteractions(mMockDnsResolver); + + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does + // "is-reachable" testing in order to not program netd with unreachable + // nameservers that it might try repeated to validate. + cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24")); + cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), + MOBILE_IFNAME)); + cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); + cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), + MOBILE_IFNAME)); + cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); + cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); + + mCellNetworkAgent.sendLinkProperties(cellLp); + mCellNetworkAgent.connect(false); + waitForIdle(); + verify(mMockDnsResolver, times(1)).createNetworkCache( + eq(mCellNetworkAgent.getNetwork().netId)); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, + new String[] { "2001:db8::1", "192.0.2.1" })); + // Opportunistic mode. + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, + new String[] { "2001:db8::1", "192.0.2.1" })); + reset(mMockDnsResolver); + cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, + mCellNetworkAgent); + CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback( + CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertFalse(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + + setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); + verify(mMockDnsResolver, times(1)).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, + new String[] { "2001:db8::1", "192.0.2.1" })); + reset(mMockDnsResolver); + cellNetworkCallback.assertNoCallback(); + + setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(2, resolvrParams.servers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.servers, + new String[] { "2001:db8::1", "192.0.2.1" })); + assertEquals(2, resolvrParams.tlsServers.length); + assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, + new String[] { "2001:db8::1", "192.0.2.1" })); + reset(mMockDnsResolver); + cellNetworkCallback.assertNoCallback(); + + setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com"); + // Can't test dns configuration for strict mode without properly mocking + // out the DNS lookups, but can test that LinkProperties is updated. + cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertTrue(cbi.getLp().isPrivateDnsActive()); + assertEquals("strict.example.com", cbi.getLp().getPrivateDnsServerName()); + } + + private PrivateDnsValidationEventParcel makePrivateDnsValidationEvent( + final int netId, final String ipAddress, final String hostname, final int validation) { + final PrivateDnsValidationEventParcel event = new PrivateDnsValidationEventParcel(); + event.netId = netId; + event.ipAddress = ipAddress; + event.hostname = hostname; + event.validation = validation; + return event; + } + + @Test + public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception { + // The default on Android is opportunistic mode ("Automatic"). + setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); + + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + waitForIdle(); + LinkProperties lp = new LinkProperties(); + mCellNetworkAgent.sendLinkProperties(lp); + mCellNetworkAgent.connect(false); + waitForIdle(); + cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, + mCellNetworkAgent); + CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback( + CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertFalse(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + Set dnsServers = new HashSet<>(); + checkDnsServers(cbi.getLp(), dnsServers); + + // Send a validation event for a server that is not part of the current + // resolver config. The validation event should be ignored. + mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( + makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, "", + "145.100.185.18", VALIDATION_RESULT_SUCCESS)); + cellNetworkCallback.assertNoCallback(); + + // Add a dns server to the LinkProperties. + LinkProperties lp2 = new LinkProperties(lp); + lp2.addDnsServer(InetAddress.getByName("145.100.185.16")); + mCellNetworkAgent.sendLinkProperties(lp2); + cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertFalse(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + dnsServers.add(InetAddress.getByName("145.100.185.16")); + checkDnsServers(cbi.getLp(), dnsServers); + + // Send a validation event containing a hostname that is not part of + // the current resolver config. The validation event should be ignored. + mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( + makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, + "145.100.185.16", "hostname", VALIDATION_RESULT_SUCCESS)); + cellNetworkCallback.assertNoCallback(); + + // Send a validation event where validation failed. + mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( + makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, + "145.100.185.16", "", VALIDATION_RESULT_FAILURE)); + cellNetworkCallback.assertNoCallback(); + + // Send a validation event where validation succeeded for a server in + // the current resolver config. A LinkProperties callback with updated + // private dns fields should be sent. + mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( + makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, + "145.100.185.16", "", VALIDATION_RESULT_SUCCESS)); + cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertTrue(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + checkDnsServers(cbi.getLp(), dnsServers); + + // The private dns fields in LinkProperties should be preserved when + // the network agent sends unrelated changes. + LinkProperties lp3 = new LinkProperties(lp2); + lp3.setMtu(1300); + mCellNetworkAgent.sendLinkProperties(lp3); + cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertTrue(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + checkDnsServers(cbi.getLp(), dnsServers); + assertEquals(1300, cbi.getLp().getMtu()); + + // Removing the only validated server should affect the private dns + // fields in LinkProperties. + LinkProperties lp4 = new LinkProperties(lp3); + lp4.removeDnsServer(InetAddress.getByName("145.100.185.16")); + mCellNetworkAgent.sendLinkProperties(lp4); + cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + cellNetworkCallback.assertNoCallback(); + assertFalse(cbi.getLp().isPrivateDnsActive()); + assertNull(cbi.getLp().getPrivateDnsServerName()); + dnsServers.remove(InetAddress.getByName("145.100.185.16")); + checkDnsServers(cbi.getLp(), dnsServers); + assertEquals(1300, cbi.getLp().getMtu()); + } + + private void checkDirectlyConnectedRoutes(Object callbackObj, + Collection linkAddresses, Collection otherRoutes) { + assertTrue(callbackObj instanceof LinkProperties); + LinkProperties lp = (LinkProperties) callbackObj; + + Set expectedRoutes = new ArraySet<>(); + expectedRoutes.addAll(otherRoutes); + for (LinkAddress address : linkAddresses) { + RouteInfo localRoute = new RouteInfo(address, null, lp.getInterfaceName()); + // Duplicates in linkAddresses are considered failures + assertTrue(expectedRoutes.add(localRoute)); + } + List observedRoutes = lp.getRoutes(); + assertEquals(expectedRoutes.size(), observedRoutes.size()); + assertTrue(observedRoutes.containsAll(expectedRoutes)); + } + + private static void checkDnsServers(Object callbackObj, Set dnsServers) { + assertTrue(callbackObj instanceof LinkProperties); + LinkProperties lp = (LinkProperties) callbackObj; + assertEquals(dnsServers.size(), lp.getDnsServers().size()); + assertTrue(lp.getDnsServers().containsAll(dnsServers)); + } + + @Test + public void testApplyUnderlyingCapabilities() throws Exception { + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mCellNetworkAgent.connect(false /* validated */); + mWiFiNetworkAgent.connect(false /* validated */); + + final NetworkCapabilities cellNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setLinkDownstreamBandwidthKbps(10); + final NetworkCapabilities wifiNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_NOT_ROAMING) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setLinkUpstreamBandwidthKbps(20); + mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); + mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); + waitForIdle(); + + final Network mobile = mCellNetworkAgent.getNetwork(); + final Network wifi = mWiFiNetworkAgent.getNetwork(); + + final NetworkCapabilities initialCaps = new NetworkCapabilities(); + initialCaps.addTransportType(TRANSPORT_VPN); + initialCaps.addCapability(NET_CAPABILITY_INTERNET); + initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withNoUnderlying = new NetworkCapabilities(); + withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + withNoUnderlying.addTransportType(TRANSPORT_VPN); + withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying); + withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + withMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + + final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying); + withWifiUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + withWifiUnderlying.setLinkUpstreamBandwidthKbps(20); + + final NetworkCapabilities withWifiAndMobileUnderlying = + new NetworkCapabilities(withNoUnderlying); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20); + + final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps); + initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED); + + NetworkCapabilities caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps); + assertEquals(withMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps); + assertEquals(withWifiUnderlying, caps); + + withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps); + assertEquals(withWifiUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps); + assertEquals(withWifiAndMobileUnderlying, caps); + + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + initialCapsNotMetered, caps); + assertEquals(withWifiAndMobileUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + initialCapsNotMetered, caps); + assertEquals(withWifiAndMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps); + assertEquals(withWifiUnderlying, caps); + } + + @Test + public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN).build(); + + mCm.registerNetworkCallback(request, callback); + + // Bring up a VPN that specifies an underlying network that does not exist yet. + // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet, + // (and doing so is difficult without using reflection) but it's good to test that the code + // behaves approximately correctly. + mMockVpn.establishForMyUid(false, true, false); + assertUidRangesUpdatedForMyUid(true); + final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); + mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork}); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Make that underlying network connect, and expect to see its capabilities immediately + // reflected in the VPN's capabilities. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork()); + mWiFiNetworkAgent.connect(false); + // TODO: the callback for the VPN happens before any callbacks are called for the wifi + // network that has just connected. There appear to be two issues here: + // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for + // it returns non-null (which happens very early, during handleRegisterNetworkAgent). + // This is not correct because that that point the network is not connected and cannot + // pass any traffic. + // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities + // before rematching networks. + // Given that this scenario can't really happen, this is probably fine for now. + callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Disconnect the network, and expect to see the VPN capabilities change accordingly. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (nc) -> + nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN)); + + mMockVpn.disconnect(); + mCm.unregisterNetworkCallback(callback); + } + + private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) { + // What Chromium used to do before https://chromium-review.googlesource.com/2605304 + assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())", + expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected()); + } + + @Test + public void testVpnUnderlyingNetworkSuspended() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + // Connect a VPN. + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, + false /* isStrictMode */); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + + // Connect cellular data. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Suspend the cellular network and expect the VPN to be suspended. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + // VPN's main underlying network is suspended, so no connectivity. + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Switch to another network. The VPN should no longer be suspended. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_WIFI)); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Unsuspend cellular and then switch back to it. The VPN remains not suspended. + mCellNetworkAgent.resume(); + callback.assertNoCallback(); + mWiFiNetworkAgent.disconnect(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + // Spurious double callback? + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Suspend cellular and expect no connectivity. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Resume cellular and expect that connectivity comes back. + mCellNetworkAgent.resume(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + } + + @Test + public void testVpnNetworkActive() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final int uid = Process.myUid(); + + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN).build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + mCm.registerDefaultNetworkCallback(defaultCallback); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + defaultCallback.assertNoCallback(); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnNetworkCallback.assertNoCallback(); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + final Set ranges = uidRangesForUids(uid); + mMockVpn.registerAgent(ranges); + mMockVpn.setUnderlyingNetworks(new Network[0]); + + // VPN networks do not satisfy the default request and are automatically validated + // by NetworkMonitor + assertFalse(NetworkMonitorUtils.isValidationRequired( + mMockVpn.getAgent().getNetworkCapabilities())); + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); + + mMockVpn.connect(false); + + genericNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + genericNotVpnNetworkCallback.assertNoCallback(); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(mWiFiNetworkAgent.getNetwork(), + systemDefaultCallback.getLastAvailableNetwork()); + + ranges.clear(); + mMockVpn.setUids(ranges); + + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + genericNotVpnNetworkCallback.assertNoCallback(); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + + // TODO : The default network callback should actually get a LOST call here (also see the + // comment below for AVAILABLE). This is because ConnectivityService does not look at UID + // ranges at all when determining whether a network should be rematched. In practice, VPNs + // can't currently update their UIDs without disconnecting, so this does not matter too + // much, but that is the reason the test here has to check for an update to the + // capabilities instead of the expected LOST then AVAILABLE. + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); + + ranges.add(new UidRange(uid, uid)); + mMockVpn.setUids(ranges); + + genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); + genericNotVpnNetworkCallback.assertNoCallback(); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); + // TODO : Here like above, AVAILABLE would be correct, but because this can't actually + // happen outside of the test, ConnectivityService does not rematch callbacks. + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); + + mWiFiNetworkAgent.disconnect(); + + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + genericNotVpnNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + vpnNetworkCallback.assertNoCallback(); + defaultCallback.assertNoCallback(); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + mMockVpn.disconnect(); + + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + genericNotVpnNetworkCallback.assertNoCallback(); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); + assertEquals(null, mCm.getActiveNetwork()); + + mCm.unregisterNetworkCallback(genericNetworkCallback); + mCm.unregisterNetworkCallback(wifiNetworkCallback); + mCm.unregisterNetworkCallback(vpnNetworkCallback); + mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(systemDefaultCallback); + } + + @Test + public void testVpnWithoutInternet() throws Exception { + final int uid = Process.myUid(); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + + defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + defaultCallback.assertNoCallback(); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mMockVpn.disconnect(); + defaultCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(defaultCallback); + } + + @Test + public void testVpnWithInternet() throws Exception { + final int uid = Process.myUid(); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + + defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + + mMockVpn.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(defaultCallback); + } + + @Test + public void testVpnUnvalidated() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + // Bring up Ethernet. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + callback.assertNoCallback(); + + // Bring up a VPN that has the INTERNET capability, initially unvalidated. + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + // Even though the VPN is unvalidated, it becomes the default network for our app. + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + callback.assertNoCallback(); + + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + + NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)); + assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET)); + + assertFalse(NetworkMonitorUtils.isValidationRequired( + mMockVpn.getAgent().getNetworkCapabilities())); + assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired( + mMockVpn.getAgent().getNetworkCapabilities())); + + // Pretend that the VPN network validates. + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); + mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid()); + // Expect to see the validated capability, but no other changes, because the VPN is already + // the default network for the app. + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn); + callback.assertNoCallback(); + + mMockVpn.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent); + } + + @Test + public void testVpnStartsWithUnderlyingCaps() throws Exception { + final int uid = Process.myUid(); + + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN) + .build(); + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + vpnNetworkCallback.assertNoCallback(); + + // Connect cell. It will become the default network, and in the absence of setting + // underlying networks explicitly it will become the sole underlying network for the vpn. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mCellNetworkAgent.connect(true); + + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), + false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS, + nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)); + + final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + + assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); + } + + private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) { + final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser( + userId, "com.android.calling.package", "com.test"); + final String defaultCapsString = Arrays.toString(defaultCaps); + assertEquals(defaultCapsString, defaultCaps.length, networks.length); + final Set defaultCapsSet = new ArraySet<>(defaultCaps); + for (NetworkAgentWrapper network : networks) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork()); + final String msg = "Did not find " + nc + " in " + Arrays.toString(defaultCaps); + assertTrue(msg, defaultCapsSet.contains(nc)); + } + } + + @Test + public void testVpnSetUnderlyingNetworks() throws Exception { + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN) + .build(); + NetworkCapabilities nc; + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + vpnNetworkCallback.assertNoCallback(); + + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + // For safety reasons a VPN without underlying networks is considered metered. + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + // A VPN without underlying networks is not suspended. + assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); + + final int userId = UserHandle.getUserId(Process.myUid()); + assertDefaultNetworkCapabilities(userId /* no networks */); + + // Connect cell and use it as an underlying network. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mCellNetworkAgent.connect(true); + + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mWiFiNetworkAgent.connect(true); + + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); + + // Don't disconnect, but note the VPN is not using wifi any more. + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + // The return value of getDefaultNetworkCapabilitiesForUser always includes the default + // network (wifi) as well as the underlying networks (cell). + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); + + // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended. + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + + // Add NOT_SUSPENDED again and observe VPN is no longer suspended. + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + + // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. + mMockVpn.setUnderlyingNetworks( + new Network[] { mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); + + // Use both again. + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); + + // Cell is suspended again. As WiFi is not, this should not cause a callback. + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + vpnNetworkCallback.assertNoCallback(); + + // Stop using WiFi. The VPN is suspended again. + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork() }); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); + + // Use both again. + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) + && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); + + // Disconnect cell. Receive update without even removing the dead network from the + // underlying networks – it's dead anyway. Not metered any more. + mCellNetworkAgent.disconnect(); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); + + // Disconnect wifi too. No underlying networks means this is now metered. + mWiFiNetworkAgent.disconnect(); + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + // When a network disconnects, the callbacks are fired before all state is updated, so for a + // short time, synchronous calls will behave as if the network is still connected. Wait for + // things to settle. + waitForIdle(); + assertDefaultNetworkCapabilities(userId /* no networks */); + + mMockVpn.disconnect(); + } + + @Test + public void testNullUnderlyingNetworks() throws Exception { + final int uid = Process.myUid(); + + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .addTransportType(TRANSPORT_VPN) + .build(); + NetworkCapabilities nc; + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + vpnNetworkCallback.assertNoCallback(); + + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, + false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); + + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertTrue(nc.hasTransport(TRANSPORT_VPN)); + assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + // By default, VPN is set to track default network (i.e. its underlying networks is null). + // In case of no default network, VPN is considered metered. + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); + assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); + + // Connect to Cell; Cell is the default network. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + + // Connect to WiFi; WiFi is the new default. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) + && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + + // Disconnect Cell. The default network did not change, so there shouldn't be any changes in + // the capabilities. + mCellNetworkAgent.disconnect(); + + // Disconnect wifi too. Now we have no default network. + mWiFiNetworkAgent.disconnect(); + + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, + (caps) -> caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) + && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); + + mMockVpn.disconnect(); + } + + @Test + public void testRestrictedProfileAffectsVpnUidRanges() throws Exception { + // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + // Bring up a VPN + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + callback.assertNoCallback(); + + final int uid = Process.myUid(); + NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertNotNull("nc=" + nc, nc.getUids()); + assertEquals(nc.getUids(), UidRange.toIntRanges(uidRangesForUids(uid))); + assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); + + // Set an underlying network and expect to see the VPN transports change. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_WIFI)); + callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps) + -> caps.hasCapability(NET_CAPABILITY_VALIDATED)); + + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); + + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + + // Send a USER_ADDED broadcast for it. + processBroadcast(addedIntent); + + // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added + // restricted user. + final UidRange rRange = UidRange.createForUser(UserHandle.of(RESTRICTED_USER)); + final Range restrictUidRange = new Range(rRange.start, rRange.stop); + final Range singleUidRange = new Range(uid, uid); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 2 + && caps.getUids().contains(singleUidRange) + && caps.getUids().contains(restrictUidRange) + && caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_WIFI)); + + // Change the VPN's capabilities somehow (specifically, disconnect wifi). + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 2 + && caps.getUids().contains(singleUidRange) + && caps.getUids().contains(restrictUidRange) + && caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_WIFI)); + + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + processBroadcast(removedIntent); + + // Expect that the VPN gains the UID range for the restricted user, and that the capability + // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 1 + && caps.getUids().contains(singleUidRange) + && caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_WIFI)); + } + + @Test + public void testLockdownVpnWithRestrictedProfiles() throws Exception { + // For ConnectivityService#setAlwaysOnVpnPackage. + mServiceContext.setPermission( + Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); + // For call Vpn#setAlwaysOnPackage. + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + // Necessary to see the UID ranges in NetworkCapabilities. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final int uid = Process.myUid(); + + // Connect wifi and check that UIDs in the main and restricted profiles have network access. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true /* validated */); + final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */); + assertNotNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Enable always-on VPN lockdown. The main user loses network access because no VPN is up. + final ArrayList allowList = new ArrayList<>(); + mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, + true /* lockdown */, allowList); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + // This is arguably overspecified: a UID that is not running doesn't have an active network. + // But it's useful to check that non-default users do not lose network access, and to prove + // that the loss of connectivity below is indeed due to the restricted profile coming up. + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Start the restricted profile, and check that the UID within it loses network access. + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO, + RESTRICTED_USER_INFO)); + // TODO: check that VPN app within restricted profile still has access, etc. + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + processBroadcast(addedIntent); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Stop the restricted profile, and check that the UID within it has network access again. + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + processBroadcast(removedIntent); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, + allowList); + waitForIdle(); + } + + @Test + public void testIsActiveNetworkMeteredOverWifi() throws Exception { + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + + assertFalse(mCm.isActiveNetworkMetered()); + } + + @Test + public void testIsActiveNetworkMeteredOverCell() throws Exception { + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + mCellNetworkAgent.connect(true); + waitForIdle(); + + assertTrue(mCm.isActiveNetworkMetered()); + } + + @Test + public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception { + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + mCellNetworkAgent.connect(true); + waitForIdle(); + assertTrue(mCm.isActiveNetworkMetered()); + + // Connect VPN network. By default it is using current default network (Cell). + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + + // Ensure VPN is now the active network. + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + + // Expect VPN to be metered. + assertTrue(mCm.isActiveNetworkMetered()); + + // Connect WiFi. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + // VPN should still be the active network. + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + + // Expect VPN to be unmetered as it should now be using WiFi (new default). + assertFalse(mCm.isActiveNetworkMetered()); + + // Disconnecting Cell should not affect VPN's meteredness. + mCellNetworkAgent.disconnect(); + waitForIdle(); + + assertFalse(mCm.isActiveNetworkMetered()); + + // Disconnect WiFi; Now there is no platform default network. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + + // VPN without any underlying networks is treated as metered. + assertTrue(mCm.isActiveNetworkMetered()); + + mMockVpn.disconnect(); + } + + @Test + public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception { + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + mCellNetworkAgent.connect(true); + waitForIdle(); + assertTrue(mCm.isActiveNetworkMetered()); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + assertFalse(mCm.isActiveNetworkMetered()); + + // Connect VPN network. + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + + // Ensure VPN is now the active network. + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + // VPN is using Cell + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork() }); + waitForIdle(); + + // Expect VPN to be metered. + assertTrue(mCm.isActiveNetworkMetered()); + + // VPN is now using WiFi + mMockVpn.setUnderlyingNetworks( + new Network[] { mWiFiNetworkAgent.getNetwork() }); + waitForIdle(); + + // Expect VPN to be unmetered + assertFalse(mCm.isActiveNetworkMetered()); + + // VPN is using Cell | WiFi. + mMockVpn.setUnderlyingNetworks( + new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); + waitForIdle(); + + // Expect VPN to be metered. + assertTrue(mCm.isActiveNetworkMetered()); + + // VPN is using WiFi | Cell. + mMockVpn.setUnderlyingNetworks( + new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() }); + waitForIdle(); + + // Order should not matter and VPN should still be metered. + assertTrue(mCm.isActiveNetworkMetered()); + + // VPN is not using any underlying networks. + mMockVpn.setUnderlyingNetworks(new Network[0]); + waitForIdle(); + + // VPN without underlying networks is treated as metered. + assertTrue(mCm.isActiveNetworkMetered()); + + mMockVpn.disconnect(); + } + + @Test + public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception { + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + assertFalse(mCm.isActiveNetworkMetered()); + + // Connect VPN network. + mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUids(Process.myUid()), + new LinkProperties()); + mMockVpn.connect(true); + waitForIdle(); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + + // VPN is tracking current platform default (WiFi). + mMockVpn.setUnderlyingNetworks(null); + waitForIdle(); + + // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered. + assertTrue(mCm.isActiveNetworkMetered()); + + + // VPN explicitly declares WiFi as its underlying network. + mMockVpn.setUnderlyingNetworks( + new Network[] { mWiFiNetworkAgent.getNetwork() }); + waitForIdle(); + + // Doesn't really matter whether VPN declares its underlying networks explicitly. + assertTrue(mCm.isActiveNetworkMetered()); + + // With WiFi lost, VPN is basically without any underlying networks. And in that case it is + // anyways suppose to be metered. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + + assertTrue(mCm.isActiveNetworkMetered()); + + mMockVpn.disconnect(); + } + + private class DetailedBlockedStatusCallback extends TestNetworkCallback { + public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) { + super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS); + } + public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) { + // This doesn't work: + // super.expectBlockedStatusCallback(blockedStatus, n.getNetwork()); + super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS); + } + public void onBlockedStatusChanged(Network network, int blockedReasons) { + getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons)); + } + } + + @Test + public void testNetworkBlockedStatus() throws Exception { + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .build(); + mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback(); + mCm.registerNetworkCallback(cellRequest, detailedCallback); + + mockUidNetworkingBlocked(); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent, + BLOCKED_REASON_NONE); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_REASON_BATTERY_SAVER); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mCellNetworkAgent); + + // If blocked state does not change but blocked reason does, the boolean callback is called. + // TODO: investigate de-duplicating. + setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_USER_RESTRICTED); + + setBlockedReasonChanged(BLOCKED_REASON_NONE); + cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mCellNetworkAgent); + + // Restrict the network based on UID rule and NOT_METERED capability change. + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); + cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, + mCellNetworkAgent); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, + mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mCellNetworkAgent); + + setBlockedReasonChanged(BLOCKED_REASON_NONE); + cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + setBlockedReasonChanged(BLOCKED_REASON_NONE); + cellNetworkCallback.assertNoCallback(); + detailedCallback.assertNoCallback(); + + // Restrict background data. Networking is not blocked because the network is unmetered. + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mCellNetworkAgent); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + cellNetworkCallback.assertNoCallback(); + + setBlockedReasonChanged(BLOCKED_REASON_NONE); + cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + setBlockedReasonChanged(BLOCKED_REASON_NONE); + cellNetworkCallback.assertNoCallback(); + detailedCallback.assertNoCallback(); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + + mCm.unregisterNetworkCallback(cellNetworkCallback); + } + + @Test + public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception { + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + mockUidNetworkingBlocked(); + + // No Networkcallbacks invoked before any network is active. + setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); + setBlockedReasonChanged(BLOCKED_REASON_NONE); + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + defaultCallback.assertNoCallback(); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent); + + // Allow to use the network after switching to NOT_METERED network. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + + // Switch to METERED network. Restrict the use of the network. + mWiFiNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent); + + // Network becomes NOT_METERED. + mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); + defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + + // Verify there's no Networkcallbacks invoked after data saver on/off. + setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); + setBlockedReasonChanged(BLOCKED_REASON_NONE); + defaultCallback.assertNoCallback(); + + mCellNetworkAgent.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(defaultCallback); + } + + private void expectNetworkRejectNonSecureVpn(InOrder inOrder, boolean add, + UidRangeParcel... expected) throws Exception { + inOrder.verify(mMockNetd).networkRejectNonSecureVpn(eq(add), aryEq(expected)); + } + + private void checkNetworkInfo(NetworkInfo ni, int type, DetailedState state) { + assertNotNull(ni); + assertEquals(type, ni.getType()); + assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState()); + if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) { + assertNotNull(ni.getExtraInfo()); + } else { + // Technically speaking, a network that's in CONNECTING state will generally have a + // non-null extraInfo. This doesn't actually happen in this test because it never calls + // a legacy API while a network is connecting. When a network is in CONNECTING state + // because of legacy lockdown VPN, its extraInfo is always null. + assertNull(ni.getExtraInfo()); + } + } + + private void assertActiveNetworkInfo(int type, DetailedState state) { + checkNetworkInfo(mCm.getActiveNetworkInfo(), type, state); + } + private void assertNetworkInfo(int type, DetailedState state) { + checkNetworkInfo(mCm.getNetworkInfo(type), type, state); + } + + private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) { + final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork()); + final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType()); + if (present) { + assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo()); + assertEquals(network.getExtraInfo(), niForType.getExtraInfo()); + } else { + assertNull(niForNetwork.getExtraInfo()); + assertNull(niForType.getExtraInfo()); + } + } + + private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) { + assertExtraInfoFromCm(network, false); + } + + private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) { + assertExtraInfoFromCm(network, true); + } + + // Checks that each of the |agents| receive a blocked status change callback with the specified + // |blocked| value, in any order. This is needed because when an event affects multiple + // networks, ConnectivityService does not guarantee the order in which callbacks are fired. + private void assertBlockedCallbackInAnyOrder(TestNetworkCallback callback, boolean blocked, + TestNetworkAgentWrapper... agents) { + final List expectedNetworks = Arrays.asList(agents).stream() + .map((agent) -> agent.getNetwork()) + .collect(Collectors.toList()); + + // Expect exactly one blocked callback for each agent. + for (int i = 0; i < agents.length; i++) { + CallbackEntry e = callback.expectCallbackThat(TIMEOUT_MS, (c) -> + c instanceof CallbackEntry.BlockedStatus + && ((CallbackEntry.BlockedStatus) c).getBlocked() == blocked); + Network network = e.getNetwork(); + assertTrue("Received unexpected blocked callback for network " + network, + expectedNetworks.remove(network)); + } + } + + @Test + public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception { + mServiceContext.setPermission( + Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback callback = new TestNetworkCallback(); + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + mCm.registerNetworkCallback(request, callback); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + final TestNetworkCallback vpnUidCallback = new TestNetworkCallback(); + final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build(); + registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID); + + final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback(); + registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID); + + final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallbackForUid(VPN_UID, vpnDefaultCallbackAsUid, + new Handler(ConnectivityThread.getInstanceLooper())); + + final int uid = Process.myUid(); + final int userId = UserHandle.getUserId(uid); + final ArrayList allowList = new ArrayList<>(); + mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); + waitForIdle(); + + UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1); + UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999); + InOrder inOrder = inOrder(mMockNetd); + expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); + + // Connect a network when lockdown is active, expect to see it blocked. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnDefaultCallbackAsUid.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + // Mobile is BLOCKED even though it's not actually connected. + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + + // Disable lockdown, expect to see the network unblocked. + mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); + defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked. + allowList.add(TEST_PACKAGE_NAME); + mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); + callback.assertNoCallback(); + defaultCallback.assertNoCallback(); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + + // The following requires that the UID of this test package is greater than VPN_UID. This + // is always true in practice because a plain AOSP build with no apps installed has almost + // 200 packages installed. + final UidRangeParcel piece1 = new UidRangeParcel(1, VPN_UID - 1); + final UidRangeParcel piece2 = new UidRangeParcel(VPN_UID + 1, uid - 1); + final UidRangeParcel piece3 = new UidRangeParcel(uid + 1, 99999); + expectNetworkRejectNonSecureVpn(inOrder, true, piece1, piece2, piece3); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + // Connect a new network, expect it to be unblocked. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + defaultCallback.assertNoCallback(); + vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + // Cellular is DISCONNECTED because it's not the default and there are no requests for it. + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown. + // Everything should now be blocked. + mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + waitForIdle(); + expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3); + allowList.clear(); + mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); + waitForIdle(); + expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); + defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); + assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + + // Disable lockdown. Everything is unblocked. + mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); + assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + // Enable and disable an always-on VPN package without lockdown. Expect no changes. + reset(mMockNetd); + mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, + allowList); + inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); + callback.assertNoCallback(); + defaultCallback.assertNoCallback(); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); + callback.assertNoCallback(); + defaultCallback.assertNoCallback(); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + // Enable lockdown and connect a VPN. The VPN is not blocked. + mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); + defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); + assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. + vpnUidDefaultCallback.assertNoCallback(); // VPN does not apply to VPN_UID + vpnDefaultCallbackAsUid.assertNoCallback(); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + + mMockVpn.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + vpnUidCallback.assertNoCallback(); + vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); + assertNull(mCm.getActiveNetwork()); + + mCm.unregisterNetworkCallback(callback); + mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(vpnUidCallback); + mCm.unregisterNetworkCallback(vpnUidDefaultCallback); + mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid); + } + + private void setupLegacyLockdownVpn() { + final String profileName = "testVpnProfile"; + final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8); + when(mVpnProfileStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag); + + final VpnProfile profile = new VpnProfile(profileName); + profile.name = "My VPN"; + profile.server = "192.0.2.1"; + profile.dnsServers = "8.8.8.8"; + profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK; + final byte[] encodedProfile = profile.encode(); + when(mVpnProfileStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); + } + + private void establishLegacyLockdownVpn(Network underlying) throws Exception { + // The legacy lockdown VPN only supports userId 0, and must have an underlying network. + assertNotNull(underlying); + mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); + // The legacy lockdown VPN only supports userId 0. + final Set ranges = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.registerAgent(ranges); + mMockVpn.setUnderlyingNetworks(new Network[]{underlying}); + mMockVpn.connect(true); + } + + @Test + public void testLegacyLockdownVpn() throws Exception { + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + // For LockdownVpnTracker to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); + + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + + // Pretend lockdown VPN was configured. + setupLegacyLockdownVpn(); + + // LockdownVpnTracker disables the Vpn teardown code and enables lockdown. + // Check the VPN's state before it does so. + assertTrue(mMockVpn.getEnableTeardown()); + assertFalse(mMockVpn.getLockdown()); + + // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker. + final int userId = UserHandle.getUserId(Process.myUid()); + final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + processBroadcast(addedIntent); + + // Lockdown VPN disables teardown and enables lockdown. + assertFalse(mMockVpn.getEnableTeardown()); + assertTrue(mMockVpn.getLockdown()); + + // Bring up a network. + // Expect nothing to happen because the network does not have an IPv4 default route: legacy + // VPN only supports IPv4. + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName("rmnet0"); + cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0")); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls + // LockdownVpnTracker#handleStateChangedLocked. This is a bug. + // TODO: consider fixing this. + cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25")); + cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0")); + mCellNetworkAgent.sendLinkProperties(cellLp); + callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, + mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Disconnect, then try again with a network that supports IPv4 at connection time. + // Expect lockdown VPN to come up. + ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + mCellNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + b1.expectBroadcast(); + + // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten + // with the state of the VPN network. So expect a CONNECTING broadcast. + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + b1.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mCellNetworkAgent); + + // TODO: it would be nice if we could simply rely on the production code here, and have + // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with + // ConnectivityService, etc. That would require duplicating a fair bit of code from the + // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not + // work for at least two reasons: + // 1. In this test, calling registerNetworkAgent does not actually result in an agent being + // registered. This is because nothing calls onNetworkMonitorCreated, which is what + // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test + // that wants to register an agent must use TestNetworkAgentWrapper. + // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call + // the TestNetworkAgentWrapper code, this would deadlock because the + // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls + // waitForIdle(). + mMockVpn.expectStartLegacyVpnRunner(); + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + establishLegacyLockdownVpn(mCellNetworkAgent.getNetwork()); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); + NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + b1.expectBroadcast(); + b2.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mCellNetworkAgent); + assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); + assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI)); + assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); + assertVpnTransportInfo(vpnNc, VpnManager.TYPE_VPN_LEGACY); + + // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName("wlan0"); + wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25")); + wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0")); + final NetworkCapabilities wifiNc = new NetworkCapabilities(); + wifiNc.addTransportType(TRANSPORT_WIFI); + wifiNc.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc); + + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + // Wifi is CONNECTING because the VPN isn't up yet. + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING); + ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.connect(false /* validated */); + b1.expectBroadcast(); + b2.expectBroadcast(); + b3.expectBroadcast(); + mMockVpn.expectStopVpnRunnerPrivileged(); + mMockVpn.expectStartLegacyVpnRunner(); + + // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still + // connected, so the network is not considered blocked by the lockdown UID ranges? But the + // fact that a VPN is connected should only result in the VPN itself being unblocked, not + // any other network. Bug in isUidBlockedByVpn? + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // While the VPN is reconnecting on the new network, everything is blocked. + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + assertExtraInfoFromCmBlocked(mWiFiNetworkAgent); + + // The VPN comes up again on wifi. + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + establishLegacyLockdownVpn(mWiFiNetworkAgent.getNetwork()); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); + b1.expectBroadcast(); + b2.expectBroadcast(); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mWiFiNetworkAgent); + vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); + assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI)); + assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR)); + assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); + + // Disconnect cell. Nothing much happens since it's not the default network. + mCellNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); + + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertExtraInfoFromCmPresent(mWiFiNetworkAgent); + + b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b1.expectBroadcast(); + callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI)); + mMockVpn.expectStopVpnRunnerPrivileged(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + b2.expectBroadcast(); + } + + /** + * Test mutable and requestable network capabilities such as + * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the + * {@code ConnectivityService} re-assign the networks accordingly. + */ + @Test + public final void testLoseMutableAndRequestableCaps() throws Exception { + final int[] testCaps = new int [] { + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_NOT_VCN_MANAGED + }; + for (final int testCap : testCaps) { + // Create requests with and without the testing capability. + final TestNetworkCallback callbackWithCap = new TestNetworkCallback(); + final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(), + callbackWithCap); + mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(), + callbackWithoutCap); + + // Setup networks with testing capability and verify the default network changes. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(testCap); + mCellNetworkAgent.connect(true); + callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(testCap); + mWiFiNetworkAgent.connect(true); + callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + // Remove the testing capability on wifi, verify the callback and default network + // changes back to cellular. + mWiFiNetworkAgent.removeCapability(testCap); + callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); + callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + mCellNetworkAgent.removeCapability(testCap); + callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + callbackWithoutCap.assertNoCallback(); + verify(mMockNetd).networkClearDefault(); + + mCm.unregisterNetworkCallback(callbackWithCap); + mCm.unregisterNetworkCallback(callbackWithoutCap); + } + } + + @Test + public final void testBatteryStatsNetworkType() throws Exception { + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName("cell0"); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(true); + waitForIdle(); + verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, + cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName("wifi0"); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, + wifiLp.getInterfaceName(), + new int[] { TRANSPORT_WIFI }); + + mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); + + cellLp.setInterfaceName("wifi0"); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(true); + waitForIdle(); + verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, + cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + mCellNetworkAgent.disconnect(); + } + + /** + * Make simulated InterfaceConfigParcel for Nat464Xlat to query clat lower layer info. + */ + private InterfaceConfigurationParcel getClatInterfaceConfigParcel(LinkAddress la) { + final InterfaceConfigurationParcel cfg = new InterfaceConfigurationParcel(); + cfg.hwAddr = "11:22:33:44:55:66"; + cfg.ipv4Addr = la.getAddress().getHostAddress(); + cfg.prefixLength = la.getPrefixLength(); + return cfg; + } + + /** + * Make expected stack link properties, copied from Nat464Xlat. + */ + private LinkProperties makeClatLinkProperties(LinkAddress la) { + LinkAddress clatAddress = la; + LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME); + RouteInfo ipv4Default = new RouteInfo( + new LinkAddress(Inet4Address.ANY, 0), + clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME); + stacked.addRoute(ipv4Default); + stacked.addLinkAddress(clatAddress); + return stacked; + } + + private Nat64PrefixEventParcel makeNat64PrefixEvent(final int netId, final int prefixOperation, + final String prefixAddress, final int prefixLength) { + final Nat64PrefixEventParcel event = new Nat64PrefixEventParcel(); + event.netId = netId; + event.prefixOperation = prefixOperation; + event.prefixAddress = prefixAddress; + event.prefixLength = prefixLength; + return event; + } + + @Test + public void testStackedLinkProperties() throws Exception { + final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24"); + final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64"); + final String kNat64PrefixString = "2001:db8:64:64:64:64::"; + final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96); + final String kOtherNat64PrefixString = "64:ff9b::"; + final IpPrefix kOtherNat64Prefix = new IpPrefix( + InetAddress.getByName(kOtherNat64PrefixString), 96); + final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(), + MOBILE_IFNAME); + final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME); + final RouteInfo ipv4Subnet = new RouteInfo(myIpv4, null, MOBILE_IFNAME); + final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(), + CLAT_PREFIX + MOBILE_IFNAME); + + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + // Prepare ipv6 only link properties. + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + cellLp.addLinkAddress(myIpv6); + cellLp.addRoute(defaultRoute); + cellLp.addRoute(ipv6Subnet); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + reset(mMockDnsResolver); + reset(mMockNetd); + + // Connect with ipv6 link properties. Expect prefix discovery to be started. + mCellNetworkAgent.connect(true); + final int cellNetId = mCellNetworkAgent.getNetwork().netId; + waitForIdle(); + + verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId, + INetd.PERMISSION_NONE)); + assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); + verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); + verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, + cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + + networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); + + // Switching default network updates TCP buffer sizes. + verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); + // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that + // the NAT64 prefix was removed because one was never discovered. + cellLp.addLinkAddress(myIpv4); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + assertRoutesAdded(cellNetId, ipv4Subnet); + verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); + verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); + + // Make sure BatteryStats was not told about any v4- interfaces, as none should have + // come online yet. + waitForIdle(); + verify(mDeps, never()) + .reportNetworkInterfaceForTransports(eq(mServiceContext), startsWith("v4-"), any()); + + verifyNoMoreInteractions(mMockNetd); + verifyNoMoreInteractions(mMockDnsResolver); + reset(mMockNetd); + reset(mMockDnsResolver); + when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) + .thenReturn(getClatInterfaceConfigParcel(myIpv4)); + + // Remove IPv4 address. Expect prefix discovery to be started again. + cellLp.removeLinkAddress(myIpv4); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); + assertRoutesRemoved(cellNetId, ipv4Subnet); + + // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started. + Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent); + assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix()); + mService.mResolverUnsolEventCallback.onNat64PrefixEvent( + makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96)); + LinkProperties lpBeforeClat = networkCallback.expectCallback( + CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp(); + assertEquals(0, lpBeforeClat.getStackedLinks().size()); + assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix()); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); + + // Clat iface comes up. Expect stacked link to be added. + clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + List stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()) + .getStackedLinks(); + assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0)); + assertRoutesAdded(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + // Change trivial linkproperties and see if stacked link is preserved. + cellLp.addDnsServer(InetAddress.getByName("8.8.8.8")); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + + List stackedLpsAfterChange = + mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks(); + assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST); + assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0)); + + verify(mMockDnsResolver, times(1)).setResolverConfiguration( + mResolverParamsParcelCaptor.capture()); + ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); + assertEquals(1, resolvrParams.servers.length); + assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); + + for (final LinkProperties stackedLp : stackedLpsAfterChange) { + verify(mDeps).reportNetworkInterfaceForTransports( + mServiceContext, stackedLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + } + reset(mMockNetd); + when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) + .thenReturn(getClatInterfaceConfigParcel(myIpv4)); + // Change the NAT64 prefix without first removing it. + // Expect clatd to be stopped and started with the new prefix. + mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( + cellNetId, PREFIX_OPERATION_ADDED, kOtherNat64PrefixString, 96)); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getStackedLinks().size() == 0); + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); + assertRoutesRemoved(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString()); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix)); + clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getStackedLinks().size() == 1); + assertRoutesAdded(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + reset(mMockNetd); + + // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked + // linkproperties are cleaned up. + cellLp.addLinkAddress(myIpv4); + cellLp.addRoute(ipv4Subnet); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + assertRoutesAdded(cellNetId, ipv4Subnet); + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); + verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); + + // As soon as stop is called, the linkproperties lose the stacked interface. + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()); + LinkProperties expected = new LinkProperties(cellLp); + expected.setNat64Prefix(kOtherNat64Prefix); + assertEquals(expected, actualLpAfterIpv4); + assertEquals(0, actualLpAfterIpv4.getStackedLinks().size()); + assertRoutesRemoved(cellNetId, stackedDefault); + + // The interface removed callback happens but has no effect after stop is called. + clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); + networkCallback.assertNoCallback(); + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + verifyNoMoreInteractions(mMockNetd); + verifyNoMoreInteractions(mMockDnsResolver); + reset(mMockNetd); + reset(mMockDnsResolver); + when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) + .thenReturn(getClatInterfaceConfigParcel(myIpv4)); + + // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone. + mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( + cellNetId, PREFIX_OPERATION_REMOVED, kOtherNat64PrefixString, 96)); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getNat64Prefix() == null); + + // Remove IPv4 address and expect prefix discovery and clatd to be started again. + cellLp.removeLinkAddress(myIpv4); + cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME)); + cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8")); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + assertRoutesRemoved(cellNetId, ipv4Subnet); // Directly-connected routes auto-added. + verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); + mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( + cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96)); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); + + // Clat iface comes up. Expect stacked link to be added. + clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null); + assertRoutesAdded(cellNetId, stackedDefault); + verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + + // NAT64 prefix is removed. Expect that clat is stopped. + mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( + cellNetId, PREFIX_OPERATION_REMOVED, kNat64PrefixString, 96)); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null); + assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault); + + // Stop has no effect because clat is already stopped. + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + (lp) -> lp.getStackedLinks().size() == 0); + verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); + verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME); + verifyNoMoreInteractions(mMockNetd); + // Clean up. + mCellNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + networkCallback.assertNoCallback(); + mCm.unregisterNetworkCallback(networkCallback); + } + + private void expectNat64PrefixChange(TestableNetworkCallback callback, + TestNetworkAgentWrapper agent, IpPrefix prefix) { + callback.expectLinkPropertiesThat(agent, x -> Objects.equals(x.getNat64Prefix(), prefix)); + } + + @Test + public void testNat64PrefixMultipleSources() throws Exception { + final String iface = "wlan0"; + final String pref64FromRaStr = "64:ff9b::"; + final String pref64FromDnsStr = "2001:db8:64::"; + final IpPrefix pref64FromRa = new IpPrefix(InetAddress.getByName(pref64FromRaStr), 96); + final IpPrefix pref64FromDns = new IpPrefix(InetAddress.getByName(pref64FromDnsStr), 96); + final IpPrefix newPref64FromRa = new IpPrefix("2001:db8:64:64:64:64::/96"); + + final NetworkRequest request = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final LinkProperties baseLp = new LinkProperties(); + baseLp.setInterfaceName(iface); + baseLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); + baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464")); + + reset(mMockNetd, mMockDnsResolver); + InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver); + + // If a network already has a NAT64 prefix on connect, clatd is started immediately and + // prefix discovery is never started. + LinkProperties lp = new LinkProperties(baseLp); + lp.setNat64Prefix(pref64FromRa); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); + mWiFiNetworkAgent.connect(false); + final Network network = mWiFiNetworkAgent.getNetwork(); + int netId = network.getNetId(); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + callback.assertNoCallback(); + assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); + + // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started. + lp.setNat64Prefix(null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); + inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); + + // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and + // clatd is started with the prefix from the RA. + lp.setNat64Prefix(pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); + inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); + inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); + + // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS + // discovery has succeeded. + lp.setNat64Prefix(null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); + inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); + + mService.mResolverUnsolEventCallback.onNat64PrefixEvent( + makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96)); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); + inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); + + // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix + // discovery is not stopped, and there are no callbacks. + lp.setNat64Prefix(pref64FromDns); + mWiFiNetworkAgent.sendLinkProperties(lp); + callback.assertNoCallback(); + inOrder.verify(mMockNetd, never()).clatdStop(iface); + inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); + inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); + + // If the RA is later withdrawn, nothing happens again. + lp.setNat64Prefix(null); + mWiFiNetworkAgent.sendLinkProperties(lp); + callback.assertNoCallback(); + inOrder.verify(mMockNetd, never()).clatdStop(iface); + inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); + inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); + + // If the RA prefix changes, clatd is restarted and prefix discovery is stopped. + lp.setNat64Prefix(pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); + + // Stopping prefix discovery results in a prefix removed notification. + mService.mResolverUnsolEventCallback.onNat64PrefixEvent( + makeNat64PrefixEvent(netId, PREFIX_OPERATION_REMOVED, pref64FromDnsStr, 96)); + + inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + + // If the RA prefix changes, clatd is restarted and prefix discovery is not started. + lp.setNat64Prefix(newPref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa); + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); + inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString()); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString()); + inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + + // If the RA prefix changes to the same value, nothing happens. + lp.setNat64Prefix(newPref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + callback.assertNoCallback(); + assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); + inOrder.verify(mMockNetd, never()).clatdStop(iface); + inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); + inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); + + // The transition between no prefix and DNS prefix is tested in testStackedLinkProperties. + + // If the same prefix is learned first by DNS and then by RA, and clat is later stopped, + // (e.g., because the network disconnects) setPrefix64(netid, "") is never called. + lp.setNat64Prefix(null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); + inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); + mService.mResolverUnsolEventCallback.onNat64PrefixEvent( + makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96)); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); + inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any()); + + lp.setNat64Prefix(pref64FromDns); + mWiFiNetworkAgent.sendLinkProperties(lp); + callback.assertNoCallback(); + inOrder.verify(mMockNetd, never()).clatdStop(iface); + inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); + inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); + + // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but + // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that + // clat has been stopped, or the test will be flaky. + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b.expectBroadcast(); + + inOrder.verify(mMockNetd).clatdStop(iface); + inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); + inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); + + mCm.unregisterNetworkCallback(callback); + } + + @Test + public void testWith464XlatDisable() throws Exception { + doReturn(false).when(mDeps).getCellular464XlatEnabled(); + + final TestNetworkCallback callback = new TestNetworkCallback(); + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + mCm.registerNetworkCallback(networkRequest, callback); + mCm.registerDefaultNetworkCallback(defaultCallback); + + // Bring up validated cell. + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); + cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME)); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + + mCellNetworkAgent.sendLinkProperties(cellLp); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + final int cellNetId = mCellNetworkAgent.getNetwork().netId; + waitForIdle(); + + verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId); + Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent); + assertTrue("Nat464Xlat was not IDLE", !clat.isStarted()); + + // This cannot happen because prefix discovery cannot succeed if it is never started. + mService.mResolverUnsolEventCallback.onNat64PrefixEvent( + makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96)); + + // ... but still, check that even if it did, clatd would not be started. + verify(mMockNetd, never()).clatdStart(anyString(), anyString()); + assertTrue("Nat464Xlat was not IDLE", !clat.isStarted()); + } + + @Test + public void testDataActivityTracking() throws Exception { + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + mCellNetworkAgent.sendLinkProperties(cellLp); + mCellNetworkAgent.connect(true); + networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + + // Network switch + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + + // Disconnect wifi and switch back to cell + reset(mMockNetd); + mWiFiNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + assertNoCallbacks(networkCallback); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + + // reconnect wifi + reset(mMockNetd); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + + // Disconnect cell + reset(mMockNetd); + mCellNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + // LOST callback is triggered earlier than removing idle timer. Broadcast should also be + // sent as network being switched. Ensure rule removal for cell will not be triggered + // unexpectedly before network being removed. + waitForIdle(); + verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId)); + verify(mMockDnsResolver, times(1)) + .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); + + // Disconnect wifi + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + b.expectBroadcast(); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + + // Clean up + mCm.unregisterNetworkCallback(networkCallback); + } + + private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception { + String[] values = tcpBufferSizes.split(","); + String rmemValues = String.join(" ", values[0], values[1], values[2]); + String wmemValues = String.join(" ", values[3], values[4], values[5]); + verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues); + reset(mMockNetd); + } + + @Test + public void testTcpBufferReset() throws Exception { + final String testTcpBufferSizes = "1,2,3,4,5,6"; + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + reset(mMockNetd); + // Switching default network updates TCP buffer sizes. + mCellNetworkAgent.connect(false); + networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); + // Change link Properties should have updated tcp buffer size. + LinkProperties lp = new LinkProperties(); + lp.setTcpBufferSizes(testTcpBufferSizes); + mCellNetworkAgent.sendLinkProperties(lp); + networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + verifyTcpBufferSizeChange(testTcpBufferSizes); + // Clean up. + mCellNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + networkCallback.assertNoCallback(); + mCm.unregisterNetworkCallback(networkCallback); + } + + @Test + public void testGetGlobalProxyForNetwork() throws Exception { + final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + final Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); + when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo); + assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork)); + } + + @Test + public void testGetProxyForActiveNetwork() throws Exception { + final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + assertNull(mService.getProxyForNetwork(null)); + + final LinkProperties testLinkProperties = new LinkProperties(); + testLinkProperties.setHttpProxy(testProxyInfo); + + mWiFiNetworkAgent.sendLinkProperties(testLinkProperties); + waitForIdle(); + + assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); + } + + @Test + public void testGetProxyForVPN() throws Exception { + final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); + + // Set up a WiFi network with no proxy + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + waitForIdle(); + assertNull(mService.getProxyForNetwork(null)); + + // Connect a VPN network with a proxy. + LinkProperties testLinkProperties = new LinkProperties(); + testLinkProperties.setHttpProxy(testProxyInfo); + mMockVpn.establishForMyUid(testLinkProperties); + assertUidRangesUpdatedForMyUid(true); + + // Test that the VPN network returns a proxy, and the WiFi does not. + assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); + assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); + assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); + + // Test that the VPN network returns no proxy when it is set to null. + testLinkProperties.setHttpProxy(null); + mMockVpn.sendLinkProperties(testLinkProperties); + waitForIdle(); + assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork())); + assertNull(mService.getProxyForNetwork(null)); + + // Set WiFi proxy and check that the vpn proxy is still null. + testLinkProperties.setHttpProxy(testProxyInfo); + mWiFiNetworkAgent.sendLinkProperties(testLinkProperties); + waitForIdle(); + assertNull(mService.getProxyForNetwork(null)); + + // Disconnect from VPN and check that the active network, which is now the WiFi, has the + // correct proxy setting. + mMockVpn.disconnect(); + waitForIdle(); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); + assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); + } + + @Test + public void testFullyRoutedVpnResultsInInterfaceFilteringRules() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + // The uid range needs to cover the test app so the network is visible to it. + final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); + + // A connected VPN should have interface rules set up. There are two expected invocations, + // one during the VPN initial connection, one during the VPN LinkProperties update. + ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); + verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); + assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); + assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange)); + + mMockVpn.disconnect(); + waitForIdle(); + + // Disconnected VPN should have interface rules removed + verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + assertNull(mService.mPermissionMonitor.getVpnUidRanges("tun0")); + } + + @Test + public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); + + // Legacy VPN should not have interface rules set up + verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); + } + + @Test + public void testLocalIpv4OnlyVpnDoesNotResultInInterfaceFilteringRule() + throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + // The uid range needs to cover the test app so the network is visible to it. + final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); + + // IPv6 unreachable route should not be misinterpreted as a default route + verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); + } + + @Test + public void testVpnHandoverChangesInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); + + // Connected VPN should have interface rules set up. There are two expected invocations, + // one during VPN uid update, one during VPN LinkProperties update + ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); + verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); + assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); + + reset(mMockNetd); + InOrder inOrder = inOrder(mMockNetd); + lp.setInterfaceName("tun1"); + mMockVpn.sendLinkProperties(lp); + waitForIdle(); + // VPN handover (switch to a new interface) should result in rules being updated (old rules + // removed first, then new rules added) + inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + + reset(mMockNetd); + lp = new LinkProperties(); + lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1")); + mMockVpn.sendLinkProperties(lp); + waitForIdle(); + // VPN not routing everything should no longer have interface filtering rules + verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + + reset(mMockNetd); + lp = new LinkProperties(); + lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + mMockVpn.sendLinkProperties(lp); + waitForIdle(); + // Back to routing all IPv6 traffic should have filtering rules + verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + } + + @Test + public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + // The uid range needs to cover the test app so the network is visible to it. + final UidRange vpnRange = PRIMARY_UIDRANGE; + final Set vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); + + reset(mMockNetd); + InOrder inOrder = inOrder(mMockNetd); + + // Update to new range which is old range minus APP1, i.e. only APP2 + final Set newRanges = new HashSet<>(Arrays.asList( + new UidRange(vpnRange.start, APP1_UID - 1), + new UidRange(APP1_UID + 1, vpnRange.stop))); + mMockVpn.setUids(newRanges); + waitForIdle(); + + ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); + // Verify old rules are removed before new rules are added + inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); + inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); + assertContainsExactly(uidCaptor.getValue(), APP2_UID); + } + + @Test + public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception { + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_WOL_IFNAME); + wifiLp.setWakeOnLanSupported(false); + + // Default network switch should update ifaces. + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + waitForIdle(); + + // ConnectivityService should have changed the WakeOnLanSupported to true + wifiLp.setWakeOnLanSupported(true); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + } + + @Test + public void testLegacyExtraInfoSentToNetworkMonitor() throws Exception { + class TestNetworkAgent extends NetworkAgent { + TestNetworkAgent(Context context, Looper looper, NetworkAgentConfig config) { + super(context, looper, "MockAgent", new NetworkCapabilities(), + new LinkProperties(), 40 , config, null /* provider */); + } + } + final NetworkAgent naNoExtraInfo = new TestNetworkAgent( + mServiceContext, mCsHandlerThread.getLooper(), new NetworkAgentConfig()); + naNoExtraInfo.register(); + verify(mNetworkStack).makeNetworkMonitor(any(), isNull(String.class), any()); + naNoExtraInfo.unregister(); + + reset(mNetworkStack); + final NetworkAgentConfig config = + new NetworkAgentConfig.Builder().setLegacyExtraInfo("legacyinfo").build(); + final NetworkAgent naExtraInfo = new TestNetworkAgent( + mServiceContext, mCsHandlerThread.getLooper(), config); + naExtraInfo.register(); + verify(mNetworkStack).makeNetworkMonitor(any(), eq("legacyinfo"), any()); + naExtraInfo.unregister(); + } + + // To avoid granting location permission bypass. + private void denyAllLocationPrivilegedPermissions() { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD, + PERMISSION_DENIED); + } + + private void setupLocationPermissions( + int targetSdk, boolean locationToggle, String op, String perm) throws Exception { + denyAllLocationPrivilegedPermissions(); + + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = targetSdk; + when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) + .thenReturn(applicationInfo); + when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk); + + when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); + + if (op != null) { + when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), + eq(mContext.getPackageName()), eq(getAttributionTag()), anyString())) + .thenReturn(AppOpsManager.MODE_ALLOWED); + } + + if (perm != null) { + mServiceContext.setPermission(perm, PERMISSION_GRANTED); + } + } + + private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid, + boolean includeLocationSensitiveInfo) { + final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); + + return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, includeLocationSensitiveInfo, Process.myUid(), callerUid, + mContext.getPackageName(), getAttributionTag()) + .getOwnerUid(); + } + + private void verifyTransportInfoCopyNetCapsPermission( + int callerUid, boolean includeLocationSensitiveInfo, + boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { + final TransportInfo transportInfo = mock(TransportInfo.class); + when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION); + final NetworkCapabilities netCap = + new NetworkCapabilities().setTransportInfo(transportInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, includeLocationSensitiveInfo, Process.myPid(), callerUid, + mContext.getPackageName(), getAttributionTag()); + if (shouldMakeCopyWithLocationSensitiveFieldsParcelable) { + verify(transportInfo).makeCopy(REDACT_NONE); + } else { + verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); + } + } + + private void verifyOwnerUidAndTransportInfoNetCapsPermission( + boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag, + boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag, + boolean shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag, + boolean shouldInclLocationSensitiveTransportInfoWithIncludeFlag) { + final int myUid = Process.myUid(); + + final int expectedOwnerUidWithoutIncludeFlag = + shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag + ? myUid : INVALID_UID; + assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission( + myUid, myUid, false /* includeLocationSensitiveInfo */)); + + final int expectedOwnerUidWithIncludeFlag = + shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID; + assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission( + myUid, myUid, true /* includeLocationSensitiveInfo */)); + + verifyTransportInfoCopyNetCapsPermission(myUid, + false, /* includeLocationSensitiveInfo */ + shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag); + + verifyTransportInfoCopyNetCapsPermission(myUid, + true, /* includeLocationSensitiveInfo */ + shouldInclLocationSensitiveTransportInfoWithIncludeFlag); + + } + + private void verifyOwnerUidAndTransportInfoNetCapsPermissionPreS() { + verifyOwnerUidAndTransportInfoNetCapsPermission( + // Ensure that owner uid is included even if the request asks to remove it (which is + // the default) since the app has necessary permissions and targetSdk < S. + true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + // Ensure that location info is removed if the request asks to remove it even if the + // app has necessary permissions. + false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ + true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ + ); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQPreS() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); + } + + @Test + public void + testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsPermission( + // Ensure that the owner UID is removed if the request asks us to remove it even + // if the app has necessary permissions since targetSdk >= S. + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + // Ensure that location info is removed if the request asks to remove it even if the + // app has necessary permissions. + false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ + true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ + ); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); + } + + private void verifyOwnerUidAndTransportInfoNetCapsNotIncluded() { + verifyOwnerUidAndTransportInfoNetCapsPermission( + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ + false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ + ); + } + + @Test + public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception { + // Test that even with fine location permission, and UIDs matching, the UID is sanitized. + setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); + } + + @Test + public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception { + // Test that even with fine location permission, not being the owner leads to sanitization. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + final int myUid = Process.myUid(); + assertEquals(Process.INVALID_UID, + getOwnerUidNetCapsPermission(myUid + 1, myUid, + true /* includeLocationSensitiveInfo */)); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ() + throws Exception { + // Test that not having fine location permission leads to sanitization. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterS() + throws Exception { + // Test that not having fine location permission leads to sanitization. + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_COARSE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION); + + verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); + } + + @Test + public void testCreateForCallerWithLocalMacAddressSanitizedWithLocalMacAddressPermission() + throws Exception { + mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED); + + final TransportInfo transportInfo = mock(TransportInfo.class); + when(transportInfo.getApplicableRedactions()) + .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS); + final NetworkCapabilities netCap = + new NetworkCapabilities().setTransportInfo(transportInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, false /* includeLocationSensitiveInfoInTransportInfo */, + Process.myPid(), Process.myUid(), + mContext.getPackageName(), getAttributionTag()); + // don't redact MAC_ADDRESS fields, only location sensitive fields. + verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); + } + + @Test + public void testCreateForCallerWithLocalMacAddressSanitizedWithoutLocalMacAddressPermission() + throws Exception { + mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED); + + final TransportInfo transportInfo = mock(TransportInfo.class); + when(transportInfo.getApplicableRedactions()) + .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS); + final NetworkCapabilities netCap = + new NetworkCapabilities().setTransportInfo(transportInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, false /* includeLocationSensitiveInfoInTransportInfo */, + Process.myPid(), Process.myUid(), + mContext.getPackageName(), getAttributionTag()); + // redact both MAC_ADDRESS & location sensitive fields. + verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION + | REDACT_FOR_LOCAL_MAC_ADDRESS); + } + + @Test + public void testCreateForCallerWithLocalMacAddressSanitizedWithSettingsPermission() + throws Exception { + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TransportInfo transportInfo = mock(TransportInfo.class); + when(transportInfo.getApplicableRedactions()) + .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); + final NetworkCapabilities netCap = + new NetworkCapabilities().setTransportInfo(transportInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, false /* includeLocationSensitiveInfoInTransportInfo */, + Process.myPid(), Process.myUid(), + mContext.getPackageName(), getAttributionTag()); + // don't redact NETWORK_SETTINGS fields, only location sensitive fields. + verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); + } + + @Test + public void testCreateForCallerWithLocalMacAddressSanitizedWithoutSettingsPermission() + throws Exception { + mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED); + + final TransportInfo transportInfo = mock(TransportInfo.class); + when(transportInfo.getApplicableRedactions()) + .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); + final NetworkCapabilities netCap = + new NetworkCapabilities().setTransportInfo(transportInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, false /* includeLocationSensitiveInfoInTransportInfo */, + Process.myPid(), Process.myUid(), + mContext.getPackageName(), getAttributionTag()); + // redact both NETWORK_SETTINGS & location sensitive fields. + verify(transportInfo).makeCopy( + REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); + } + + /** + * Test TransportInfo to verify redaction mechanism. + */ + private static class TestTransportInfo implements TransportInfo { + public final boolean locationRedacted; + public final boolean localMacAddressRedacted; + public final boolean settingsRedacted; + + TestTransportInfo() { + locationRedacted = false; + localMacAddressRedacted = false; + settingsRedacted = false; + } + + TestTransportInfo(boolean locationRedacted, boolean localMacAddressRedacted, + boolean settingsRedacted) { + this.locationRedacted = locationRedacted; + this.localMacAddressRedacted = + localMacAddressRedacted; + this.settingsRedacted = settingsRedacted; + } + + @Override + public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { + return new TestTransportInfo( + locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, + localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, + settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 + ); + } + + @Override + public @NetworkCapabilities.RedactionType long getApplicableRedactions() { + return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS + | REDACT_FOR_NETWORK_SETTINGS; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TestTransportInfo)) return false; + TestTransportInfo that = (TestTransportInfo) other; + return that.locationRedacted == this.locationRedacted + && that.localMacAddressRedacted == this.localMacAddressRedacted + && that.settingsRedacted == this.settingsRedacted; + } + + @Override + public int hashCode() { + return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted); + } + + @Override + public String toString() { + return String.format( + "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}", + locationRedacted, localMacAddressRedacted, settingsRedacted); + } + } + + private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) { + return (TestTransportInfo) nc.getTransportInfo(); + } + + private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork()); + assertNotNull(nc); + return getTestTransportInfo(nc); + } + + + private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( + @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid, + @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid, + @NonNull TransportInfo expectedTransportInfo) throws Exception { + when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S); + final NetworkCapabilities ncTemplate = + new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .setOwnerUid(actualOwnerUid); + + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), + ncTemplate); + mWiFiNetworkAgent.connect(false); + + wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Send network capabilities update with TransportInfo to trigger capabilities changed + // callback. + mWiFiNetworkAgent.setNetworkCapabilities( + ncTemplate.setTransportInfo(actualTransportInfo), true); + + wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent, + nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid()) + && Objects.equals(expectedTransportInfo, nc.getTransportInfo())); + } + + @Test + public void testVerifyLocationDataIsNotIncludedWhenInclFlagNotSet() throws Exception { + final TestNetworkCallback wifiNetworkCallack = new TestNetworkCallback(); + final int ownerUid = Process.myUid(); + final TransportInfo transportInfo = new TestTransportInfo(); + // Even though the test uid holds privileged permissions, mask location fields since + // the callback did not explicitly opt-in to get location data. + final TransportInfo sanitizedTransportInfo = new TestTransportInfo( + true, /* locationRedacted */ + true, /* localMacAddressRedacted */ + true /* settingsRedacted */ + ); + // Should not expect location data since the callback does not set the flag for including + // location data. + verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( + wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo); + } + + @Test + public void testTransportInfoRedactionInSynchronousCalls() throws Exception { + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .setTransportInfo(new TestTransportInfo()); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), + ncTemplate); + mWiFiNetworkAgent.connect(true /* validated; waits for callback */); + + // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + withPermission(NETWORK_SETTINGS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + + // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + withPermission(LOCAL_MAC_ADDRESS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + + // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive + // information. + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + denyAllLocationPrivilegedPermissions(); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + } + + private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) + throws Exception { + final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); + mMockVpn.setVpnType(vpnType); + mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); + + final UnderlyingNetworkInfo underlyingNetworkInfo = + new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList()); + mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo); + when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42); + } + + private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType) + throws Exception { + setupConnectionOwnerUid(vpnOwnerUid, vpnType); + + // Test as VPN app + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + mServiceContext.setPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED); + } + + private ConnectionInfo getTestConnectionInfo() throws Exception { + return new ConnectionInfo( + IPPROTO_TCP, + new InetSocketAddress(InetAddresses.parseNumericAddress("1.2.3.4"), 1234), + new InetSocketAddress(InetAddresses.parseNumericAddress("2.3.4.5"), 2345)); + } + + @Test + public void testGetConnectionOwnerUidPlatformVpn() throws Exception { + final int myUid = Process.myUid(); + setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM); + + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); + } + + @Test + public void testGetConnectionOwnerUidVpnServiceWrongUser() throws Exception { + final int myUid = Process.myUid(); + setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE); + + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); + } + + @Test + public void testGetConnectionOwnerUidVpnServiceDoesNotThrow() throws Exception { + final int myUid = Process.myUid(); + setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE); + + assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); + } + + @Test + public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception { + final int myUid = Process.myUid(); + setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE); + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); + + assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); + } + + @Test + public void testGetConnectionOwnerUidVpnServiceMainlineNetworkStackDoesNotThrow() + throws Exception { + final int myUid = Process.myUid(); + setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE); + mServiceContext.setPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED); + + assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); + } + + private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { + final PackageInfo packageInfo = new PackageInfo(); + if (hasSystemPermission) { + packageInfo.requestedPermissions = new String[] { + CHANGE_NETWORK_STATE, CONNECTIVITY_USE_RESTRICTED_NETWORKS }; + packageInfo.requestedPermissionsFlags = new int[] { + REQUESTED_PERMISSION_GRANTED, REQUESTED_PERMISSION_GRANTED }; + } else { + packageInfo.requestedPermissions = new String[0]; + } + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.privateFlags = 0; + packageInfo.applicationInfo.uid = UserHandle.getUid(UserHandle.USER_SYSTEM, + 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, mContext.getPackageName()); + fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest"); + } catch (IllegalArgumentException expected) { + } + } + + private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) { + assertEquals(route.getDestination().toString(), parcel.destination); + assertEquals(route.getInterface(), parcel.ifName); + assertEquals(route.getMtu(), parcel.mtu); + + switch (route.getType()) { + case RouteInfo.RTN_UNICAST: + if (route.hasGateway()) { + assertEquals(route.getGateway().getHostAddress(), parcel.nextHop); + } else { + assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop); + } + break; + case RouteInfo.RTN_UNREACHABLE: + assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop); + break; + case RouteInfo.RTN_THROW: + assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop); + break; + default: + assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop); + break; + } + } + + private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception { + ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); + verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture()); + for (int i = 0; i < routes.length; i++) { + assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i)); + } + } + + private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception { + ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); + verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId), + captor.capture()); + for (int i = 0; i < routes.length; i++) { + assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i)); + } + } + + @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, mContext.getPackageName()); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + verify(mConnectivityDiagnosticsCallback).asBinder(); + assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); + + mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback); + verify(mIBinder, timeout(TIMEOUT_MS)) + .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); + 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, mContext.getPackageName()); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + verify(mConnectivityDiagnosticsCallback).asBinder(); + assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); + + // Register the same callback again + mService.registerConnectivityDiagnosticsCallback( + mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); + } + + public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) { + final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, + ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), + TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); + return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), + nc, new NetworkScore.Builder().setLegacyInt(0).build(), + mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, + INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker, + new ConnectivityService.Dependencies()); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception { + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); + assertTrue( + "NetworkStack permission not applied", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid(), Process.myUid(), naiWithoutUid, + mContext.getOpPackageName())); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception { + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + + assertFalse( + "Mismatched uid/package name should not pass the location permission check", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid, + mContext.getOpPackageName())); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception { + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + + assertFalse( + "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid(), Process.myUid(), naiWithoutUid, + mContext.getOpPackageName())); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception { + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + + mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); + + // Wait for networks to connect and broadcasts to be sent before removing permissions. + waitForIdle(); + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network})); + waitForIdle(); + assertTrue( + "Active VPN permission not applied", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid(), Process.myUid(), naiWithoutUid, + mContext.getOpPackageName())); + + assertTrue(mMockVpn.setUnderlyingNetworks(null)); + waitForIdle(); + assertFalse( + "VPN shouldn't receive callback on non-underlying network", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid(), Process.myUid(), naiWithoutUid, + mContext.getOpPackageName())); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception { + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setAdministratorUids(new int[] {Process.myUid()}); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); + + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + + assertTrue( + "NetworkCapabilities administrator uid permission not applied", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName())); + } + + @Test + public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception { + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setOwnerUid(Process.myUid()); + nc.setAdministratorUids(new int[] {Process.myUid()}); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); + + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + + // Use wrong pid and uid + assertFalse( + "Permissions allowed when they shouldn't be granted", + mService.checkConnectivityDiagnosticsPermissions( + Process.myPid() + 1, Process.myUid() + 1, naiWithUid, + mContext.getOpPackageName())); + } + + @Test + public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport() + throws Exception { + // Set up the Network, which leads to a ConnectivityReport being cached for the network. + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(INTERFACE_NAME); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callback.assertNoCallback(); + + final NetworkRequest request = new NetworkRequest.Builder().build(); + when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); + + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); + + mService.registerConnectivityDiagnosticsCallback( + mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + verify(mConnectivityDiagnosticsCallback) + .onConnectivityReportAvailable(argThat(report -> { + return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName()) + && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR); + })); + } + + private void setUpConnectivityDiagnosticsCallback() throws Exception { + final NetworkRequest request = new NetworkRequest.Builder().build(); + when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); + + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); + + mService.registerConnectivityDiagnosticsCallback( + mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Connect the cell agent verify that it notifies TestNetworkCallback that it is available + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .setTransportInfo(new TestTransportInfo()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), + ncTemplate); + mCellNetworkAgent.connect(true); + callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callback.assertNoCallback(); + } + + private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) { + TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo(); + return nc.getUids() == null + && nc.getAdministratorUids().length == 0 + && nc.getOwnerUid() == Process.INVALID_UID + && getTestTransportInfo(nc).locationRedacted + && getTestTransportInfo(nc).localMacAddressRedacted + && getTestTransportInfo(nc).settingsRedacted; + } + + @Test + public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() + throws Exception { + setUpConnectivityDiagnosticsCallback(); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onConnectivityReport fired + verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable( + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception { + setUpConnectivityDiagnosticsCallback(); + + // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the + // cellular network agent + mCellNetworkAgent.notifyDataStallSuspected(); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onDataStallSuspected fired + verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception { + setUpConnectivityDiagnosticsCallback(); + + final Network n = mCellNetworkAgent.getNetwork(); + final boolean hasConnectivity = true; + mService.reportNetworkConnectivity(n, hasConnectivity); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onNetworkConnectivityReported fired + verify(mConnectivityDiagnosticsCallback) + .onNetworkConnectivityReported(eq(n), eq(hasConnectivity)); + + final boolean noConnectivity = false; + mService.reportNetworkConnectivity(n, noConnectivity); + + // Block until all other events are done processing. + HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Wait for onNetworkConnectivityReported to fire + verify(mConnectivityDiagnosticsCallback) + .onNetworkConnectivityReported(eq(n), eq(noConnectivity)); + } + + @Test + public void testRouteAddDeleteUpdate() throws Exception { + final NetworkRequest request = new NetworkRequest.Builder().build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, networkCallback); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + reset(mMockNetd); + mCellNetworkAgent.connect(false); + networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + final int netId = mCellNetworkAgent.getNetwork().netId; + + final String iface = "rmnet_data0"; + final InetAddress gateway = InetAddress.getByName("fe80::5678"); + RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface); + RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface); + RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface); + RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface); + RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST, + 1280 /* mtu */); + + // Send LinkProperties and check that we ask netd to add routes. + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(iface); + lp.addRoute(direct); + lp.addRoute(rio1); + lp.addRoute(defaultRoute); + mCellNetworkAgent.sendLinkProperties(lp); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3); + + assertRoutesAdded(netId, direct, rio1, defaultRoute); + reset(mMockNetd); + + // Send updated LinkProperties and check that we ask netd to add, remove, update routes. + assertTrue(lp.getRoutes().contains(defaultRoute)); + lp.removeRoute(rio1); + lp.addRoute(rio2); + lp.addRoute(defaultWithMtu); + // Ensure adding the same route with a different MTU replaces the previous route. + assertFalse(lp.getRoutes().contains(defaultRoute)); + assertTrue(lp.getRoutes().contains(defaultWithMtu)); + + mCellNetworkAgent.sendLinkProperties(lp); + networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, + x -> x.getRoutes().contains(rio2)); + + assertRoutesRemoved(netId, rio1); + assertRoutesAdded(netId, rio2); + + ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); + verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture()); + assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue()); + + + mCm.unregisterNetworkCallback(networkCallback); + } + + @Test + public void testDumpDoesNotCrash() { + mServiceContext.setPermission(DUMP, PERMISSION_GRANTED); + // Filing a couple requests prior to testing the dump. + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + final StringWriter stringWriter = new StringWriter(); + + mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); + + assertFalse(stringWriter.toString().isEmpty()); + } + + @Test + public void testRequestsSortedByIdSortsCorrectly() { + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + waitForIdle(); + + final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); + + assertTrue(nriOutput.length > 1); + for (int i = 0; i < nriOutput.length - 1; i++) { + final boolean isRequestIdInOrder = + nriOutput[i].mRequests.get(0).requestId + < nriOutput[i + 1].mRequests.get(0).requestId; + assertTrue(isRequestIdInOrder); + } + } + + private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception { + final int uid = Process.myUid(); + assertVpnUidRangesUpdated(add, uidRangesForUids(uid), uid); + } + + private void assertVpnUidRangesUpdated(boolean add, Set vpnRanges, int exemptUid) + throws Exception { + InOrder inOrder = inOrder(mMockNetd); + ArgumentCaptor exemptUidCaptor = ArgumentCaptor.forClass(int[].class); + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + + if (add) { + inOrder.verify(mMockNetd, times(1)) + .networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } else { + inOrder.verify(mMockNetd, times(1)) + .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + } + + @Test + public void testVpnUidRangesUpdate() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + final UidRange vpnRange = PRIMARY_UIDRANGE; + Set vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); + + reset(mMockNetd); + // Update to new range which is old range minus APP1, i.e. only APP2 + final Set newRanges = new HashSet<>(Arrays.asList( + new UidRange(vpnRange.start, APP1_UID - 1), + new UidRange(APP1_UID + 1, vpnRange.stop))); + mMockVpn.setUids(newRanges); + waitForIdle(); + + assertVpnUidRangesUpdated(true, newRanges, VPN_UID); + assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID); + } + + @Test + public void testInvalidRequestTypes() { + final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(), + NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length}; + final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); + + for (int reqTypeInt : invalidReqTypeInts) { + assertThrows("Expect throws for invalid request type " + reqTypeInt, + IllegalArgumentException.class, + () -> mService.requestNetwork(Process.INVALID_UID, nc, reqTypeInt, null, 0, + null, ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, + mContext.getPackageName(), getAttributionTag()) + ); + } + } + + @Test + public void testKeepConnected() throws Exception { + setAlwaysOnNetworks(false); + registerDefaultNetworkCallbacks(); + final TestNetworkCallback allNetworksCb = new TestNetworkCallback(); + final NetworkRequest allNetworksRequest = new NetworkRequest.Builder().clearCapabilities() + .build(); + mCm.registerNetworkCallback(allNetworksRequest, allNetworksCb); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true /* validated */); + + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true /* validated */); + + mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + // While the default callback doesn't see the network before it's validated, the listen + // sees the network come up and validate later + allNetworksCb.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); + allNetworksCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, + TEST_LINGER_DELAY_MS * 2); + + // The cell network has disconnected (see LOST above) because it was outscored and + // had no requests (see setAlwaysOnNetworks(false) above) + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + final NetworkScore score = new NetworkScore.Builder().setLegacyInt(30).build(); + mCellNetworkAgent.setScore(score); + mCellNetworkAgent.connect(false /* validated */); + + // The cell network gets torn down right away. + allNetworksCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, + TEST_NASCENT_DELAY_MS * 2); + allNetworksCb.assertNoCallback(); + + // Now create a cell network with KEEP_CONNECTED_FOR_HANDOVER and make sure it's + // not disconnected immediately when outscored. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + final NetworkScore scoreKeepup = new NetworkScore.Builder().setLegacyInt(30) + .setKeepConnectedReason(KEEP_CONNECTED_FOR_HANDOVER).build(); + mCellNetworkAgent.setScore(scoreKeepup); + mCellNetworkAgent.connect(true /* validated */); + + allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.assertNoCallback(); + + mWiFiNetworkAgent.disconnect(); + + allNetworksCb.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + + // Reconnect a WiFi network and make sure the cell network is still not torn down. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true /* validated */); + + allNetworksCb.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + + // Now remove the reason to keep connected and make sure the network lingers and is + // torn down. + mCellNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).build()); + allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent, + TEST_NASCENT_DELAY_MS * 2); + allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, + TEST_LINGER_DELAY_MS * 2); + mDefaultNetworkCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(allNetworksCb); + // mDefaultNetworkCallback will be unregistered by tearDown() + } + + private class QosCallbackMockHelper { + @NonNull public final QosFilter mFilter; + @NonNull public final IQosCallback mCallback; + @NonNull public final TestNetworkAgentWrapper mAgentWrapper; + @NonNull private final List mCallbacks = new ArrayList(); + + QosCallbackMockHelper() throws Exception { + Log.d(TAG, "QosCallbackMockHelper: "); + mFilter = mock(QosFilter.class); + + // Ensure the network is disconnected before anything else occurs + assertNull(mCellNetworkAgent); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + verifyActiveNetwork(TRANSPORT_CELLULAR); + waitForIdle(); + final Network network = mCellNetworkAgent.getNetwork(); + + final Pair pair = createQosCallback(); + mCallback = pair.first; + + when(mFilter.getNetwork()).thenReturn(network); + when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mAgentWrapper = mCellNetworkAgent; + } + + void registerQosCallback(@NonNull final QosFilter filter, + @NonNull final IQosCallback callback) { + mCallbacks.add(callback); + final NetworkAgentInfo nai = + mService.getNetworkAgentInfoForNetwork(filter.getNetwork()); + mService.registerQosCallbackInternal(filter, callback, nai); + } + + void tearDown() { + for (int i = 0; i < mCallbacks.size(); i++) { + mService.unregisterQosCallback(mCallbacks.get(i)); + } + } + } + + private Pair createQosCallback() { + final IQosCallback callback = mock(IQosCallback.class); + final IBinder binder = mock(Binder.class); + when(callback.asBinder()).thenReturn(binder); + when(binder.isBinderAlive()).thenReturn(true); + return new Pair<>(callback, binder); + } + + + @Test + public void testQosCallbackRegistration() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper; + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + + final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 = + (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister) + wrapper.getCallbackHistory().poll(1000, x -> true); + assertNotNull(cbRegister1); + + final int registerCallbackId = cbRegister1.mQosCallbackId; + mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback); + final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister; + cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister) + wrapper.getCallbackHistory().poll(1000, x -> true); + assertNotNull(cbUnregister); + assertEquals(registerCallbackId, cbUnregister.mQosCallbackId); + assertNull(wrapper.getCallbackHistory().poll(200, x -> true)); + } + + @Test + public void testQosCallbackNoRegistrationOnValidationError() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback) + .onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED)); + } + + @Test + public void testQosCallbackAvailableAndLost() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + final int sessionId = 10; + final int qosCallbackId = 1; + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + waitForIdle(); + + final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes( + 1, 2, 3, 4, 5, new ArrayList<>()); + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); + waitForIdle(); + + verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes)); + + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_EPS_BEARER)); + } + + @Test + public void testNrQosCallbackAvailableAndLost() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + final int sessionId = 10; + final int qosCallbackId = 1; + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + waitForIdle(); + + final NrQosSessionAttributes attributes = new NrQosSessionAttributes( + 1, 2, 3, 4, 5, 6, 7, new ArrayList<>()); + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); + waitForIdle(); + + verify(mQosCallbackMockHelper.mCallback).onNrQosSessionAvailable(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_NR_BEARER), eq(attributes)); + + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_NR_BEARER); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_NR_BEARER)); + } + + @Test + public void testQosCallbackTooManyRequests() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + for (int i = 0; i < 100; i++) { + final Pair pair = createQosCallback(); + + try { + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, pair.first); + } catch (ServiceSpecificException e) { + assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS); + if (i < 50) { + fail("TOO_MANY_REQUESTS thrown too early, the count is " + i); + } + + // As long as there is at least 50 requests, it is safe to assume it works. + // Note: The count isn't being tested precisely against 100 because the counter + // is shared with request network. + return; + } + } + fail("TOO_MANY_REQUESTS never thrown"); + } + + private UidRange createUidRange(int userId) { + return UidRange.createForUser(UserHandle.of(userId)); + } + + private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid) { + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = uid; + try { + when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())) + .thenReturn(applicationInfo); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName) + throws Exception { + when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())) + .thenThrow(new PackageManager.NameNotFoundException(packageName)); + } + + private void mockHasSystemFeature(@NonNull final String featureName, + @NonNull final boolean hasFeature) { + when(mPackageManager.hasSystemFeature(eq(featureName))) + .thenReturn(hasFeature); + } + + private Range getNriFirstUidRange( + @NonNull final ConnectivityService.NetworkRequestInfo nri) { + return nri.mRequests.get(0).networkCapabilities.getUids().iterator().next(); + } + + private OemNetworkPreferences createDefaultOemNetworkPreferences( + @OemNetworkPreferences.OemNetworkPreference final int preference) { + // Arrange PackageManager mocks + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + + // Build OemNetworkPreferences object + return new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, preference) + .build(); + } + + @Test + public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError() + throws PackageManager.NameNotFoundException { + @OemNetworkPreferences.OemNetworkPreference final int prefToTest = + OEM_NETWORK_PREFERENCE_UNINITIALIZED; + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + assertThrows(IllegalArgumentException.class, + () -> mService.new OemNetworkRequestFactory() + .createNrisFromOemNetworkPreferences( + createDefaultOemNetworkPreferences(prefToTest))); + } + + @Test + public void testOemNetworkRequestFactoryPreferenceOemPaid() + throws Exception { + // Expectations + final int expectedNumOfNris = 1; + final int expectedNumOfRequests = 3; + + @OemNetworkPreferences.OemNetworkPreference final int prefToTest = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory() + .createNrisFromOemNetworkPreferences( + createDefaultOemNetworkPreferences(prefToTest)); + + final List mRequests = nris.iterator().next().mRequests; + assertEquals(expectedNumOfNris, nris.size()); + assertEquals(expectedNumOfRequests, mRequests.size()); + assertTrue(mRequests.get(0).isListen()); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED)); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED)); + assertTrue(mRequests.get(1).isRequest()); + assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID)); + assertEquals(NetworkRequest.Type.TRACK_DEFAULT, mRequests.get(2).type); + assertTrue(mService.getDefaultRequest().networkCapabilities.equalsNetCapabilities( + mRequests.get(2).networkCapabilities)); + } + + @Test + public void testOemNetworkRequestFactoryPreferenceOemPaidNoFallback() + throws Exception { + // Expectations + final int expectedNumOfNris = 1; + final int expectedNumOfRequests = 2; + + @OemNetworkPreferences.OemNetworkPreference final int prefToTest = + OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory() + .createNrisFromOemNetworkPreferences( + createDefaultOemNetworkPreferences(prefToTest)); + + final List mRequests = nris.iterator().next().mRequests; + assertEquals(expectedNumOfNris, nris.size()); + assertEquals(expectedNumOfRequests, mRequests.size()); + assertTrue(mRequests.get(0).isListen()); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED)); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED)); + assertTrue(mRequests.get(1).isRequest()); + assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID)); + } + + @Test + public void testOemNetworkRequestFactoryPreferenceOemPaidOnly() + throws Exception { + // Expectations + final int expectedNumOfNris = 1; + final int expectedNumOfRequests = 1; + + @OemNetworkPreferences.OemNetworkPreference final int prefToTest = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory() + .createNrisFromOemNetworkPreferences( + createDefaultOemNetworkPreferences(prefToTest)); + + final List mRequests = nris.iterator().next().mRequests; + assertEquals(expectedNumOfNris, nris.size()); + assertEquals(expectedNumOfRequests, mRequests.size()); + assertTrue(mRequests.get(0).isRequest()); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID)); + } + + @Test + public void testOemNetworkRequestFactoryPreferenceOemPrivateOnly() + throws Exception { + // Expectations + final int expectedNumOfNris = 1; + final int expectedNumOfRequests = 1; + + @OemNetworkPreferences.OemNetworkPreference final int prefToTest = + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory() + .createNrisFromOemNetworkPreferences( + createDefaultOemNetworkPreferences(prefToTest)); + + final List mRequests = nris.iterator().next().mRequests; + assertEquals(expectedNumOfNris, nris.size()); + assertEquals(expectedNumOfRequests, mRequests.size()); + assertTrue(mRequests.get(0).isRequest()); + assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PRIVATE)); + assertFalse(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID)); + } + + @Test + public void testOemNetworkRequestFactoryCreatesCorrectNumOfNris() + throws Exception { + // Expectations + final int expectedNumOfNris = 2; + + // Arrange PackageManager mocks + final String testPackageName2 = "com.google.apps.dialer"; + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + mockGetApplicationInfo(testPackageName2, TEST_PACKAGE_UID); + + // Build OemNetworkPreferences object + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) + .addNetworkPreference(testPackageName2, testOemPref2) + .build(); + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref); + + assertNotNull(nris); + assertEquals(expectedNumOfNris, nris.size()); + } + + @Test + public void testOemNetworkRequestFactoryMultiplePrefsCorrectlySetsUids() + throws Exception { + // Arrange PackageManager mocks + final String testPackageName2 = "com.google.apps.dialer"; + final int testPackageNameUid2 = 456; + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + mockGetApplicationInfo(testPackageName2, testPackageNameUid2); + + // Build OemNetworkPreferences object + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) + .addNetworkPreference(testPackageName2, testOemPref2) + .build(); + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final List nris = + new ArrayList<>( + mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences( + pref)); + + // Sort by uid to access nris by index + nris.sort(Comparator.comparingInt(nri -> getNriFirstUidRange(nri).getLower())); + assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getLower()); + assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getUpper()); + assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getLower()); + assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getUpper()); + } + + @Test + public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids() + throws Exception { + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + + // Arrange PackageManager mocks + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + + // Build OemNetworkPreferences object + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) + .build(); + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final List nris = + new ArrayList<>( + mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences( + pref)); + + // UIDs for all users and all managed packages should be present. + // Two users each with two packages. + final int expectedUidSize = 2; + final List> uids = + new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids()); + assertEquals(expectedUidSize, uids.size()); + + // Sort by uid to access nris by index + uids.sort(Comparator.comparingInt(uid -> uid.getLower())); + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getLower()); + assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getUpper()); + assertEquals(secondUserTestPackageUid, (int) uids.get(1).getLower()); + assertEquals(secondUserTestPackageUid, (int) uids.get(1).getUpper()); + } + + @Test + public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference() + throws Exception { + // Expectations + final int expectedNumOfNris = 1; + final int expectedNumOfAppUids = 2; + + // Arrange PackageManager mocks + final String testPackageName2 = "com.google.apps.dialer"; + final int testPackageNameUid2 = 456; + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + mockGetApplicationInfo(testPackageName2, testPackageNameUid2); + + // Build OemNetworkPreferences object + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) + .addNetworkPreference(testPackageName2, testOemPref) + .build(); + + // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() + final ArraySet nris = + mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref); + + assertEquals(expectedNumOfNris, nris.size()); + assertEquals(expectedNumOfAppUids, + nris.iterator().next().mRequests.get(0).networkCapabilities.getUids().size()); + } + + @Test + public void testSetOemNetworkPreferenceNullListenerAndPrefParamThrowsNpe() { + mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); + + // Act on ConnectivityService.setOemNetworkPreference() + assertThrows(NullPointerException.class, + () -> mService.setOemNetworkPreference( + null, + null)); + } + + @Test + public void testSetOemNetworkPreferenceFailsForNonAutomotive() + throws Exception { + mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false); + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + + // Act on ConnectivityService.setOemNetworkPreference() + assertThrows(UnsupportedOperationException.class, + () -> mService.setOemNetworkPreference( + createDefaultOemNetworkPreferences(networkPref), + new TestOemListenerCallback())); + } + + private void setOemNetworkPreferenceAgentConnected(final int transportType, + final boolean connectAgent) throws Exception { + switch(transportType) { + // Corresponds to a metered cellular network. Will be used for the default network. + case TRANSPORT_CELLULAR: + if (!connectAgent) { + mCellNetworkAgent.disconnect(); + break; + } + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + mCellNetworkAgent.connect(true); + break; + // Corresponds to a restricted ethernet network with OEM_PAID/OEM_PRIVATE. + case TRANSPORT_ETHERNET: + if (!connectAgent) { + stopOemManagedNetwork(); + break; + } + startOemManagedNetwork(true); + break; + // Corresponds to unmetered Wi-Fi. + case TRANSPORT_WIFI: + if (!connectAgent) { + mWiFiNetworkAgent.disconnect(); + break; + } + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + break; + default: + throw new AssertionError("Unsupported transport type passed in."); + + } + waitForIdle(); + } + + private void startOemManagedNetwork(final boolean isOemPaid) throws Exception { + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.addCapability( + isOemPaid ? NET_CAPABILITY_OEM_PAID : NET_CAPABILITY_OEM_PRIVATE); + mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + mEthernetNetworkAgent.connect(true); + } + + private void stopOemManagedNetwork() { + mEthernetNetworkAgent.disconnect(); + waitForIdle(); + } + + private void verifyMultipleDefaultNetworksTracksCorrectly( + final int expectedOemRequestsSize, + @NonNull final Network expectedDefaultNetwork, + @NonNull final Network expectedPerAppNetwork) { + // The current test setup assumes two tracked default network requests; one for the default + // network and the other for the OEM network preference being tested. This will be validated + // each time to confirm it doesn't change under test. + final int expectedDefaultNetworkRequestsSize = 2; + assertEquals(expectedDefaultNetworkRequestsSize, mService.mDefaultNetworkRequests.size()); + for (final ConnectivityService.NetworkRequestInfo defaultRequest + : mService.mDefaultNetworkRequests) { + final Network defaultNetwork = defaultRequest.getSatisfier() == null + ? null : defaultRequest.getSatisfier().network(); + // If this is the default request. + if (defaultRequest == mService.mDefaultRequest) { + assertEquals( + expectedDefaultNetwork, + defaultNetwork); + // Make sure this value doesn't change. + assertEquals(1, defaultRequest.mRequests.size()); + continue; + } + assertEquals(expectedPerAppNetwork, defaultNetwork); + assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size()); + } + verifyMultipleDefaultCallbacks(expectedDefaultNetwork, expectedPerAppNetwork); + } + + /** + * Verify default callbacks for 'available' fire as expected. This will only run if + * registerDefaultNetworkCallbacks() was executed prior and will only be different if the + * setOemNetworkPreference() per-app API was used for the current process. + * @param expectedSystemDefault the expected network for the system default. + * @param expectedPerAppDefault the expected network for the current process's default. + */ + private void verifyMultipleDefaultCallbacks( + @NonNull final Network expectedSystemDefault, + @NonNull final Network expectedPerAppDefault) { + if (null != mSystemDefaultNetworkCallback && null != expectedSystemDefault + && mService.mNoServiceNetwork.network() != expectedSystemDefault) { + // getLastAvailableNetwork() is used as this method can be called successively with + // the same network to validate therefore expectAvailableThenValidatedCallbacks + // can't be used. + assertEquals(mSystemDefaultNetworkCallback.getLastAvailableNetwork(), + expectedSystemDefault); + } + if (null != mDefaultNetworkCallback && null != expectedPerAppDefault + && mService.mNoServiceNetwork.network() != expectedPerAppDefault) { + assertEquals(mDefaultNetworkCallback.getLastAvailableNetwork(), + expectedPerAppDefault); + } + } + + private void registerDefaultNetworkCallbacks() { + // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback() + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); + mSystemDefaultNetworkCallback = new TestNetworkCallback(); + mDefaultNetworkCallback = new TestNetworkCallback(); + mProfileDefaultNetworkCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback); + registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback, + TEST_WORK_PROFILE_APP_UID); + // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); + } + + private void unregisterDefaultNetworkCallbacks() { + if (null != mDefaultNetworkCallback) { + mCm.unregisterNetworkCallback(mDefaultNetworkCallback); + } + if (null != mSystemDefaultNetworkCallback) { + mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback); + } + if (null != mProfileDefaultNetworkCallback) { + mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback); + } + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) + throws Exception { + final int testPackageNameUid = TEST_PACKAGE_UID; + final String testPackageName = "per.app.defaults.package"; + setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + networkPrefToSetup, testPackageNameUid, testPackageName); + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) + throws Exception { + final int testPackageNameUid = Process.myUid(); + final String testPackageName = "per.app.defaults.package"; + setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + networkPrefToSetup, testPackageNameUid, testPackageName); + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, + final int testPackageUid, @NonNull final String testPackageName) throws Exception { + // Only the default request should be included at start. + assertEquals(1, mService.mDefaultNetworkRequests.size()); + + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(testPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest( + networkPrefToSetup, uidRanges, testPackageName); + } + + private void setupSetOemNetworkPreferenceForPreferenceTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, + @NonNull final UidRangeParcel[] uidRanges, + @NonNull final String testPackageName) + throws Exception { + // These tests work off a single UID therefore using 'start' is valid. + mockGetApplicationInfo(testPackageName, uidRanges[0].start); + + setOemNetworkPreference(networkPrefToSetup, testPackageName); + } + + private void setOemNetworkPreference(final int networkPrefToSetup, + @NonNull final String... testPackageNames) + throws Exception { + mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); + + // Build OemNetworkPreferences object + final OemNetworkPreferences.Builder builder = new OemNetworkPreferences.Builder(); + for (final String packageName : testPackageNames) { + builder.addNetworkPreference(packageName, networkPrefToSetup); + } + final OemNetworkPreferences pref = builder.build(); + + // Act on ConnectivityService.setOemNetworkPreference() + final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback(); + mService.setOemNetworkPreference(pref, oemPrefListener); + + // Verify call returned successfully + oemPrefListener.expectOnComplete(); + } + + private static class TestOemListenerCallback implements IOnCompleteListener { + final CompletableFuture mDone = new CompletableFuture<>(); + + @Override + public void onComplete() { + mDone.complete(new Object()); + } + + void expectOnComplete() { + try { + mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + fail("Expected onComplete() not received after " + TIMEOUT_MS + " ms"); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + @Override + public IBinder asBinder() { + return null; + } + } + + @Test + public void testMultiDefaultGetActiveNetworkIsCorrect() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active network for the default should be null at this point as this is a retricted + // network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Verify that the active network is correct + verifyActiveNetwork(TRANSPORT_ETHERNET); + // default NCs will be unregistered in tearDown + } + + @Test + public void testMultiDefaultIsActiveNetworkMeteredIsCorrect() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + + // Connect to an unmetered restricted network that will only be available to the OEM pref. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_OEM_PAID); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + mEthernetNetworkAgent.connect(true); + waitForIdle(); + + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + assertFalse(mCm.isActiveNetworkMetered()); + // default NCs will be unregistered in tearDown + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallback() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + + // Register the default network callback before the pref is already set. This means that + // the policy will be applied to the callback on setOemNetworkPreference(). + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // At this point with a restricted network used, the available callback should trigger. + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); + + // Now bring down the default network which should trigger a LOST callback. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + + // At this point, with no network is available, the lost callback should trigger + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallbackAfterPrefSet() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Register the default network callback after the pref is already set. This means that + // the policy will be applied to the callback on requestNetwork(). + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // At this point with a restricted network used, the available callback should trigger + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); + + // Now bring down the default network which should trigger a LOST callback. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + otherUidDefaultCallback.assertNoCallback(); + + // At this point, with no network is available, the lost callback should trigger + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallbackDoesNotFire() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + final int userId = UserHandle.getUserId(Process.myUid()); + + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + + // Setup a process different than the test process to use the default network. This means + // that the defaultNetworkCallback won't be tracked by the per-app policy. + setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // As this callback does not have access to the OEM_PAID network, it will not fire. + defaultNetworkCallback.assertNoCallback(); + assertDefaultNetworkCapabilities(userId /* no networks */); + + // The other UID does have access, and gets a callback. + otherUidDefaultCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + + // Bring up unrestricted cellular. This should now satisfy the default network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // At this point with an unrestricted network used, the available callback should trigger + // The other UID is unaffected and remains on the paid network. + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mCellNetworkAgent.getNetwork()); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); + + // Now bring down the per-app network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + + // Since the callback didn't use the per-app network, only the other UID gets a callback. + // Because the preference specifies no fallback, it does not switch to cellular. + defaultNetworkCallback.assertNoCallback(); + otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + + // Now bring down the default network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + + // As this callback was tracking the default, this should now trigger. + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); + } + + /** + * This method assumes that the same uidRanges input will be used to verify that dependencies + * are called as expected. + */ + private void verifySetOemNetworkPreferenceForPreference( + @NonNull final UidRangeParcel[] uidRanges, + final int addUidRangesNetId, + final int addUidRangesTimes, + final int removeUidRangesNetId, + final int removeUidRangesTimes, + final boolean shouldDestroyNetwork) throws RemoteException { + verifySetOemNetworkPreferenceForPreference(uidRanges, uidRanges, + addUidRangesNetId, addUidRangesTimes, removeUidRangesNetId, removeUidRangesTimes, + shouldDestroyNetwork); + } + + private void verifySetOemNetworkPreferenceForPreference( + @NonNull final UidRangeParcel[] addedUidRanges, + @NonNull final UidRangeParcel[] removedUidRanges, + final int addUidRangesNetId, + final int addUidRangesTimes, + final int removeUidRangesNetId, + final int removeUidRangesTimes, + final boolean shouldDestroyNetwork) throws RemoteException { + final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId; + final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId; + + // Validate netd. + verify(mMockNetd, times(addUidRangesTimes)) + .networkAddUidRanges( + (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(addedUidRanges)); + verify(mMockNetd, times(removeUidRangesTimes)) + .networkRemoveUidRanges( + (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), + eq(removedUidRanges)); + if (shouldDestroyNetwork) { + verify(mMockNetd, times(1)) + .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId))); + } + reset(mMockNetd); + } + + /** + * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference(). + */ + @Test + public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception { + @OemNetworkPreferences.OemNetworkPreference int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testPackageUid = 123; + final String testPackageName = "com.google.apps.contacts"; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(testPackageUid)); + + // Validate the starting requests only includes the fallback request. + assertEquals(1, mService.mDefaultNetworkRequests.size()); + + // Add an OEM default network request to track. + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); + + // Two requests should exist, one for the fallback and one for the pref. + assertEquals(2, mService.mDefaultNetworkRequests.size()); + + networkPref = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); + + // Two requests should still exist validating the previous per-app request was replaced. + assertEquals(2, mService.mDefaultNetworkRequests.size()); + } + + /** + * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback + */ + @Test + public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange PackageManager mocks + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should have no effect as it is lower in priority then unmetered. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + // netd should not be called as default networks haven't changed. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting unmetered should put PANS on lowest priority fallback request. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + + // Disconnecting the fallback network should result in no connectivity. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + mCellNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID + */ + @Test + public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + + // Arrange PackageManager mocks + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + // This preference should not use this network as it doesn't support fallback usage. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting unmetered should put PANS on OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should result in no connectivity. + // OEM_PAID_NO_FALLBACK not supporting a fallback now uses the disconnected network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: + * NET_CAPABILITY_OEM_PAID + * This preference should only apply to OEM_PAID networks. + */ + @Test + public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + + // Arrange PackageManager mocks + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up metered cellular. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should result in no connectivity. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: + * NET_CAPABILITY_OEM_PRIVATE + * This preference should only apply to OEM_PRIVATE networks. + */ + @Test + public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + + // Arrange PackageManager mocks + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up metered cellular. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. + startOemManagedNetwork(false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PRIVATE should result in no connectivity. + stopOemManagedNetwork(); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + @Test + public void testMultilayerForMultipleUsersEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + + // Arrange PackageManager mocks + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels( + uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly add the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly remove the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + mCellNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + @Test + public void testMultilayerForBroadcastedUsersEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange users + final int secondUser = 10; + final UserHandle secondUserHandle = new UserHandle(secondUser); + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); + + // Arrange PackageManager mocks + final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); + final UidRangeParcel[] uidRangesSingleUser = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + final UidRangeParcel[] uidRangesBothUsers = + toUidRangeStableParcels( + uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest( + networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly add the expected values for multiple users. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Send a broadcast indicating a user was added. + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); + processBroadcast(addedIntent); + + // Test that we correctly add values for all users and remove for the single user. + verifySetOemNetworkPreferenceForPreference(uidRangesBothUsers, uidRangesSingleUser, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Send a broadcast indicating a user was removed. + when(mUserManager.getUserHandles(anyBoolean())).thenReturn( + Arrays.asList(PRIMARY_USER_HANDLE)); + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); + processBroadcast(removedIntent); + + // Test that we correctly add values for the single user and remove for the all users. + verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, uidRangesBothUsers, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + } + + @Test + public void testMultilayerForPackageChangesEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + final String packageScheme = "package:"; + + // Arrange PackageManager mocks + final String packageToInstall = "package.to.install"; + final int packageToInstallUid = 81387; + final UidRangeParcel[] uidRangesSinglePackage = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); + mockGetApplicationInfoThrowsNameNotFound(packageToInstall); + setOemNetworkPreference(networkPref, TEST_PACKAGE_NAME, packageToInstall); + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid(), packageToInstall); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test that we correctly add the expected values for installed packages. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Set the system to recognize the package to be installed + mockGetApplicationInfo(packageToInstall, packageToInstallUid); + final UidRangeParcel[] uidRangesAllPackages = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID, packageToInstallUid)); + + // Send a broadcast indicating a package was installed. + final Intent addedIntent = new Intent(ACTION_PACKAGE_ADDED); + addedIntent.setData(Uri.parse(packageScheme + packageToInstall)); + processBroadcast(addedIntent); + + // Test the single package is removed and the combined packages are added. + verifySetOemNetworkPreferenceForPreference(uidRangesAllPackages, uidRangesSinglePackage, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Set the system to no longer recognize the package to be installed + mockGetApplicationInfoThrowsNameNotFound(packageToInstall); + + // Send a broadcast indicating a package was removed. + final Intent removedIntent = new Intent(ACTION_PACKAGE_REMOVED); + removedIntent.setData(Uri.parse(packageScheme + packageToInstall)); + processBroadcast(removedIntent); + + // Test the combined packages are removed and the single package is added. + verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, uidRangesAllPackages, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Set the system to change the installed package's uid + final int replacedTestPackageUid = TEST_PACKAGE_UID + 1; + mockGetApplicationInfo(TEST_PACKAGE_NAME, replacedTestPackageUid); + final UidRangeParcel[] uidRangesReplacedPackage = + toUidRangeStableParcels(uidRangesForUids(replacedTestPackageUid)); + + // Send a broadcast indicating a package was replaced. + final Intent replacedIntent = new Intent(ACTION_PACKAGE_REPLACED); + replacedIntent.setData(Uri.parse(packageScheme + TEST_PACKAGE_NAME)); + processBroadcast(replacedIntent); + + // Test the original uid is removed and is replaced with the new uid. + verifySetOemNetworkPreferenceForPreference(uidRangesReplacedPackage, uidRangesSinglePackage, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + } + + /** + * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 3; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mCellNetworkAgent.getNetwork()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mWiFiNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi will put the pref on OEM_PAID and fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID will put both on null as it is the last network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + null); + + // default callbacks will be unregistered in tearDown + } + + @Test + public void testNetworkFactoryRequestsWithMultilayerRequest() + throws Exception { + // First use OEM_PAID preference to create a multi-layer request : 1. listen for + // unmetered, 2. request network with cap OEM_PAID, 3, request the default network for + // fallback. + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + final HandlerThread handlerThread = new HandlerThread("MockFactory"); + handlerThread.start(); + NetworkCapabilities internetFilter = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "internetFactory", internetFilter, mCsHandlerThread); + internetFactory.setScoreFilter(40); + internetFactory.register(); + // Default internet request only. The unmetered request is never sent to factories (it's a + // LISTEN, not requestable). The 3rd (fallback) request in OEM_PAID NRI is TRACK_DEFAULT + // which is also not sent to factories. Finally, the OEM_PAID request doesn't match the + // internetFactory filter. + internetFactory.expectRequestAdds(1); + internetFactory.assertRequestCountEquals(1); + + NetworkCapabilities oemPaidFilter = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_OEM_PAID) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + final MockNetworkFactory oemPaidFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "oemPaidFactory", oemPaidFilter, mCsHandlerThread); + oemPaidFactory.setScoreFilter(40); + oemPaidFactory.register(); + oemPaidFactory.expectRequestAdd(); // Because nobody satisfies the request + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + // A network connected that satisfies the default internet request. For the OEM_PAID + // preference, this is not as good as an OEM_PAID network, so even if the score of + // the network is better than the factory announced, it still should try to bring up + // the network. + expectNoRequestChanged(oemPaidFactory); + oemPaidFactory.assertRequestCountEquals(1); + // The internet factory however is outscored, and should lose its requests. + internetFactory.expectRequestRemove(); + internetFactory.assertRequestCountEquals(0); + + final NetworkCapabilities oemPaidNc = new NetworkCapabilities(); + oemPaidNc.addCapability(NET_CAPABILITY_OEM_PAID); + oemPaidNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + final TestNetworkAgentWrapper oemPaidAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, + new LinkProperties(), oemPaidNc); + oemPaidAgent.connect(true); + + // The oemPaidAgent has score 50/cell transport, so it beats what the oemPaidFactory can + // provide, therefore it loses the request. + oemPaidFactory.expectRequestRemove(); + oemPaidFactory.assertRequestCountEquals(0); + expectNoRequestChanged(internetFactory); + internetFactory.assertRequestCountEquals(0); + + oemPaidAgent.setScore(new NetworkScore.Builder().setLegacyInt(20).setExiting(true).build()); + // Now the that the agent is weak, the oemPaidFactory can beat the existing network for the + // OEM_PAID request. The internet factory however can't beat a network that has OEM_PAID + // for the preference request, so it doesn't see the request. + oemPaidFactory.expectRequestAdd(); + oemPaidFactory.assertRequestCountEquals(1); + expectNoRequestChanged(internetFactory); + internetFactory.assertRequestCountEquals(0); + + mCellNetworkAgent.disconnect(); + // The network satisfying the default internet request has disconnected, so the + // internetFactory sees the default request again. However there is a network with OEM_PAID + // connected, so the 2nd OEM_PAID req is already satisfied, so the oemPaidFactory doesn't + // care about networks that don't have OEM_PAID. + expectNoRequestChanged(oemPaidFactory); + oemPaidFactory.assertRequestCountEquals(1); + internetFactory.expectRequestAdd(); + internetFactory.assertRequestCountEquals(1); + + // Cell connects again, still with score 50. Back to the previous state. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + expectNoRequestChanged(oemPaidFactory); + oemPaidFactory.assertRequestCountEquals(1); + internetFactory.expectRequestRemove(); + internetFactory.assertRequestCountEquals(0); + + // Create a request that holds the upcoming wifi network. + final TestNetworkCallback wifiCallback = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(), + wifiCallback); + + // Now WiFi connects and it's unmetered, but it's weaker than cell. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).setExiting(true) + .build()); // Not the best Internet network, but unmetered + mWiFiNetworkAgent.connect(true); + + // The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so + // the oemPaidFactory can't beat wifi no matter how high its score. + oemPaidFactory.expectRequestRemove(); + expectNoRequestChanged(internetFactory); + + mCellNetworkAgent.disconnect(); + // Now that the best internet network (cell, with its 50 score compared to 30 for WiFi + // at this point), the default internet request is satisfied by a network worse than + // the internetFactory announced, so it gets the request. However, there is still an + // unmetered network, so the oemPaidNetworkFactory still can't beat this. + expectNoRequestChanged(oemPaidFactory); + internetFactory.expectRequestAdd(); + mCm.unregisterNetworkCallback(wifiCallback); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidNoFallbackCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 2; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network but not the pref. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mWiFiNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi will put the OEM pref on OEM_PAID and fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID puts the fallback on null and the pref on the disconnected net. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default callbacks will be unregistered in tearDown + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: + * NET_CAPABILITY_OEM_PAID + * This preference should only apply to OEM_PAID networks. + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidOnlyCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PAID will keep the fallback on cellular and nothing for OEM_PAID. + // OEM_PAID_ONLY not supporting a fallback now uses the disconnected network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Disconnecting cellular will put the fallback on null and the pref on disconnected. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default callbacks will be unregistered in tearDown + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: + * NET_CAPABILITY_OEM_PRIVATE + * This preference should only apply to OEM_PRIVATE networks. + */ + @Test + public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPrivateOnlyCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + final int expectedDefaultRequestSize = 2; + final int expectedOemPrefRequestSize = 1; + registerDefaultNetworkCallbacks(); + + // The fallback as well as the OEM preference should now be tracked. + assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. + startOemManagedNetwork(false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mWiFiNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Disconnecting OEM_PRIVATE will keep the fallback on cellular. + // OEM_PRIVATE_ONLY not supporting a fallback now uses to the disconnected network. + stopOemManagedNetwork(); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mService.mNoServiceNetwork.network()); + + // Disconnecting cellular will put the fallback on null and pref on disconnected. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mService.mNoServiceNetwork.network()); + + // default callbacks will be unregistered in tearDown + } + + @Test + public void testCapabilityWithOemNetworkPreference() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); + registerDefaultNetworkCallbacks(); + + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + + mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> + nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> + nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + + // default callbacks will be unregistered in tearDown + } + + @Test + public void testSetOemNetworkPreferenceLogsRequest() throws Exception { + mServiceContext.setPermission(DUMP, PERMISSION_GRANTED); + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + final StringWriter stringWriter = new StringWriter(); + final String logIdentifier = "UPDATE INITIATED: OemNetworkPreferences"; + final Pattern pattern = Pattern.compile(logIdentifier); + + final int expectedNumLogs = 2; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); + + // Call twice to generate two logs. + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); + + final String dumpOutput = stringWriter.toString(); + final Matcher matcher = pattern.matcher(dumpOutput); + int count = 0; + while (matcher.find()) { + count++; + } + assertEquals(expectedNumLogs, count); + } + + @Test + public void testGetAllNetworkStateSnapshots() 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 snapshots = mCm.getAllNetworkStateSnapshots(); + 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.getAllNetworkStateSnapshots(); + 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.getAllNetworkStateSnapshots(); + assertLength(1, snapshots); + assertEquals(wifiSnapshot, snapshots.get(0)); + + // Disconnect wifi, verify the snapshots contain nothing. + mWiFiNetworkAgent.disconnect(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshots(); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertLength(0, snapshots); + + mCellNetworkAgent.resume(); + waitForIdle(); + snapshots = mCm.getAllNetworkStateSnapshots(); + assertLength(1, snapshots); + assertEquals(cellSnapshot, snapshots.get(0)); + + mCellNetworkAgent.disconnect(); + waitForIdle(); + verifyNoNetwork(); + 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_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. It doesn't see the default request because its filter does + // not include INTERNET. + testFactory.register(); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(0); + // 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(0); + 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(1); + assertTrue(testFactory.getMyStartRequested()); + + // Unregister best matching callback, verify factory see no change. + mCm.unregisterNetworkCallback(bestMatchingCb); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(1); + 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); + } + + private UidRangeParcel[] uidRangeFor(final UserHandle handle) { + UidRange range = UidRange.createForUser(handle); + return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) }; + } + + private static class TestOnCompleteListener implements Runnable { + final class OnComplete {} + final ArrayTrackRecord.ReadHead mHistory = + new ArrayTrackRecord().newReadHead(); + + @Override + public void run() { + mHistory.add(new OnComplete()); + } + + public void expectOnComplete() { + assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true)); + } + } + + private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception { + final NetworkCapabilities workNc = new NetworkCapabilities(); + workNc.addCapability(NET_CAPABILITY_ENTERPRISE); + workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc); + } + + private TestNetworkCallback mEnterpriseCallback; + private UserHandle setupEnterpriseNetwork() { + final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); + mServiceContext.setWorkProfile(userHandle, true); + + // File a request to avoid the enterprise network being disconnected as soon as the default + // request goes away – it would make impossible to test that networkRemoveUidRanges + // is called, as the network would disconnect first for lack of a request. + mEnterpriseCallback = new TestNetworkCallback(); + final NetworkRequest keepUpRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_ENTERPRISE) + .build(); + mCm.requestNetwork(keepUpRequest, mEnterpriseCallback); + return userHandle; + } + + private void maybeTearDownEnterpriseNetwork() { + if (null != mEnterpriseCallback) { + mCm.unregisterNetworkCallback(mEnterpriseCallback); + } + } + + /** + * Make sure per-profile networking preference behaves as expected when the enterprise network + * goes up and down while the preference is active. Make sure they behave as expected whether + * there is a general default network or not. + */ + @Test + public void testPreferenceForUserNetworkUpDown() throws Exception { + final InOrder inOrder = inOrder(mMockNetd); + final UserHandle testHandle = setupEnterpriseNetwork(); + registerDefaultNetworkCallbacks(); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + + + final TestOnCompleteListener listener = new TestOnCompleteListener(); + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + + // Setting a network preference for this user will create a new set of routing rules for + // the UID range that corresponds to this user, so as to define the default network + // for these apps separately. This is true because the multi-layer request relevant to + // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific + // rules to the correct network – in this case the system default network. The case where + // the default network for the profile happens to be the same as the system default + // is not handled specially, the rules are always active as long as a preference is set. + inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle)); + + // The enterprise network is not ready yet. + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, + mProfileDefaultNetworkCallback); + + final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); + workAgent.connect(false); + + mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent); + mSystemDefaultNetworkCallback.assertNoCallback(); + mDefaultNetworkCallback.assertNoCallback(); + inOrder.verify(mMockNetd).networkCreate( + nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle)); + inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle)); + + // Make sure changes to the work agent send callbacks to the app in the work profile, but + // not to the other apps. + workAgent.setNetworkValid(true /* isStrictMode */); + workAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, + nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED) + && nc.hasCapability(NET_CAPABILITY_ENTERPRISE)); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + + workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc -> + nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + + // Conversely, change a capability on the system-wide default network and make sure + // that only the apps outside of the work profile receive the callbacks. + mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> + nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> + nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + mProfileDefaultNetworkCallback.assertNoCallback(); + + // Disconnect and reconnect the system-wide default network and make sure that the + // apps on this network see the appropriate callbacks, and the app on the work profile + // doesn't because it continues to use the enterprise network. + mCellNetworkAgent.disconnect(); + mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + mProfileDefaultNetworkCallback.assertNoCallback(); + inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mProfileDefaultNetworkCallback.assertNoCallback(); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + + // When the agent disconnects, test that the app on the work profile falls back to the + // default network. + workAgent.disconnect(); + mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent); + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle)); + inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId); + + mCellNetworkAgent.disconnect(); + mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + + // Waiting for the handler to be idle before checking for networkDestroy is necessary + // here because ConnectivityService calls onLost before the network is fully torn down. + waitForIdle(); + inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); + + // If the control comes here, callbacks seem to behave correctly in the presence of + // a default network when the enterprise network goes up and down. Now, make sure they + // also behave correctly in the absence of a system-wide default network. + final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(); + workAgent2.connect(false); + + mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId, + uidRangeFor(testHandle)); + + workAgent2.setNetworkValid(true /* isStrictMode */); + workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid()); + mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2, + nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE) + && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any()); + + // When the agent disconnects, test that the app on the work profile falls back to the + // default network. + workAgent2.disconnect(); + mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId); + + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, + mProfileDefaultNetworkCallback); + + // Callbacks will be unregistered by tearDown() + } + + /** + * Test that, in a given networking context, calling setPreferenceForUser to set per-profile + * defaults on then off works as expected. + */ + @Test + public void testSetPreferenceForUserOnOff() throws Exception { + final InOrder inOrder = inOrder(mMockNetd); + final UserHandle testHandle = setupEnterpriseNetwork(); + + // Connect both a regular cell agent and an enterprise network first. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); + workAgent.connect(true); + + final TestOnCompleteListener listener = new TestOnCompleteListener(); + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle)); + + registerDefaultNetworkCallbacks(); + + mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent); + + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT, + r -> r.run(), listener); + listener.expectOnComplete(); + + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle)); + + workAgent.disconnect(); + mCellNetworkAgent.disconnect(); + + // Callbacks will be unregistered by tearDown() + } + + /** + * Test per-profile default networks for two different profiles concurrently. + */ + @Test + public void testSetPreferenceForTwoProfiles() throws Exception { + final InOrder inOrder = inOrder(mMockNetd); + final UserHandle testHandle2 = setupEnterpriseNetwork(); + final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2); + mServiceContext.setWorkProfile(testHandle4, true); + registerDefaultNetworkCallbacks(); + + final TestNetworkCallback app4Cb = new TestNetworkCallback(); + final int testWorkProfileAppUid4 = + UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID); + registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4); + + // Connect both a regular cell agent and an enterprise network first. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); + workAgent.connect(true); + + mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + + final TestOnCompleteListener listener = new TestOnCompleteListener(); + mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle2)); + + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, + app4Cb); + + mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle4)); + + app4Cb.expectAvailableCallbacksValidated(workAgent); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, + mProfileDefaultNetworkCallback); + + mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT, + r -> r.run(), listener); + listener.expectOnComplete(); + inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId, + uidRangeFor(testHandle2)); + + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, + app4Cb); + + workAgent.disconnect(); + mCellNetworkAgent.disconnect(); + + mCm.unregisterNetworkCallback(app4Cb); + // Other callbacks will be unregistered by tearDown() + } + + @Test + public void testProfilePreferenceRemovedUponUserRemoved() throws Exception { + final InOrder inOrder = inOrder(mMockNetd); + final UserHandle testHandle = setupEnterpriseNetwork(); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + final TestOnCompleteListener listener = new TestOnCompleteListener(); + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle)); + + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, testHandle); + processBroadcast(removedIntent); + + inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle)); + } + + /** + * Make sure that OEM preference and per-profile preference can't be used at the same + * time and throw ISE if tried + */ + @Test + public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception { + final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); + mServiceContext.setWorkProfile(testHandle, true); + final TestOnCompleteListener listener = new TestOnCompleteListener(); + + setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY); + assertThrows("Should not be able to set per-profile pref while OEM prefs present", + IllegalStateException.class, () -> + mCm.setProfileNetworkPreference(testHandle, + PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener)); + + // Empty the OEM prefs + final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback(); + final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build(); + mService.setOemNetworkPreference(emptyOemPref, oemPrefListener); + oemPrefListener.expectOnComplete(); + + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + r -> r.run(), listener); + listener.expectOnComplete(); + assertThrows("Should not be able to set OEM prefs while per-profile pref is on", + IllegalStateException.class , () -> + mService.setOemNetworkPreference(emptyOemPref, oemPrefListener)); + } + + /** + * Make sure wrong preferences for per-profile default networking are rejected. + */ + @Test + public void testProfileNetworkPrefWrongPreference() throws Exception { + final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); + mServiceContext.setWorkProfile(testHandle, true); + assertThrows("Should not be able to set an illegal preference", + IllegalArgumentException.class, + () -> mCm.setProfileNetworkPreference(testHandle, + PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null)); + } + + /** + * Make sure requests for per-profile default networking for a non-work profile are + * rejected + */ + @Test + public void testProfileNetworkPrefWrongProfile() throws Exception { + final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); + mServiceContext.setWorkProfile(testHandle, false); + assertThrows("Should not be able to set a user pref for a non-work profile", + IllegalArgumentException.class , () -> + mCm.setProfileNetworkPreference(testHandle, + PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null)); + } + + @Test + public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception { + mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED); + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setSubscriptionIds(Collections.singleton(Process.myUid())); + + final NetworkCapabilities result = + mService.networkCapabilitiesRestrictedForCallerPermissions( + nc, Process.myPid(), Process.myUid()); + assertTrue(result.getSubscriptionIds().isEmpty()); + } + + @Test + public void testSubIdsExistWithNetworkFactoryPermission() throws Exception { + mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED); + + final Set subIds = Collections.singleton(Process.myUid()); + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setSubscriptionIds(subIds); + + final NetworkCapabilities result = + mService.networkCapabilitiesRestrictedForCallerPermissions( + nc, Process.myPid(), Process.myUid()); + assertEquals(subIds, result.getSubscriptionIds()); + } + + private NetworkRequest getRequestWithSubIds() { + return new NetworkRequest.Builder() + .setSubscriptionIds(Collections.singleton(Process.myUid())) + .build(); + } + + @Test + public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception { + mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED); + final PendingIntent pendingIntent = PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); + final NetworkCallback networkCallback1 = new NetworkCallback(); + final NetworkCallback networkCallback2 = new NetworkCallback(); + + mCm.requestNetwork(getRequestWithSubIds(), networkCallback1); + mCm.requestNetwork(getRequestWithSubIds(), pendingIntent); + mCm.registerNetworkCallback(getRequestWithSubIds(), networkCallback2); + + mCm.unregisterNetworkCallback(networkCallback1); + mCm.releaseNetworkRequest(pendingIntent); + mCm.unregisterNetworkCallback(networkCallback2); + } + + @Test + public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception { + mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED); + final PendingIntent pendingIntent = PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); + + final Class expected = SecurityException.class; + assertThrows( + expected, () -> mCm.requestNetwork(getRequestWithSubIds(), new NetworkCallback())); + assertThrows(expected, () -> mCm.requestNetwork(getRequestWithSubIds(), pendingIntent)); + assertThrows( + expected, + () -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback())); + } + + /** + * Validate request counts are counted accurately on setProfileNetworkPreference on set/replace. + */ + @Test + public void testProfileNetworkPrefCountsRequestsCorrectlyOnSet() throws Exception { + final UserHandle testHandle = setupEnterpriseNetwork(); + testRequestCountLimits(() -> { + // Set initially to test the limit prior to having existing requests. + final TestOnCompleteListener listener = new TestOnCompleteListener(); + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + Runnable::run, listener); + listener.expectOnComplete(); + + // re-set so as to test the limit as part of replacing existing requests. + mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + Runnable::run, listener); + listener.expectOnComplete(); + }); + } + + /** + * Validate request counts are counted accurately on setOemNetworkPreference on set/replace. + */ + @Test + public void testSetOemNetworkPreferenceCountsRequestsCorrectlyOnSet() throws Exception { + mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + testRequestCountLimits(() -> { + // Set initially to test the limit prior to having existing requests. + final TestOemListenerCallback listener = new TestOemListenerCallback(); + mService.setOemNetworkPreference( + createDefaultOemNetworkPreferences(networkPref), listener); + listener.expectOnComplete(); + + // re-set so as to test the limit as part of replacing existing requests. + mService.setOemNetworkPreference( + createDefaultOemNetworkPreferences(networkPref), listener); + listener.expectOnComplete(); + }); + } + + private void testRequestCountLimits(@NonNull final Runnable r) throws Exception { + final ArraySet callbacks = new ArraySet<>(); + try { + final int requestCount = mService.mSystemNetworkRequestCounter + .mUidToNetworkRequestCount.get(Process.myUid()); + // The limit is hit when total requests <= limit. + final int maxCount = + ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount; + // Need permission so registerDefaultNetworkCallback uses mSystemNetworkRequestCounter + withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { + for (int i = 1; i < maxCount - 1; i++) { + final TestNetworkCallback cb = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(cb); + callbacks.add(cb); + } + + // Code to run to check if it triggers a max request count limit error. + r.run(); + }); + } finally { + for (final TestNetworkCallback cb : callbacks) { + mCm.unregisterNetworkCallback(cb); + } + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java new file mode 100644 index 000000000000..cf2c9c783ac7 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java @@ -0,0 +1,1004 @@ +/* + * Copyright (C) 2017 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 static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; +import static android.net.IpSecManager.DIRECTION_FWD; +import static android.net.IpSecManager.DIRECTION_IN; +import static android.net.IpSecManager.DIRECTION_OUT; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.INetd; +import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; +import android.net.IpSecAlgorithm; +import android.net.IpSecConfig; +import android.net.IpSecManager; +import android.net.IpSecSpiResponse; +import android.net.IpSecTransform; +import android.net.IpSecTransformResponse; +import android.net.IpSecTunnelInterfaceResponse; +import android.net.IpSecUdpEncapResponse; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.system.Os; +import android.test.mock.MockContext; +import android.util.ArraySet; + +import androidx.test.filters.SmallTest; + +import com.android.server.IpSecService.TunnelInterfaceRecord; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.Inet4Address; +import java.net.Socket; +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +/** Unit tests for {@link IpSecService}. */ +@SmallTest +@RunWith(Parameterized.class) +public class IpSecServiceParameterizedTest { + + private static final int TEST_SPI = 0xD1201D; + + private final String mSourceAddr; + private final String mDestinationAddr; + private final LinkAddress mLocalInnerAddress; + private final int mFamily; + + private static final int[] ADDRESS_FAMILIES = + new int[] {AF_INET, AF_INET6}; + + @Parameterized.Parameters + public static Collection ipSecConfigs() { + return Arrays.asList( + new Object[][] { + {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET}, + {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6} + }); + } + + private static final byte[] AEAD_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x73, 0x61, 0x6C, 0x74 + }; + private static final byte[] CRYPT_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + private static final byte[] AUTH_KEY = { + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F + }; + + AppOpsManager mMockAppOps = mock(AppOpsManager.class); + ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class); + + TestContext mTestContext = new TestContext(); + + private class TestContext extends MockContext { + private Set mAllowedPermissions = new ArraySet<>(Arrays.asList( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, + android.Manifest.permission.NETWORK_STACK, + PERMISSION_MAINLINE_NETWORK_STACK)); + + private void setAllowedPermissions(String... permissions) { + mAllowedPermissions = new ArraySet<>(permissions); + } + + @Override + public Object getSystemService(String name) { + switch(name) { + case Context.APP_OPS_SERVICE: + return mMockAppOps; + case Context.CONNECTIVITY_SERVICE: + return mMockConnectivityMgr; + default: + return null; + } + } + + @Override + public String getSystemServiceName(Class serviceClass) { + if (ConnectivityManager.class == serviceClass) { + return Context.CONNECTIVITY_SERVICE; + } + return null; + } + + @Override + public PackageManager getPackageManager() { + return mMockPkgMgr; + } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + if (mAllowedPermissions.contains(permission)) { + return; + } else { + throw new SecurityException("Unavailable permission requested"); + } + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (mAllowedPermissions.contains(permission)) { + return PERMISSION_GRANTED; + } else { + return PERMISSION_DENIED; + } + } + } + + INetd mMockNetd; + PackageManager mMockPkgMgr; + IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; + IpSecService mIpSecService; + Network fakeNetwork = new Network(0xAB); + int mUid = Os.getuid(); + + private static final IpSecAlgorithm AUTH_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); + private static final IpSecAlgorithm CRYPT_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); + private static final IpSecAlgorithm AEAD_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); + private static final int REMOTE_ENCAP_PORT = 4500; + + private static final String BLESSED_PACKAGE = "blessedPackage"; + private static final String SYSTEM_PACKAGE = "systemPackage"; + private static final String BAD_PACKAGE = "badPackage"; + + public IpSecServiceParameterizedTest( + String sourceAddr, String destAddr, String localInnerAddr, int family) { + mSourceAddr = sourceAddr; + mDestinationAddr = destAddr; + mLocalInnerAddress = new LinkAddress(localInnerAddr); + mFamily = family; + } + + @Before + public void setUp() throws Exception { + mMockNetd = mock(INetd.class); + mMockPkgMgr = mock(PackageManager.class); + mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); + mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig); + + // Injecting mock netd + when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); + + // PackageManager should always return true (feature flag tests in IpSecServiceTest) + when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true); + + // A package granted the AppOp for MANAGE_IPSEC_TUNNELS will be MODE_ALLOWED. + when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BLESSED_PACKAGE))) + .thenReturn(AppOpsManager.MODE_ALLOWED); + // A system package will not be granted the app op, so this should fall back to + // a permissions check, which should pass. + when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(SYSTEM_PACKAGE))) + .thenReturn(AppOpsManager.MODE_DEFAULT); + // A mismatch between the package name and the UID will return MODE_IGNORED. + when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BAD_PACKAGE))) + .thenReturn(AppOpsManager.MODE_IGNORED); + } + + //TODO: Add a test to verify SPI. + + @Test + public void testIpSecServiceReserveSpi() throws Exception { + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) + .thenReturn(TEST_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.allocateSecurityParameterIndex( + mDestinationAddr, TEST_SPI, new Binder()); + assertEquals(IpSecManager.Status.OK, spiResp.status); + assertEquals(TEST_SPI, spiResp.spi); + } + + @Test + public void testReleaseSecurityParameterIndex() throws Exception { + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) + .thenReturn(TEST_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.allocateSecurityParameterIndex( + mDestinationAddr, TEST_SPI, new Binder()); + + mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(mUid), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), + anyInt(), + anyInt()); + + // Verify quota and RefcountedResource objects cleaned up + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); + try { + userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testSecurityParameterIndexBinderDeath() throws Exception { + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) + .thenReturn(TEST_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.allocateSecurityParameterIndex( + mDestinationAddr, TEST_SPI, new Binder()); + + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); + + refcountedRecord.binderDied(); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(mUid), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), + anyInt(), + anyInt()); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); + try { + userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception { + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt())) + .thenReturn(returnSpi); + + IpSecSpiResponse spi = + mIpSecService.allocateSecurityParameterIndex( + InetAddresses.parseNumericAddress(remoteAddress).getHostAddress(), + IpSecManager.INVALID_SECURITY_PARAMETER_INDEX, + new Binder()); + return spi.resourceId; + } + + private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception { + config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI)); + config.setSourceAddress(mSourceAddr); + config.setDestinationAddress(mDestinationAddr); + } + + private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception { + config.setEncryption(CRYPT_ALGO); + config.setAuthentication(AUTH_ALGO); + } + + private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception { + config.setEncapType(IpSecTransform.ENCAP_ESPINUDP); + config.setEncapSocketResourceId(resourceId); + config.setEncapRemotePort(REMOTE_ENCAP_PORT); + } + + private void verifyTransformNetdCalledForCreatingSA( + IpSecConfig config, IpSecTransformResponse resp) throws Exception { + verifyTransformNetdCalledForCreatingSA(config, resp, 0); + } + + private void verifyTransformNetdCalledForCreatingSA( + IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception { + IpSecAlgorithm auth = config.getAuthentication(); + IpSecAlgorithm crypt = config.getEncryption(); + IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption(); + + verify(mMockNetd, times(1)) + .ipSecAddSecurityAssociation( + eq(mUid), + eq(config.getMode()), + eq(config.getSourceAddress()), + eq(config.getDestinationAddress()), + eq((config.getNetwork() != null) ? config.getNetwork().netId : 0), + eq(TEST_SPI), + eq(0), + eq(0), + eq((auth != null) ? auth.getName() : ""), + eq((auth != null) ? auth.getKey() : new byte[] {}), + eq((auth != null) ? auth.getTruncationLengthBits() : 0), + eq((crypt != null) ? crypt.getName() : ""), + eq((crypt != null) ? crypt.getKey() : new byte[] {}), + eq((crypt != null) ? crypt.getTruncationLengthBits() : 0), + eq((authCrypt != null) ? authCrypt.getName() : ""), + eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}), + eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0), + eq(config.getEncapType()), + eq(encapSocketPort), + eq(config.getEncapRemotePort()), + eq(config.getXfrmInterfaceId())); + } + + @Test + public void testCreateTransform() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + @Test + public void testCreateTransformAead() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + + ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + @Test + public void testCreateTransportModeTransformWithEncap() throws Exception { + IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); + + if (mFamily == AF_INET) { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); + } else { + try { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testCreateTunnelModeTransformWithEncap() throws Exception { + IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); + + if (mFamily == AF_INET) { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); + } else { + try { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testCreateTwoTransformsWithSameSpis() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + // Attempting to create transform a second time with the same SPIs should throw an error... + try { + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + fail("IpSecService should have thrown an error for reuse of SPI"); + } catch (IllegalStateException expected) { + } + + // ... even if the transform is deleted + mIpSecService.deleteTransform(createTransformResp.resourceId); + try { + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + fail("IpSecService should have thrown an error for reuse of SPI"); + } catch (IllegalStateException expected) { + } + } + + @Test + public void testReleaseOwnedSpi() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + verify(mMockNetd, times(0)) + .ipSecDeleteSecurityAssociation( + eq(mUid), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), + anyInt(), + anyInt()); + // quota is not released until the SPI is released by the Transform + assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); + } + + @Test + public void testDeleteTransform() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + mIpSecService.deleteTransform(createTransformResp.resourceId); + + verify(mMockNetd, times(1)) + .ipSecDeleteSecurityAssociation( + eq(mUid), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), + anyInt(), + anyInt()); + + // Verify quota and RefcountedResource objects cleaned up + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent); + assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); + + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + // Verify that ipSecDeleteSa was not called when the SPI was released because the + // ownedByTransform property should prevent it; (note, the called count is cumulative). + verify(mMockNetd, times(1)) + .ipSecDeleteSecurityAssociation( + anyInt(), + anyString(), + anyString(), + anyInt(), + anyInt(), + anyInt(), + anyInt()); + assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); + + try { + userRecord.mTransformRecords.getRefcountedResourceOrThrow( + createTransformResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testTransportModeTransformBinderDeath() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTransformRecords.getRefcountedResourceOrThrow( + createTransformResp.resourceId); + + refcountedRecord.binderDied(); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(mUid), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), + anyInt(), + anyInt()); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent); + try { + userRecord.mTransformRecords.getRefcountedResourceOrThrow( + createTransformResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testApplyTransportModeTransform() throws Exception { + verifyApplyTransportModeTransformCommon(false); + } + + @Test + public void testApplyTransportModeTransformReleasedSpi() throws Exception { + verifyApplyTransportModeTransformCommon(true); + } + + public void verifyApplyTransportModeTransformCommon( + boolean closeSpiBeforeApply) throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + + if (closeSpiBeforeApply) { + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + } + + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + + int resourceId = createTransformResp.resourceId; + mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId); + + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd), + eq(mUid), + eq(IpSecManager.DIRECTION_OUT), + anyString(), + anyString(), + eq(TEST_SPI)); + } + + @Test + public void testApplyTransportModeTransformWithClosedSpi() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + + // Close SPI record + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + + int resourceId = createTransformResp.resourceId; + mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId); + + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd), + eq(mUid), + eq(IpSecManager.DIRECTION_OUT), + anyString(), + anyString(), + eq(TEST_SPI)); + } + + @Test + public void testRemoveTransportModeTransform() throws Exception { + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + mIpSecService.removeTransportModeTransforms(pfd); + + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); + } + + private IpSecTunnelInterfaceResponse createAndValidateTunnel( + String localAddr, String remoteAddr, String pkgName) throws Exception { + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config); + IpSecTunnelInterfaceResponse createTunnelResp = + mIpSecService.createTunnelInterface( + mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName); + + assertNotNull(createTunnelResp); + assertEquals(IpSecManager.Status.OK, createTunnelResp.status); + for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT, DIRECTION_FWD}) { + for (int selAddrFamily : ADDRESS_FAMILIES) { + verify(mMockNetd).ipSecAddSecurityPolicy( + eq(mUid), + eq(selAddrFamily), + eq(direction), + anyString(), + anyString(), + eq(0), + anyInt(), // iKey/oKey + anyInt(), // mask + eq(createTunnelResp.resourceId)); + } + } + + return createTunnelResp; + } + + @Test + public void testCreateTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + + // Check that we have stored the tracking object, and retrieve it + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd) + .ipSecAddTunnelInterface( + eq(createTunnelResp.interfaceName), + eq(mSourceAddr), + eq(mDestinationAddr), + anyInt(), + anyInt(), + anyInt()); + verify(mMockNetd).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(IF_STATE_UP))); + } + + @Test + public void testDeleteTunnelInterface() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + + mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, BLESSED_PACKAGE); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + private Network createFakeUnderlyingNetwork(String interfaceName) { + final Network fakeNetwork = new Network(1000); + final LinkProperties fakeLp = new LinkProperties(); + fakeLp.setInterfaceName(interfaceName); + when(mMockConnectivityMgr.getLinkProperties(eq(fakeNetwork))).thenReturn(fakeLp); + return fakeNetwork; + } + + @Test + public void testSetNetworkForTunnelInterface() throws Exception { + final IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + final Network newFakeNetwork = createFakeUnderlyingNetwork("newFakeNetworkInterface"); + final int tunnelIfaceResourceId = createTunnelResp.resourceId; + mIpSecService.setNetworkForTunnelInterface( + tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE); + + final IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(mUid); + assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); + + final TunnelInterfaceRecord tunnelInterfaceInfo = + userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId); + assertEquals(newFakeNetwork, tunnelInterfaceInfo.getUnderlyingNetwork()); + } + + @Test + public void testSetNetworkForTunnelInterfaceFailsForInvalidResourceId() throws Exception { + final IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + final Network newFakeNetwork = new Network(1000); + + try { + mIpSecService.setNetworkForTunnelInterface( + IpSecManager.INVALID_RESOURCE_ID, newFakeNetwork, BLESSED_PACKAGE); + fail("Expected an IllegalArgumentException for invalid resource ID."); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetNetworkForTunnelInterfaceFailsWhenSettingTunnelNetwork() throws Exception { + final IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + final int tunnelIfaceResourceId = createTunnelResp.resourceId; + final IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(mUid); + final TunnelInterfaceRecord tunnelInterfaceInfo = + userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId); + + final Network newFakeNetwork = + createFakeUnderlyingNetwork(tunnelInterfaceInfo.getInterfaceName()); + + try { + mIpSecService.setNetworkForTunnelInterface( + tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE); + fail( + "Expected an IllegalArgumentException because the underlying network is the" + + " network being exposed by this tunnel."); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testTunnelInterfaceBinderDeath() throws Exception { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + + IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + + refcountedRecord.binderDied(); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); + verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); + try { + userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( + createTunnelResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testApplyTunnelModeTransformOutbound() throws Exception { + verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); + } + + @Test + public void testApplyTunnelModeTransformOutboundNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); + } + + @Test + public void testApplyTunnelModeTransformOutboundReleasedSpi() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_OUT); + } + + @Test + public void testApplyTunnelModeTransformInbound() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); + } + + @Test + public void testApplyTunnelModeTransformInboundNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); + } + + @Test + public void testApplyTunnelModeTransformForward() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); + } + + @Test + public void testApplyTunnelModeTransformForwardNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + + try { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); + fail("Expected security exception due to use of forward policies without NETWORK_STACK" + + " or MAINLINE_NETWORK_STACK permission"); + } catch (SecurityException expected) { + } + } + + public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply, int direction) + throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + + if (closeSpiBeforeApply) { + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + } + + int transformResourceId = createTransformResp.resourceId; + int tunnelResourceId = createTunnelResp.resourceId; + mIpSecService.applyTunnelModeTransform( + tunnelResourceId, direction, transformResourceId, BLESSED_PACKAGE); + + for (int selAddrFamily : ADDRESS_FAMILIES) { + verify(mMockNetd) + .ipSecUpdateSecurityPolicy( + eq(mUid), + eq(selAddrFamily), + eq(direction), + anyString(), + anyString(), + eq(direction == DIRECTION_OUT ? TEST_SPI : 0), + anyInt(), // iKey/oKey + anyInt(), // mask + eq(tunnelResourceId)); + } + + ipSecConfig.setXfrmInterfaceId(tunnelResourceId); + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + + @Test + public void testApplyTunnelModeTransformWithClosedSpi() throws Exception { + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); + + // Close SPI record + mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); + + int transformResourceId = createTransformResp.resourceId; + int tunnelResourceId = createTunnelResp.resourceId; + mIpSecService.applyTunnelModeTransform( + tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE); + + for (int selAddrFamily : ADDRESS_FAMILIES) { + verify(mMockNetd) + .ipSecUpdateSecurityPolicy( + eq(mUid), + eq(selAddrFamily), + eq(IpSecManager.DIRECTION_OUT), + anyString(), + anyString(), + eq(TEST_SPI), + anyInt(), // iKey/oKey + anyInt(), // mask + eq(tunnelResourceId)); + } + + ipSecConfig.setXfrmInterfaceId(tunnelResourceId); + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + @Test + public void testAddRemoveAddressFromTunnelInterface() throws Exception { + for (String pkgName : new String[] {BLESSED_PACKAGE, SYSTEM_PACKAGE}) { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, pkgName); + mIpSecService.addAddressToTunnelInterface( + createTunnelResp.resourceId, mLocalInnerAddress, pkgName); + verify(mMockNetd, times(1)) + .interfaceAddAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + mIpSecService.removeAddressFromTunnelInterface( + createTunnelResp.resourceId, mLocalInnerAddress, pkgName); + verify(mMockNetd, times(1)) + .interfaceDelAddress( + eq(createTunnelResp.interfaceName), + eq(mLocalInnerAddress.getAddress().getHostAddress()), + eq(mLocalInnerAddress.getPrefixLength())); + mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, pkgName); + } + } + + @Ignore + @Test + public void testAddTunnelFailsForBadPackageName() throws Exception { + try { + IpSecTunnelInterfaceResponse createTunnelResp = + createAndValidateTunnel(mSourceAddr, mDestinationAddr, BAD_PACKAGE); + fail("Expected a SecurityException for badPackage."); + } catch (SecurityException expected) { + } + } + + @Test + public void testFeatureFlagVerification() throws Exception { + when(mMockPkgMgr.hasSystemFeature(eq(PackageManager.FEATURE_IPSEC_TUNNELS))) + .thenReturn(false); + + try { + String addr = Inet4Address.getLoopbackAddress().getHostAddress(); + mIpSecService.createTunnelInterface( + addr, addr, new Network(0), new Binder(), BLESSED_PACKAGE); + fail("Expected UnsupportedOperationException for disabled feature"); + } catch (UnsupportedOperationException expected) { + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java new file mode 100644 index 000000000000..22a2c94fc194 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2017 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.IpSecService.IResource; +import com.android.server.IpSecService.RefcountedResource; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +/** Unit tests for {@link IpSecService.RefcountedResource}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class IpSecServiceRefcountedResourceTest { + Context mMockContext; + IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; + IpSecService mIpSecService; + + @Before + public void setUp() throws Exception { + mMockContext = mock(Context.class); + mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); + } + + private void assertResourceState( + RefcountedResource resource, + int refCount, + int userReleaseCallCount, + int releaseReferenceCallCount, + int invalidateCallCount, + int freeUnderlyingResourcesCallCount) + throws RemoteException { + // Check refcount on RefcountedResource + assertEquals(refCount, resource.mRefCount); + + // Check call count of RefcountedResource + verify(resource, times(userReleaseCallCount)).userRelease(); + verify(resource, times(releaseReferenceCallCount)).releaseReference(); + + // Check call count of IResource + verify(resource.getResource(), times(invalidateCallCount)).invalidate(); + verify(resource.getResource(), times(freeUnderlyingResourcesCallCount)) + .freeUnderlyingResources(); + } + + /** Adds mockito instrumentation */ + private RefcountedResource getTestRefcountedResource( + RefcountedResource... children) { + return getTestRefcountedResource(new Binder(), children); + } + + /** Adds mockito instrumentation with provided binder */ + private RefcountedResource getTestRefcountedResource( + IBinder binder, RefcountedResource... children) { + return spy( + mIpSecService + .new RefcountedResource(mock(IResource.class), binder, children)); + } + + @Test + public void testConstructor() throws RemoteException { + IBinder binderMock = mock(IBinder.class); + RefcountedResource resource = getTestRefcountedResource(binderMock); + + // Verify resource's refcount starts at 1 (for user-reference) + assertResourceState(resource, 1, 0, 0, 0, 0); + + // Verify linking to binder death + verify(binderMock).linkToDeath(anyObject(), anyInt()); + } + + @Test + public void testConstructorWithChildren() throws RemoteException { + IBinder binderMockChild = mock(IBinder.class); + IBinder binderMockParent = mock(IBinder.class); + RefcountedResource childResource = getTestRefcountedResource(binderMockChild); + RefcountedResource parentResource = + getTestRefcountedResource(binderMockParent, childResource); + + // Verify parent's refcount starts at 1 (for user-reference) + assertResourceState(parentResource, 1, 0, 0, 0, 0); + + // Verify child's refcounts were incremented + assertResourceState(childResource, 2, 0, 0, 0, 0); + + // Verify linking to binder death + verify(binderMockChild).linkToDeath(anyObject(), anyInt()); + verify(binderMockParent).linkToDeath(anyObject(), anyInt()); + } + + @Test + public void testFailLinkToDeath() throws RemoteException { + IBinder binderMock = mock(IBinder.class); + doThrow(new RemoteException()).when(binderMock).linkToDeath(anyObject(), anyInt()); + + try { + getTestRefcountedResource(binderMock); + fail("Expected exception to propogate when binder fails to link to death"); + } catch (RuntimeException expected) { + } + } + + @Test + public void testCleanupAndRelease() throws RemoteException { + IBinder binderMock = mock(IBinder.class); + RefcountedResource refcountedResource = getTestRefcountedResource(binderMock); + + // Verify user-initiated cleanup path decrements refcount and calls full cleanup flow + refcountedResource.userRelease(); + assertResourceState(refcountedResource, -1, 1, 1, 1, 1); + + // Verify user-initated cleanup path unlinks from binder + verify(binderMock).unlinkToDeath(eq(refcountedResource), eq(0)); + assertNull(refcountedResource.mBinder); + } + + @Test + public void testMultipleCallsToCleanupAndRelease() throws RemoteException { + RefcountedResource refcountedResource = getTestRefcountedResource(); + + // Verify calling userRelease multiple times does not trigger any other cleanup + // methods + refcountedResource.userRelease(); + assertResourceState(refcountedResource, -1, 1, 1, 1, 1); + + refcountedResource.userRelease(); + refcountedResource.userRelease(); + assertResourceState(refcountedResource, -1, 3, 1, 1, 1); + } + + @Test + public void testBinderDeathAfterCleanupAndReleaseDoesNothing() throws RemoteException { + RefcountedResource refcountedResource = getTestRefcountedResource(); + + refcountedResource.userRelease(); + assertResourceState(refcountedResource, -1, 1, 1, 1, 1); + + // Verify binder death call does not trigger any other cleanup methods if called after + // userRelease() + refcountedResource.binderDied(); + assertResourceState(refcountedResource, -1, 2, 1, 1, 1); + } + + @Test + public void testBinderDeath() throws RemoteException { + RefcountedResource refcountedResource = getTestRefcountedResource(); + + // Verify binder death caused cleanup + refcountedResource.binderDied(); + verify(refcountedResource, times(1)).binderDied(); + assertResourceState(refcountedResource, -1, 1, 1, 1, 1); + assertNull(refcountedResource.mBinder); + } + + @Test + public void testCleanupParentDecrementsChildRefcount() throws RemoteException { + RefcountedResource childResource = getTestRefcountedResource(); + RefcountedResource parentResource = getTestRefcountedResource(childResource); + + parentResource.userRelease(); + + // Verify parent gets cleaned up properly, and triggers releaseReference on + // child + assertResourceState(childResource, 1, 0, 1, 0, 0); + assertResourceState(parentResource, -1, 1, 1, 1, 1); + } + + @Test + public void testCleanupReferencedChildDoesNotTriggerRelease() throws RemoteException { + RefcountedResource childResource = getTestRefcountedResource(); + RefcountedResource parentResource = getTestRefcountedResource(childResource); + + childResource.userRelease(); + + // Verify that child does not clean up kernel resources and quota. + assertResourceState(childResource, 1, 1, 1, 1, 0); + assertResourceState(parentResource, 1, 0, 0, 0, 0); + } + + @Test + public void testTwoParents() throws RemoteException { + RefcountedResource childResource = getTestRefcountedResource(); + RefcountedResource parentResource1 = getTestRefcountedResource(childResource); + RefcountedResource parentResource2 = getTestRefcountedResource(childResource); + + // Verify that child does not cleanup kernel resources and quota until all references + // have been released. Assumption: parents release correctly based on + // testCleanupParentDecrementsChildRefcount() + childResource.userRelease(); + assertResourceState(childResource, 2, 1, 1, 1, 0); + + parentResource1.userRelease(); + assertResourceState(childResource, 1, 1, 2, 1, 0); + + parentResource2.userRelease(); + assertResourceState(childResource, -1, 1, 3, 1, 1); + } + + @Test + public void testTwoChildren() throws RemoteException { + RefcountedResource childResource1 = getTestRefcountedResource(); + RefcountedResource childResource2 = getTestRefcountedResource(); + RefcountedResource parentResource = + getTestRefcountedResource(childResource1, childResource2); + + childResource1.userRelease(); + assertResourceState(childResource1, 1, 1, 1, 1, 0); + assertResourceState(childResource2, 2, 0, 0, 0, 0); + + parentResource.userRelease(); + assertResourceState(childResource1, -1, 1, 2, 1, 1); + assertResourceState(childResource2, 1, 0, 1, 0, 0); + + childResource2.userRelease(); + assertResourceState(childResource1, -1, 1, 2, 1, 1); + assertResourceState(childResource2, -1, 1, 2, 1, 1); + } + + @Test + public void testSampleUdpEncapTranform() throws RemoteException { + RefcountedResource spi1 = getTestRefcountedResource(); + RefcountedResource spi2 = getTestRefcountedResource(); + RefcountedResource udpEncapSocket = getTestRefcountedResource(); + RefcountedResource transform = + getTestRefcountedResource(spi1, spi2, udpEncapSocket); + + // Pretend one SPI goes out of reference (releaseManagedResource -> userRelease) + spi1.userRelease(); + + // User called releaseManagedResource on udpEncap socket + udpEncapSocket.userRelease(); + + // User dies, and binder kills the rest + spi2.binderDied(); + transform.binderDied(); + + // Check resource states + assertResourceState(spi1, -1, 1, 2, 1, 1); + assertResourceState(spi2, -1, 1, 2, 1, 1); + assertResourceState(udpEncapSocket, -1, 1, 2, 1, 1); + assertResourceState(transform, -1, 1, 1, 1, 1); + } + + @Test + public void testSampleDualTransformEncapSocket() throws RemoteException { + RefcountedResource spi1 = getTestRefcountedResource(); + RefcountedResource spi2 = getTestRefcountedResource(); + RefcountedResource spi3 = getTestRefcountedResource(); + RefcountedResource spi4 = getTestRefcountedResource(); + RefcountedResource udpEncapSocket = getTestRefcountedResource(); + RefcountedResource transform1 = + getTestRefcountedResource(spi1, spi2, udpEncapSocket); + RefcountedResource transform2 = + getTestRefcountedResource(spi3, spi4, udpEncapSocket); + + // Pretend one SPIs goes out of reference (releaseManagedResource -> userRelease) + spi1.userRelease(); + + // User called releaseManagedResource on udpEncap socket and spi4 + udpEncapSocket.userRelease(); + spi4.userRelease(); + + // User dies, and binder kills the rest + spi2.binderDied(); + spi3.binderDied(); + transform2.binderDied(); + transform1.binderDied(); + + // Check resource states + assertResourceState(spi1, -1, 1, 2, 1, 1); + assertResourceState(spi2, -1, 1, 2, 1, 1); + assertResourceState(spi3, -1, 1, 2, 1, 1); + assertResourceState(spi4, -1, 1, 2, 1, 1); + assertResourceState(udpEncapSocket, -1, 1, 3, 1, 1); + assertResourceState(transform1, -1, 1, 1, 1, 1); + assertResourceState(transform2, -1, 1, 1, 1, 1); + } + + @Test + public void fuzzTest() throws RemoteException { + List> resources = new ArrayList<>(); + + // Build a tree of resources + for (int i = 0; i < 100; i++) { + // Choose a random number of children from the existing list + int numChildren = ThreadLocalRandom.current().nextInt(0, resources.size() + 1); + + // Build a (random) list of children + Set> children = new HashSet<>(); + for (int j = 0; j < numChildren; j++) { + int childIndex = ThreadLocalRandom.current().nextInt(0, resources.size()); + children.add(resources.get(childIndex)); + } + + RefcountedResource newRefcountedResource = + getTestRefcountedResource( + children.toArray(new RefcountedResource[children.size()])); + resources.add(newRefcountedResource); + } + + // Cleanup all resources in a random order + List> clonedResources = + new ArrayList<>(resources); // shallow copy + while (!clonedResources.isEmpty()) { + int index = ThreadLocalRandom.current().nextInt(0, clonedResources.size()); + RefcountedResource refcountedResource = clonedResources.get(index); + refcountedResource.userRelease(); + clonedResources.remove(index); + } + + // Verify all resources were cleaned up properly + for (RefcountedResource refcountedResource : resources) { + assertEquals(-1, refcountedResource.mRefCount); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java new file mode 100644 index 000000000000..6232423b4f9e --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2017 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 static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.EADDRINUSE; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.INetd; +import android.net.IpSecAlgorithm; +import android.net.IpSecConfig; +import android.net.IpSecManager; +import android.net.IpSecSpiResponse; +import android.net.IpSecUdpEncapResponse; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.os.Process; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; +import android.util.Range; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import dalvik.system.SocketTagger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +/** Unit tests for {@link IpSecService}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class IpSecServiceTest { + + private static final int DROID_SPI = 0xD1201D; + private static final int MAX_NUM_ENCAP_SOCKETS = 100; + private static final int MAX_NUM_SPIS = 100; + private static final int TEST_UDP_ENCAP_INVALID_PORT = 100; + private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000; + + private static final InetAddress INADDR_ANY; + + private static final byte[] AEAD_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x73, 0x61, 0x6C, 0x74 + }; + private static final byte[] CRYPT_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + private static final byte[] AUTH_KEY = { + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F + }; + + private static final IpSecAlgorithm AUTH_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); + private static final IpSecAlgorithm CRYPT_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); + private static final IpSecAlgorithm AEAD_ALGO = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); + + static { + try { + INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + + Context mMockContext; + INetd mMockNetd; + IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; + IpSecService mIpSecService; + + @Before + public void setUp() throws Exception { + mMockContext = mock(Context.class); + mMockNetd = mock(INetd.class); + mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); + + // Injecting mock netd + when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); + } + + @Test + public void testIpSecServiceCreate() throws InterruptedException { + IpSecService ipSecSrv = IpSecService.create(mMockContext); + assertNotNull(ipSecSrv); + } + + @Test + public void testReleaseInvalidSecurityParameterIndex() throws Exception { + try { + mIpSecService.releaseSecurityParameterIndex(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + /** This function finds an available port */ + int findUnusedPort() throws Exception { + // Get an available port. + ServerSocket s = new ServerSocket(0); + int port = s.getLocalPort(); + s.close(); + return port; + } + + @Test + public void testOpenAndCloseUdpEncapsulationSocket() throws Exception { + int localport = -1; + IpSecUdpEncapResponse udpEncapResp = null; + + for (int i = 0; i < IpSecService.MAX_PORT_BIND_ATTEMPTS; i++) { + localport = findUnusedPort(); + + udpEncapResp = mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertNotNull(udpEncapResp); + if (udpEncapResp.status == IpSecManager.Status.OK) { + break; + } + + // Else retry to reduce possibility for port-bind failures. + } + + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertEquals(localport, udpEncapResp.port); + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + + // Verify quota and RefcountedResource objects cleaned up + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); + try { + userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testUdpEncapsulationSocketBinderDeath() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + IpSecService.UserRecord userRecord = + mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); + IpSecService.RefcountedResource refcountedRecord = + userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow( + udpEncapResp.resourceId); + + refcountedRecord.binderDied(); + + // Verify quota and RefcountedResource objects cleaned up + assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); + try { + userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId); + fail("Expected IllegalArgumentException on attempt to access deleted resource"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testOpenUdpEncapsulationSocketAfterClose() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + int localport = udpEncapResp.port; + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + + /** Check if localport is available. */ + FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + Os.bind(newSocket, INADDR_ANY, localport); + Os.close(newSocket); + } + + /** + * This function checks if the IpSecService holds the reserved port. If + * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete. + */ + @Test + public void testUdpEncapPortNotReleased() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + int localport = udpEncapResp.port; + + udpEncapResp.fileDescriptor.close(); + + FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + try { + Os.bind(newSocket, INADDR_ANY, localport); + fail("ErrnoException not thrown"); + } catch (ErrnoException e) { + assertEquals(EADDRINUSE, e.errno); + } + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + } + + @Test + public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + assertNotEquals(0, udpEncapResp.port); + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testOpenUdpEncapsulationSocketPortRange() throws Exception { + try { + mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder()); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + + try { + mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder()); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testOpenUdpEncapsulationSocketTwice() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + int localport = udpEncapResp.port; + + IpSecUdpEncapResponse testUdpEncapResp = + mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); + assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status); + + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testCloseInvalidUdpEncapsulationSocket() throws Exception { + try { + mIpSecService.closeUdpEncapsulationSocket(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testValidateAlgorithmsAuth() { + // Validate that correct algorithm type succeeds + IpSecConfig config = new IpSecConfig(); + config.setAuthentication(AUTH_ALGO); + mIpSecService.validateAlgorithms(config); + + // Validate that incorrect algorithm types fails + for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) { + try { + config = new IpSecConfig(); + config.setAuthentication(algo); + mIpSecService.validateAlgorithms(config); + fail("Did not throw exception on invalid algorithm type"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testValidateAlgorithmsCrypt() { + // Validate that correct algorithm type succeeds + IpSecConfig config = new IpSecConfig(); + config.setEncryption(CRYPT_ALGO); + mIpSecService.validateAlgorithms(config); + + // Validate that incorrect algorithm types fails + for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) { + try { + config = new IpSecConfig(); + config.setEncryption(algo); + mIpSecService.validateAlgorithms(config); + fail("Did not throw exception on invalid algorithm type"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testValidateAlgorithmsAead() { + // Validate that correct algorithm type succeeds + IpSecConfig config = new IpSecConfig(); + config.setAuthenticatedEncryption(AEAD_ALGO); + mIpSecService.validateAlgorithms(config); + + // Validate that incorrect algorithm types fails + for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) { + try { + config = new IpSecConfig(); + config.setAuthenticatedEncryption(algo); + mIpSecService.validateAlgorithms(config); + fail("Did not throw exception on invalid algorithm type"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testValidateAlgorithmsAuthCrypt() { + // Validate that correct algorithm type succeeds + IpSecConfig config = new IpSecConfig(); + config.setAuthentication(AUTH_ALGO); + config.setEncryption(CRYPT_ALGO); + mIpSecService.validateAlgorithms(config); + } + + @Test + public void testValidateAlgorithmsNoAlgorithms() { + IpSecConfig config = new IpSecConfig(); + try { + mIpSecService.validateAlgorithms(config); + fail("Expected exception; no algorithms specified"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testValidateAlgorithmsAeadWithAuth() { + IpSecConfig config = new IpSecConfig(); + config.setAuthenticatedEncryption(AEAD_ALGO); + config.setAuthentication(AUTH_ALGO); + try { + mIpSecService.validateAlgorithms(config); + fail("Expected exception; both AEAD and auth algorithm specified"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testValidateAlgorithmsAeadWithCrypt() { + IpSecConfig config = new IpSecConfig(); + config.setAuthenticatedEncryption(AEAD_ALGO); + config.setEncryption(CRYPT_ALGO); + try { + mIpSecService.validateAlgorithms(config); + fail("Expected exception; both AEAD and crypt algorithm specified"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testValidateAlgorithmsAeadWithAuthAndCrypt() { + IpSecConfig config = new IpSecConfig(); + config.setAuthenticatedEncryption(AEAD_ALGO); + config.setAuthentication(AUTH_ALGO); + config.setEncryption(CRYPT_ALGO); + try { + mIpSecService.validateAlgorithms(config); + fail("Expected exception; AEAD, auth and crypt algorithm specified"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testDeleteInvalidTransform() throws Exception { + try { + mIpSecService.deleteTransform(1); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testRemoveTransportModeTransform() throws Exception { + Socket socket = new Socket(); + socket.bind(null); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + mIpSecService.removeTransportModeTransforms(pfd); + + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); + } + + @Test + public void testValidateIpAddresses() throws Exception { + String[] invalidAddresses = + new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""}; + for (String address : invalidAddresses) { + try { + IpSecSpiResponse spiResp = + mIpSecService.allocateSecurityParameterIndex( + address, DROID_SPI, new Binder()); + fail("Invalid address was passed through IpSecService validation: " + address); + } catch (IllegalArgumentException e) { + } catch (Exception e) { + fail( + "Invalid InetAddress was not caught in validation: " + + address + + ", Exception: " + + e); + } + } + } + + /** + * This function checks if the number of encap UDP socket that one UID can reserve has a + * reasonable limit. + */ + @Test + public void testSocketResourceTrackerLimitation() throws Exception { + List openUdpEncapSockets = new ArrayList(); + // Reserve sockets until it fails. + for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) { + IpSecUdpEncapResponse newUdpEncapSocket = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(newUdpEncapSocket); + if (IpSecManager.Status.OK != newUdpEncapSocket.status) { + break; + } + openUdpEncapSockets.add(newUdpEncapSocket); + } + // Assert that the total sockets quota has a reasonable limit. + assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty()); + assertTrue( + "Number of open UDP encap sockets is out of bound", + openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS); + + // Try to reserve one more UDP encapsulation socket, and should fail. + IpSecUdpEncapResponse extraUdpEncapSocket = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(extraUdpEncapSocket); + assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status); + + // Close one of the open UDP encapsulation sockets. + mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId); + openUdpEncapSockets.get(0).fileDescriptor.close(); + openUdpEncapSockets.remove(0); + + // Try to reserve one more UDP encapsulation socket, and should be successful. + extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(extraUdpEncapSocket); + assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status); + openUdpEncapSockets.add(extraUdpEncapSocket); + + // Close open UDP sockets. + for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) { + mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId); + openSocket.fileDescriptor.close(); + } + } + + /** + * This function checks if the number of SPI that one UID can reserve has a reasonable limit. + * This test does not test for both address families or duplicate SPIs because resource tracking + * code does not depend on them. + */ + @Test + public void testSpiResourceTrackerLimitation() throws Exception { + List reservedSpis = new ArrayList(); + // Return the same SPI for all SPI allocation since IpSecService only + // tracks the resource ID. + when(mMockNetd.ipSecAllocateSpi( + anyInt(), + anyString(), + eq(InetAddress.getLoopbackAddress().getHostAddress()), + anyInt())) + .thenReturn(DROID_SPI); + // Reserve spis until it fails. + for (int i = 0; i < MAX_NUM_SPIS; i++) { + IpSecSpiResponse newSpi = + mIpSecService.allocateSecurityParameterIndex( + InetAddress.getLoopbackAddress().getHostAddress(), + DROID_SPI + i, + new Binder()); + assertNotNull(newSpi); + if (IpSecManager.Status.OK != newSpi.status) { + break; + } + reservedSpis.add(newSpi); + } + // Assert that the SPI quota has a reasonable limit. + assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS); + + // Try to reserve one more SPI, and should fail. + IpSecSpiResponse extraSpi = + mIpSecService.allocateSecurityParameterIndex( + InetAddress.getLoopbackAddress().getHostAddress(), + DROID_SPI + MAX_NUM_SPIS, + new Binder()); + assertNotNull(extraSpi); + assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status); + + // Release one reserved spi. + mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId); + reservedSpis.remove(0); + + // Should successfully reserve one more spi. + extraSpi = + mIpSecService.allocateSecurityParameterIndex( + InetAddress.getLoopbackAddress().getHostAddress(), + DROID_SPI + MAX_NUM_SPIS, + new Binder()); + assertNotNull(extraSpi); + assertEquals(IpSecManager.Status.OK, extraSpi.status); + + // Release reserved SPIs. + for (IpSecSpiResponse spiResp : reservedSpis) { + mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); + } + } + + @Test + public void testUidFdtagger() throws Exception { + SocketTagger actualSocketTagger = SocketTagger.get(); + + try { + FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + // Has to be done after socket creation because BlockGuardOS calls tag on new sockets + SocketTagger mockSocketTagger = mock(SocketTagger.class); + SocketTagger.set(mockSocketTagger); + + mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID); + verify(mockSocketTagger).tag(eq(sockFd)); + } finally { + SocketTagger.set(actualSocketTagger); + } + } + + /** + * Checks if two file descriptors point to the same file. + * + *

According to stat.h documentation, the correct way to check for equivalent or duplicated + * file descriptors is to check their inode and device. These two entries uniquely identify any + * file. + */ + private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) { + try { + StructStat fd1Stat = Os.fstat(fd1); + StructStat fd2Stat = Os.fstat(fd2); + + return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev; + } catch (ErrnoException e) { + return false; + } + } + + @Test + public void testOpenUdpEncapSocketTagsSocket() throws Exception { + IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class); + IpSecService testIpSecService = new IpSecService( + mMockContext, mMockIpSecSrvConfig, mockTagger); + + IpSecUdpEncapResponse udpEncapResp = + testIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + + FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); + ArgumentMatcher fdMatcher = + (argFd) -> { + return fileDescriptorsEqual(sockFd, argFd); + }; + verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid())); + + testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); + ArgumentMatcher fdMatcher = (arg) -> { + try { + StructStat sockStat = Os.fstat(sockFd); + StructStat argStat = Os.fstat(arg.getFileDescriptor()); + + return sockStat.st_ino == argStat.st_ino + && sockStat.st_dev == argStat.st_dev; + } catch (ErrnoException e) { + return false; + } + }; + + verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid())); + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + } + + @Test + public void testReserveNetId() { + final Range netIdRange = ConnectivityManager.getIpSecNetIdRange(); + for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) { + assertEquals(netId, mIpSecService.reserveNetId()); + } + + // Check that resource exhaustion triggers an exception + try { + mIpSecService.reserveNetId(); + fail("Did not throw error for all netIds reserved"); + } catch (IllegalStateException expected) { + } + + // Now release one and try again + int releasedNetId = + netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2; + mIpSecService.releaseNetId(releasedNetId); + assertEquals(releasedNetId, mIpSecService.reserveNetId()); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt new file mode 100644 index 000000000000..5ec111954fcc --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019 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. + */ + +// Don't warn about deprecated types anywhere in this test, because LegacyTypeTracker's very reason +// for existence is to power deprecated APIs. The annotation has to apply to the whole file because +// otherwise warnings will be generated by the imports of deprecated constants like TYPE_xxx. +@file:Suppress("DEPRECATION") + +package com.android.server + +import android.content.Context +import android.content.pm.PackageManager +import android.content.pm.PackageManager.FEATURE_WIFI +import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT +import android.net.ConnectivityManager.TYPE_ETHERNET +import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_MOBILE_CBS +import android.net.ConnectivityManager.TYPE_MOBILE_DUN +import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY +import android.net.ConnectivityManager.TYPE_MOBILE_FOTA +import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI +import android.net.ConnectivityManager.TYPE_MOBILE_IA +import android.net.ConnectivityManager.TYPE_MOBILE_IMS +import android.net.ConnectivityManager.TYPE_MOBILE_MMS +import android.net.ConnectivityManager.TYPE_MOBILE_SUPL +import android.net.ConnectivityManager.TYPE_VPN +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.ConnectivityManager.TYPE_WIFI_P2P +import android.net.ConnectivityManager.TYPE_WIMAX +import android.net.EthernetManager +import android.net.NetworkInfo.DetailedState.CONNECTED +import android.net.NetworkInfo.DetailedState.DISCONNECTED +import android.telephony.TelephonyManager +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.server.ConnectivityService.LegacyTypeTracker +import com.android.server.connectivity.NetworkAgentInfo +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify + +const val UNSUPPORTED_TYPE = TYPE_WIMAX + +@RunWith(AndroidJUnit4::class) +@SmallTest +class LegacyTypeTrackerTest { + private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE, + TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI, + TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA, + TYPE_MOBILE_EMERGENCY, TYPE_VPN) + + private val mMockService = mock(ConnectivityService::class.java).apply { + doReturn(false).`when`(this).isDefaultNetwork(any()) + } + private val mPm = mock(PackageManager::class.java) + private val mContext = mock(Context::class.java).apply { + doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI) + doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) + doReturn(mPm).`when`(this).packageManager + doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService( + Context.ETHERNET_SERVICE) + } + private val mTm = mock(TelephonyManager::class.java).apply { + doReturn(true).`when`(this).isDataCapable + } + + private fun makeTracker() = LegacyTypeTracker(mMockService).apply { + loadSupportedTypes(mContext, mTm) + } + + @Test + fun testSupportedTypes() { + val tracker = makeTracker() + supportedTypes.forEach { + assertTrue(tracker.isTypeSupported(it)) + } + assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE)) + } + + @Test + fun testSupportedTypes_NoEthernet() { + doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE) + assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET)) + } + + @Test + fun testSupportedTypes_NoTelephony() { + doReturn(false).`when`(mTm).isDataCapable + val tracker = makeTracker() + val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN) + nonMobileTypes.forEach { + assertTrue(tracker.isTypeSupported(it)) + } + supportedTypes.toSet().minus(nonMobileTypes).forEach { + assertFalse(tracker.isTypeSupported(it)) + } + } + + @Test + fun testSupportedTypes_NoWifiDirect() { + doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) + val tracker = makeTracker() + assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P)) + supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach { + assertTrue(tracker.isTypeSupported(it)) + } + } + + @Test + fun testSupl() { + val tracker = makeTracker() + val mobileNai = mock(NetworkAgentInfo::class.java) + tracker.add(TYPE_MOBILE, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE) + reset(mMockService) + tracker.add(TYPE_MOBILE_SUPL, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + tracker.add(TYPE_MOBILE_SUPL, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + tracker.remove(mobileNai, false) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE) + } + + @Test + fun testAddNetwork() { + val tracker = makeTracker() + val mobileNai = mock(NetworkAgentInfo::class.java) + val wifiNai = mock(NetworkAgentInfo::class.java) + tracker.add(TYPE_MOBILE, mobileNai) + tracker.add(TYPE_WIFI, wifiNai) + assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure adding a second NAI does not change the results. + val secondMobileNai = mock(NetworkAgentInfo::class.java) + tracker.add(TYPE_MOBILE, secondMobileNai) + assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure removing a network that wasn't added for this type is a no-op. + tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) + assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) + assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Remove the top network for mobile and make sure the second one becomes the network + // of record for this type. + tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) + assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) + assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) + // Make sure adding a network for an unsupported type does not register it. + tracker.add(UNSUPPORTED_TYPE, mobileNai) + assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE)) + } + + @Test + fun testBroadcastOnDisconnect() { + val tracker = makeTracker() + val mobileNai1 = mock(NetworkAgentInfo::class.java) + val mobileNai2 = mock(NetworkAgentInfo::class.java) + doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) + tracker.add(TYPE_MOBILE, mobileNai1) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) + reset(mMockService) + doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) + tracker.add(TYPE_MOBILE, mobileNai2) + verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) + tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE) + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt new file mode 100644 index 000000000000..6f5e740d344c --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 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 androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.server.NetIdManager.MIN_NET_ID +import com.android.testutils.assertThrows +import com.android.testutils.ExceptionUtils.ThrowingRunnable +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetIdManagerTest { + @Test + fun testReserveReleaseNetId() { + val manager = NetIdManager(MIN_NET_ID + 4) + assertEquals(MIN_NET_ID, manager.reserveNetId()) + assertEquals(MIN_NET_ID + 1, manager.reserveNetId()) + assertEquals(MIN_NET_ID + 2, manager.reserveNetId()) + assertEquals(MIN_NET_ID + 3, manager.reserveNetId()) + + manager.releaseNetId(MIN_NET_ID + 1) + manager.releaseNetId(MIN_NET_ID + 3) + // IDs only loop once there is no higher ID available + assertEquals(MIN_NET_ID + 4, manager.reserveNetId()) + assertEquals(MIN_NET_ID + 1, manager.reserveNetId()) + assertEquals(MIN_NET_ID + 3, manager.reserveNetId()) + assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() }) + manager.releaseNetId(MIN_NET_ID + 5) + // Still no ID available: MIN_NET_ID + 5 was not reserved + assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() }) + manager.releaseNetId(MIN_NET_ID + 2) + // Throwing an exception still leaves the manager in a working state + assertEquals(MIN_NET_ID + 2, manager.reserveNetId()) + } +} \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java new file mode 100644 index 000000000000..13516d75a50d --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2012 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 static android.util.DebugUtils.valueToString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.INetd; +import android.net.INetdUnsolicitedEventListener; +import android.net.LinkAddress; +import android.net.NetworkPolicyManager; +import android.os.BatteryStats; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArrayMap; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.app.IBatteryStats; +import com.android.server.NetworkManagementService.Dependencies; +import com.android.server.net.BaseNetworkObserver; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.BiFunction; + +/** + * Tests for {@link NetworkManagementService}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkManagementServiceTest { + private NetworkManagementService mNMService; + @Mock private Context mContext; + @Mock private IBatteryStats.Stub mBatteryStatsService; + @Mock private INetd.Stub mNetdService; + + private static final int TEST_UID = 111; + + @NonNull + @Captor + private ArgumentCaptor mUnsolListenerCaptor; + + private final MockDependencies mDeps = new MockDependencies(); + + private final class MockDependencies extends Dependencies { + @Override + public IBinder getService(String name) { + switch (name) { + case BatteryStats.SERVICE_NAME: + return mBatteryStatsService; + default: + throw new UnsupportedOperationException("Unknown service " + name); + } + } + + @Override + public void registerLocalService(NetworkManagementInternal nmi) { + } + + @Override + public INetd getNetd() { + return mNetdService; + } + + @Override + public int getCallingUid() { + return Process.SYSTEM_UID; + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doNothing().when(mNetdService) + .registerUnsolicitedEventListener(mUnsolListenerCaptor.capture()); + // Start the service and wait until it connects to our socket. + mNMService = NetworkManagementService.create(mContext, mDeps); + } + + @After + public void tearDown() throws Exception { + mNMService.shutdown(); + } + + private static T expectSoon(T mock) { + return verify(mock, timeout(200)); + } + + /** + * Tests that network observers work properly. + */ + @Test + public void testNetworkObservers() throws Exception { + BaseNetworkObserver observer = mock(BaseNetworkObserver.class); + doReturn(new Binder()).when(observer).asBinder(); // Used by registerObserver. + mNMService.registerObserver(observer); + + // Forget everything that happened to the mock so far, so we can explicitly verify + // everything that happens and does not happen to it from now on. + + INetdUnsolicitedEventListener unsolListener = mUnsolListenerCaptor.getValue(); + reset(observer); + // Now call unsolListener methods and ensure that the observer methods are + // called. After every method we expect a callback soon after; to ensure that + // invalid messages don't cause any callbacks, we call verifyNoMoreInteractions at the end. + + /** + * Interface changes. + */ + unsolListener.onInterfaceAdded("rmnet12"); + expectSoon(observer).interfaceAdded("rmnet12"); + + unsolListener.onInterfaceRemoved("eth1"); + expectSoon(observer).interfaceRemoved("eth1"); + + unsolListener.onInterfaceChanged("clat4", true); + expectSoon(observer).interfaceStatusChanged("clat4", true); + + unsolListener.onInterfaceLinkStateChanged("rmnet0", false); + expectSoon(observer).interfaceLinkStateChanged("rmnet0", false); + + /** + * Bandwidth control events. + */ + unsolListener.onQuotaLimitReached("data", "rmnet_usb0"); + expectSoon(observer).limitReached("data", "rmnet_usb0"); + + /** + * Interface class activity. + */ + unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, TEST_UID); + expectSoon(observer).interfaceClassDataActivityChanged(1, true, 1234, TEST_UID); + + unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, TEST_UID); + expectSoon(observer).interfaceClassDataActivityChanged(9, false, 5678, TEST_UID); + + unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, TEST_UID); + expectSoon(observer).interfaceClassDataActivityChanged(9, false, 4321, TEST_UID); + + /** + * IP address changes. + */ + unsolListener.onInterfaceAddressUpdated("fe80::1/64", "wlan0", 128, 253); + expectSoon(observer).addressUpdated("wlan0", new LinkAddress("fe80::1/64", 128, 253)); + + unsolListener.onInterfaceAddressRemoved("fe80::1/64", "wlan0", 128, 253); + expectSoon(observer).addressRemoved("wlan0", new LinkAddress("fe80::1/64", 128, 253)); + + unsolListener.onInterfaceAddressRemoved("2001:db8::1/64", "wlan0", 1, 0); + expectSoon(observer).addressRemoved("wlan0", new LinkAddress("2001:db8::1/64", 1, 0)); + + /** + * DNS information broadcasts. + */ + unsolListener.onInterfaceDnsServerInfo("rmnet_usb0", 3600, new String[]{"2001:db8::1"}); + expectSoon(observer).interfaceDnsServerInfo("rmnet_usb0", 3600, + new String[]{"2001:db8::1"}); + + unsolListener.onInterfaceDnsServerInfo("wlan0", 14400, + new String[]{"2001:db8::1", "2001:db8::2"}); + expectSoon(observer).interfaceDnsServerInfo("wlan0", 14400, + new String[]{"2001:db8::1", "2001:db8::2"}); + + // We don't check for negative lifetimes, only for parse errors. + unsolListener.onInterfaceDnsServerInfo("wlan0", -3600, new String[]{"::1"}); + expectSoon(observer).interfaceDnsServerInfo("wlan0", -3600, + new String[]{"::1"}); + + // No syntax checking on the addresses. + unsolListener.onInterfaceDnsServerInfo("wlan0", 600, + new String[]{"", "::", "", "foo", "::1"}); + expectSoon(observer).interfaceDnsServerInfo("wlan0", 600, + new String[]{"", "::", "", "foo", "::1"}); + + // Make sure nothing else was called. + verifyNoMoreInteractions(observer); + } + + @Test + public void testFirewallEnabled() { + mNMService.setFirewallEnabled(true); + assertTrue(mNMService.isFirewallEnabled()); + + mNMService.setFirewallEnabled(false); + assertFalse(mNMService.isFirewallEnabled()); + } + + @Test + public void testNetworkRestrictedDefault() { + assertFalse(mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + public void testMeteredNetworkRestrictions() throws RemoteException { + // Make sure the mocked netd method returns true. + doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean()); + + // Restrict usage of mobile data in background + mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, true); + assertTrue("Should be true since mobile data usage is restricted", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setDataSaverModeEnabled(true); + verify(mNetdService).bandwidthEnableDataSaver(true); + + mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false); + assertTrue("Should be true since data saver is on and the uid is not allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, true); + assertFalse("Should be false since data saver is on and the uid is allowlisted", + mNMService.isNetworkRestricted(TEST_UID)); + + // remove uid from allowlist and turn datasaver off again + mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false); + mNMService.setDataSaverModeEnabled(false); + verify(mNetdService).bandwidthEnableDataSaver(false); + assertFalse("Network should not be restricted when data saver is off", + mNMService.isNetworkRestricted(TEST_UID)); + } + + @Test + public void testFirewallChains() { + final ArrayMap> expected = new ArrayMap<>(); + // Dozable chain + final ArrayMap isRestrictedForDozable = new ArrayMap<>(); + isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable); + // Powersaver chain + final ArrayMap isRestrictedForPowerSave = new ArrayMap<>(); + isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave); + // Standby chain + final ArrayMap isRestrictedForStandby = new ArrayMap<>(); + isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false); + isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby); + // Restricted mode chain + final ArrayMap isRestrictedForRestrictedMode = new ArrayMap<>(); + isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode); + + final int[] chains = { + INetd.FIREWALL_CHAIN_STANDBY, + INetd.FIREWALL_CHAIN_POWERSAVE, + INetd.FIREWALL_CHAIN_DOZABLE, + INetd.FIREWALL_CHAIN_RESTRICTED + }; + final int[] states = { + INetd.FIREWALL_RULE_ALLOW, + INetd.FIREWALL_RULE_DENY, + NetworkPolicyManager.FIREWALL_RULE_DEFAULT + }; + BiFunction errorMsg = (chain, state) -> { + return String.format("Unexpected value for chain: %s and state: %s", + valueToString(INetd.class, "FIREWALL_CHAIN_", chain), + valueToString(INetd.class, "FIREWALL_RULE_", state)); + }; + for (int chain : chains) { + final ArrayMap expectedValues = expected.get(chain); + mNMService.setFirewallChainEnabled(chain, true); + for (int state : states) { + mNMService.setFirewallUidRule(chain, TEST_UID, state); + assertEquals(errorMsg.apply(chain, state), + expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID)); + } + mNMService.setFirewallChainEnabled(chain, false); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java new file mode 100644 index 000000000000..a90fa6882c25 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2017 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 static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.NsdService.DaemonConnection; +import com.android.server.NsdService.DaemonConnectionSupplier; +import com.android.server.NsdService.NativeCallbackReceiver; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +// TODOs: +// - test client can send requests and receive replies +// - test NSD_ON ENABLE/DISABLED listening +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NsdServiceTest { + + static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; + + long mTimeoutMs = 100; // non-final so that tests can adjust the value. + + @Mock Context mContext; + @Mock ContentResolver mResolver; + @Mock NsdService.NsdSettings mSettings; + @Mock DaemonConnection mDaemon; + NativeCallbackReceiver mDaemonCallback; + HandlerThread mThread; + TestHandler mHandler; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mThread = new HandlerThread("mock-service-handler"); + mThread.start(); + mHandler = new TestHandler(mThread.getLooper()); + when(mContext.getContentResolver()).thenReturn(mResolver); + } + + @After + public void tearDown() throws Exception { + if (mThread != null) { + mThread.quit(); + mThread = null; + } + } + + @Test + public void testClientsCanConnectAndDisconnect() { + when(mSettings.isEnabled()).thenReturn(true); + + NsdService service = makeService(); + + NsdManager client1 = connectClient(service); + verify(mDaemon, timeout(100).times(1)).start(); + + NsdManager client2 = connectClient(service); + + client1.disconnect(); + client2.disconnect(); + + verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); + + client1.disconnect(); + client2.disconnect(); + } + + @Test + public void testClientRequestsAreGCedAtDisconnection() { + when(mSettings.isEnabled()).thenReturn(true); + when(mDaemon.execute(any())).thenReturn(true); + + NsdService service = makeService(); + NsdManager client = connectClient(service); + + verify(mDaemon, timeout(100).times(1)).start(); + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + request.setPort(2201); + + // Client registration request + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + client.registerService(request, PROTOCOL, listener1); + verifyDaemonCommand("register 2 a_name a_type 2201"); + + // Client discovery request + NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); + client.discoverServices("a_type", PROTOCOL, listener2); + verifyDaemonCommand("discover 3 a_type"); + + // Client resolve request + NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); + client.resolveService(request, listener3); + verifyDaemonCommand("resolve 4 a_name a_type local."); + + // Client disconnects + client.disconnect(); + verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); + + // checks that request are cleaned + verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); + + client.disconnect(); + } + + NsdService makeService() { + DaemonConnectionSupplier supplier = (callback) -> { + mDaemonCallback = callback; + return mDaemon; + }; + NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); + verify(mDaemon, never()).execute(any(String.class)); + return service; + } + + NsdManager connectClient(NsdService service) { + return new NsdManager(mContext, service); + } + + void verifyDaemonCommands(String... wants) { + verifyDaemonCommand(String.join(" ", wants), wants.length); + } + + void verifyDaemonCommand(String want) { + verifyDaemonCommand(want, 1); + } + + void verifyDaemonCommand(String want, int n) { + ArgumentCaptor argumentsCaptor = ArgumentCaptor.forClass(Object.class); + verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); + String got = ""; + for (Object o : argumentsCaptor.getAllValues()) { + got += o + " "; + } + assertEquals(want, got.trim()); + // rearm deamon for next command verification + reset(mDaemon); + when(mDaemon.execute(any())).thenReturn(true); + } + + public static class TestHandler extends Handler { + public Message lastMessage; + + TestHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + lastMessage = obtainMessage(); + lastMessage.copyFrom(msg); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java new file mode 100644 index 000000000000..0ffeec98cf90 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2018, 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.connectivity; + +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER; +import static android.net.NetworkCapabilities.MAX_TRANSPORT; +import static android.net.NetworkCapabilities.MIN_TRANSPORT; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; +import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; + +import static com.android.testutils.MiscAsserts.assertContainsExactly; +import static com.android.testutils.MiscAsserts.assertContainsStringsExactly; +import static com.android.testutils.MiscAsserts.assertFieldCountEquals; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.ConnectivitySettingsManager; +import android.net.IDnsResolver; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.ResolverOptionsParcel; +import android.net.ResolverParamsParcel; +import android.net.RouteInfo; +import android.net.shared.PrivateDnsConfig; +import android.provider.Settings; +import android.test.mock.MockContentResolver; +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.MessageUtils; +import com.android.internal.util.test.FakeSettingsProvider; + +import libcore.net.InetAddressUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.net.InetAddress; +import java.util.Arrays; + +/** + * Tests for {@link DnsManager}. + * + * Build, install and run with: + * runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DnsManagerTest { + static final String TEST_IFACENAME = "test_wlan0"; + static final int TEST_NETID = 100; + static final int TEST_NETID_ALTERNATE = 101; + static final int TEST_NETID_UNTRACKED = 102; + static final int TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800; + static final int TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25; + static final int TEST_DEFAULT_MIN_SAMPLES = 8; + static final int TEST_DEFAULT_MAX_SAMPLES = 64; + static final int[] TEST_TRANSPORT_TYPES = {TRANSPORT_WIFI, TRANSPORT_VPN}; + + DnsManager mDnsManager; + MockContentResolver mContentResolver; + + @Mock Context mCtx; + @Mock IDnsResolver mMockDnsResolver; + + private void assertResolverOptionsEquals( + @NonNull ResolverOptionsParcel actual, + @NonNull ResolverOptionsParcel expected) { + assertEquals(actual.hosts, expected.hosts); + assertEquals(actual.tcMode, expected.tcMode); + assertEquals(actual.enforceDnsUid, expected.enforceDnsUid); + assertFieldCountEquals(3, ResolverOptionsParcel.class); + } + + private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual, + @NonNull ResolverParamsParcel expected) { + assertEquals(actual.netId, expected.netId); + assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds); + assertEquals(actual.successThreshold, expected.successThreshold); + assertEquals(actual.minSamples, expected.minSamples); + assertEquals(actual.maxSamples, expected.maxSamples); + assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec); + assertEquals(actual.retryCount, expected.retryCount); + assertContainsStringsExactly(actual.servers, expected.servers); + assertContainsStringsExactly(actual.domains, expected.domains); + assertEquals(actual.tlsName, expected.tlsName); + assertContainsStringsExactly(actual.tlsServers, expected.tlsServers); + assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints); + assertEquals(actual.caCertificate, expected.caCertificate); + assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs); + assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions); + assertContainsExactly(actual.transportTypes, expected.transportTypes); + assertFieldCountEquals(16, ResolverParamsParcel.class); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContentResolver = new MockContentResolver(); + mContentResolver.addProvider(Settings.AUTHORITY, + new FakeSettingsProvider()); + when(mCtx.getContentResolver()).thenReturn(mContentResolver); + mDnsManager = new DnsManager(mCtx, mMockDnsResolver); + + // Clear the private DNS settings + Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, ""); + Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, ""); + Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, ""); + } + + @Test + public void testTrackedValidationUpdates() throws Exception { + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + mDnsManager.getPrivateDnsConfig()); + mDnsManager.updatePrivateDns(new Network(TEST_NETID_ALTERNATE), + mDnsManager.getPrivateDnsConfig()); + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(TEST_IFACENAME); + lp.addDnsServer(InetAddress.getByName("3.3.3.3")); + lp.addDnsServer(InetAddress.getByName("4.4.4.4")); + + // Send a validation event that is tracked on the alternate netId + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp); + mDnsManager.flushVmDnsCache(); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE, + InetAddress.parseNumericAddress("4.4.4.4"), "", + VALIDATION_RESULT_SUCCESS)); + LinkProperties fixedLp = new LinkProperties(lp); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); + assertFalse(fixedLp.isPrivateDnsActive()); + assertNull(fixedLp.getPrivateDnsServerName()); + fixedLp = new LinkProperties(lp); + mDnsManager.updatePrivateDnsStatus(TEST_NETID_ALTERNATE, fixedLp); + assertTrue(fixedLp.isPrivateDnsActive()); + assertNull(fixedLp.getPrivateDnsServerName()); + assertEquals(Arrays.asList(InetAddress.getByName("4.4.4.4")), + fixedLp.getValidatedPrivateDnsServers()); + + // Set up addresses for strict mode and switch to it. + lp.addLinkAddress(new LinkAddress("192.0.2.4/24")); + lp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), + TEST_IFACENAME)); + lp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); + lp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), + TEST_IFACENAME)); + + ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); + ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com"); + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + new PrivateDnsConfig("strictmode.com", new InetAddress[] { + InetAddress.parseNumericAddress("6.6.6.6"), + InetAddress.parseNumericAddress("2001:db8:66:66::1") + })); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + fixedLp = new LinkProperties(lp); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); + assertTrue(fixedLp.isPrivateDnsActive()); + assertEquals("strictmode.com", fixedLp.getPrivateDnsServerName()); + // No validation events yet. + assertEquals(Arrays.asList(new InetAddress[0]), fixedLp.getValidatedPrivateDnsServers()); + // Validate one. + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("6.6.6.6"), "strictmode.com", + VALIDATION_RESULT_SUCCESS)); + fixedLp = new LinkProperties(lp); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); + assertEquals(Arrays.asList(InetAddress.parseNumericAddress("6.6.6.6")), + fixedLp.getValidatedPrivateDnsServers()); + // Validate the 2nd one. + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("2001:db8:66:66::1"), "strictmode.com", + VALIDATION_RESULT_SUCCESS)); + fixedLp = new LinkProperties(lp); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); + assertEquals(Arrays.asList( + InetAddress.parseNumericAddress("2001:db8:66:66::1"), + InetAddress.parseNumericAddress("6.6.6.6")), + fixedLp.getValidatedPrivateDnsServers()); + } + + @Test + public void testIgnoreUntrackedValidationUpdates() throws Exception { + // The PrivateDnsConfig map is empty, so no validation events will + // be tracked. + LinkProperties lp = new LinkProperties(); + lp.addDnsServer(InetAddress.getByName("3.3.3.3")); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("3.3.3.3"), "", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Validation event has untracked netId + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + mDnsManager.getPrivateDnsConfig()); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED, + InetAddress.parseNumericAddress("3.3.3.3"), "", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Validation event has untracked ipAddress + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("4.4.4.4"), "", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Validation event has untracked hostname + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("3.3.3.3"), "hostname", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Validation event failed + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("3.3.3.3"), "", + VALIDATION_RESULT_FAILURE)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Network removed + mDnsManager.removeNetwork(new Network(TEST_NETID)); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("3.3.3.3"), "", VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + + // Turn private DNS mode off + ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_OFF); + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + mDnsManager.getPrivateDnsConfig()); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, + InetAddress.parseNumericAddress("3.3.3.3"), "", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + assertFalse(lp.isPrivateDnsActive()); + assertNull(lp.getPrivateDnsServerName()); + } + + @Test + public void testOverrideDefaultMode() throws Exception { + // Hard-coded default is opportunistic mode. + final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx); + assertTrue(cfgAuto.useTls); + assertEquals("", cfgAuto.hostname); + assertEquals(new InetAddress[0], cfgAuto.ips); + + // Pretend a gservices push sets the default to "off". + ConnectivitySettingsManager.setPrivateDnsDefaultMode(mCtx, PRIVATE_DNS_MODE_OFF); + final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx); + assertFalse(cfgOff.useTls); + assertEquals("", cfgOff.hostname); + assertEquals(new InetAddress[0], cfgOff.ips); + + // Strict mode still works. + ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); + ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com"); + final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx); + assertTrue(cfgStrict.useTls); + assertEquals("strictmode.com", cfgStrict.hostname); + assertEquals(new InetAddress[0], cfgStrict.ips); + } + + @Test + public void testSendDnsConfiguration() throws Exception { + reset(mMockDnsResolver); + mDnsManager.updatePrivateDns(new Network(TEST_NETID), + mDnsManager.getPrivateDnsConfig()); + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(TEST_IFACENAME); + lp.addDnsServer(InetAddress.getByName("3.3.3.3")); + lp.addDnsServer(InetAddress.getByName("4.4.4.4")); + mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.flushVmDnsCache(); + + final ArgumentCaptor resolverParamsParcelCaptor = + ArgumentCaptor.forClass(ResolverParamsParcel.class); + verify(mMockDnsResolver, times(1)).setResolverConfiguration( + resolverParamsParcelCaptor.capture()); + final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue(); + final ResolverParamsParcel expectedParams = new ResolverParamsParcel(); + expectedParams.netId = TEST_NETID; + expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS; + expectedParams.successThreshold = TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT; + expectedParams.minSamples = TEST_DEFAULT_MIN_SAMPLES; + expectedParams.maxSamples = TEST_DEFAULT_MAX_SAMPLES; + expectedParams.servers = new String[]{"3.3.3.3", "4.4.4.4"}; + expectedParams.domains = new String[]{}; + expectedParams.tlsName = ""; + expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"}; + expectedParams.transportTypes = TEST_TRANSPORT_TYPES; + expectedParams.resolverOptions = new ResolverOptionsParcel(); + assertResolverParamsEquals(actualParams, expectedParams); + } + + @Test + public void testTransportTypesEqual() throws Exception { + SparseArray ncTransTypes = MessageUtils.findMessageNames( + new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" }); + SparseArray dnsTransTypes = MessageUtils.findMessageNames( + new Class[] { IDnsResolver.class }, new String[]{ "TRANSPORT_" }); + assertEquals(0, MIN_TRANSPORT); + assertEquals(MAX_TRANSPORT + 1, ncTransTypes.size()); + // TRANSPORT_UNKNOWN in IDnsResolver is defined to -1 and only for resolver. + assertEquals("TRANSPORT_UNKNOWN", dnsTransTypes.get(-1)); + assertEquals(ncTransTypes.size(), dnsTransTypes.size() - 1); + for (int i = MIN_TRANSPORT; i < MAX_TRANSPORT; i++) { + String name = ncTransTypes.get(i, null); + assertNotNull("Could not find NetworkCapabilies.TRANSPORT_* constant equal to " + + i, name); + assertEquals(name, dnsTransTypes.get(i)); + } + } + + @Test + public void testGetPrivateDnsConfigForNetwork() throws Exception { + final Network network = new Network(TEST_NETID); + final InetAddress dnsAddr = InetAddressUtils.parseNumericAddress("3.3.3.3"); + final InetAddress[] tlsAddrs = new InetAddress[]{ + InetAddressUtils.parseNumericAddress("6.6.6.6"), + InetAddressUtils.parseNumericAddress("2001:db8:66:66::1") + }; + final String tlsName = "strictmode.com"; + LinkProperties lp = new LinkProperties(); + lp.addDnsServer(dnsAddr); + + // The PrivateDnsConfig map is empty, so the default PRIVATE_DNS_OFF is returned. + PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); + assertFalse(privateDnsCfg.useTls); + assertEquals("", privateDnsCfg.hostname); + assertEquals(new InetAddress[0], privateDnsCfg.ips); + + // An entry with default PrivateDnsConfig is added to the PrivateDnsConfig map. + mDnsManager.updatePrivateDns(network, mDnsManager.getPrivateDnsConfig()); + mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); + mDnsManager.updatePrivateDnsValidation( + new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, dnsAddr, "", + VALIDATION_RESULT_SUCCESS)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); + assertTrue(privateDnsCfg.useTls); + assertEquals("", privateDnsCfg.hostname); + assertEquals(new InetAddress[0], privateDnsCfg.ips); + + // The original entry is overwritten by a new PrivateDnsConfig. + mDnsManager.updatePrivateDns(network, new PrivateDnsConfig(tlsName, tlsAddrs)); + mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); + privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); + assertTrue(privateDnsCfg.useTls); + assertEquals(tlsName, privateDnsCfg.hostname); + assertEquals(tlsAddrs, privateDnsCfg.ips); + + // The network is removed, so the PrivateDnsConfig map becomes empty again. + mDnsManager.removeNetwork(network); + privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); + assertFalse(privateDnsCfg.useTls); + assertEquals("", privateDnsCfg.hostname); + assertEquals(new InetAddress[0], privateDnsCfg.ips); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt new file mode 100644 index 000000000000..45b575a4365d --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2021 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.connectivity + +import android.net.NetworkAgentConfig +import android.net.NetworkCapabilities +import android.net.NetworkScore.KEEP_CONNECTED_NONE +import android.text.TextUtils +import android.util.ArraySet +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY +import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED +import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED +import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED +import com.android.server.connectivity.FullScore.POLICY_IS_VPN +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.collections.minOfOrNull +import kotlin.collections.maxOfOrNull +import kotlin.reflect.full.staticProperties +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +@RunWith(AndroidJUnit4::class) +@SmallTest +class FullScoreTest { + // Convenience methods + fun FullScore.withPolicies( + validated: Boolean = false, + vpn: Boolean = false, + onceChosen: Boolean = false, + acceptUnvalidated: Boolean = false + ): FullScore { + val nac = NetworkAgentConfig.Builder().apply { + setUnvalidatedConnectivityAcceptable(acceptUnvalidated) + setExplicitlySelected(onceChosen) + }.build() + val nc = NetworkCapabilities.Builder().apply { + if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN) + if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + }.build() + return mixInScore(nc, nac, validated, false /* yieldToBadWifi */) + } + + @Test + fun testGetLegacyInt() { + val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE) + assertEquals(10, ns.legacyInt) // -40 penalty for not being validated + assertEquals(50, ns.legacyIntAsValidated) + + val vpnNs = FullScore(101, 0L /* policy */, KEEP_CONNECTED_NONE).withPolicies(vpn = true) + assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty + assertEquals(101, vpnNs.legacyIntAsValidated) + assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt) + assertEquals(101, vpnNs.withPolicies(validated = true).legacyIntAsValidated) + + val validatedNs = ns.withPolicies(validated = true) + assertEquals(50, validatedNs.legacyInt) // No penalty, this is validated + assertEquals(50, validatedNs.legacyIntAsValidated) + + val chosenNs = ns.withPolicies(onceChosen = true) + assertEquals(10, chosenNs.legacyInt) + assertEquals(100, chosenNs.legacyIntAsValidated) + assertEquals(10, chosenNs.withPolicies(acceptUnvalidated = true).legacyInt) + assertEquals(50, chosenNs.withPolicies(acceptUnvalidated = true).legacyIntAsValidated) + } + + @Test + fun testToString() { + val string = FullScore(10, 0L /* policy */, KEEP_CONNECTED_NONE) + .withPolicies(vpn = true, acceptUnvalidated = true).toString() + assertTrue(string.contains("Score(10"), string) + assertTrue(string.contains("ACCEPT_UNVALIDATED"), string) + assertTrue(string.contains("IS_VPN"), string) + assertFalse(string.contains("IS_VALIDATED"), string) + val foundNames = ArraySet() + getAllPolicies().forEach { + val name = FullScore.policyNameOf(it.get() as Int) + assertFalse(TextUtils.isEmpty(name)) + assertFalse(foundNames.contains(name)) + foundNames.add(name) + } + assertFailsWith { + FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1) + } + } + + fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex -> + FullScore::class.staticProperties.filter { it.name.matches(nameRegex) } + } + + @Test + fun testHasPolicy() { + val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE) + assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED)) + assertFalse(ns.hasPolicy(POLICY_IS_VPN)) + assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED)) + assertFalse(ns.hasPolicy(POLICY_ACCEPT_UNVALIDATED)) + assertTrue(ns.withPolicies(validated = true).hasPolicy(POLICY_IS_VALIDATED)) + assertTrue(ns.withPolicies(vpn = true).hasPolicy(POLICY_IS_VPN)) + assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED)) + assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED)) + } + + @Test + fun testMinMaxPolicyConstants() { + val policies = getAllPolicies() + + policies.forEach { policy -> + assertTrue(policy.get() as Int >= FullScore.MIN_CS_MANAGED_POLICY) + assertTrue(policy.get() as Int <= FullScore.MAX_CS_MANAGED_POLICY) + } + assertEquals(FullScore.MIN_CS_MANAGED_POLICY, + policies.minOfOrNull { it.get() as Int }) + assertEquals(FullScore.MAX_CS_MANAGED_POLICY, + policies.maxOfOrNull { it.get() as Int }) + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java new file mode 100644 index 000000000000..70495cced536 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2016 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.connectivity; + +import static com.android.server.connectivity.MetricsTestUtil.aLong; +import static com.android.server.connectivity.MetricsTestUtil.aString; +import static com.android.server.connectivity.MetricsTestUtil.aType; +import static com.android.server.connectivity.MetricsTestUtil.anInt; +import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.BLUETOOTH; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.CELLULAR; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE; +import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.net.ConnectivityMetricsEvent; +import android.net.metrics.ApfProgramEvent; +import android.net.metrics.ApfStats; +import android.net.metrics.DefaultNetworkEvent; +import android.net.metrics.DhcpClientEvent; +import android.net.metrics.DhcpErrorEvent; +import android.net.metrics.IpManagerEvent; +import android.net.metrics.IpReachabilityEvent; +import android.net.metrics.NetworkEvent; +import android.net.metrics.RaEvent; +import android.net.metrics.ValidationProbeEvent; +import android.net.metrics.WakeupStats; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.List; + +// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConnectivityEventBuilderTest { + + @Test + public void testLinkLayerInferrence() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpReachabilityEvent.class), + anInt(IpReachabilityEvent.NUD_FAILED)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.netId = 123; + ev.transports = 3; // transports have priority for inferrence of link layer + ev.ifname = "wlan0"; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", MULTIPLE), + " network_id: 123", + " time_ms: 1", + " transports: 3", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.transports = 1; + ev.ifname = null; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", CELLULAR), + " network_id: 123", + " time_ms: 1", + " transports: 1", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.transports = 0; + ev.ifname = "not_inferred"; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"not_inferred\"", + " link_layer: 0", + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.ifname = "bt-pan"; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", BLUETOOTH), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.ifname = "rmnet_ipa0"; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", CELLULAR), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + + ev.ifname = "wlan0"; + want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + String.format(" link_layer: %d", WIFI), + " network_id: 123", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + verifySerialization(want, ev); + } + + @Test + public void testDefaultNetworkEventSerialization() { + DefaultNetworkEvent ev = new DefaultNetworkEvent(1001); + ev.netId = 102; + ev.transports = 2; + ev.previousTransports = 4; + ev.ipv4 = true; + ev.initialScore = 20; + ev.finalScore = 60; + ev.durationMs = 54; + ev.validatedMs = 27; + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 102", + " time_ms: 0", + " transports: 2", + " default_network_event <", + " default_network_duration_ms: 54", + " final_score: 60", + " initial_score: 20", + " ip_support: 1", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 1", + " previous_network_ip_support: 0", + " validation_duration_ms: 27", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, IpConnectivityEventBuilder.toProto(ev)); + } + + @Test + public void testDhcpClientEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DhcpClientEvent.class), + aString("SomeState"), + anInt(192)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " dhcp_event <", + " duration_ms: 192", + " if_name: \"\"", + " state_transition: \"SomeState\"", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testDhcpErrorEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(DhcpErrorEvent.class), + anInt(DhcpErrorEvent.L4_NOT_UDP)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " dhcp_event <", + " duration_ms: 0", + " if_name: \"\"", + " error_code: 50397184", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testIpManagerEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpManagerEvent.class), + anInt(IpManagerEvent.PROVISIONING_OK), + aLong(5678)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " ip_provisioning_event <", + " event_type: 1", + " if_name: \"\"", + " latency_ms: 5678", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testIpReachabilityEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(IpReachabilityEvent.class), + anInt(IpReachabilityEvent.NUD_FAILED)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testNetworkEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(NetworkEvent.class), + anInt(5), + aLong(20410)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " network_event <", + " event_type: 5", + " latency_ms: 20410", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testValidationProbeEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ValidationProbeEvent.class), + aLong(40730), + anInt(ValidationProbeEvent.PROBE_HTTP), + anInt(204)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " validation_probe_event <", + " latency_ms: 40730", + " probe_result: 204", + " probe_type: 1", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testApfProgramEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ApfProgramEvent.class), + aLong(200), + aLong(18), + anInt(7), + anInt(9), + anInt(2048), + anInt(3)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " apf_program_event <", + " current_ras: 9", + " drop_multicast: true", + " effective_lifetime: 18", + " filtered_ras: 7", + " has_ipv4_addr: true", + " lifetime: 200", + " program_length: 2048", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testApfStatsSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(ApfStats.class), + aLong(45000), + anInt(10), + anInt(2), + anInt(2), + anInt(1), + anInt(2), + anInt(4), + anInt(7), + anInt(3), + anInt(2048)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " apf_statistics <", + " dropped_ras: 2", + " duration_ms: 45000", + " matching_ras: 2", + " max_program_size: 2048", + " parse_errors: 2", + " program_updates: 4", + " program_updates_all: 7", + " program_updates_allowing_multicast: 3", + " received_ras: 10", + " total_packet_dropped: 0", + " total_packet_processed: 0", + " zero_lifetime_ras: 1", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testRaEventSerialization() { + ConnectivityMetricsEvent ev = describeIpEvent( + aType(RaEvent.class), + aLong(2000), + aLong(400), + aLong(300), + aLong(-1), + aLong(1000), + aLong(-1)); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 0", + " network_id: 0", + " time_ms: 1", + " transports: 0", + " ra_event <", + " dnssl_lifetime: -1", + " prefix_preferred_lifetime: 300", + " prefix_valid_lifetime: 400", + " rdnss_lifetime: 1000", + " route_info_lifetime: -1", + " router_lifetime: 2000", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, ev); + } + + @Test + public void testWakeupStatsSerialization() { + WakeupStats stats = new WakeupStats("wlan0"); + stats.totalWakeups = 14; + stats.applicationWakeups = 5; + stats.nonApplicationWakeups = 1; + stats.rootWakeups = 2; + stats.systemWakeups = 3; + stats.noUidWakeups = 3; + stats.l2UnicastCount = 5; + stats.l2MulticastCount = 1; + stats.l2BroadcastCount = 2; + stats.ethertypes.put(0x800, 3); + stats.ethertypes.put(0x86dd, 3); + stats.ipNextHeaders.put(6, 5); + + + IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats); + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " wakeup_stats <", + " application_wakeups: 5", + " duration_sec: 0", + " ethertype_counts <", + " key: 2048", + " value: 3", + " >", + " ethertype_counts <", + " key: 34525", + " value: 3", + " >", + " ip_next_header_counts <", + " key: 6", + " value: 5", + " >", + " l2_broadcast_count: 2", + " l2_multicast_count: 1", + " l2_unicast_count: 5", + " no_uid_wakeups: 3", + " non_application_wakeups: 1", + " root_wakeups: 2", + " system_wakeups: 3", + " total_wakeups: 14", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, got); + } + + static void verifySerialization(String want, ConnectivityMetricsEvent... input) { + List protoInput = + IpConnectivityEventBuilder.toProto(Arrays.asList(input)); + verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0])); + } + + static void verifySerialization(String want, IpConnectivityEvent... input) { + try { + byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input)); + IpConnectivityLog log = IpConnectivityLog.parseFrom(got); + assertEquals(want, log.toString()); + } catch (Exception e) { + fail(e.toString()); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java new file mode 100644 index 000000000000..8b072c49de82 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2016, 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.connectivity; + +import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; +import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityMetricsEvent; +import android.net.IIpConnectivityMetrics; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.RouteInfo; +import android.net.metrics.ApfProgramEvent; +import android.net.metrics.ApfStats; +import android.net.metrics.DhcpClientEvent; +import android.net.metrics.IpConnectivityLog; +import android.net.metrics.IpManagerEvent; +import android.net.metrics.IpReachabilityEvent; +import android.net.metrics.RaEvent; +import android.net.metrics.ValidationProbeEvent; +import android.os.Parcelable; +import android.system.OsConstants; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.BitUtils; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.PrintWriter; +import java.io.StringWriter; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConnectivityMetricsTest { + static final IpReachabilityEvent FAKE_EV = + new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); + + private static final String EXAMPLE_IPV4 = "192.0.2.1"; + private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; + + private static final byte[] MAC_ADDR = + {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b}; + + @Mock Context mCtx; + @Mock IIpConnectivityMetrics mMockService; + @Mock ConnectivityManager mCm; + + IpConnectivityMetrics mService; + NetdEventListenerService mNetdListener; + private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); + mNetdListener = new NetdEventListenerService(mCm); + mService.mNetdListener = mNetdListener; + } + + @Test + public void testBufferFlushing() { + String output1 = getdump("flush"); + assertEquals("", output1); + + new IpConnectivityLog(mService.impl).log(1, FAKE_EV); + String output2 = getdump("flush"); + assertFalse("".equals(output2)); + + String output3 = getdump("flush"); + assertEquals("", output3); + } + + @Test + public void testRateLimiting() { + final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); + final ApfProgramEvent ev = new ApfProgramEvent.Builder().build(); + final long fakeTimestamp = 1; + + int attempt = 100; // More than burst quota, but less than buffer size. + for (int i = 0; i < attempt; i++) { + logger.log(ev); + } + + String output1 = getdump("flush"); + assertFalse("".equals(output1)); + + for (int i = 0; i < attempt; i++) { + assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev)); + } + + String output2 = getdump("flush"); + assertEquals("", output2); + } + + private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai, + NetworkAgentInfo oldNai) { + final Network network = (nai != null) ? nai.network() : null; + final int score = (nai != null) ? nai.getCurrentScore() : 0; + final boolean validated = (nai != null) ? nai.lastValidated : false; + final LinkProperties lp = (nai != null) ? nai.linkProperties : null; + final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null; + + final Network prevNetwork = (oldNai != null) ? oldNai.network() : null; + final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0; + final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null; + final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null; + + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated, + lp, nc, prevNetwork, prevScore, prevLp, prevNc); + } + @Test + public void testDefaultNetworkEvents() throws Exception { + final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkAgentInfo[][] defaultNetworks = { + // nothing -> cell + {null, makeNai(100, 10, false, true, cell)}, + // cell -> wifi + {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)}, + // wifi -> nothing + {makeNai(101, 60, true, false, wifi), null}, + // nothing -> cell + {null, makeNai(102, 10, true, true, cell)}, + // cell -> wifi + {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)}, + }; + + long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; + long durationMs = 1001; + for (NetworkAgentInfo[] pair : defaultNetworks) { + timeMs += durationMs; + durationMs += durationMs; + logDefaultNetworkEvent(timeMs, pair[1], pair[0]); + } + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 5", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 1001", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 0", + " previous_network_ip_support: 0", + " validation_duration_ms: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 100", + " time_ms: 0", + " transports: 1", + " default_network_event <", + " default_network_duration_ms: 2002", + " final_score: 50", + " initial_score: 10", + " ip_support: 3", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 0", + " previous_network_ip_support: 0", + " validation_duration_ms: 2002", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 101", + " time_ms: 0", + " transports: 2", + " default_network_event <", + " default_network_duration_ms: 4004", + " final_score: 60", + " initial_score: 20", + " ip_support: 1", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 2", + " previous_network_ip_support: 0", + " validation_duration_ms: 4004", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 5", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 8008", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 4", + " previous_network_ip_support: 0", + " validation_duration_ms: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 102", + " time_ms: 0", + " transports: 1", + " default_network_event <", + " default_network_duration_ms: 16016", + " final_score: 50", + " initial_score: 10", + " ip_support: 3", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 4", + " previous_network_ip_support: 0", + " validation_duration_ms: 16016", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, getdump("flush")); + } + + @Test + public void testEndToEndLogging() throws Exception { + // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. + IpConnectivityLog logger = new IpConnectivityLog(mService.impl); + + ApfStats apfStats = new ApfStats.Builder() + .setDurationMs(45000) + .setReceivedRas(10) + .setMatchingRas(2) + .setDroppedRas(2) + .setParseErrors(2) + .setZeroLifetimeRas(1) + .setProgramUpdates(4) + .setProgramUpdatesAll(7) + .setProgramUpdatesAllowingMulticast(3) + .setMaxProgramSize(2048) + .build(); + + final ValidationProbeEvent validationEv = new ValidationProbeEvent.Builder() + .setDurationMs(40730) + .setProbeType(ValidationProbeEvent.PROBE_HTTP, true) + .setReturnCode(204) + .build(); + + final DhcpClientEvent event = new DhcpClientEvent.Builder() + .setMsg("SomeState") + .setDurationMs(192) + .build(); + Parcelable[] events = { + new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), event, + new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), + validationEv, + apfStats, + new RaEvent(2000, 400, 300, -1, 1000, -1) + }; + + for (int i = 0; i < events.length; i++) { + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = 100 * (i + 1); + ev.ifname = "wlan0"; + ev.data = events[i]; + logger.log(ev); + } + + // netId, errno, latency, destination + connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); + connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); + connectEvent(100, 0, 110, EXAMPLE_IPV4); + connectEvent(101, 0, 23, EXAMPLE_IPV4); + connectEvent(101, 0, 45, EXAMPLE_IPV6); + connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); + + // netId, type, return code, latency + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); + dnsEvent(100, EVENT_GETADDRINFO, 3, 45); + dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); + dnsEvent(101, EVENT_GETADDRINFO, 0, 56); + dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); + + // iface, uid + final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b}; + final String srcIp = "192.168.2.1"; + final String dstIp = "192.168.2.23"; + final int sport = 2356; + final int dport = 13489; + final long now = 1001L; + final int v4 = 0x800; + final int tcp = 6; + final int udp = 17; + wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); + wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); + wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); + wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); + wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); + wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); + + long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; + final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); + final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); + NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); + logDefaultNetworkEvent(timeMs + 200L, cellNai, null); + logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai); + + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 100", + " transports: 0", + " ip_reachability_event <", + " event_type: 512", + " if_name: \"\"", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 200", + " transports: 0", + " dhcp_event <", + " duration_ms: 192", + " if_name: \"\"", + " state_transition: \"SomeState\"", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 300", + " transports: 0", + " ip_provisioning_event <", + " event_type: 1", + " if_name: \"\"", + " latency_ms: 5678", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 400", + " transports: 0", + " validation_probe_event <", + " latency_ms: 40730", + " probe_result: 204", + " probe_type: 257", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 500", + " transports: 0", + " apf_statistics <", + " dropped_ras: 2", + " duration_ms: 45000", + " matching_ras: 2", + " max_program_size: 2048", + " parse_errors: 2", + " program_updates: 4", + " program_updates_all: 7", + " program_updates_allowing_multicast: 3", + " received_ras: 10", + " total_packet_dropped: 0", + " total_packet_processed: 0", + " zero_lifetime_ras: 1", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 600", + " transports: 0", + " ra_event <", + " dnssl_lifetime: -1", + " prefix_preferred_lifetime: 300", + " prefix_valid_lifetime: 400", + " rdnss_lifetime: 1000", + " route_info_lifetime: -1", + " router_lifetime: 2000", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 5", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " default_network_event <", + " default_network_duration_ms: 200", + " final_score: 0", + " initial_score: 0", + " ip_support: 0", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 0", + " previous_network_ip_support: 0", + " validation_duration_ms: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 100", + " time_ms: 0", + " transports: 1", + " default_network_event <", + " default_network_duration_ms: 100", + " final_score: 50", + " initial_score: 50", + " ip_support: 2", + " no_default_network_duration_ms: 0", + " previous_default_network_link_layer: 0", + " previous_network_ip_support: 0", + " validation_duration_ms: 100", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 1", + " connect_count: 3", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 110", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 2", + " connect_count: 2", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " getaddrinfo_error_count: 0", + " getaddrinfo_query_count: 0", + " gethostbyname_error_count: 0", + " gethostbyname_query_count: 0", + " latencies_ms: 3456", + " latencies_ms: 45", + " latencies_ms: 638", + " return_codes: 0", + " return_codes: 3", + " return_codes: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 2", + " getaddrinfo_error_count: 0", + " getaddrinfo_query_count: 0", + " gethostbyname_error_count: 0", + " gethostbyname_query_count: 0", + " latencies_ms: 56", + " latencies_ms: 34", + " return_codes: 0", + " return_codes: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " wakeup_stats <", + " application_wakeups: 3", + " duration_sec: 0", + " ethertype_counts <", + " key: 2048", + " value: 6", + " >", + " ip_next_header_counts <", + " key: 6", + " value: 3", + " >", + " ip_next_header_counts <", + " key: 17", + " value: 3", + " >", + " l2_broadcast_count: 0", + " l2_multicast_count: 0", + " l2_unicast_count: 6", + " no_uid_wakeups: 1", + " non_application_wakeups: 0", + " root_wakeups: 0", + " system_wakeups: 2", + " total_wakeups: 6", + " >", + ">", + "version: 2\n"); + + verifySerialization(want, getdump("flush")); + } + + String getdump(String ... command) { + StringWriter buffer = new StringWriter(); + PrintWriter writer = new PrintWriter(buffer); + mService.impl.dump(null, writer, command); + return buffer.toString(); + } + + private void setCapabilities(int netId) { + final ArgumentCaptor networkCallback = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); + networkCallback.getValue().onCapabilitiesChanged(new Network(netId), + netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); + } + + void connectEvent(int netId, int error, int latencyMs, String ipAddr) throws Exception { + setCapabilities(netId); + mNetdListener.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); + } + + void dnsEvent(int netId, int type, int result, int latency) throws Exception { + setCapabilities(netId); + mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); + } + + void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, + String dstIp, int sport, int dport, long now) throws Exception { + String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; + mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now); + } + + NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) { + NetworkAgentInfo nai = mock(NetworkAgentInfo.class); + when(nai.network()).thenReturn(new Network(netId)); + when(nai.getCurrentScore()).thenReturn(score); + nai.linkProperties = new LinkProperties(); + nai.networkCapabilities = new NetworkCapabilities(); + nai.lastValidated = true; + for (int t : BitUtils.unpackBits(transports)) { + nai.networkCapabilities.addTransportType(t); + } + if (ipv4) { + nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24")); + nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"))); + } + if (ipv6) { + nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64")); + nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0"))); + } + return nai; + } + + + + static void verifySerialization(String want, String output) { + try { + byte[] got = Base64.decode(output, Base64.DEFAULT); + IpConnectivityLogClass.IpConnectivityLog log = + IpConnectivityLogClass.IpConnectivityLog.parseFrom(got); + assertEquals(want, log.toString()); + } catch (Exception e) { + fail(e.toString()); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java new file mode 100644 index 000000000000..36e229d8aa73 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2016, 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.connectivity; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.ConnectivityResources; +import android.net.IDnsResolver; +import android.net.INetd; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkAgentConfig; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkProvider; +import android.net.NetworkScore; +import android.os.Binder; +import android.text.format.DateUtils; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.connectivity.resources.R; +import com.android.server.ConnectivityService; +import com.android.server.connectivity.NetworkNotificationManager.NotificationType; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LingerMonitorTest { + static final String CELLULAR = "CELLULAR"; + static final String WIFI = "WIFI"; + + static final long LOW_RATE_LIMIT = DateUtils.MINUTE_IN_MILLIS; + static final long HIGH_RATE_LIMIT = 0; + + static final int LOW_DAILY_LIMIT = 2; + static final int HIGH_DAILY_LIMIT = 1000; + + private static final int TEST_LINGER_DELAY_MS = 400; + + LingerMonitor mMonitor; + + @Mock ConnectivityService mConnService; + @Mock IDnsResolver mDnsResolver; + @Mock INetd mNetd; + @Mock Context mCtx; + @Mock NetworkNotificationManager mNotifier; + @Mock Resources mResources; + @Mock QosCallbackTracker mQosCallbackTracker; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mCtx.getResources()).thenReturn(mResources); + when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity"); + ConnectivityResources.setResourcesContextForTest(mCtx); + + mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT); + } + + @After + public void tearDown() { + ConnectivityResources.setResourcesContextForTest(null); + } + + @Test + public void testTransitions() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + NetworkAgentInfo nai1 = wifiNai(100); + NetworkAgentInfo nai2 = cellNai(101); + + assertTrue(mMonitor.isNotificationEnabled(nai1, nai2)); + assertFalse(mMonitor.isNotificationEnabled(nai2, nai1)); + } + + @Test + public void testNotificationOnLinger() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNotification(from, to); + } + + @Test + public void testToastOnLinger() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyToast(from, to); + } + + @Test + public void testNotificationClearedAfterDisconnect() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNotification(from, to); + + mMonitor.noteDisconnect(to); + verify(mNotifier, times(1)).clearNotification(100); + } + + @Test + public void testNotificationClearedAfterSwitchingBack() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNotification(from, to); + + mMonitor.noteLingerDefaultNetwork(to, from); + verify(mNotifier, times(1)).clearNotification(100); + } + + @Test + public void testUniqueToast() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyToast(from, to); + + mMonitor.noteLingerDefaultNetwork(to, from); + verify(mNotifier, times(1)).clearNotification(100); + + reset(mNotifier); + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + @Test + public void testMultipleNotifications() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo wifi1 = wifiNai(100); + NetworkAgentInfo wifi2 = wifiNai(101); + NetworkAgentInfo cell = cellNai(102); + + mMonitor.noteLingerDefaultNetwork(wifi1, cell); + verifyNotification(wifi1, cell); + + mMonitor.noteLingerDefaultNetwork(cell, wifi2); + verify(mNotifier, times(1)).clearNotification(100); + + reset(mNotifier); + mMonitor.noteLingerDefaultNetwork(wifi2, cell); + verifyNotification(wifi2, cell); + } + + @Test + public void testRateLimiting() throws InterruptedException { + mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, LOW_RATE_LIMIT); + + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo wifi1 = wifiNai(100); + NetworkAgentInfo wifi2 = wifiNai(101); + NetworkAgentInfo wifi3 = wifiNai(102); + NetworkAgentInfo cell = cellNai(103); + + mMonitor.noteLingerDefaultNetwork(wifi1, cell); + verifyNotification(wifi1, cell); + reset(mNotifier); + + Thread.sleep(50); + mMonitor.noteLingerDefaultNetwork(cell, wifi2); + mMonitor.noteLingerDefaultNetwork(wifi2, cell); + verifyNoNotifications(); + + Thread.sleep(50); + mMonitor.noteLingerDefaultNetwork(cell, wifi3); + mMonitor.noteLingerDefaultNetwork(wifi3, cell); + verifyNoNotifications(); + } + + @Test + public void testDailyLimiting() throws InterruptedException { + mMonitor = new TestableLingerMonitor(mCtx, mNotifier, LOW_DAILY_LIMIT, HIGH_RATE_LIMIT); + + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo wifi1 = wifiNai(100); + NetworkAgentInfo wifi2 = wifiNai(101); + NetworkAgentInfo wifi3 = wifiNai(102); + NetworkAgentInfo cell = cellNai(103); + + mMonitor.noteLingerDefaultNetwork(wifi1, cell); + verifyNotification(wifi1, cell); + reset(mNotifier); + + Thread.sleep(50); + mMonitor.noteLingerDefaultNetwork(cell, wifi2); + mMonitor.noteLingerDefaultNetwork(wifi2, cell); + verifyNotification(wifi2, cell); + reset(mNotifier); + + Thread.sleep(50); + mMonitor.noteLingerDefaultNetwork(cell, wifi3); + mMonitor.noteLingerDefaultNetwork(wifi3, cell); + verifyNoNotifications(); + } + + @Test + public void testUniqueNotification() { + setNotificationSwitch(transition(WIFI, CELLULAR)); + setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNotification(from, to); + + mMonitor.noteLingerDefaultNetwork(to, from); + verify(mNotifier, times(1)).clearNotification(100); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNotification(from, to); + } + + @Test + public void testIgnoreNeverValidatedNetworks() { + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + setNotificationSwitch(transition(WIFI, CELLULAR)); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + from.everValidated = false; + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + @Test + public void testIgnoreCurrentlyValidatedNetworks() { + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + setNotificationSwitch(transition(WIFI, CELLULAR)); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + from.lastValidated = true; + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + @Test + public void testNoNotificationType() { + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + setNotificationSwitch(); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + @Test + public void testNoTransitionToNotify() { + setNotificationType(LingerMonitor.NOTIFY_TYPE_NONE); + setNotificationSwitch(transition(WIFI, CELLULAR)); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + @Test + public void testDifferentTransitionToNotify() { + setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); + setNotificationSwitch(transition(CELLULAR, WIFI)); + NetworkAgentInfo from = wifiNai(100); + NetworkAgentInfo to = cellNai(101); + + mMonitor.noteLingerDefaultNetwork(from, to); + verifyNoNotifications(); + } + + void setNotificationSwitch(String... transitions) { + when(mResources.getStringArray(R.array.config_networkNotifySwitches)) + .thenReturn(transitions); + } + + String transition(String from, String to) { + return from + "-" + to; + } + + void setNotificationType(int type) { + when(mResources.getInteger(R.integer.config_networkNotifySwitchType)).thenReturn(type); + } + + void verifyNoToast() { + verify(mNotifier, never()).showToast(any(), any()); + } + + void verifyNoNotification() { + verify(mNotifier, never()) + .showNotification(anyInt(), any(), any(), any(), any(), anyBoolean()); + } + + void verifyNoNotifications() { + verifyNoToast(); + verifyNoNotification(); + } + + void verifyToast(NetworkAgentInfo from, NetworkAgentInfo to) { + verifyNoNotification(); + verify(mNotifier, times(1)).showToast(from, to); + } + + void verifyNotification(NetworkAgentInfo from, NetworkAgentInfo to) { + verifyNoToast(); + verify(mNotifier, times(1)).showNotification(eq(from.network.netId), + eq(NotificationType.NETWORK_SWITCH), eq(from), eq(to), any(), eq(true)); + } + + NetworkAgentInfo nai(int netId, int transport, int networkType, String networkTypeName) { + NetworkInfo info = new NetworkInfo(networkType, 0, networkTypeName, ""); + NetworkCapabilities caps = new NetworkCapabilities(); + caps.addCapability(0); + caps.addTransportType(transport); + NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, + new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), + mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, + mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS, + mQosCallbackTracker, new ConnectivityService.Dependencies()); + nai.everValidated = true; + return nai; + } + + NetworkAgentInfo wifiNai(int netId) { + return nai(netId, NetworkCapabilities.TRANSPORT_WIFI, + ConnectivityManager.TYPE_WIFI, WIFI); + } + + NetworkAgentInfo cellNai(int netId) { + return nai(netId, NetworkCapabilities.TRANSPORT_CELLULAR, + ConnectivityManager.TYPE_MOBILE, CELLULAR); + } + + public static class TestableLingerMonitor extends LingerMonitor { + public TestableLingerMonitor(Context c, NetworkNotificationManager n, int l, long r) { + super(c, n, l, r); + } + @Override protected PendingIntent createNotificationIntent() { + return null; + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java new file mode 100644 index 000000000000..5064b9bd91b9 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 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.connectivity; + +import android.net.ConnectivityMetricsEvent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.function.Consumer; + +abstract public class MetricsTestUtil { + private MetricsTestUtil() { + } + + static ConnectivityMetricsEvent ev(Parcelable p) { + ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); + ev.timestamp = 1L; + ev.data = p; + return ev; + } + + static ConnectivityMetricsEvent describeIpEvent(Consumer... fs) { + Parcel p = Parcel.obtain(); + for (Consumer f : fs) { + f.accept(p); + } + p.setDataPosition(0); + return ev(p.readParcelable(ClassLoader.getSystemClassLoader())); + } + + static Consumer aType(Class c) { + return aString(c.getName()); + } + + static Consumer aBool(boolean b) { + return aByte((byte) (b ? 1 : 0)); + } + + static Consumer aByte(byte b) { + return (p) -> p.writeByte(b); + } + + static Consumer anInt(int i) { + return (p) -> p.writeInt(i); + } + + static Consumer aLong(long l) { + return (p) -> p.writeLong(l); + } + + static Consumer aString(String s) { + return (p) -> p.writeString(s); + } + + static Consumer aByteArray(byte... ary) { + return (p) -> p.writeByteArray(ary); + } + + static Consumer anIntArray(int... ary) { + return (p) -> p.writeIntArray(ary); + } + + static byte b(int i) { + return (byte) i; + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java new file mode 100644 index 000000000000..38f6d7f3172d --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2018 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.connectivity; + +import static android.content.Intent.ACTION_CONFIGURATION_CHANGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkPolicy.LIMIT_DISABLED; +import static android.net.NetworkPolicy.SNOOZE_NEVER; +import static android.net.NetworkPolicy.WARNING_DISABLED; +import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; + +import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; +import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; + +import static junit.framework.TestCase.assertNotNull; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.NetworkStatsManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.EthernetNetworkSpecifier; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; +import android.net.NetworkTemplate; +import android.net.TelephonyNetworkSpecifier; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.test.mock.MockContentResolver; +import android.util.DataUnit; +import android.util.RecurrenceRule; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.LocalServices; +import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.net.NetworkStatsManagerInternal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.time.Clock; +import java.time.Instant; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MultipathPolicyTrackerTest { + private static final Network TEST_NETWORK = new Network(123); + private static final int POLICY_SNOOZED = -100; + + @Mock private Context mContext; + @Mock private Context mUserAllContext; + @Mock private Resources mResources; + @Mock private Handler mHandler; + @Mock private MultipathPolicyTracker.Dependencies mDeps; + @Mock private Clock mClock; + @Mock private ConnectivityManager mCM; + @Mock private NetworkPolicyManager mNPM; + @Mock private NetworkStatsManager mStatsManager; + @Mock private NetworkPolicyManagerInternal mNPMI; + @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal; + @Mock private TelephonyManager mTelephonyManager; + private MockContentResolver mContentResolver; + + private ArgumentCaptor mConfigChangeReceiverCaptor; + + private MultipathPolicyTracker mTracker; + + private Clock mPreviousRecurrenceRuleClock; + private boolean mRecurrenceRuleClockMocked; + + private void mockService(String serviceName, Class serviceClass, T service) { + when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName); + when(mContext.getSystemService(serviceName)).thenReturn(service); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mPreviousRecurrenceRuleClock = RecurrenceRule.sClock; + RecurrenceRule.sClock = mClock; + mRecurrenceRuleClockMocked = true; + + mConfigChangeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); + + when(mContext.getResources()).thenReturn(mResources); + when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); + // Mock user id to all users that Context#registerReceiver will register with all users too. + doReturn(UserHandle.ALL.getIdentifier()).when(mUserAllContext).getUserId(); + when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())) + .thenReturn(mUserAllContext); + when(mUserAllContext.registerReceiver(mConfigChangeReceiverCaptor.capture(), + argThat(f -> f.hasAction(ACTION_CONFIGURATION_CHANGED)), any(), any())) + .thenReturn(null); + + when(mDeps.getClock()).thenReturn(mClock); + + when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + + mContentResolver = Mockito.spy(new MockContentResolver(mContext)); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + Settings.Global.clearProviderForTest(); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + + mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, mCM); + mockService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, mNPM); + mockService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, mStatsManager); + mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager); + + LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); + LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI); + + LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class); + LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal); + + mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps); + } + + @After + public void tearDown() { + // Avoid setting static clock to null (which should normally not be the case) + // if MockitoAnnotations.initMocks threw an exception + if (mRecurrenceRuleClockMocked) { + RecurrenceRule.sClock = mPreviousRecurrenceRuleClock; + } + mRecurrenceRuleClockMocked = false; + } + + private void setDefaultQuotaGlobalSetting(long setting) { + Settings.Global.putInt(mContentResolver, NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, + (int) setting); + } + + private void testGetMultipathPreference( + long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit, + long defaultGlobalSetting, long defaultResSetting, boolean roaming) { + + // TODO: tests should not use ZoneId.systemDefault() once code handles TZ correctly. + final ZonedDateTime now = ZonedDateTime.ofInstant( + Instant.parse("2017-04-02T10:11:12Z"), ZoneId.systemDefault()); + final ZonedDateTime startOfDay = now.truncatedTo(ChronoUnit.DAYS); + when(mClock.millis()).thenReturn(now.toInstant().toEpochMilli()); + when(mClock.instant()).thenReturn(now.toInstant()); + when(mClock.getZone()).thenReturn(ZoneId.systemDefault()); + + // Setup plan quota + when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH)) + .thenReturn(subscriptionQuota); + + // Setup user policy warning / limit + if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) { + final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z"); + final RecurrenceRule recurrenceRule = new RecurrenceRule( + ZonedDateTime.ofInstant( + recurrenceStart, + ZoneId.systemDefault()), + null /* end */, + Period.ofMonths(1)); + final boolean snoozeWarning = policyWarning == POLICY_SNOOZED; + final boolean snoozeLimit = policyLimit == POLICY_SNOOZED; + when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] { + new NetworkPolicy( + NetworkTemplate.buildTemplateMobileWildcard(), + recurrenceRule, + snoozeWarning ? 0 : policyWarning, + snoozeLimit ? 0 : policyLimit, + snoozeWarning ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER, + snoozeLimit ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER, + SNOOZE_NEVER, + true /* metered */, + false /* inferred */) + }); + } else { + when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); + } + + // Setup default quota in settings and resources + if (defaultGlobalSetting > 0) { + setDefaultQuotaGlobalSetting(defaultGlobalSetting); + } + when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes)) + .thenReturn((int) defaultResSetting); + + when(mNetworkStatsManagerInternal.getNetworkTotalBytes( + any(), + eq(startOfDay.toInstant().toEpochMilli()), + eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday); + + ArgumentCaptor networkCallback = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + mTracker.start(); + verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any()); + + // Simulate callback after capability changes + NetworkCapabilities capabilities = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addTransportType(TRANSPORT_CELLULAR) + .setNetworkSpecifier(new EthernetNetworkSpecifier("eth234")); + if (!roaming) { + capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING); + } + networkCallback.getValue().onCapabilitiesChanged( + TEST_NETWORK, + capabilities); + + // make sure it also works with the new introduced TelephonyNetworkSpecifier + capabilities = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addTransportType(TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(234).build()); + if (!roaming) { + capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING); + } + networkCallback.getValue().onCapabilitiesChanged( + TEST_NETWORK, + capabilities); + } + + @Test + public void testGetMultipathPreference_SubscriptionQuota() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, + DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */, + DataUnit.MEGABYTES.toBytes(100) /* policyWarning */, + LIMIT_DISABLED, + DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, + 2_500_000 /* defaultResSetting */, + false /* roaming */); + + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); + } + + @Test + public void testGetMultipathPreference_UserWarningQuota() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, + OPPORTUNISTIC_QUOTA_UNKNOWN, + // 29 days from Apr. 2nd to May 1st + DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */, + LIMIT_DISABLED, + DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, + 2_500_000 /* defaultResSetting */, + false /* roaming */); + + // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); + } + + @Test + public void testGetMultipathPreference_SnoozedWarningQuota() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, + OPPORTUNISTIC_QUOTA_UNKNOWN, + // 29 days from Apr. 2nd to May 1st + POLICY_SNOOZED /* policyWarning */, + DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */, + DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, + 2_500_000 /* defaultResSetting */, + false /* roaming */); + + // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); + } + + @Test + public void testGetMultipathPreference_SnoozedBothQuota() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, + OPPORTUNISTIC_QUOTA_UNKNOWN, + // 29 days from Apr. 2nd to May 1st + POLICY_SNOOZED /* policyWarning */, + POLICY_SNOOZED /* policyLimit */, + DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, + 2_500_000 /* defaultResSetting */, + false /* roaming */); + + // Default global setting should be used: 12 - 7 = 5 + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any()); + } + + @Test + public void testGetMultipathPreference_SettingChanged() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, + OPPORTUNISTIC_QUOTA_UNKNOWN, + WARNING_DISABLED, + LIMIT_DISABLED, + -1 /* defaultGlobalSetting */, + DataUnit.MEGABYTES.toBytes(10) /* defaultResSetting */, + false /* roaming */); + + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); + + // Update setting + setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14)); + mTracker.mSettingsObserver.onChange( + false, Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES)); + + // Callback must have been re-registered with new setting + verify(mStatsManager, times(1)).unregisterUsageCallback(any()); + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); + } + + @Test + public void testGetMultipathPreference_ResourceChanged() { + testGetMultipathPreference( + DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, + OPPORTUNISTIC_QUOTA_UNKNOWN, + WARNING_DISABLED, + LIMIT_DISABLED, + -1 /* defaultGlobalSetting */, + DataUnit.MEGABYTES.toBytes(14) /* defaultResSetting */, + false /* roaming */); + + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); + + when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes)) + .thenReturn((int) DataUnit.MEGABYTES.toBytes(16)); + + final BroadcastReceiver configChangeReceiver = mConfigChangeReceiverCaptor.getValue(); + assertNotNull(configChangeReceiver); + configChangeReceiver.onReceive(mContext, new Intent()); + + // Uses the new setting (16 - 2 = 14MB) + verify(mStatsManager, times(1)).registerUsageCallback( + any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any()); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java new file mode 100644 index 000000000000..9b2a638f8b39 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2017 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.connectivity; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.net.ConnectivityManager; +import android.net.IDnsResolver; +import android.net.INetd; +import android.net.InterfaceConfigurationParcel; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.NetworkAgentConfig; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.Handler; +import android.os.test.TestLooper; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.ConnectivityService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class Nat464XlatTest { + + static final String BASE_IFACE = "test0"; + static final String STACKED_IFACE = "v4-test0"; + static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64"); + static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); + static final String NAT64_PREFIX = "64:ff9b::/96"; + static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96"; + static final int NETID = 42; + + @Mock ConnectivityService mConnectivity; + @Mock IDnsResolver mDnsResolver; + @Mock INetd mNetd; + @Mock NetworkAgentInfo mNai; + + TestLooper mLooper; + Handler mHandler; + NetworkAgentConfig mAgentConfig = new NetworkAgentConfig(); + + Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) { + return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) { + @Override protected int getNetId() { + return NETID; + } + + @Override protected boolean isCellular464XlatEnabled() { + return isCellular464XlatEnabled; + } + }; + } + + private void markNetworkConnected() { + mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", ""); + } + + private void markNetworkDisconnected() { + mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", ""); + } + + @Before + public void setUp() throws Exception { + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); + + MockitoAnnotations.initMocks(this); + + mNai.linkProperties = new LinkProperties(); + mNai.linkProperties.setInterfaceName(BASE_IFACE); + mNai.networkInfo = new NetworkInfo(null); + mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); + mNai.networkCapabilities = new NetworkCapabilities(); + markNetworkConnected(); + when(mNai.connService()).thenReturn(mConnectivity); + when(mNai.netAgentConfig()).thenReturn(mAgentConfig); + when(mNai.handler()).thenReturn(mHandler); + final InterfaceConfigurationParcel mConfig = new InterfaceConfigurationParcel(); + when(mNetd.interfaceGetCfg(eq(STACKED_IFACE))).thenReturn(mConfig); + mConfig.ipv4Addr = ADDR.getAddress().getHostAddress(); + mConfig.prefixLength = ADDR.getPrefixLength(); + } + + private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) { + Nat464Xlat nat = makeNat464Xlat(true); + String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b " + + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), + nai.networkInfo.getDetailedState(), + mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), + nai.linkProperties.getLinkAddresses()); + assertEquals(msg, expected, nat.requiresClat(nai)); + } + + private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) { + Nat464Xlat nat = makeNat464Xlat(true); + String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b " + + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), + nai.networkInfo.getDetailedState(), + mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), + nai.linkProperties.getLinkAddresses()); + assertEquals(msg, expected, nat.shouldStartClat(nai)); + } + + @Test + public void testRequiresClat() throws Exception { + final int[] supportedTypes = { + ConnectivityManager.TYPE_MOBILE, + ConnectivityManager.TYPE_WIFI, + ConnectivityManager.TYPE_ETHERNET, + }; + + // NetworkInfo doesn't allow setting the State directly, but rather + // requires setting DetailedState in order set State as a side-effect. + final NetworkInfo.DetailedState[] supportedDetailedStates = { + NetworkInfo.DetailedState.CONNECTED, + NetworkInfo.DetailedState.SUSPENDED, + }; + + LinkProperties oldLp = new LinkProperties(mNai.linkProperties); + for (int type : supportedTypes) { + mNai.networkInfo.setType(type); + for (NetworkInfo.DetailedState state : supportedDetailedStates) { + mNai.networkInfo.setDetailedState(state, "reason", "extraInfo"); + + mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX)); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64")); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); + + mAgentConfig.skip464xlat = true; + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mAgentConfig.skip464xlat = false; + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24")); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24")); + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); + + mNai.linkProperties.setNat64Prefix(null); + assertRequiresClat(true, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties = new LinkProperties(oldLp); + } + } + } + + private void makeClatUnnecessary(boolean dueToDisconnect) { + if (dueToDisconnect) { + markNetworkDisconnected(); + } else { + mNai.linkProperties.addLinkAddress(ADDR); + } + } + + private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception { + Nat464Xlat nat = makeNat464Xlat(true); + ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); + + mNai.linkProperties.addLinkAddress(V6ADDR); + + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); + + // Start clat. + nat.start(); + + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE)); + verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // Stop clat (Network disconnects, IPv4 addr appears, ...). + makeClatUnnecessary(dueToDisconnect); + nat.stop(); + + verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); + assertIdle(nat); + + // Stacked interface removed notification arrives and is ignored. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + verifyNoMoreInteractions(mNetd, mConnectivity); + } + + @Test + public void testNormalStartAndStopDueToDisconnect() throws Exception { + checkNormalStartAndStop(true); + } + + @Test + public void testNormalStartAndStopDueToIpv4Addr() throws Exception { + checkNormalStartAndStop(false); + } + + private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception { + Nat464Xlat nat = makeNat464Xlat(true); + ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); + InOrder inOrder = inOrder(mNetd, mConnectivity); + + mNai.linkProperties.addLinkAddress(V6ADDR); + + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); + + nat.start(); + + inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). + nat.stop(); + + inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); + + inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + if (interfaceRemovedFirst) { + // Stacked interface removed notification arrives and is ignored. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + nat.interfaceLinkStateChanged(STACKED_IFACE, false); + mLooper.dispatchNext(); + } + + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + inOrder.verifyNoMoreInteractions(); + + nat.start(); + + inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + if (!interfaceRemovedFirst) { + // Stacked interface removed notification arrives and is ignored. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + nat.interfaceLinkStateChanged(STACKED_IFACE, false); + mLooper.dispatchNext(); + } + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // ConnectivityService stops clat again. + nat.stop(); + + inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); + + inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testStartStopStart() throws Exception { + checkStartStopStart(true); + } + + @Test + public void testStartStopStartBeforeInterfaceRemoved() throws Exception { + checkStartStopStart(false); + } + + @Test + public void testClatdCrashWhileRunning() throws Exception { + Nat464Xlat nat = makeNat464Xlat(true); + ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); + + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); + + nat.start(); + + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE)); + verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // Stacked interface removed notification arrives (clatd crashed, ...). + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + // ConnectivityService stops clat: no-op. + nat.stop(); + + verifyNoMoreInteractions(mNetd, mConnectivity); + } + + private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception { + Nat464Xlat nat = makeNat464Xlat(true); + + mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); + + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); + + nat.start(); + + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) + makeClatUnnecessary(dueToDisconnect); + nat.stop(); + + verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); + assertIdle(nat); + + // In-flight interface up notification arrives: no-op + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + // Interface removed notification arrives after stopClatd() takes effect: no-op. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + assertIdle(nat); + + verifyNoMoreInteractions(mNetd, mConnectivity); + } + + @Test + public void testStopDueToDisconnectBeforeClatdStarts() throws Exception { + checkStopBeforeClatdStarts(true); + } + + @Test + public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception { + checkStopBeforeClatdStarts(false); + } + + private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception { + Nat464Xlat nat = makeNat464Xlat(true); + + mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); + + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); + + nat.start(); + + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) + makeClatUnnecessary(dueToDisconnect); + nat.stop(); + + verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); + assertIdle(nat); + + verifyNoMoreInteractions(mNetd, mConnectivity); + } + + @Test + public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception { + checkStopAndClatdNeverStarts(true); + } + + @Test + public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception { + checkStopAndClatdNeverStarts(false); + } + + @Test + public void testNat64PrefixPreference() throws Exception { + final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX); + final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX); + + Nat464Xlat nat = makeNat464Xlat(true); + + final LinkProperties emptyLp = new LinkProperties(); + LinkProperties fixedupLp; + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromDns(prefixFromDns); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(prefixFromDns, fixedupLp.getNat64Prefix()); + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromRa(prefixFromRa); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromRa(null); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(prefixFromDns, fixedupLp.getNat64Prefix()); + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromRa(prefixFromRa); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromDns(null); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); + + fixedupLp = new LinkProperties(); + nat.setNat64PrefixFromRa(null); + nat.fixupLinkProperties(emptyLp, fixedupLp); + assertEquals(null, fixedupLp.getNat64Prefix()); + } + + private void checkClatDisabledOnCellular(boolean onCellular) throws Exception { + // Disable 464xlat on cellular networks. + Nat464Xlat nat = makeNat464Xlat(false); + mNai.linkProperties.addLinkAddress(V6ADDR); + mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular); + nat.update(); + + final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX); + if (onCellular) { + // Prefix discovery is never started. + verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID)); + assertIdle(nat); + + // If a NAT64 prefix comes in from an RA, clat is not started either. + mNai.linkProperties.setNat64Prefix(nat64Prefix); + nat.setNat64PrefixFromRa(nat64Prefix); + nat.update(); + verify(mNetd, never()).clatdStart(anyString(), anyString()); + assertIdle(nat); + } else { + // Prefix discovery is started. + verify(mDnsResolver).startPrefix64Discovery(eq(NETID)); + assertIdle(nat); + + // If a NAT64 prefix comes in from an RA, clat is started. + mNai.linkProperties.setNat64Prefix(nat64Prefix); + nat.setNat64PrefixFromRa(nat64Prefix); + nat.update(); + verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX); + assertStarting(nat); + } + } + + @Test + public void testClatDisabledOnCellular() throws Exception { + checkClatDisabledOnCellular(true); + } + + @Test + public void testClatDisabledOnNonCellular() throws Exception { + checkClatDisabledOnCellular(false); + } + + static void assertIdle(Nat464Xlat nat) { + assertTrue("Nat464Xlat was not IDLE", !nat.isStarted()); + } + + static void assertStarting(Nat464Xlat nat) { + assertTrue("Nat464Xlat was not STARTING", nat.isStarting()); + } + + static void assertRunning(Nat464Xlat nat) { + assertTrue("Nat464Xlat was not RUNNING", nat.isRunning()); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java new file mode 100644 index 000000000000..50aaaee24418 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2016, 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.connectivity; + +import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; +import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; + +import static com.android.testutils.MiscAsserts.assertStringContains; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.system.OsConstants; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetdEventListenerServiceTest { + private static final String EXAMPLE_IPV4 = "192.0.2.1"; + private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; + + private static final byte[] MAC_ADDR = + {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b}; + + NetdEventListenerService mService; + ConnectivityManager mCm; + private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + @Before + public void setUp() { + mCm = mock(ConnectivityManager.class); + mService = new NetdEventListenerService(mCm); + } + + @Test + public void testWakeupEventLogging() throws Exception { + final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH; + final long now = System.currentTimeMillis(); + final String iface = "wlan0"; + final byte[] mac = MAC_ADDR; + final String srcIp = "192.168.2.1"; + final String dstIp = "192.168.2.23"; + final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4"; + final String dstIp6 = "2001:db8:4006:807::200a"; + final int sport = 2356; + final int dport = 13489; + + final int v4 = 0x800; + final int v6 = 0x86dd; + final int tcp = 6; + final int udp = 17; + final int icmp6 = 58; + + // Baseline without any event + String[] baseline = listNetdEvent(); + + int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004}; + wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + + String[] events2 = remove(listNetdEvent(), baseline); + int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line + assertEquals(expectedLength2, events2.length); + assertStringContains(events2[0], "WakeupStats"); + assertStringContains(events2[0], "wlan0"); + assertStringContains(events2[0], "0x800"); + assertStringContains(events2[0], "0x86dd"); + for (int i = 0; i < uids.length; i++) { + String got = events2[i+1]; + assertStringContains(got, "WakeupEvent"); + assertStringContains(got, "wlan0"); + assertStringContains(got, "uid: " + uids[i]); + } + + int uid = 20000; + for (int i = 0; i < BUFFER_LENGTH * 2; i++) { + long ts = now + 10; + wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts); + } + + String[] events3 = remove(listNetdEvent(), baseline); + int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line + assertEquals(expectedLength3, events3.length); + assertStringContains(events2[0], "WakeupStats"); + assertStringContains(events2[0], "wlan0"); + for (int i = 1; i < expectedLength3; i++) { + String got = events3[i]; + assertStringContains(got, "WakeupEvent"); + assertStringContains(got, "wlan0"); + assertStringContains(got, "uid: " + uid); + } + + uid = 45678; + wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now); + + String[] events4 = remove(listNetdEvent(), baseline); + String lastEvent = events4[events4.length - 1]; + assertStringContains(lastEvent, "WakeupEvent"); + assertStringContains(lastEvent, "wlan0"); + assertStringContains(lastEvent, "uid: " + uid); + } + + @Test + public void testWakeupStatsLogging() throws Exception { + final byte[] mac = MAC_ADDR; + final String srcIp = "192.168.2.1"; + final String dstIp = "192.168.2.23"; + final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4"; + final String dstIp6 = "2404:6800:4006:807::200a"; + final int sport = 2356; + final int dport = 13489; + final long now = 1001L; + + final int v4 = 0x800; + final int v6 = 0x86dd; + final int tcp = 6; + final int udp = 17; + final int icmp6 = 58; + + wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now); + wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); + wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now); + + String got = flushStatistics(); + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " wakeup_stats <", + " application_wakeups: 3", + " duration_sec: 0", + " ethertype_counts <", + " key: 2048", + " value: 4", + " >", + " ethertype_counts <", + " key: 34525", + " value: 1", + " >", + " ip_next_header_counts <", + " key: 6", + " value: 5", + " >", + " l2_broadcast_count: 0", + " l2_multicast_count: 0", + " l2_unicast_count: 5", + " no_uid_wakeups: 0", + " non_application_wakeups: 0", + " root_wakeups: 0", + " system_wakeups: 2", + " total_wakeups: 5", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 0", + " time_ms: 0", + " transports: 0", + " wakeup_stats <", + " application_wakeups: 2", + " duration_sec: 0", + " ethertype_counts <", + " key: 2048", + " value: 5", + " >", + " ethertype_counts <", + " key: 34525", + " value: 5", + " >", + " ip_next_header_counts <", + " key: 6", + " value: 3", + " >", + " ip_next_header_counts <", + " key: 17", + " value: 5", + " >", + " ip_next_header_counts <", + " key: 58", + " value: 2", + " >", + " l2_broadcast_count: 0", + " l2_multicast_count: 0", + " l2_unicast_count: 10", + " no_uid_wakeups: 2", + " non_application_wakeups: 1", + " root_wakeups: 2", + " system_wakeups: 3", + " total_wakeups: 10", + " >", + ">", + "version: 2\n"); + assertEquals(want, got); + } + + @Test + public void testDnsLogging() throws Exception { + asyncDump(100); + + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); + dnsEvent(100, EVENT_GETADDRINFO, 0, 267); + dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230); + dnsEvent(100, EVENT_GETADDRINFO, 3, 45); + dnsEvent(100, EVENT_GETADDRINFO, 1, 2111); + dnsEvent(100, EVENT_GETADDRINFO, 0, 450); + dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638); + dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300); + dnsEvent(101, EVENT_GETADDRINFO, 0, 56); + dnsEvent(101, EVENT_GETADDRINFO, 0, 78); + dnsEvent(101, EVENT_GETADDRINFO, 0, 14); + dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56); + dnsEvent(101, EVENT_GETADDRINFO, 0, 78); + dnsEvent(101, EVENT_GETADDRINFO, 0, 14); + + String got = flushStatistics(); + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " event_types: 1", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " event_types: 2", + " getaddrinfo_error_count: 0", + " getaddrinfo_query_count: 0", + " gethostbyname_error_count: 0", + " gethostbyname_query_count: 0", + " latencies_ms: 3456", + " latencies_ms: 267", + " latencies_ms: 1230", + " latencies_ms: 45", + " latencies_ms: 2111", + " latencies_ms: 450", + " latencies_ms: 638", + " latencies_ms: 1300", + " return_codes: 0", + " return_codes: 0", + " return_codes: 22", + " return_codes: 3", + " return_codes: 1", + " return_codes: 0", + " return_codes: 200", + " return_codes: 178", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " event_types: 1", + " event_types: 1", + " getaddrinfo_error_count: 0", + " getaddrinfo_query_count: 0", + " gethostbyname_error_count: 0", + " gethostbyname_query_count: 0", + " latencies_ms: 56", + " latencies_ms: 78", + " latencies_ms: 14", + " latencies_ms: 56", + " latencies_ms: 78", + " latencies_ms: 14", + " return_codes: 0", + " return_codes: 0", + " return_codes: 0", + " return_codes: 0", + " return_codes: 0", + " return_codes: 0", + " >", + ">", + "version: 2\n"); + assertEquals(want, got); + } + + @Test + public void testConnectLogging() throws Exception { + asyncDump(100); + + final int OK = 0; + Thread[] logActions = { + // ignored + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + // valid latencies + connectEventAction(100, OK, 110, EXAMPLE_IPV4), + connectEventAction(100, OK, 23, EXAMPLE_IPV4), + connectEventAction(100, OK, 45, EXAMPLE_IPV4), + connectEventAction(101, OK, 56, EXAMPLE_IPV4), + connectEventAction(101, OK, 523, EXAMPLE_IPV6), + connectEventAction(101, OK, 214, EXAMPLE_IPV6), + connectEventAction(101, OK, 67, EXAMPLE_IPV6), + // errors + connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), + }; + + for (Thread t : logActions) { + t.start(); + } + for (Thread t : logActions) { + t.join(); + } + + String got = flushStatistics(); + String want = String.join("\n", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 3", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 1", + " >", + " errnos_counters <", + " key: 98", + " value: 1", + " >", + " errnos_counters <", + " key: 110", + " value: 2", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " latencies_ms: 110", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 4", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 2", + " >", + " errnos_counters <", + " key: 110", + " value: 1", + " >", + " errnos_counters <", + " key: 111", + " value: 1", + " >", + " ipv6_addr_count: 5", + " latencies_ms: 56", + " latencies_ms: 67", + " latencies_ms: 214", + " latencies_ms: 523", + " >", + ">", + "version: 2\n"); + assertEquals(want, got); + } + + private void setCapabilities(int netId) { + final ArgumentCaptor networkCallback = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); + networkCallback.getValue().onCapabilitiesChanged(new Network(netId), + netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); + } + + Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) { + setCapabilities(netId); + return new Thread(() -> { + try { + mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); + } catch (Exception e) { + fail(e.toString()); + } + }); + } + + void dnsEvent(int netId, int type, int result, int latency) throws Exception { + setCapabilities(netId); + mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0); + } + + void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, + String dstIp, int sport, int dport, long now) throws Exception { + String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; + mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now); + } + + void asyncDump(long durationMs) throws Exception { + final long stop = System.currentTimeMillis() + durationMs; + final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null")); + new Thread(() -> { + while (System.currentTimeMillis() < stop) { + mService.list(pw); + } + }).start(); + } + + // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. + String flushStatistics() throws Exception { + IpConnectivityMetrics metricsService = + new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000); + metricsService.mNetdListener = mService; + + StringWriter buffer = new StringWriter(); + PrintWriter writer = new PrintWriter(buffer); + metricsService.impl.dump(null, writer, new String[]{"flush"}); + byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT); + IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes); + for (IpConnectivityEvent ev : log.events) { + if (ev.getConnectStatistics() == null) { + continue; + } + // Sort repeated fields of connect() events arriving in non-deterministic order. + Arrays.sort(ev.getConnectStatistics().latenciesMs); + Arrays.sort(ev.getConnectStatistics().errnosCounters, + Comparator.comparingInt((p) -> p.key)); + } + return log.toString(); + } + + String[] listNetdEvent() throws Exception { + StringWriter buffer = new StringWriter(); + PrintWriter writer = new PrintWriter(buffer); + mService.list(writer); + return buffer.toString().split("\\n"); + } + + static T[] remove(T[] array, T[] filtered) { + List c = Arrays.asList(filtered); + int next = 0; + for (int i = 0; i < array.length; i++) { + if (c.contains(array[i])) { + continue; + } + array[next++] = array[i]; + } + return Arrays.copyOf(array, next); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java new file mode 100644 index 000000000000..c353cea266bb --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2016 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.connectivity; + +import static android.app.Notification.FLAG_ONGOING_EVENT; + +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.LOST_INTERNET; +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NETWORK_SWITCH; +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NO_INTERNET; +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PARTIAL_CONNECTIVITY; +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PRIVATE_DNS_BROKEN; +import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.SIGN_IN; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.net.ConnectivityResources; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.UserHandle; +import android.telephony.TelephonyManager; +import android.util.DisplayMetrics; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.connectivity.resources.R; +import com.android.server.connectivity.NetworkNotificationManager.NotificationType; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkNotificationManagerTest { + + private static final String TEST_SSID = "Test SSID"; + private static final String TEST_EXTRA_INFO = "extra"; + static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities(); + static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities(); + static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities(); + static { + CELL_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + CELL_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + + WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + WIFI_CAPABILITIES.setSSID(TEST_SSID); + + // Set the underyling network to wifi. + VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_VPN); + VPN_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + VPN_CAPABILITIES.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); + } + + @Mock Context mCtx; + @Mock Resources mResources; + @Mock DisplayMetrics mDisplayMetrics; + @Mock PackageManager mPm; + @Mock TelephonyManager mTelephonyManager; + @Mock NotificationManager mNotificationManager; + @Mock NetworkAgentInfo mWifiNai; + @Mock NetworkAgentInfo mCellNai; + @Mock NetworkAgentInfo mVpnNai; + @Mock NetworkInfo mNetworkInfo; + ArgumentCaptor mCaptor; + + NetworkNotificationManager mManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mCaptor = ArgumentCaptor.forClass(Notification.class); + mWifiNai.networkCapabilities = WIFI_CAPABILITIES; + mWifiNai.networkInfo = mNetworkInfo; + mCellNai.networkCapabilities = CELL_CAPABILITIES; + mCellNai.networkInfo = mNetworkInfo; + mVpnNai.networkCapabilities = VPN_CAPABILITIES; + mVpnNai.networkInfo = mNetworkInfo; + mDisplayMetrics.density = 2.275f; + doReturn(true).when(mVpnNai).isVPN(); + when(mCtx.getResources()).thenReturn(mResources); + when(mCtx.getPackageManager()).thenReturn(mPm); + when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo()); + final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mCtx)); + doReturn(UserHandle.ALL).when(asUserCtx).getUser(); + when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); + when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE))) + .thenReturn(mNotificationManager); + when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO); + ConnectivityResources.setResourcesContextForTest(mCtx); + when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + + // Come up with some credible-looking transport names. The actual values do not matter. + String[] transportNames = new String[NetworkCapabilities.MAX_TRANSPORT + 1]; + for (int transport = 0; transport <= NetworkCapabilities.MAX_TRANSPORT; transport++) { + transportNames[transport] = NetworkCapabilities.transportNameOf(transport); + } + when(mResources.getStringArray(R.array.network_switch_type_name)) + .thenReturn(transportNames); + + mManager = new NetworkNotificationManager(mCtx, mTelephonyManager); + } + + @After + public void tearDown() { + ConnectivityResources.setResourcesContextForTest(null); + } + + private void verifyTitleByNetwork(final int id, final NetworkAgentInfo nai, final int title) { + final String tag = NetworkNotificationManager.tagFor(id); + mManager.showNotification(id, PRIVATE_DNS_BROKEN, nai, null, null, true); + verify(mNotificationManager, times(1)) + .notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any()); + final int transportType = NetworkNotificationManager.approximateTransportType(nai); + if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { + verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO)); + } else { + verify(mResources, times(1)).getString(title); + } + verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed)); + } + + @Test + public void testTitleOfPrivateDnsBroken() { + // Test the title of mobile data. + verifyTitleByNetwork(100, mCellNai, R.string.mobile_no_internet); + clearInvocations(mResources); + + // Test the title of wifi. + verifyTitleByNetwork(101, mWifiNai, R.string.wifi_no_internet); + clearInvocations(mResources); + + // Test the title of other networks. + verifyTitleByNetwork(102, mVpnNai, R.string.other_networks_no_internet); + clearInvocations(mResources); + } + + @Test + public void testNotificationsShownAndCleared() { + final int NETWORK_ID_BASE = 100; + List types = Arrays.asList(NotificationType.values()); + List ids = new ArrayList<>(types.size()); + for (int i = 0; i < types.size(); i++) { + ids.add(NETWORK_ID_BASE + i); + } + Collections.shuffle(ids); + Collections.shuffle(types); + + for (int i = 0; i < ids.size(); i++) { + mManager.showNotification(ids.get(i), types.get(i), mWifiNai, mCellNai, null, false); + } + + List idsToClear = new ArrayList<>(ids); + Collections.shuffle(idsToClear); + for (int i = 0; i < ids.size(); i++) { + mManager.clearNotification(idsToClear.get(i)); + } + + for (int i = 0; i < ids.size(); i++) { + final int id = ids.get(i); + final int eventId = types.get(i).eventId; + final String tag = NetworkNotificationManager.tagFor(id); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(eventId)); + } + } + + @Test + @Ignore + // Ignored because the code under test calls Log.wtf, which crashes the tests on eng builds. + // TODO: re-enable after fixing this (e.g., turn Log.wtf into exceptions that this test catches) + public void testNoInternetNotificationsNotShownForCellular() { + mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false); + mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false); + + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); + + mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false); + + final int eventId = NO_INTERNET.eventId; + final String tag = NetworkNotificationManager.tagFor(102); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); + } + + @Test + public void testNotificationsNotShownIfNoInternetCapability() { + mWifiNai.networkCapabilities = new NetworkCapabilities(); + mWifiNai.networkCapabilities .addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false); + mManager.showNotification(103, LOST_INTERNET, mWifiNai, mCellNai, null, false); + mManager.showNotification(104, NETWORK_SWITCH, mWifiNai, mCellNai, null, false); + + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); + } + + private void assertNotification(NotificationType type, boolean ongoing) { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + final ArgumentCaptor noteCaptor = ArgumentCaptor.forClass(Notification.class); + mManager.showNotification(id, type, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(type.eventId), + noteCaptor.capture()); + + assertEquals("Notification ongoing flag should be " + (ongoing ? "set" : "unset"), + ongoing, (noteCaptor.getValue().flags & FLAG_ONGOING_EVENT) != 0); + } + + @Test + public void testDuplicatedNotificationsNoInternetThenSignIn() { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + // Show first NO_INTERNET + assertNotification(NO_INTERNET, false /* ongoing */); + + // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET + assertNotification(SIGN_IN, false /* ongoing */); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); + + // Network disconnects + mManager.clearNotification(id); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); + } + + @Test + public void testOngoingSignInNotification() { + doReturn(true).when(mResources).getBoolean(R.bool.config_ongoingSignInNotification); + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + // Show first NO_INTERNET + assertNotification(NO_INTERNET, false /* ongoing */); + + // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET + assertNotification(SIGN_IN, true /* ongoing */); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); + + // Network disconnects + mManager.clearNotification(id); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); + } + + @Test + public void testDuplicatedNotificationsSignInThenNoInternet() { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + // Show first SIGN_IN + mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); + reset(mNotificationManager); + + // NO_INTERNET arrives after, but is ignored. + mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, never()).cancel(any(), anyInt()); + verify(mNotificationManager, never()).notify(any(), anyInt(), any()); + + // Network disconnects + mManager.clearNotification(id); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); + } + + @Test + public void testClearNotificationByType() { + final int id = 101; + final String tag = NetworkNotificationManager.tagFor(id); + + // clearNotification(int id, NotificationType notifyType) will check if given type is equal + // to previous type or not. If they are equal then clear the notification; if they are not + // equal then return. + mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(NO_INTERNET.eventId), any()); + + // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification + // should be cleared. + mManager.clearNotification(id, NO_INTERNET); + verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); + + // SIGN_IN is popped-up. + mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); + verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); + + // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be + // cleared. + mManager.clearNotification(id, PARTIAL_CONNECTIVITY); + verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId)); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt new file mode 100644 index 000000000000..409f8c309935 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.connectivity + +import android.net.INetworkOfferCallback +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.net.NetworkScore.KEEP_CONNECTED_NONE +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +const val POLICY_NONE = 0L + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetworkOfferTest { + val mockCallback = mock(INetworkOfferCallback::class.java) + + @Test + fun testOfferNeededUnneeded() { + val score = FullScore(50, POLICY_NONE, KEEP_CONNECTED_NONE) + val offer = NetworkOffer(score, NetworkCapabilities.Builder().build(), mockCallback, + 1 /* providerId */) + val request1 = mock(NetworkRequest::class.java) + val request2 = mock(NetworkRequest::class.java) + offer.onNetworkNeeded(request1) + verify(mockCallback).onNetworkNeeded(eq(request1)) + assertTrue(offer.neededFor(request1)) + assertFalse(offer.neededFor(request2)) + + offer.onNetworkNeeded(request2) + verify(mockCallback).onNetworkNeeded(eq(request2)) + assertTrue(offer.neededFor(request1)) + assertTrue(offer.neededFor(request2)) + + // Note that the framework never calls onNetworkNeeded multiple times with the same + // request without calling onNetworkUnneeded first. It would be incorrect usage and the + // behavior would be undefined, so there is nothing to test. + + offer.onNetworkUnneeded(request1) + verify(mockCallback).onNetworkUnneeded(eq(request1)) + assertFalse(offer.neededFor(request1)) + assertTrue(offer.neededFor(request2)) + + offer.onNetworkUnneeded(request2) + verify(mockCallback).onNetworkUnneeded(eq(request2)) + assertFalse(offer.neededFor(request1)) + assertFalse(offer.neededFor(request2)) + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt new file mode 100644 index 000000000000..551b94c7668e --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt @@ -0,0 +1,90 @@ +/* + * 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.connectivity + +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.net.NetworkScore.KEEP_CONNECTED_NONE +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import kotlin.test.assertEquals +import kotlin.test.assertNull + +@RunWith(AndroidJUnit4::class) +@SmallTest +class NetworkRankerTest { + private val ranker = NetworkRanker() + + private fun makeNai(satisfy: Boolean, legacyScore: Int) = + mock(NetworkAgentInfo::class.java).also { + doReturn(satisfy).`when`(it).satisfies(any()) + val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE) + doReturn(fs).`when`(it).getScore() + val nc = NetworkCapabilities.Builder().build() + doReturn(nc).`when`(it).getCapsNoCopy() + } + + @Test + fun testGetBestNetwork() { + val scores = listOf(20, 50, 90, 60, 23, 68) + val nais = scores.map { makeNai(true, it) } + val bestNetwork = nais[2] // The one with the top score + val someRequest = mock(NetworkRequest::class.java) + assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, bestNetwork)) + } + + @Test + fun testIgnoreNonSatisfying() { + val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90), + makeNai(false, 60), makeNai(true, 23), makeNai(false, 68)) + val bestNetwork = nais[1] // Top score that's satisfying + val someRequest = mock(NetworkRequest::class.java) + assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, nais[1])) + } + + @Test + fun testNoMatch() { + val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90)) + val someRequest = mock(NetworkRequest::class.java) + assertNull(ranker.getBestNetwork(someRequest, nais, null)) + } + + @Test + fun testEmpty() { + val someRequest = mock(NetworkRequest::class.java) + assertNull(ranker.getBestNetwork(someRequest, emptyList(), null)) + } + + // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST + // network satisfying the request if multiple of them have the same score. + @Test + fun testStable() { + val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30), + makeNai(true, 30), makeNai(true, 30), makeNai(true, 30)) + val someRequest = mock(NetworkRequest::class.java) + assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1, nais1[0])) + + val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20), + makeNai(true, 50), makeNai(true, 50), makeNai(true, 40)) + assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2, nais2[1])) + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java new file mode 100644 index 000000000000..02a58080fefd --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -0,0 +1,803 @@ +/* + * Copyright (C) 2018 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.connectivity; + +import static android.Manifest.permission.CHANGE_NETWORK_STATE; +import static android.Manifest.permission.CHANGE_WIFI_STATE; +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.INTERNET; +import static android.Manifest.permission.NETWORK_STACK; +import static android.Manifest.permission.UPDATE_DEVICE_STATS; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED; +import static android.content.pm.PackageManager.GET_PERMISSIONS; +import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.os.Process.SYSTEM_UID; + +import static com.android.server.connectivity.PermissionMonitor.NETWORK; +import static com.android.server.connectivity.PermissionMonitor.SYSTEM; + +import static junit.framework.Assert.fail; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalMatchers.aryEq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.INetd; +import android.net.UidRange; +import android.net.Uri; +import android.os.Build; +import android.os.SystemConfigManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.SparseIntArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class PermissionMonitorTest { + private static final UserHandle MOCK_USER1 = UserHandle.of(0); + private static final UserHandle MOCK_USER2 = UserHandle.of(1); + private static final int MOCK_UID1 = 10001; + private static final int MOCK_UID2 = 10086; + private static final int SYSTEM_UID1 = 1000; + private static final int SYSTEM_UID2 = 1008; + private static final int VPN_UID = 10002; + private static final String REAL_SYSTEM_PACKAGE_NAME = "android"; + private static final String MOCK_PACKAGE1 = "appName1"; + private static final String MOCK_PACKAGE2 = "appName2"; + private static final String SYSTEM_PACKAGE1 = "sysName1"; + private static final String SYSTEM_PACKAGE2 = "sysName2"; + private static final String PARTITION_SYSTEM = "system"; + private static final String PARTITION_OEM = "oem"; + private static final String PARTITION_PRODUCT = "product"; + private static final String PARTITION_VENDOR = "vendor"; + private static final int VERSION_P = Build.VERSION_CODES.P; + private static final int VERSION_Q = Build.VERSION_CODES.Q; + + @Mock private Context mContext; + @Mock private PackageManager mPackageManager; + @Mock private INetd mNetdService; + @Mock private UserManager mUserManager; + @Mock private PermissionMonitor.Dependencies mDeps; + @Mock private SystemConfigManager mSystemConfigManager; + + private PermissionMonitor mPermissionMonitor; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); + when(mUserManager.getUserHandles(eq(true))).thenReturn( + Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 })); + when(mContext.getSystemServiceName(SystemConfigManager.class)) + .thenReturn(Context.SYSTEM_CONFIG_SERVICE); + when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) + .thenReturn(mSystemConfigManager); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); + final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); + doReturn(UserHandle.ALL).when(asUserCtx).getUser(); + when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); + + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); + + when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); + mPermissionMonitor.startMonitoring(); + } + + private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, + String... permissions) { + final PackageInfo packageInfo = + packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition); + packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; + packageInfo.applicationInfo.uid = uid; + return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo); + } + + private static PackageInfo systemPackageInfoWithPermissions(String... permissions) { + return packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM); + } + + private static PackageInfo vendorPackageInfoWithPermissions(String... permissions) { + return packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_VENDOR); + } + + private static PackageInfo packageInfoWithPermissions(int permissionsFlags, + String[] permissions, String partition) { + int[] requestedPermissionsFlags = new int[permissions.length]; + for (int i = 0; i < permissions.length; i++) { + requestedPermissionsFlags[i] = permissionsFlags; + } + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.requestedPermissions = permissions; + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.requestedPermissionsFlags = requestedPermissionsFlags; + int privateFlags = 0; + switch (partition) { + case PARTITION_OEM: + privateFlags = PRIVATE_FLAG_OEM; + break; + case PARTITION_PRODUCT: + privateFlags = PRIVATE_FLAG_PRODUCT; + break; + case PARTITION_VENDOR: + privateFlags = PRIVATE_FLAG_VENDOR; + break; + } + packageInfo.applicationInfo.privateFlags = privateFlags; + return packageInfo; + } + + private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, + UserHandle user) { + final PackageInfo pkgInfo; + if (hasSystemPermission) { + pkgInfo = systemPackageInfoWithPermissions( + CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS); + } else { + pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, ""); + } + pkgInfo.applicationInfo.uid = user.getUid(UserHandle.getAppId(uid)); + return pkgInfo; + } + + @Test + public void testHasPermission() { + PackageInfo app = systemPackageInfoWithPermissions(); + assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); + + app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE, NETWORK_STACK); + assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); + + app = systemPackageInfoWithPermissions( + CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL); + assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); + assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); + + app = packageInfoWithPermissions(REQUESTED_PERMISSION_REQUIRED, new String[] { + CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL, NETWORK_STACK }, + PARTITION_SYSTEM); + assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); + + app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); + app.requestedPermissions = null; + assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + + app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); + app.requestedPermissionsFlags = null; + assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); + } + + @Test + public void testIsVendorApp() { + PackageInfo app = systemPackageInfoWithPermissions(); + assertFalse(mPermissionMonitor.isVendorApp(app.applicationInfo)); + app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, + new String[] {}, PARTITION_OEM); + assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); + app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, + new String[] {}, PARTITION_PRODUCT); + assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); + app = vendorPackageInfoWithPermissions(); + assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); + } + + @Test + public void testHasNetworkPermission() { + PackageInfo app = systemPackageInfoWithPermissions(); + assertFalse(mPermissionMonitor.hasNetworkPermission(app)); + app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); + assertTrue(mPermissionMonitor.hasNetworkPermission(app)); + app = systemPackageInfoWithPermissions(NETWORK_STACK); + assertFalse(mPermissionMonitor.hasNetworkPermission(app)); + app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS); + assertFalse(mPermissionMonitor.hasNetworkPermission(app)); + app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL); + assertFalse(mPermissionMonitor.hasNetworkPermission(app)); + } + + @Test + public void testHasRestrictedNetworkPermission() { + assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + + assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); + } + + @Test + public void testHasRestrictedNetworkPermissionSystemUid() { + doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); + assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + + doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); + assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + } + + @Test + public void testHasRestrictedNetworkPermissionVendorApp() { + assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + assertTrue(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + + assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertFalse(hasRestrictedNetworkPermission( + PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE)); + } + + private void assertBackgroundPermission(boolean hasPermission, String name, int uid, + String... permissions) throws Exception { + when(mPackageManager.getPackageInfo(eq(name), anyInt())) + .thenReturn(packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM)); + mPermissionMonitor.onPackageAdded(name, uid); + assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid)); + } + + @Test + public void testHasUseBackgroundNetworksPermission() throws Exception { + assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID)); + assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID); + assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL); + assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE); + assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK); + + assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1)); + assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1); + assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1, + CONNECTIVITY_USE_RESTRICTED_NETWORKS); + + assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2)); + assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2); + assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2, + CONNECTIVITY_INTERNAL); + assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK); + } + + private class NetdMonitor { + private final HashMap mApps = new HashMap<>(); + + NetdMonitor(INetd mockNetd) throws Exception { + // Add hook to verify and track result of setPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM); + for (final int uid : (int[]) args[1]) { + // TODO: Currently, permission monitor will send duplicate commands for each uid + // corresponding to each user. Need to fix that and uncomment below test. + // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) { + // fail("uid " + uid + " is already set to " + isSystem); + // } + mApps.put(uid, isSystem); + } + return null; + }).when(mockNetd).networkSetPermissionForUser(anyInt(), any(int[].class)); + + // Add hook to verify and track result of clearPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + for (final int uid : (int[]) args[0]) { + // TODO: Currently, permission monitor will send duplicate commands for each uid + // corresponding to each user. Need to fix that and uncomment below test. + // if (!mApps.containsKey(uid)) { + // fail("uid " + uid + " does not exist."); + // } + mApps.remove(uid); + } + return null; + }).when(mockNetd).networkClearPermissionForUser(any(int[].class)); + } + + public void expectPermission(Boolean permission, UserHandle[] users, int[] apps) { + for (final UserHandle user : users) { + for (final int app : apps) { + final int uid = user.getUid(app); + if (!mApps.containsKey(uid)) { + fail("uid " + uid + " does not exist."); + } + if (mApps.get(uid) != permission) { + fail("uid " + uid + " has wrong permission: " + permission); + } + } + } + } + + public void expectNoPermission(UserHandle[] users, int[] apps) { + for (final UserHandle user : users) { + for (final int app : apps) { + final int uid = user.getUid(app); + if (mApps.containsKey(uid)) { + fail("uid " + uid + " has listed permissions, expected none."); + } + } + } + } + } + + @Test + public void testUserAndPackageAddRemove() throws Exception { + final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService); + + // MOCK_UID1: MOCK_PACKAGE1 only has network permission. + // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission. + // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission. + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString()); + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE1)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE2)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(MOCK_PACKAGE1)); + + // Add SYSTEM_PACKAGE2, expect only have network permission. + mPermissionMonitor.onUserAdded(MOCK_USER1); + addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + // Add SYSTEM_PACKAGE1, expect permission escalate. + addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserAdded(MOCK_USER2); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); + mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{MOCK_UID1}); + + // Remove MOCK_UID1, expect no permission left for all user. + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{MOCK_UID1}); + + // Remove SYSTEM_PACKAGE1, expect permission downgrade. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2}); + removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, + SYSTEM_PACKAGE1, SYSTEM_UID); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserRemoved(MOCK_USER1); + mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER2}, new int[]{SYSTEM_UID}); + + // Remove all packages, expect no permission left. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{}); + removePackageForUsers(new UserHandle[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + + // Remove last user, expect no redundant clearPermission is invoked. + mPermissionMonitor.onUserRemoved(MOCK_USER2); + mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + } + + @Test + public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception { + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + Arrays.asList(new PackageInfo[] { + buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), + buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1), + buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1), + buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) + })); + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), + eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); + mPermissionMonitor.startMonitoring(); + // Every app on user 0 except MOCK_UID2 are under VPN. + final Set vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] { + new UidRange(0, MOCK_UID2 - 1), + new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1)})); + final Set vpnRange2 = Collections.singleton(new UidRange(MOCK_UID2, MOCK_UID2)); + + // When VPN is connected, expect a rule to be set up for user app MOCK_UID1 + mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID); + verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), + aryEq(new int[] {MOCK_UID1})); + + reset(mNetdService); + + // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated + mPermissionMonitor.onPackageRemoved( + MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); + verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); + mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); + verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), + aryEq(new int[] {MOCK_UID1})); + + reset(mNetdService); + + // During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the + // old UID rules then adds the new ones. Expect netd to be updated + mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID); + verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); + mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID); + verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), + aryEq(new int[] {MOCK_UID2})); + + reset(mNetdService); + + // When VPN is disconnected, expect rules to be torn down + mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID); + verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2})); + assertNull(mPermissionMonitor.getVpnUidRanges("tun0")); + } + + @Test + public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception { + when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + Arrays.asList(new PackageInfo[] { + buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), + buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) + })); + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), + eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( + buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); + + mPermissionMonitor.startMonitoring(); + final Set vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1)); + mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID); + + // Newly-installed package should have uid rules added + mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); + verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), + aryEq(new int[] {MOCK_UID1})); + + // Removed package should have its uid rules removed + mPermissionMonitor.onPackageRemoved( + MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); + verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); + } + + + // Normal package add/remove operations will trigger multiple intent for uids corresponding to + // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be + // called multiple times with the uid corresponding to each user. + private void addPackageForUsers(UserHandle[] users, String packageName, int uid) { + for (final UserHandle user : users) { + mPermissionMonitor.onPackageAdded(packageName, user.getUid(uid)); + } + } + + private void removePackageForUsers(UserHandle[] users, String packageName, int uid) { + for (final UserHandle user : users) { + mPermissionMonitor.onPackageRemoved(packageName, user.getUid(uid)); + } + } + + private class NetdServiceMonitor { + private final HashMap mPermissions = new HashMap<>(); + + NetdServiceMonitor(INetd mockNetdService) throws Exception { + // Add hook to verify and track result of setPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + final int permission = (int) args[0]; + for (final int uid : (int[]) args[1]) { + mPermissions.put(uid, permission); + } + return null; + }).when(mockNetdService).trafficSetNetPermForUids(anyInt(), any(int[].class)); + } + + public void expectPermission(int permission, int[] apps) { + for (final int app : apps) { + if (!mPermissions.containsKey(app)) { + fail("uid " + app + " does not exist."); + } + if (mPermissions.get(app) != permission) { + fail("uid " + app + " has wrong permission: " + mPermissions.get(app)); + } + } + } + } + + @Test + public void testPackagePermissionUpdate() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + // MOCK_UID1: MOCK_PACKAGE1 only has internet permission. + // MOCK_UID2: MOCK_PACKAGE2 does not have any permission. + // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission. + // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission. + + SparseIntArray netdPermissionsAppIds = new SparseIntArray(); + netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET); + netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE); + netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS); + netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS); + + // Send the permission information to netd, expect permission updated. + mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds); + + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, + new int[]{MOCK_UID1}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS, + new int[]{SYSTEM_UID2}); + + // Update permission of MOCK_UID1, expect new permission show up. + mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + // Change permissions of SYSTEM_UID2, expect new permission show up and old permission + // revoked. + mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, + INetd.PERMISSION_INTERNET); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2}); + + // Revoke permission from SYSTEM_UID1, expect no permission stored. + mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1}); + } + + private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions) + throws Exception { + PackageInfo packageInfo = packageInfoWithPermissions( + REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM); + when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo); + when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName}); + return packageInfo; + } + + private PackageInfo addPackage(String packageName, int uid, String[] permissions) + throws Exception { + PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions); + mPermissionMonitor.onPackageAdded(packageName, uid); + return packageInfo; + } + + @Test + public void testPackageInstall() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2}); + } + + @Test + public void testPackageInstallSharedUid() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1, + new String[] {INTERNET, UPDATE_DEVICE_STATS}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + // Install another package with the same uid and no permissions should not cause the UID to + // lose permissions. + PackageInfo packageInfo2 = systemPackageInfoWithPermissions(); + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); + when(mPackageManager.getPackagesForUid(MOCK_UID1)) + .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); + mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + } + + @Test + public void testPackageUninstallBasic() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); + } + + @Test + public void testPackageRemoveThenAdd() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + } + + @Test + public void testPackageUpdate() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1}); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + } + + @Test + public void testPackageUninstallWithMultiplePackages() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + + addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); + + // Mock another package with the same uid but different permissions. + PackageInfo packageInfo2 = systemPackageInfoWithPermissions(INTERNET); + when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{ + MOCK_PACKAGE2}); + + mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); + } + + @Test + public void testRealSystemPermission() throws Exception { + // Use the real context as this test must ensure the *real* system package holds the + // necessary permission. + final Context realContext = InstrumentationRegistry.getContext(); + final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService); + final PackageManager manager = realContext.getPackageManager(); + final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME, + GET_PERMISSIONS | MATCH_ANY_USER); + assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + } + + @Test + public void testUpdateUidPermissionsFromSystemConfig() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>()); + when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET))) + .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 }); + when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS))) + .thenReturn(new int[]{ MOCK_UID2 }); + + mPermissionMonitor.startMonitoring(); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{ MOCK_UID1 }); + mNetdServiceMonitor.expectPermission( + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, + new int[]{ MOCK_UID2 }); + } + + @Test + public void testIntentReceiver() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + final ArgumentCaptor receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver receiver = receiverCaptor.getValue(); + + // Verify receiving PACKAGE_ADDED intent. + final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED, + Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); + addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); + setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, + new String[] { INTERNET, UPDATE_DEVICE_STATS }); + receiver.onReceive(mContext, addedIntent); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 }); + + // Verify receiving PACKAGE_REMOVED intent. + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null); + final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED, + Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); + removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); + receiver.onReceive(mContext, removedIntent); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 }); + } + +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java new file mode 100644 index 000000000000..b725b826b14f --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java @@ -0,0 +1,1283 @@ +/* + * Copyright (C) 2016 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.connectivity; + +import static android.content.pm.UserInfo.FLAG_ADMIN; +import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static android.content.pm.UserInfo.FLAG_PRIMARY; +import static android.content.pm.UserInfo.FLAG_RESTRICTED; +import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; +import static android.os.UserHandle.PER_USER_RANGE; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.AppOpsManager; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.INetd; +import android.net.Ikev2VpnProfile; +import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; +import android.net.IpPrefix; +import android.net.IpSecManager; +import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkProperties; +import android.net.LocalSocket; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo.DetailedState; +import android.net.RouteInfo; +import android.net.UidRangeParcel; +import android.net.VpnManager; +import android.net.VpnService; +import android.net.VpnTransportInfo; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.os.ConditionVariable; +import android.os.INetworkManagementService; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.security.Credentials; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Range; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.R; +import com.android.internal.net.LegacyVpnInfo; +import com.android.internal.net.VpnConfig; +import com.android.internal.net.VpnProfile; +import com.android.server.IpSecService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +/** + * Tests for {@link Vpn}. + * + * Build, install and run with: + * runtest frameworks-net -c com.android.server.connectivity.VpnTest + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VpnTest { + private static final String TAG = "VpnTest"; + + // Mock users + static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY); + static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN); + static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED); + static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED); + static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE); + static { + restrictedProfileA.restrictedProfileParentId = primaryUser.id; + restrictedProfileB.restrictedProfileParentId = secondaryUser.id; + managedProfileA.profileGroupId = primaryUser.id; + } + + static final Network EGRESS_NETWORK = new Network(101); + static final String EGRESS_IFACE = "wlan0"; + static final String TEST_VPN_PKG = "com.testvpn.vpn"; + private static final String TEST_VPN_SERVER = "1.2.3.4"; + private static final String TEST_VPN_IDENTITY = "identity"; + private static final byte[] TEST_VPN_PSK = "psk".getBytes(); + + private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE); + private static final String TEST_IFACE_NAME = "TEST_IFACE"; + private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345; + private static final long TEST_TIMEOUT_MS = 500L; + + /** + * Names and UIDs for some fake packages. Important points: + * - UID is ordered increasing. + * - One pair of packages have consecutive UIDs. + */ + static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"}; + static final int[] PKG_UIDS = {66, 77, 78, 400}; + + // Mock packages + static final Map mPackages = new ArrayMap<>(); + static { + for (int i = 0; i < PKGS.length; i++) { + mPackages.put(PKGS[i], PKG_UIDS[i]); + } + } + private static final Range PRI_USER_RANGE = uidRangeForUser(primaryUser.id); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; + @Mock private UserManager mUserManager; + @Mock private PackageManager mPackageManager; + @Mock private INetworkManagementService mNetService; + @Mock private INetd mNetd; + @Mock private AppOpsManager mAppOps; + @Mock private NotificationManager mNotificationManager; + @Mock private Vpn.SystemServices mSystemServices; + @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator; + @Mock private ConnectivityManager mConnectivityManager; + @Mock private IpSecService mIpSecService; + @Mock private VpnProfileStore mVpnProfileStore; + private final VpnProfile mVpnProfile; + + private IpSecManager mIpSecManager; + + public VpnTest() throws Exception { + // Build an actual VPN profile that is capable of being converted to and from an + // Ikev2VpnProfile + final Ikev2VpnProfile.Builder builder = + new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY); + builder.setAuthPsk(TEST_VPN_PSK); + mVpnProfile = builder.build().toVpnProfile(); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mIpSecManager = new IpSecManager(mContext, mIpSecService); + + when(mContext.getPackageManager()).thenReturn(mPackageManager); + setMockedPackages(mPackages); + + when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG); + when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG); + when(mContext.getSystemServiceName(UserManager.class)) + .thenReturn(Context.USER_SERVICE); + when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); + when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps); + when(mContext.getSystemServiceName(NotificationManager.class)) + .thenReturn(Context.NOTIFICATION_SERVICE); + when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) + .thenReturn(mNotificationManager); + when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) + .thenReturn(mConnectivityManager); + when(mContext.getSystemServiceName(eq(ConnectivityManager.class))) + .thenReturn(Context.CONNECTIVITY_SERVICE); + when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager); + when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) + .thenReturn(Resources.getSystem().getString( + R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) + .thenReturn(true); + + // Used by {@link Notification.Builder} + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; + when(mContext.getApplicationInfo()).thenReturn(applicationInfo); + when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(applicationInfo); + + doNothing().when(mNetService).registerObserver(any()); + + // Deny all appops by default. + when(mAppOps.noteOpNoThrow(anyString(), anyInt(), anyString(), any(), any())) + .thenReturn(AppOpsManager.MODE_IGNORED); + + // Setup IpSecService + final IpSecTunnelInterfaceResponse tunnelResp = + new IpSecTunnelInterfaceResponse( + IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME); + when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any())) + .thenReturn(tunnelResp); + } + + private Set> rangeSet(Range ... ranges) { + final Set> range = new ArraySet<>(); + for (Range r : ranges) range.add(r); + + return range; + } + + private static Range uidRangeForUser(int userId) { + return new Range(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1); + } + + private Range uidRange(int start, int stop) { + return new Range(start, stop); + } + + @Test + public void testRestrictedProfilesAreAddedToVpn() { + setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB); + + final Vpn vpn = createVpn(primaryUser.id); + + // Assume the user can have restricted profiles. + doReturn(true).when(mUserManager).canHaveRestrictedProfile(); + final Set> ranges = + vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); + + assertEquals(rangeSet(PRI_USER_RANGE, uidRangeForUser(restrictedProfileA.id)), ranges); + } + + @Test + public void testManagedProfilesAreNotAddedToVpn() { + setMockedUsers(primaryUser, managedProfileA); + + final Vpn vpn = createVpn(primaryUser.id); + final Set> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, + null, null); + + assertEquals(rangeSet(PRI_USER_RANGE), ranges); + } + + @Test + public void testAddUserToVpnOnlyAddsOneUser() { + setMockedUsers(primaryUser, restrictedProfileA, managedProfileA); + + final Vpn vpn = createVpn(primaryUser.id); + final Set> ranges = new ArraySet<>(); + vpn.addUserToRanges(ranges, primaryUser.id, null, null); + + assertEquals(rangeSet(PRI_USER_RANGE), ranges); + } + + @Test + public void testUidAllowAndDenylist() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final Range user = PRI_USER_RANGE; + final int userStart = user.getLower(); + final int userStop = user.getUpper(); + final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; + + // Allowed list + final Set> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, + Arrays.asList(packages), null /* disallowedApplications */); + assertEquals(rangeSet( + uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]), + uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2])), + allow); + + // Denied list + final Set> disallow = + vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, + null /* allowedApplications */, Arrays.asList(packages)); + assertEquals(rangeSet( + uidRange(userStart, userStart + PKG_UIDS[0] - 1), + uidRange(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), + /* Empty range between UIDS[1] and UIDS[2], should be excluded, */ + uidRange(userStart + PKG_UIDS[2] + 1, userStop)), + disallow); + } + + @Test + public void testGetAlwaysAndOnGetLockDown() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + + // Default state. + assertFalse(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + + // Set always-on without lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList())); + assertTrue(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + + // Set always-on with lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList())); + assertTrue(vpn.getAlwaysOn()); + assertTrue(vpn.getLockdown()); + + // Remove always-on configuration. + assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList())); + assertFalse(vpn.getAlwaysOn()); + assertFalse(vpn.getLockdown()); + } + + @Test + public void testLockdownChangingPackage() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final Range user = PRI_USER_RANGE; + final int userStart = user.getLower(); + final int userStop = user.getUpper(); + // Set always-on without lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null)); + + // Set always-on with lockdown. + assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null)); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) + })); + + // Switch to another app. + assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null)); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[3] - 1), + new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) + })); + } + + @Test + public void testLockdownAllowlist() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final Range user = PRI_USER_RANGE; + final int userStart = user.getLower(); + final int userStop = user.getUpper(); + // Set always-on with lockdown and allow app PKGS[2] from lockdown. + assertTrue(vpn.setAlwaysOnPackage( + PKGS[1], true, Collections.singletonList(PKGS[2]))); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) + })); + // Change allowed app list to PKGS[3]. + assertTrue(vpn.setAlwaysOnPackage( + PKGS[1], true, Collections.singletonList(PKGS[3]))); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1), + new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) + })); + + // Change the VPN app. + assertTrue(vpn.setAlwaysOnPackage( + PKGS[0], true, Collections.singletonList(PKGS[3]))); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart, userStart + PKG_UIDS[0] - 1), + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1) + })); + + // Remove the list of allowed packages. + assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null)); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1), + new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop), + })); + + // Add the list of allowed packages. + assertTrue(vpn.setAlwaysOnPackage( + PKGS[0], true, Collections.singletonList(PKGS[1]))); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) + })); + + // Try allowing a package with a comma, should be rejected. + assertFalse(vpn.setAlwaysOnPackage( + PKGS[0], true, Collections.singletonList("a.b,c.d"))); + + // Pass a non-existent packages in the allowlist, they (and only they) should be ignored. + // allowed package should change from PGKS[1] to PKGS[2]. + assertTrue(vpn.setAlwaysOnPackage( + PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"))); + verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), + new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) + })); + verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { + new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[2] - 1), + new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) + })); + } + + @Test + public void testLockdownRuleRepeatability() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { + new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper())}; + // Given legacy lockdown is already enabled, + vpn.setLockdown(true); + verify(mConnectivityManager, times(1)).setRequireVpnForUids(true, + toRanges(primaryUserRangeParcel)); + + // Enabling legacy lockdown twice should do nothing. + vpn.setLockdown(true); + verify(mConnectivityManager, times(1)).setRequireVpnForUids(anyBoolean(), any()); + + // And disabling should remove the rules exactly once. + vpn.setLockdown(false); + verify(mConnectivityManager, times(1)).setRequireVpnForUids(false, + toRanges(primaryUserRangeParcel)); + + // Removing the lockdown again should have no effect. + vpn.setLockdown(false); + verify(mConnectivityManager, times(2)).setRequireVpnForUids(anyBoolean(), any()); + } + + private ArrayList> toRanges(UidRangeParcel[] ranges) { + ArrayList> rangesArray = new ArrayList<>(ranges.length); + for (int i = 0; i < ranges.length; i++) { + rangesArray.add(new Range<>(ranges[i].start, ranges[i].stop)); + } + return rangesArray; + } + + @Test + public void testLockdownRuleReversibility() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + final UidRangeParcel[] entireUser = { + new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper()) + }; + final UidRangeParcel[] exceptPkg0 = { + new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1), + new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop) + }; + + final InOrder order = inOrder(mConnectivityManager); + + // Given lockdown is enabled with no package (legacy VPN), + vpn.setLockdown(true); + order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser)); + + // When a new VPN package is set the rules should change to cover that package. + vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE); + order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(entireUser)); + order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(exceptPkg0)); + + // When that VPN package is unset, everything should be undone again in reverse. + vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE); + order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(exceptPkg0)); + order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser)); + } + + @Test + public void testIsAlwaysOnPackageSupported() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + + ApplicationInfo appInfo = new ApplicationInfo(); + when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id))) + .thenReturn(appInfo); + + ServiceInfo svcInfo = new ServiceInfo(); + ResolveInfo resInfo = new ResolveInfo(); + resInfo.serviceInfo = svcInfo; + when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA), + eq(primaryUser.id))) + .thenReturn(Collections.singletonList(resInfo)); + + // null package name should return false + assertFalse(vpn.isAlwaysOnPackageSupported(null)); + + // Pre-N apps are not supported + appInfo.targetSdkVersion = VERSION_CODES.M; + assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); + + // N+ apps are supported by default + appInfo.targetSdkVersion = VERSION_CODES.N; + assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0])); + + // Apps that opt out explicitly are not supported + appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; + Bundle metaData = new Bundle(); + metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false); + svcInfo.metaData = metaData; + assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); + } + + @Test + public void testNotificationShownForAlwaysOnApp() throws Exception { + final UserHandle userHandle = UserHandle.of(primaryUser.id); + final Vpn vpn = createVpn(primaryUser.id); + setMockedUsers(primaryUser); + + final InOrder order = inOrder(mNotificationManager); + + // Don't show a notification for regular disconnected states. + vpn.updateState(DetailedState.DISCONNECTED, TAG); + order.verify(mNotificationManager, atLeastOnce()).cancel(anyString(), anyInt()); + + // Start showing a notification for disconnected once always-on. + vpn.setAlwaysOnPackage(PKGS[0], false, null); + order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); + + // Stop showing the notification once connected. + vpn.updateState(DetailedState.CONNECTED, TAG); + order.verify(mNotificationManager).cancel(anyString(), anyInt()); + + // Show the notification if we disconnect again. + vpn.updateState(DetailedState.DISCONNECTED, TAG); + order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); + + // Notification should be cleared after unsetting always-on package. + vpn.setAlwaysOnPackage(null, false, null); + order.verify(mNotificationManager).cancel(anyString(), anyInt()); + } + + /** + * The profile name should NOT change between releases for backwards compatibility + * + *

If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST + * be updated to ensure backward compatibility. + */ + @Test + public void testGetProfileNameForPackage() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + setMockedUsers(primaryUser); + + final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG; + assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG)); + } + + private Vpn createVpnAndSetupUidChecks(String... grantedOps) throws Exception { + return createVpnAndSetupUidChecks(primaryUser, grantedOps); + } + + private Vpn createVpnAndSetupUidChecks(UserInfo user, String... grantedOps) throws Exception { + final Vpn vpn = createVpn(user.id); + setMockedUsers(user); + + when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt())) + .thenReturn(Process.myUid()); + + for (final String opStr : grantedOps) { + when(mAppOps.noteOpNoThrow(opStr, Process.myUid(), TEST_VPN_PKG, + null /* attributionTag */, null /* message */)) + .thenReturn(AppOpsManager.MODE_ALLOWED); + } + + return vpn; + } + + private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, String... checkedOps) { + assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile)); + + // The profile should always be stored, whether or not consent has been previously granted. + verify(mVpnProfileStore) + .put( + eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), + eq(mVpnProfile.encode())); + + for (final String checkedOpStr : checkedOps) { + verify(mAppOps).noteOpNoThrow(checkedOpStr, Process.myUid(), TEST_VPN_PKG, + null /* attributionTag */, null /* message */); + } + } + + @Test + public void testProvisionVpnProfileNoIpsecTunnels() throws Exception { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) + .thenReturn(false); + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + try { + checkProvisionVpnProfile( + vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + fail("Expected exception due to missing feature"); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testProvisionVpnProfilePreconsented() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + checkProvisionVpnProfile( + vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + } + + @Test + public void testProvisionVpnProfileNotPreconsented() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller + // had neither. + checkProvisionVpnProfile(vpn, false /* expectedResult */, + AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN, AppOpsManager.OPSTR_ACTIVATE_VPN); + } + + @Test + public void testProvisionVpnProfileVpnServicePreconsented() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN); + + checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_VPN); + } + + @Test + public void testProvisionVpnProfileTooLarge() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + final VpnProfile bigProfile = new VpnProfile(""); + bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]); + + try { + vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile); + fail("Expected IAE due to profile size"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testProvisionVpnProfileRestrictedUser() throws Exception { + final Vpn vpn = + createVpnAndSetupUidChecks( + restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + try { + vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile); + fail("Expected SecurityException due to restricted user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testDeleteVpnProfile() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + vpn.deleteVpnProfile(TEST_VPN_PKG); + + verify(mVpnProfileStore) + .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); + } + + @Test + public void testDeleteVpnProfileRestrictedUser() throws Exception { + final Vpn vpn = + createVpnAndSetupUidChecks( + restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + try { + vpn.deleteVpnProfile(TEST_VPN_PKG); + fail("Expected SecurityException due to restricted user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testGetVpnProfilePrivileged() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) + .thenReturn(new VpnProfile("").encode()); + + vpn.getVpnProfilePrivileged(TEST_VPN_PKG); + + verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); + } + + @Test + public void testStartVpnProfile() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) + .thenReturn(mVpnProfile.encode()); + + vpn.startVpnProfile(TEST_VPN_PKG); + + verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); + verify(mAppOps) + .noteOpNoThrow( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(null) /* attributionTag */, + eq(null) /* message */); + } + + @Test + public void testStartVpnProfileVpnServicePreconsented() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN); + + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) + .thenReturn(mVpnProfile.encode()); + + vpn.startVpnProfile(TEST_VPN_PKG); + + // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown. + verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(), + TEST_VPN_PKG, null /* attributionTag */, null /* message */); + } + + @Test + public void testStartVpnProfileNotConsented() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + try { + vpn.startVpnProfile(TEST_VPN_PKG); + fail("Expected failure due to no user consent"); + } catch (SecurityException expected) { + } + + // Verify both appops were checked. + verify(mAppOps) + .noteOpNoThrow( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(null) /* attributionTag */, + eq(null) /* message */); + verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(), + TEST_VPN_PKG, null /* attributionTag */, null /* message */); + + // Keystore should never have been accessed. + verify(mVpnProfileStore, never()).get(any()); + } + + @Test + public void testStartVpnProfileMissingProfile() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null); + + try { + vpn.startVpnProfile(TEST_VPN_PKG); + fail("Expected failure due to missing profile"); + } catch (IllegalArgumentException expected) { + } + + verify(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG)); + verify(mAppOps) + .noteOpNoThrow( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(null) /* attributionTag */, + eq(null) /* message */); + } + + @Test + public void testStartVpnProfileRestrictedUser() throws Exception { + final Vpn vpn = + createVpnAndSetupUidChecks( + restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + try { + vpn.startVpnProfile(TEST_VPN_PKG); + fail("Expected SecurityException due to restricted user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testStopVpnProfileRestrictedUser() throws Exception { + final Vpn vpn = + createVpnAndSetupUidChecks( + restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); + + try { + vpn.stopVpnProfile(TEST_VPN_PKG); + fail("Expected SecurityException due to restricted user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testSetPackageAuthorizationVpnService() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE)); + verify(mAppOps) + .setMode( + eq(AppOpsManager.OPSTR_ACTIVATE_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(AppOpsManager.MODE_ALLOWED)); + } + + @Test + public void testSetPackageAuthorizationPlatformVpn() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM)); + verify(mAppOps) + .setMode( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(AppOpsManager.MODE_ALLOWED)); + } + + @Test + public void testSetPackageAuthorizationRevokeAuthorization() throws Exception { + final Vpn vpn = createVpnAndSetupUidChecks(); + + assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE)); + verify(mAppOps) + .setMode( + eq(AppOpsManager.OPSTR_ACTIVATE_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(AppOpsManager.MODE_IGNORED)); + verify(mAppOps) + .setMode( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), + eq(Process.myUid()), + eq(TEST_VPN_PKG), + eq(AppOpsManager.MODE_IGNORED)); + } + + private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception { + final ArgumentCaptor networkCallbackCaptor = + ArgumentCaptor.forClass(NetworkCallback.class); + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)) + .requestNetwork(any(), networkCallbackCaptor.capture()); + + // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be + // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException. + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mNetd.interfaceGetCfg(anyString())).thenReturn(config); + final NetworkCallback cb = networkCallbackCaptor.getValue(); + cb.onAvailable(TEST_NETWORK); + return cb; + } + + private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception { + // Add a timeout for waiting for interfaceSetCfg to be called. + verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(flag))); + } + + @Test + public void testStartPlatformVpnAuthenticationFailed() throws Exception { + final ArgumentCaptor captor = + ArgumentCaptor.forClass(IkeSessionCallback.class); + final IkeProtocolException exception = mock(IkeProtocolException.class); + when(exception.getErrorType()) + .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); + + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); + final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent + // state + verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)) + .createIkeSession(any(), any(), any(), any(), captor.capture(), any()); + final IkeSessionCallback ikeCb = captor.getValue(); + ikeCb.onClosedExceptionally(exception); + + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); + } + + @Test + public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { + when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) + .thenThrow(new IllegalArgumentException()); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); + final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent + // state + verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); + } + + private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { + assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null)); + + verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); + verify(mAppOps).setMode( + eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG), + eq(AppOpsManager.MODE_ALLOWED)); + + verify(mSystemServices).settingsSecurePutStringForUser( + eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id)); + verify(mSystemServices).settingsSecurePutIntForUser( + eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0), + eq(primaryUser.id)); + verify(mSystemServices).settingsSecurePutStringForUser( + eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id)); + } + + @Test + public void testSetAndStartAlwaysOnVpn() throws Exception { + final Vpn vpn = createVpn(primaryUser.id); + setMockedUsers(primaryUser); + + // UID checks must return a different UID; otherwise it'll be treated as already prepared. + final int uid = Process.myUid() + 1; + when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt())) + .thenReturn(uid); + when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) + .thenReturn(mVpnProfile.encode()); + + setAndVerifyAlwaysOnPackage(vpn, uid, false); + assertTrue(vpn.startAlwaysOnVpn()); + + // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in + // a subsequent CL. + } + + private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { + setMockedUsers(primaryUser); + + // Dummy egress interface + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(EGRESS_IFACE); + + final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), + InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE); + lp.addRoute(defaultRoute); + + vpn.startLegacyVpn(vpnProfile, EGRESS_NETWORK, lp); + return vpn; + } + + @Test + public void testStartPlatformVpn() throws Exception { + startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); + // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in + // a subsequent patch. + } + + @Test + public void testStartRacoonNumericAddress() throws Exception { + startRacoon("1.2.3.4", "1.2.3.4"); + } + + @Test + public void testStartRacoonHostname() throws Exception { + startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve + } + + private void assertTransportInfoMatches(NetworkCapabilities nc, int type) { + assertNotNull(nc); + VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo(); + assertNotNull(ti); + assertEquals(type, ti.getType()); + } + + public void startRacoon(final String serverAddr, final String expectedAddr) + throws Exception { + final ConditionVariable legacyRunnerReady = new ConditionVariable(); + final VpnProfile profile = new VpnProfile("testProfile" /* key */); + profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK; + profile.name = "testProfileName"; + profile.username = "userName"; + profile.password = "thePassword"; + profile.server = serverAddr; + profile.ipsecIdentifier = "id"; + profile.ipsecSecret = "secret"; + profile.l2tpSecret = "l2tpsecret"; + + when(mConnectivityManager.getAllNetworks()) + .thenReturn(new Network[] { new Network(101) }); + + when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), + any(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); + final TestDeps deps = (TestDeps) vpn.mDeps; + try { + // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK + assertArrayEquals( + new String[] { EGRESS_IFACE, expectedAddr, "udppsk", + profile.ipsecIdentifier, profile.ipsecSecret, "1701" }, + deps.racoonArgs.get(10, TimeUnit.SECONDS)); + // literal values are hardcoded in Vpn.java for mtpd args + assertArrayEquals( + new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret, + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", + "idle", "1800", "mtu", "1270", "mru", "1270" }, + deps.mtpdArgs.get(10, TimeUnit.SECONDS)); + + // Now wait for the runner to be ready before testing for the route. + ArgumentCaptor lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); + ArgumentCaptor ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), + lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt()); + + // In this test the expected address is always v4 so /32. + // Note that the interface needs to be specified because RouteInfo objects stored in + // LinkProperties objects always acquire the LinkProperties' interface. + final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), + null, EGRESS_IFACE, RouteInfo.RTN_THROW); + final List actualRoutes = lpCaptor.getValue().getRoutes(); + assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes, + actualRoutes.contains(expectedRoute)); + + assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY); + } finally { + // Now interrupt the thread, unblock the runner and clean up. + vpn.mVpnRunner.exitVpnRunner(); + deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier + vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup + } + } + + private static final class TestDeps extends Vpn.Dependencies { + public final CompletableFuture racoonArgs = new CompletableFuture(); + public final CompletableFuture mtpdArgs = new CompletableFuture(); + public final File mStateFile; + + private final HashMap mRunningServices = new HashMap<>(); + + TestDeps() { + try { + mStateFile = File.createTempFile("vpnTest", ".tmp"); + mStateFile.deleteOnExit(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean isCallerSystem() { + return true; + } + + @Override + public void startService(final String serviceName) { + mRunningServices.put(serviceName, true); + } + + @Override + public void stopService(final String serviceName) { + mRunningServices.put(serviceName, false); + } + + @Override + public boolean isServiceRunning(final String serviceName) { + return mRunningServices.getOrDefault(serviceName, false); + } + + @Override + public boolean isServiceStopped(final String serviceName) { + return !isServiceRunning(serviceName); + } + + @Override + public File getStateFile() { + return mStateFile; + } + + @Override + public PendingIntent getIntentForStatusPanel(Context context) { + return null; + } + + @Override + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final Vpn.RetryScheduler interruptChecker) throws IOException { + if ("racoon".equals(daemon)) { + racoonArgs.complete(arguments); + } else if ("mtpd".equals(daemon)) { + writeStateFile(arguments); + mtpdArgs.complete(arguments); + } else { + throw new UnsupportedOperationException("Unsupported daemon : " + daemon); + } + } + + private void writeStateFile(final String[] arguments) throws IOException { + mStateFile.delete(); + mStateFile.createNewFile(); + mStateFile.deleteOnExit(); + final BufferedWriter writer = new BufferedWriter( + new FileWriter(mStateFile, false /* append */)); + writer.write(EGRESS_IFACE); + writer.write("\n"); + // addresses + writer.write("10.0.0.1/24\n"); + // routes + writer.write("192.168.6.0/24\n"); + // dns servers + writer.write("192.168.6.1\n"); + // search domains + writer.write("vpn.searchdomains.com\n"); + // endpoint - intentionally empty + writer.write("\n"); + writer.flush(); + writer.close(); + } + + @Override + @NonNull + public InetAddress resolve(final String endpoint) { + try { + // If a numeric IP address, return it. + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Otherwise, return some token IP to test for. + return InetAddress.parseNumericAddress("5.6.7.8"); + } + } + + @Override + public boolean isInterfacePresent(final Vpn vpn, final String iface) { + return true; + } + } + + /** + * Mock some methods of vpn object. + */ + private Vpn createVpn(@UserIdInt int userId) { + final Context asUserContext = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); + doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); + when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) + .thenReturn(asUserContext); + final TestLooper testLooper = new TestLooper(); + final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, + mNetd, userId, mVpnProfileStore, mSystemServices, mIkev2SessionCreator); + verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat( + provider -> provider.getName().contains("VpnNetworkProvider") + )); + return vpn; + } + + /** + * Populate {@link #mUserManager} with a list of fake users. + */ + private void setMockedUsers(UserInfo... users) { + final Map userMap = new ArrayMap<>(); + for (UserInfo user : users) { + userMap.put(user.id, user); + } + + /** + * @see UserManagerService#getUsers(boolean) + */ + doAnswer(invocation -> { + final ArrayList result = new ArrayList<>(users.length); + for (UserInfo ui : users) { + if (ui.isEnabled() && !ui.partial) { + result.add(ui); + } + } + return result; + }).when(mUserManager).getAliveUsers(); + + doAnswer(invocation -> { + final int id = (int) invocation.getArguments()[0]; + return userMap.get(id); + }).when(mUserManager).getUserInfo(anyInt()); + } + + /** + * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping. + */ + private void setMockedPackages(final Map packages) { + try { + doAnswer(invocation -> { + final String appName = (String) invocation.getArguments()[0]; + final int userId = (int) invocation.getArguments()[1]; + Integer appId = packages.get(appName); + if (appId == null) throw new PackageManager.NameNotFoundException(appName); + return UserHandle.getUid(userId, appId); + }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt()); + } catch (Exception e) { + } + } + + private void setMockedNetworks(final Map networks) { + doAnswer(invocation -> { + final Network network = (Network) invocation.getArguments()[0]; + return networks.get(network); + }).when(mConnectivityManager).getNetworkCapabilities(any()); + } + + // Need multiple copies of this, but Java's Stream objects can't be reused or + // duplicated. + private Stream publicIpV4Routes() { + return Stream.of( + "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", + "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", + "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", + "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", + "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14", + "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7", + "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4"); + } + + private Stream publicIpV6Routes() { + return Stream.of( + "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6", + "fe00::/8", "2605:ef80:e:af1d::/64"); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java new file mode 100644 index 000000000000..8b730af76951 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 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.net; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.Manifest.permission; +import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManagerInternal; +import android.content.Context; +import android.content.pm.PackageManager; +import android.telephony.TelephonyManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsAccessTest { + private static final String TEST_PKG = "com.example.test"; + private static final int TEST_UID = 12345; + + @Mock private Context mContext; + @Mock private DevicePolicyManagerInternal mDpmi; + @Mock private TelephonyManager mTm; + @Mock private AppOpsManager mAppOps; + + // Hold the real service so we can restore it when tearing down the test. + private DevicePolicyManagerInternal mSystemDpmi; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); + LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); + LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi); + + when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm); + when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps); + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); + LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi); + } + + @Test + public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception { + setHasCarrierPrivileges(true); + setIsDeviceOwner(false); + setIsProfileOwner(false); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEVICE, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_isDeviceOwner() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(true); + setIsProfileOwner(false); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEVICE, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_isProfileOwner() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(true); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.USER, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(true); + setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(true); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(true); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); + setHasReadHistoryPermission(true); + assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_deniedAppOpsBit() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(false); + setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEFAULT, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + @Test + public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception { + setHasCarrierPrivileges(false); + setIsDeviceOwner(false); + setIsProfileOwner(false); + setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); + setHasReadHistoryPermission(false); + assertEquals(NetworkStatsAccess.Level.DEFAULT, + NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); + } + + private void setHasCarrierPrivileges(boolean hasPrivileges) { + when(mTm.checkCarrierPrivilegesForPackageAnyPhone(TEST_PKG)).thenReturn( + hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS + : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS); + } + + private void setIsDeviceOwner(boolean isOwner) { + when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner); + } + + private void setIsProfileOwner(boolean isOwner) { + when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner); + } + + private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) { + when(mAppOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG)) + .thenReturn(appOpsMode); + when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn( + hasPermission ? PackageManager.PERMISSION_GRANTED + : PackageManager.PERMISSION_DENIED); + } + + private void setHasReadHistoryPermission(boolean hasPermission) { + when(mContext.checkCallingOrSelfPermission(permission.READ_NETWORK_USAGE_HISTORY)) + .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED + : PackageManager.PERMISSION_DENIED); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java new file mode 100644 index 000000000000..a058a466a4ff --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2011 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.net; + +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.ROAMING_YES; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.TAG_NONE; + +import static org.junit.Assert.assertEquals; + +import android.net.NetworkStats; +import android.net.UnderlyingNetworkInfo; + +import java.util.Arrays; + +/** Superclass with utilities for NetworkStats(Service|Factory)Test */ +abstract class NetworkStatsBaseTest { + static final String TEST_IFACE = "test0"; + static final String TEST_IFACE2 = "test1"; + static final String TUN_IFACE = "test_nss_tun0"; + static final String TUN_IFACE2 = "test_nss_tun1"; + + static final int UID_RED = 1001; + static final int UID_BLUE = 1002; + static final int UID_GREEN = 1003; + static final int UID_VPN = 1004; + + void assertValues(NetworkStats stats, String iface, int uid, long rxBytes, + long rxPackets, long txBytes, long txPackets) { + assertValues( + stats, iface, uid, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + rxBytes, rxPackets, txBytes, txPackets, 0); + } + + void assertValues(NetworkStats stats, String iface, int uid, int set, int tag, + int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + final NetworkStats.Entry entry = new NetworkStats.Entry(); + final int[] sets; + if (set == SET_ALL) { + sets = new int[] {SET_ALL, SET_DEFAULT, SET_FOREGROUND}; + } else { + sets = new int[] {set}; + } + + final int[] roamings; + if (roaming == ROAMING_ALL) { + roamings = new int[] {ROAMING_ALL, ROAMING_YES, ROAMING_NO}; + } else { + roamings = new int[] {roaming}; + } + + final int[] meterings; + if (metered == METERED_ALL) { + meterings = new int[] {METERED_ALL, METERED_YES, METERED_NO}; + } else { + meterings = new int[] {metered}; + } + + final int[] defaultNetworks; + if (defaultNetwork == DEFAULT_NETWORK_ALL) { + defaultNetworks = + new int[] {DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, DEFAULT_NETWORK_NO}; + } else { + defaultNetworks = new int[] {defaultNetwork}; + } + + for (int s : sets) { + for (int r : roamings) { + for (int m : meterings) { + for (int d : defaultNetworks) { + final int i = stats.findIndex(iface, uid, s, tag, m, r, d); + if (i != -1) { + entry.add(stats.getValues(i, null)); + } + } + } + } + } + + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } + + static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) { + return createVpnInfo(TUN_IFACE, underlyingIfaces); + } + + static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { + return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces)); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java new file mode 100644 index 000000000000..505ff9b6a34b --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2012 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.net; + +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.NetworkIdentity.OEM_NONE; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStatsHistory.FIELD_ALL; +import static android.net.NetworkTemplate.buildTemplateMobileAll; +import static android.os.Process.myUid; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; + +import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkIdentity; +import android.net.NetworkStats; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.os.Process; +import android.os.UserHandle; +import android.telephony.SubscriptionPlan; +import android.telephony.TelephonyManager; +import android.text.format.DateUtils; +import android.util.RecurrenceRule; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.tests.net.R; + +import libcore.io.IoUtils; +import libcore.io.Streams; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link NetworkStatsCollection}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsCollectionTest { + + private static final String TEST_FILE = "test.bin"; + private static final String TEST_IMSI = "310260000000000"; + + private static final long TIME_A = 1326088800000L; // UTC: Monday 9th January 2012 06:00:00 AM + private static final long TIME_B = 1326110400000L; // UTC: Monday 9th January 2012 12:00:00 PM + private static final long TIME_C = 1326132000000L; // UTC: Monday 9th January 2012 06:00:00 PM + + private static Clock sOriginalClock; + + @Before + public void setUp() throws Exception { + sOriginalClock = RecurrenceRule.sClock; + // ignore any device overlay while testing + NetworkTemplate.forceAllNetworkTypes(); + } + + @After + public void tearDown() throws Exception { + RecurrenceRule.sClock = sOriginalClock; + NetworkTemplate.resetForceAllNetworkTypes(); + } + + private void setClock(Instant instant) { + RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault()); + } + + @Test + public void testReadLegacyNetwork() throws Exception { + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); + stageFile(R.raw.netstats_v1, testFile); + + final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); + collection.readLegacyNetwork(testFile); + + // verify that history read correctly + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); + + // now export into a unified format + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + collection.write(bos); + + // clear structure completely + collection.reset(); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); + + // and read back into structure, verifying that totals are same + collection.read(new ByteArrayInputStream(bos.toByteArray())); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); + } + + @Test + public void testReadLegacyUid() throws Exception { + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); + stageFile(R.raw.netstats_uid_v4, testFile); + + final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); + collection.readLegacyUid(testFile, false); + + // verify that history read correctly + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); + + // now export into a unified format + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + collection.write(bos); + + // clear structure completely + collection.reset(); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); + + // and read back into structure, verifying that totals are same + collection.read(new ByteArrayInputStream(bos.toByteArray())); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), + 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); + } + + @Test + public void testReadLegacyUidTags() throws Exception { + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); + stageFile(R.raw.netstats_uid_v4, testFile); + + final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); + collection.readLegacyUid(testFile, true); + + // verify that history read correctly + assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), + 77017831L, 100995L, 35436758L, 92344L); + + // now export into a unified format + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + collection.write(bos); + + // clear structure completely + collection.reset(); + assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), + 0L, 0L, 0L, 0L); + + // and read back into structure, verifying that totals are same + collection.read(new ByteArrayInputStream(bos.toByteArray())); + assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), + 77017831L, 100995L, 35436758L, 92344L); + } + + @Test + public void testStartEndAtomicBuckets() throws Exception { + final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); + + // record empty data straddling between buckets + final NetworkStats.Entry entry = new NetworkStats.Entry(); + entry.rxBytes = 32; + collection.recordData(null, UID_ALL, SET_DEFAULT, TAG_NONE, 30 * MINUTE_IN_MILLIS, + 90 * MINUTE_IN_MILLIS, entry); + + // assert that we report boundary in atomic buckets + assertEquals(0, collection.getStartMillis()); + assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis()); + } + + @Test + public void testAccessLevels() throws Exception { + final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); + final NetworkStats.Entry entry = new NetworkStats.Entry(); + final NetworkIdentitySet identSet = new NetworkIdentitySet(); + identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, + TEST_IMSI, null, false, true, true, OEM_NONE)); + + int myUid = Process.myUid(); + int otherUidInSameUser = Process.myUid() + 1; + int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE; + + // Record one entry for the current UID. + entry.rxBytes = 32; + collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS, + entry); + + // Record one entry for another UID in this user. + entry.rxBytes = 64; + collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0, + 60 * MINUTE_IN_MILLIS, entry); + + // Record one entry for the system UID. + entry.rxBytes = 128; + collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0, + 60 * MINUTE_IN_MILLIS, entry); + + // Record one entry for a UID in a different user. + entry.rxBytes = 256; + collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0, + 60 * MINUTE_IN_MILLIS, entry); + + // Verify the set of relevant UIDs for each access level. + assertArrayEquals(new int[] { myUid }, + collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT)); + assertArrayEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser }, + collection.getRelevantUids(NetworkStatsAccess.Level.USER)); + assertArrayEquals( + new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser }, + collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE)); + + // Verify security check in getHistory. + assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT, + TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid)); + try { + collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser, + SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid); + fail("Should have thrown SecurityException for accessing different UID"); + } catch (SecurityException e) { + // expected + } + + // Verify appropriate aggregation in getSummary. + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0, + NetworkStatsAccess.Level.DEFAULT); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0, + NetworkStatsAccess.Level.USER); + assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0, + 0, NetworkStatsAccess.Level.DEVICE); + } + + @Test + public void testAugmentPlan() throws Exception { + final File testFile = + new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); + stageFile(R.raw.netstats_v1, testFile); + + final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); + final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); + collection.readLegacyNetwork(testFile); + + // We're in the future, but not that far off + setClock(Instant.parse("2012-06-01T00:00:00.00Z")); + + // Test a bunch of plans that should result in no augmentation + final List plans = new ArrayList<>(); + + // No plan + plans.add(null); + // No usage anchor + plans.add(SubscriptionPlan.Builder + .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")).build()); + // Usage anchor far in past + plans.add(SubscriptionPlan.Builder + .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) + .setDataUsage(1000L, TIME_A - DateUtils.YEAR_IN_MILLIS).build()); + // Usage anchor far in future + plans.add(SubscriptionPlan.Builder + .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) + .setDataUsage(1000L, TIME_A + DateUtils.YEAR_IN_MILLIS).build()); + // Usage anchor near but outside cycle + plans.add(SubscriptionPlan.Builder + .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), + ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) + .setDataUsage(1000L, TIME_C).build()); + + for (SubscriptionPlan plan : plans) { + int i; + NetworkStatsHistory history; + + // Empty collection should be untouched + history = getHistory(emptyCollection, plan, TIME_A, TIME_C); + assertEquals(0L, history.getTotalBytes()); + + // Normal collection should be untouched + history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; + assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); + assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); + assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); + assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); + assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); + assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); + assertEntry(10747, 50, 16839, 55, history.getValues(i++, null)); + assertEntry(10747, 49, 16837, 54, history.getValues(i++, null)); + assertEntry(89191, 151, 18021, 140, history.getValues(i++, null)); + assertEntry(89190, 150, 18020, 139, history.getValues(i++, null)); + assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); + assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); + assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); + assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); + assertEquals(history.size(), i); + + // Slice from middle should be untouched + history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, + TIME_B + HOUR_IN_MILLIS); i = 0; + assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); + assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEquals(history.size(), i); + } + + // Lower anchor in the middle of plan + { + int i; + NetworkStatsHistory history; + + final SubscriptionPlan plan = SubscriptionPlan.Builder + .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), + ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) + .setDataUsage(200000L, TIME_B).build(); + + // Empty collection should be augmented + history = getHistory(emptyCollection, plan, TIME_A, TIME_C); + assertEquals(200000L, history.getTotalBytes()); + + // Normal collection should be augmented + history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; + assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); + assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); + assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); + assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); + assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); + assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); + // Cycle point; start data normalization + assertEntry(7507, 0, 11763, 0, history.getValues(i++, null)); + assertEntry(7507, 0, 11762, 0, history.getValues(i++, null)); + assertEntry(62309, 0, 12589, 0, history.getValues(i++, null)); + assertEntry(62309, 0, 12588, 0, history.getValues(i++, null)); + assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); + assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); + // Anchor point; end data normalization + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); + assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); + assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); + // Cycle point + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); + assertEquals(history.size(), i); + + // Slice from middle should be augmented + history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, + TIME_B + HOUR_IN_MILLIS); i = 0; + assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); + assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEquals(history.size(), i); + } + + // Higher anchor in the middle of plan + { + int i; + NetworkStatsHistory history; + + final SubscriptionPlan plan = SubscriptionPlan.Builder + .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), + ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) + .setDataUsage(400000L, TIME_B + MINUTE_IN_MILLIS).build(); + + // Empty collection should be augmented + history = getHistory(emptyCollection, plan, TIME_A, TIME_C); + assertEquals(400000L, history.getTotalBytes()); + + // Normal collection should be augmented + history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; + assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); + assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); + assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); + assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); + assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); + assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); + // Cycle point; start data normalization + assertEntry(15015, 0, 23527, 0, history.getValues(i++, null)); + assertEntry(15015, 0, 23524, 0, history.getValues(i++, null)); + assertEntry(124619, 0, 25179, 0, history.getValues(i++, null)); + assertEntry(124618, 0, 25177, 0, history.getValues(i++, null)); + assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); + assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); + // Anchor point; end data normalization + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); + assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); + assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); + assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); + // Cycle point + assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); + assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); + assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); + assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); + assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); + assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); + + // Slice from middle should be augmented + history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, + TIME_B + HOUR_IN_MILLIS); i = 0; + assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); + assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); + assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); + assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); + assertEquals(history.size(), i); + } + } + + @Test + public void testAugmentPlanGigantic() throws Exception { + // We're in the future, but not that far off + setClock(Instant.parse("2012-06-01T00:00:00.00Z")); + + // Create a simple history with a ton of measured usage + final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS); + final NetworkIdentitySet ident = new NetworkIdentitySet(); + ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null, + false, true, true, OEM_NONE)); + large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B, + new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0)); + + // Verify untouched total + assertEquals(12_730_893_164L, getHistory(large, null, TIME_A, TIME_C).getTotalBytes()); + + // Verify anchor that might cause overflows + final SubscriptionPlan plan = SubscriptionPlan.Builder + .createRecurringMonthly(ZonedDateTime.parse("2012-01-09T00:00:00.00Z")) + .setDataUsage(4_939_212_390L, TIME_B).build(); + assertEquals(4_939_212_386L, getHistory(large, plan, TIME_A, TIME_C).getTotalBytes()); + } + + @Test + public void testRounding() throws Exception { + final NetworkStatsCollection coll = new NetworkStatsCollection(HOUR_IN_MILLIS); + + // Special values should remain unchanged + for (long time : new long[] { + Long.MIN_VALUE, Long.MAX_VALUE, SubscriptionPlan.TIME_UNKNOWN + }) { + assertEquals(time, coll.roundUp(time)); + assertEquals(time, coll.roundDown(time)); + } + + assertEquals(TIME_A, coll.roundUp(TIME_A)); + assertEquals(TIME_A, coll.roundDown(TIME_A)); + + assertEquals(TIME_A + HOUR_IN_MILLIS, coll.roundUp(TIME_A + 1)); + assertEquals(TIME_A, coll.roundDown(TIME_A + 1)); + + assertEquals(TIME_A, coll.roundUp(TIME_A - 1)); + assertEquals(TIME_A - HOUR_IN_MILLIS, coll.roundDown(TIME_A - 1)); + } + + @Test + public void testMultiplySafeRational() { + assertEquals(25, multiplySafeByRational(50, 1, 2)); + assertEquals(100, multiplySafeByRational(50, 2, 1)); + + assertEquals(-10, multiplySafeByRational(30, -1, 3)); + assertEquals(0, multiplySafeByRational(30, 0, 3)); + assertEquals(10, multiplySafeByRational(30, 1, 3)); + assertEquals(20, multiplySafeByRational(30, 2, 3)); + assertEquals(30, multiplySafeByRational(30, 3, 3)); + assertEquals(40, multiplySafeByRational(30, 4, 3)); + + assertEquals(100_000_000_000L, + multiplySafeByRational(300_000_000_000L, 10_000_000_000L, 30_000_000_000L)); + assertEquals(100_000_000_010L, + multiplySafeByRational(300_000_000_000L, 10_000_000_001L, 30_000_000_000L)); + assertEquals(823_202_048L, + multiplySafeByRational(4_939_212_288L, 2_121_815_528L, 12_730_893_165L)); + + assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0)); + } + + /** + * Copy a {@link Resources#openRawResource(int)} into {@link File} for + * testing purposes. + */ + private void stageFile(int rawId, File file) throws Exception { + new File(file.getParent()).mkdirs(); + InputStream in = null; + OutputStream out = null; + try { + in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); + out = new FileOutputStream(file); + Streams.copy(in, out); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } + } + + private static NetworkStatsHistory getHistory(NetworkStatsCollection collection, + SubscriptionPlan augmentPlan, long start, long end) { + return collection.getHistory(buildTemplateMobileAll(TEST_IMSI), augmentPlan, UID_ALL, + SET_ALL, TAG_NONE, FIELD_ALL, start, end, NetworkStatsAccess.Level.DEVICE, myUid()); + } + + private static void assertSummaryTotal(NetworkStatsCollection collection, + NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets, + @NetworkStatsAccess.Level int accessLevel) { + final NetworkStats.Entry actual = collection.getSummary( + template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel, myUid()) + .getTotal(null); + assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); + } + + private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection, + NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) { + final NetworkStats.Entry actual = collection.getSummary( + template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE, myUid()) + .getTotalIncludingTags(null); + assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); + } + + private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, + NetworkStats.Entry actual) { + assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); + } + + private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, + NetworkStatsHistory.Entry actual) { + assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); + } + + private static void assertEntry(NetworkStats.Entry expected, + NetworkStatsHistory.Entry actual) { + assertEntry(expected, new NetworkStats.Entry(actual.rxBytes, actual.rxPackets, + actual.txBytes, actual.txPackets, 0L)); + } + + private static void assertEntry(NetworkStats.Entry expected, + NetworkStats.Entry actual) { + assertEquals("unexpected rxBytes", expected.rxBytes, actual.rxBytes); + assertEquals("unexpected rxPackets", expected.rxPackets, actual.rxPackets); + assertEquals("unexpected txBytes", expected.txBytes, actual.txBytes); + assertEquals("unexpected txPackets", expected.txPackets, actual.txPackets); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java new file mode 100644 index 000000000000..f3ae9b051e7c --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2011 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.net; + +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; + +import static com.android.server.NetworkManagementSocketTagger.kernelToTag; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.content.res.Resources; +import android.net.NetworkStats; +import android.net.TrafficStats; +import android.net.UnderlyingNetworkInfo; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.tests.net.R; + +import libcore.io.IoUtils; +import libcore.io.Streams; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.OutputStream; + +/** Tests for {@link NetworkStatsFactory}. */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { + private static final String CLAT_PREFIX = "v4-"; + + private File mTestProc; + private NetworkStatsFactory mFactory; + + @Before + public void setUp() throws Exception { + mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc"); + if (mTestProc.exists()) { + IoUtils.deleteContents(mTestProc); + } + + // The libandroid_servers which have the native method is not available to + // applications. So in order to have a test support native library, the native code + // related to networkStatsFactory is compiled to a minimal native library and loaded here. + System.loadLibrary("networkstatsfactorytestjni"); + mFactory = new NetworkStatsFactory(mTestProc, false); + mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]); + } + + @After + public void tearDown() throws Exception { + mFactory = null; + + if (mTestProc.exists()) { + IoUtils.deleteContents(mTestProc); + } + } + + @Test + public void testNetworkStatsDetail() throws Exception { + final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical); + + assertEquals(70, stats.size()); + assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L); + assertStatsEntry(stats, "wlan0", 10011, SET_DEFAULT, 0x0, 35777L, 5718L); + assertStatsEntry(stats, "wlan0", 10021, SET_DEFAULT, 0x7fffff01, 562386L, 49228L); + assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 227423L); + assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L); + } + + @Test + public void testVpnRewriteTrafficThroughItself() throws Exception { + UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // + // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic + + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self); + + assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0); + assertValues(tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 0); + assertValues(tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 0); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 260L, 26L); + } + + @Test + public void testVpnWithClat() throws Exception { + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { + createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over v4-WiFi, and clat + // added 20 bytes per packet of extra overhead + // + // For 1650 bytes sent over v4-WiFi, 4650 bytes were actually sent over WiFi, which is + // expected to be split as follows: + // UID_RED: 1000 bytes, 100 packets + // UID_BLUE: 500 bytes, 50 packets + // UID_VPN: 3150 bytes, 0 packets + // + // For 3300 bytes received over v4-WiFi, 9300 bytes were actually sent over WiFi, which is + // expected to be split as follows: + // UID_RED: 2000 bytes, 200 packets + // UID_BLUE: 1000 bytes, 100 packets + // UID_VPN: 6300 bytes, 0 packets + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat); + + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L); + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_VPN, 6300L, 0L, 3150L, 0L); + } + + @Test + public void testVpnWithOneUnderlyingIface() throws Exception { + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi. + // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. + // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes + // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L); + } + + @Test + public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { + // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun + // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500 + // packets) from it. Including overhead that is 6600/5500 bytes. + // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi. + // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN. + // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes + // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 5800L, 500L, 6750L, 600L); + } + + @Test + public void testVpnWithOneUnderlyingIface_withCompression() throws Exception { + // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN. + // VPN sent/received 1000 bytes (100 packets) over WiFi. + // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE, + // with nothing attributed to UID_VPN for both rx/tx traffic. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression); + + assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + } + + @Test + public void testVpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is duplicating traffic across both WiFi and Cell. + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN. + // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total). + // Of 8800 bytes over WiFi/Cell, expect: + // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE. + // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication); + + assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 1200L, 100L, 1200L, 100L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 1200L, 100L, 1200L, 100L); + } + + @Test + public void testConcurrentVpns() throws Exception { + // Assume two VPNs are connected on two different network interfaces. VPN1 is using + // TEST_IFACE and VPN2 is using TEST_IFACE2. + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { + createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}), + createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN1. + // 700 bytes (70 packets) were sent, and 3000 bytes (300 packets) were received by UID_RED + // over VPN2. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN1. + // 250 bytes (25 packets) were sent, and 500 bytes (50 packets) were received by UID_BLUE + // over VPN2. + // VPN1 sent 1650 bytes (150 packets), and received 3300 (300 packets) over TEST_IFACE. + // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. + // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes + // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. + // VPN2 sent 1045 bytes (95 packets), and received 3850 (350 packets) over TEST_IFACE2. + // Of 1045 bytes sent over Cell, expect 700 bytes attributed to UID_RED, 250 bytes + // attributed to UID_BLUE, and 95 bytes attributed to UID_VPN. + // Of 3850 bytes received over Cell, expect 3000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 350 bytes attributed to UID_VPN. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 3000L, 300L, 700L, 70L); + assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 250L, 25L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 350L, 0L, 95L, 0L); + } + + @Test + public void testVpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over + // VPN. + // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell. + // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell. + // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx) + // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell. + // + // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic. + // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split); + + assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 400L, 40L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 20L, 0L, 40L, 0L); + } + + @Test + public void testVpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface: + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell. + // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both + // rx/tx. + // UID_VPN gets nothing attributed to it (avoiding negative stats). + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression); + + assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 200L, 20L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 0L, 0L, 0L, 0L); + } + + @Test + public void testVpnWithIncorrectUnderlyingIface() throws Exception { + // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), + // but has declared only WiFi (TEST_IFACE) in its underlying network set. + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // VPN sent/received 1100 bytes (100 packets) over Cell. + // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface); + + assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 1100L, 100L, 1100L, 100L); + } + + @Test + public void testKernelTags() throws Exception { + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(0x32, kernelToTag("0x0000003200000000")); + assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); + assertEquals(0, kernelToTag("0x0000000000000000")); + assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); + + assertEquals(0, kernelToTag("0x0")); + assertEquals(0, kernelToTag("0xf00d")); + assertEquals(1, kernelToTag("0x100000000")); + assertEquals(14438007, kernelToTag("0xdc4e7700000000")); + assertEquals(TrafficStats.TAG_SYSTEM_DOWNLOAD, kernelToTag("0xffffff0100000000")); + } + + @Test + public void testNetworkStatsWithSet() throws Exception { + final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical); + assertEquals(70, stats.size()); + assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L, + 676L); + assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L); + } + + @Test + public void testNetworkStatsSingle() throws Exception { + stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all")); + + final NetworkStats stats = mFactory.readNetworkStatsSummaryDev(); + assertEquals(6, stats.size()); + assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 2112L, 24L, 700L, 10L); + assertStatsEntry(stats, "test1", UID_ALL, SET_ALL, TAG_NONE, 6L, 8L, 10L, 12L); + assertStatsEntry(stats, "test2", UID_ALL, SET_ALL, TAG_NONE, 1L, 2L, 3L, 4L); + } + + @Test + public void testNetworkStatsXt() throws Exception { + stageFile(R.raw.xt_qtaguid_iface_fmt_typical, file("net/xt_qtaguid/iface_stat_fmt")); + + final NetworkStats stats = mFactory.readNetworkStatsSummaryXt(); + assertEquals(3, stats.size()); + assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 6824L, 16L, 5692L, 10L); + assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L, + 2468L); + assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L); + } + + @Test + public void testDoubleClatAccountingSimple() throws Exception { + mFactory.noteStackedIface("v4-wlan0", "wlan0"); + + // xt_qtaguid_with_clat_simple is a synthetic file that simulates + // - 213 received 464xlat packets of size 200 bytes + // - 41 sent 464xlat packets of size 100 bytes + // - no other traffic on base interface for root uid. + NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple); + assertEquals(3, stats.size()); + + assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L); + assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); + } + + @Test + public void testDoubleClatAccounting() throws Exception { + mFactory.noteStackedIface("v4-wlan0", "wlan0"); + + NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat); + assertEquals(42, stats.size()); + + assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L); + assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L); + assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L); + assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L); + assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); + assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L); + assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L); + assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L); + assertStatsEntry(stats, "wlan0", 10060, SET_DEFAULT, 0x0, 134356L, 8705L); + assertStatsEntry(stats, "wlan0", 10079, SET_DEFAULT, 0x0, 10926L, 1507L); + assertStatsEntry(stats, "wlan0", 10102, SET_DEFAULT, 0x0, 25038L, 8245L); + assertStatsEntry(stats, "wlan0", 10103, SET_DEFAULT, 0x0, 0L, 192L); + assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L); + assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L); + + assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0); + } + + @Test + public void testDoubleClatAccounting100MBDownload() throws Exception { + // Downloading 100mb from an ipv4 only destination in a foreground activity + + long appRxBytesBefore = 328684029L; + long appRxBytesAfter = 439237478L; + assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore); + + long rootRxBytes = 330187296L; + + mFactory.noteStackedIface("v4-wlan0", "wlan0"); + NetworkStats stats; + + // Stats snapshot before the download + stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before); + assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L); + assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L); + + // Stats snapshot after the download + stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after); + assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L); + assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L); + } + + /** + * Copy a {@link Resources#openRawResource(int)} into {@link File} for + * testing purposes. + */ + private void stageFile(int rawId, File file) throws Exception { + new File(file.getParent()).mkdirs(); + InputStream in = null; + OutputStream out = null; + try { + in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); + out = new FileOutputStream(file); + Streams.copy(in, out); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } + } + + private void stageLong(long value, File file) throws Exception { + new File(file.getParent()).mkdirs(); + FileWriter out = null; + try { + out = new FileWriter(file); + out.write(Long.toString(value)); + } finally { + IoUtils.closeQuietly(out); + } + } + + private File file(String path) throws Exception { + return new File(mTestProc, path); + } + + private NetworkStats parseDetailedStats(int resourceId) throws Exception { + stageFile(resourceId, file("net/xt_qtaguid/stats")); + return mFactory.readNetworkStatsDetail(); + } + + private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, + int tag, long rxBytes, long txBytes) { + final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO); + if (i < 0) { + fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", + iface, uid, set, tag)); + } + final NetworkStats.Entry entry = stats.getValues(i, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + } + + private static void assertNoStatsEntry(NetworkStats stats, String iface, int uid, int set, + int tag) { + final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO); + if (i >= 0) { + fail("unexpected NetworkStats entry at " + i); + } + } + + private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, + int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { + assertStatsEntry(stats, iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, + rxBytes, rxPackets, txBytes, txPackets); + } + + private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets) { + final int i = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); + + if (i < 0) { + fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d, metered:" + + " %d, roaming: %d, defaultNetwork: %d)", + iface, uid, set, tag, metered, roaming, defaultNetwork)); + } + final NetworkStats.Entry entry = stats.getValues(i, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java new file mode 100644 index 000000000000..9fa1c50423d9 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2016 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.net; + +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.NetworkIdentity.OEM_NONE; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkTemplate.buildTemplateMobileAll; +import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.TrafficStats.MB_IN_BYTES; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; + +import android.app.usage.NetworkStatsManager; +import android.net.DataUsageRequest; +import android.net.NetworkIdentity; +import android.net.NetworkStats; +import android.net.NetworkTemplate; +import android.os.ConditionVariable; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Messenger; +import android.os.Process; +import android.os.UserHandle; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.net.NetworkStatsServiceTest.LatchedHandler; +import com.android.testutils.HandlerUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Objects; + +/** + * Tests for {@link NetworkStatsObservers}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsObserversTest { + private static final String TEST_IFACE = "test0"; + private static final String TEST_IFACE2 = "test1"; + private static final long TEST_START = 1194220800000L; + + private static final String IMSI_1 = "310004"; + private static final String IMSI_2 = "310260"; + private static final String TEST_SSID = "AndroidAP"; + + private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard(); + private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); + private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); + + private static final int UID_RED = UserHandle.PER_USER_RANGE + 1; + private static final int UID_BLUE = UserHandle.PER_USER_RANGE + 2; + private static final int UID_GREEN = UserHandle.PER_USER_RANGE + 3; + private static final int UID_ANOTHER_USER = 2 * UserHandle.PER_USER_RANGE + 4; + + private static final long WAIT_TIMEOUT_MS = 500; + private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES; + private static final long BASE_BYTES = 7 * MB_IN_BYTES; + private static final int INVALID_TYPE = -1; + + private long mElapsedRealtime; + + private HandlerThread mObserverHandlerThread; + private Handler mObserverNoopHandler; + + private LatchedHandler mHandler; + + private NetworkStatsObservers mStatsObservers; + private Messenger mMessenger; + private ArrayMap mActiveIfaces; + private ArrayMap mActiveUidIfaces; + + @Mock private IBinder mockBinder; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mObserverHandlerThread = new HandlerThread("HandlerThread"); + mObserverHandlerThread.start(); + final Looper observerLooper = mObserverHandlerThread.getLooper(); + mStatsObservers = new NetworkStatsObservers() { + @Override + protected Looper getHandlerLooperLocked() { + return observerLooper; + } + }; + + mHandler = new LatchedHandler(Looper.getMainLooper(), new ConditionVariable()); + mMessenger = new Messenger(mHandler); + + mActiveIfaces = new ArrayMap<>(); + mActiveUidIfaces = new ArrayMap<>(); + } + + @Test + public void testRegister_thresholdTooLow_setsDefaultThreshold() throws Exception { + long thresholdTooLowBytes = 1L; + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdTooLowBytes); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateWifi, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + } + + @Test + public void testRegister_highThreshold_accepted() throws Exception { + long highThresholdBytes = 2 * THRESHOLD_BYTES; + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, highThresholdBytes); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateWifi, request.template)); + assertEquals(highThresholdBytes, request.thresholdInBytes); + } + + @Test + public void testRegister_twoRequests_twoIds() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES); + + DataUsageRequest request1 = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request1.requestId > 0); + assertTrue(Objects.equals(sTemplateWifi, request1.template)); + assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes); + + DataUsageRequest request2 = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request2.requestId > request1.requestId); + assertTrue(Objects.equals(sTemplateWifi, request2.template)); + assertEquals(THRESHOLD_BYTES, request2.thresholdInBytes); + } + + @Test + public void testUnregister_unknownRequest_noop() throws Exception { + DataUsageRequest unknownRequest = new DataUsageRequest( + 123456 /* id */, sTemplateWifi, THRESHOLD_BYTES); + + mStatsObservers.unregister(unknownRequest, UID_RED); + } + + @Test + public void testUnregister_knownRequest_releasesCaller() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); + + mStatsObservers.unregister(request, Process.SYSTEM_UID); + waitForObserverToIdle(); + + Mockito.verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()); + } + + @Test + public void testUnregister_knownRequest_invalidUid_doesNotUnregister() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + UID_RED, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); + + mStatsObservers.unregister(request, UID_BLUE); + waitForObserverToIdle(); + + Mockito.verifyZeroInteractions(mockBinder); + } + + private NetworkIdentitySet makeTestIdentSet() { + NetworkIdentitySet identSet = new NetworkIdentitySet(); + identSet.add(new NetworkIdentity( + TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, + IMSI_1, null /* networkId */, false /* roaming */, true /* metered */, + true /* defaultNetwork */, OEM_NONE)); + return identSet; + } + + @Test + public void testUpdateStats_initialSample_doesNotNotify() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) + .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); + NetworkStats uidSnapshot = null; + + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + } + + @Test + public void testUpdateStats_belowThreshold_doesNotNotify() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) + .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); + NetworkStats uidSnapshot = null; + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) + .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + } + + + @Test + public void testUpdateStats_deviceAccess_notifies() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) + .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); + NetworkStats uidSnapshot = null; + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */) + .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L, + BASE_BYTES + THRESHOLD_BYTES, 22L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); + } + + @Test + public void testUpdateStats_defaultAccess_notifiesSameUid() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + UID_RED, NetworkStatsAccess.Level.DEFAULT); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveUidIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = null; + NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); + } + + @Test + public void testUpdateStats_defaultAccess_usageOtherUid_doesNotNotify() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + UID_BLUE, NetworkStatsAccess.Level.DEFAULT); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveUidIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = null; + NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + } + + @Test + public void testUpdateStats_userAccess_usageSameUser_notifies() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + UID_BLUE, NetworkStatsAccess.Level.USER); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveUidIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = null; + NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); + } + + @Test + public void testUpdateStats_userAccess_usageAnotherUser_doesNotNotify() throws Exception { + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); + + DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, + UID_RED, NetworkStatsAccess.Level.USER); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateImsi1, request.template)); + assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); + + NetworkIdentitySet identSet = makeTestIdentSet(); + mActiveUidIfaces.put(TEST_IFACE, identSet); + + // Baseline + NetworkStats xtSnapshot = null; + NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + + // Delta + uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) + .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, + BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); + mStatsObservers.updateStats( + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); + waitForObserverToIdle(); + } + + private void waitForObserverToIdle() { + HandlerUtils.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS); + HandlerUtils.waitForIdle(mHandler, WAIT_TIMEOUT_MS); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java new file mode 100644 index 000000000000..fd374bc9e68f --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -0,0 +1,1767 @@ +/* + * Copyright (C) 2011 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.net; + +import static android.content.Intent.ACTION_UID_REMOVED; +import static android.content.Intent.EXTRA_UID; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkIdentity.OEM_PAID; +import static android.net.NetworkIdentity.OEM_PRIVATE; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.IFACE_ALL; +import static android.net.NetworkStats.INTERFACES_ALL; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.ROAMING_YES; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.STATS_PER_UID; +import static android.net.NetworkStats.TAG_ALL; +import static android.net.NetworkStats.TAG_NONE; +import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStatsHistory.FIELD_ALL; +import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; +import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.OEM_MANAGED_NO; +import static android.net.NetworkTemplate.OEM_MANAGED_YES; +import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT; +import static android.net.NetworkTemplate.buildTemplateMobileAll; +import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; +import static android.net.NetworkTemplate.buildTemplateWifi; +import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.TrafficStats.MB_IN_BYTES; +import static android.net.TrafficStats.UID_REMOVED; +import static android.net.TrafficStats.UID_TETHERING; +import static android.text.format.DateUtils.DAY_IN_MILLIS; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; +import static android.text.format.DateUtils.WEEK_IN_MILLIS; + +import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.app.AlarmManager; +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.net.DataUsageRequest; +import android.net.INetworkManagementEventObserver; +import android.net.INetworkStatsSession; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkStateSnapshot; +import android.net.NetworkStats; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.net.TelephonyNetworkSpecifier; +import android.net.UnderlyingNetworkInfo; +import android.net.netstats.provider.INetworkStatsProviderCallback; +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.Message; +import android.os.Messenger; +import android.os.PowerManager; +import android.os.SimpleClock; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.server.net.NetworkStatsService.NetworkStatsSettings; +import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; +import com.android.testutils.HandlerUtils; +import com.android.testutils.TestableNetworkStatsProviderBinder; + +import libcore.io.IoUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.time.Clock; +import java.time.ZoneOffset; +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Tests for {@link NetworkStatsService}. + * + * TODO: This test used to be really brittle because it used Easymock - it uses Mockito now, but + * still uses the Easymock structure, which could be simplified. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsServiceTest extends NetworkStatsBaseTest { + private static final String TAG = "NetworkStatsServiceTest"; + + private static final long TEST_START = 1194220800000L; + + private static final String IMSI_1 = "310004"; + private static final String IMSI_2 = "310260"; + private static final String TEST_SSID = "AndroidAP"; + + private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID); + private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); + private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); + + private static final Network WIFI_NETWORK = new Network(100); + private static final Network MOBILE_NETWORK = new Network(101); + private static final Network VPN_NETWORK = new Network(102); + + private static final Network[] NETWORKS_WIFI = new Network[]{ WIFI_NETWORK }; + private static final Network[] NETWORKS_MOBILE = new Network[]{ MOBILE_NETWORK }; + + private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs + private static final int INVALID_TYPE = -1; + + private long mElapsedRealtime; + + private File mStatsDir; + private MockContext mServiceContext; + private @Mock TelephonyManager mTelephonyManager; + private @Mock INetworkManagementService mNetManager; + private @Mock NetworkStatsFactory mStatsFactory; + private @Mock NetworkStatsSettings mSettings; + private @Mock IBinder mBinder; + private @Mock AlarmManager mAlarmManager; + @Mock + private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor; + private HandlerThread mHandlerThread; + + private NetworkStatsService mService; + private INetworkStatsSession mSession; + private INetworkManagementEventObserver mNetworkObserver; + private ContentObserver mContentObserver; + private Handler mHandler; + + private class MockContext extends BroadcastInterceptingContext { + private final Context mBaseContext; + + MockContext(Context base) { + super(base); + mBaseContext = base; + } + + @Override + public Object getSystemService(String name) { + if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; + return mBaseContext.getSystemService(name); + } + } + + private final Clock mClock = new SimpleClock(ZoneOffset.UTC) { + @Override + public long millis() { + return currentTimeMillis(); + } + }; + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + final Context context = InstrumentationRegistry.getContext(); + mServiceContext = new MockContext(context); + mStatsDir = context.getFilesDir(); + if (mStatsDir.exists()) { + IoUtils.deleteContents(mStatsDir); + } + + PowerManager powerManager = (PowerManager) mServiceContext.getSystemService( + Context.POWER_SERVICE); + PowerManager.WakeLock wakeLock = + powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + + mHandlerThread = new HandlerThread("HandlerThread"); + final NetworkStatsService.Dependencies deps = makeDependencies(); + mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock, + mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir, + getBaseDir(mStatsDir), deps); + + mElapsedRealtime = 0L; + + expectDefaultSettings(); + expectNetworkStatsUidDetail(buildEmptyStats()); + expectSystemReady(); + mService.systemReady(); + // Verify that system ready fetches realtime stats + verify(mStatsFactory).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL); + // Wait for posting onChange() event to handler thread and verify that when system ready, + // start monitoring data usage per RAT type because the settings value is mock as false + // by default in expectSettings(). + waitForIdle(); + verify(mNetworkStatsSubscriptionsMonitor).start(); + reset(mNetworkStatsSubscriptionsMonitor); + + doReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS).when(mTelephonyManager) + .checkCarrierPrivilegesForPackageAnyPhone(anyString()); + + mSession = mService.openSession(); + assertNotNull("openSession() failed", mSession); + + // catch INetworkManagementEventObserver during systemReady() + ArgumentCaptor networkObserver = + ArgumentCaptor.forClass(INetworkManagementEventObserver.class); + verify(mNetManager).registerObserver(networkObserver.capture()); + mNetworkObserver = networkObserver.getValue(); + } + + @NonNull + private NetworkStatsService.Dependencies makeDependencies() { + return new NetworkStatsService.Dependencies() { + @Override + public HandlerThread makeHandlerThread() { + return mHandlerThread; + } + + @Override + public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor( + @NonNull Context context, @NonNull Looper looper, @NonNull Executor executor, + @NonNull NetworkStatsService service) { + + return mNetworkStatsSubscriptionsMonitor; + } + + @Override + public ContentObserver makeContentObserver(Handler handler, + NetworkStatsSettings settings, NetworkStatsSubscriptionsMonitor monitor) { + mHandler = handler; + return mContentObserver = super.makeContentObserver(handler, settings, monitor); + } + + }; + } + + @After + public void tearDown() throws Exception { + IoUtils.deleteContents(mStatsDir); + + mServiceContext = null; + mStatsDir = null; + + mNetManager = null; + mSettings = null; + + mSession.close(); + mService = null; + + mHandlerThread.quitSafely(); + } + + @Test + public void testNetworkStatsWifi() throws Exception { + // pretend that wifi network comes online; service should ask about full + // network state, and poll any existing interfaces before updating. + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // verify service has empty history for wifi + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + + // modify some number on wifi, and trigger poll event + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + + + // and bump forward again, with counters going higher. this is + // important, since polling should correctly subtract last snapshot. + incrementCurrentTime(DAY_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); + + } + + @Test + public void testStatsRebootPersist() throws Exception { + assertStatsFilesExist(false); + + // pretend that wifi network comes online; service should ask about full + // network state, and poll any existing interfaces before updating. + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // verify service has empty history for wifi + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + + + // modify some number on wifi, and trigger poll event + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L)); + mService.setUidForeground(UID_RED, false); + mService.incrementOperationCount(UID_RED, 0xFAAD, 4); + mService.setUidForeground(UID_RED, true); + mService.incrementOperationCount(UID_RED, 0xFAAD, 6); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); + assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); + assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); + assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); + assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); + + + // graceful shutdown system, which should trigger persist of stats, and + // clear any values in memory. + expectDefaultSettings(); + mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN)); + assertStatsFilesExist(true); + + // boot through serviceReady() again + expectDefaultSettings(); + expectNetworkStatsUidDetail(buildEmptyStats()); + expectSystemReady(); + + mService.systemReady(); + + // after systemReady(), we should have historical stats loaded again + assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); + assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); + assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); + assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); + assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); + + } + + // TODO: simulate reboot to test bucket resize + @Test + @Ignore + public void testStatsBucketResize() throws Exception { + NetworkStatsHistory history = null; + + assertStatsFilesExist(false); + + // pretend that wifi network comes online; service should ask about full + // network state, and poll any existing interfaces before updating. + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // modify some number on wifi, and trigger poll event + incrementCurrentTime(2 * HOUR_IN_MILLIS); + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify service recorded history + history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); + assertEquals(HOUR_IN_MILLIS, history.getBucketDuration()); + assertEquals(2, history.size()); + + + // now change bucket duration setting and trigger another poll with + // exact same values, which should resize existing buckets. + expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify identical stats, but spread across 4 buckets now + history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); + assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration()); + assertEquals(4, history.size()); + + } + + @Test + public void testUidStatsAcrossNetworks() throws Exception { + // pretend first mobile network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some traffic on first network + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); + mService.incrementOperationCount(UID_RED, 0xF00D, 10); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 10); + assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 0); + + + // now switch networks; this also tests that we're okay with interfaces + // disappearing, to verify we don't count backwards. + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_2)}; + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + forcePollAndWaitForIdle(); + + + // create traffic on second network + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L)); + mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10); + + forcePollAndWaitForIdle(); + + // verify original history still intact + assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); + assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 10); + assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 0); + + // and verify new history also recorded under different template, which + // verifies that we didn't cross the streams. + assertNetworkTotal(sTemplateImsi2, 128L, 1L, 1024L, 8L, 0); + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1L, 1024L, 8L, 10); + + } + + @Test + public void testUidRemovedIsMoved() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some traffic + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, + 4096L, 258L, 512L, 32L, 0L) + .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); + mService.incrementOperationCount(UID_RED, 0xFAAD, 10); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0); + assertUidTotal(sTemplateWifi, UID_RED, 16L, 1L, 16L, 1L, 10); + assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 258L, 512L, 32L, 0); + assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0); + + + // now pretend two UIDs are uninstalled, which should migrate stats to + // special "removed" bucket. + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L)); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, + 4096L, 258L, 512L, 32L, 0L) + .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); + final Intent intent = new Intent(ACTION_UID_REMOVED); + intent.putExtra(EXTRA_UID, UID_BLUE); + mServiceContext.sendBroadcast(intent); + intent.putExtra(EXTRA_UID, UID_RED); + mServiceContext.sendBroadcast(intent); + + // existing uid and total should remain unchanged; but removed UID + // should be gone completely. + assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0); + assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L, 0L, 0L, 0); + assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0); + assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 259L, 528L, 33L, 10); + + } + + @Test + public void testMobileStatsByRatType() throws Exception { + final NetworkTemplate template3g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); + final NetworkTemplate template4g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE); + final NetworkTemplate template5g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR); + final NetworkStateSnapshot[] states = + new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; + + // 3G network comes online. + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 12L, 18L, 14L, 1L, 0L))); + forcePollAndWaitForIdle(); + + // Verify 3g templates gets stats. + assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 4G network comes online. + incrementCurrentTime(MINUTE_IN_MILLIS); + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + // Append more traffic on existing 3g stats entry. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 16L, 22L, 17L, 2L, 0L)) + // Add entry that is new on 4g. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 33L, 27L, 8L, 10L, 1L))); + forcePollAndWaitForIdle(); + + // Verify ALL_MOBILE template gets all. 3g template counters do not increase. + assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1); + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + // Verify 4g template counts appended stats on existing entry and newly created entry. + assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); + // Verify 5g template doesn't get anything since no traffic is generated on 5g. + assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); + + // 5g network comes online. + incrementCurrentTime(MINUTE_IN_MILLIS); + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + // Existing stats remains. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 16L, 22L, 17L, 2L, 0L)) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 33L, 27L, 8L, 10L, 1L)) + // Add some traffic on 5g. + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 5L, 13L, 31L, 9L, 2L))); + forcePollAndWaitForIdle(); + + // Verify ALL_MOBILE template gets all. + assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3); + // 3g/4g template counters do not increase. + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); + // Verify 5g template gets the 5g count. + assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2); + } + + @Test + public void testMobileStatsOemManaged() throws Exception { + final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID, + SUBSCRIBER_ID_MATCH_RULE_EXACT); + + final NetworkTemplate templateOemPrivate = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE, + SUBSCRIBER_ID_MATCH_RULE_EXACT); + + final NetworkTemplate templateOemAll = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_PAID | OEM_PRIVATE, SUBSCRIBER_ID_MATCH_RULE_EXACT); + + final NetworkTemplate templateOemYes = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES, + SUBSCRIBER_ID_MATCH_RULE_EXACT); + + final NetworkTemplate templateOemNone = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO, + SUBSCRIBER_ID_MATCH_RULE_EXACT); + + // OEM_PAID network comes online. + NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ + buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 36L, 41L, 24L, 96L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_PRIVATE network comes online. + states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 49L, 71L, 72L, 48L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_PAID + OEM_PRIVATE network comes online. + states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, + NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 57L, 86L, 83L, 93L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_NONE network comes online. + states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 29L, 73L, 34L, 31L, 0L))); + forcePollAndWaitForIdle(); + + // Verify OEM_PAID template gets only relevant stats. + assertUidTotal(templateOemPaid, UID_RED, 36L, 41L, 24L, 96L, 0); + + // Verify OEM_PRIVATE template gets only relevant stats. + assertUidTotal(templateOemPrivate, UID_RED, 49L, 71L, 72L, 48L, 0); + + // Verify OEM_PAID + OEM_PRIVATE template gets only relevant stats. + assertUidTotal(templateOemAll, UID_RED, 57L, 86L, 83L, 93L, 0); + + // Verify OEM_NONE sees only non-OEM managed stats. + assertUidTotal(templateOemNone, UID_RED, 29L, 73L, 34L, 31L, 0); + + // Verify OEM_MANAGED_YES sees all OEM managed stats. + assertUidTotal(templateOemYes, UID_RED, + 36L + 49L + 57L, + 41L + 71L + 86L, + 24L + 72L + 83L, + 96L + 48L + 93L, 0); + + // Verify ALL_MOBILE template gets both OEM managed and non-OEM managed stats. + assertUidTotal(sTemplateImsi1, UID_RED, + 36L + 49L + 57L + 29L, + 41L + 71L + 86L + 73L, + 24L + 72L + 83L + 34L, + 96L + 48L + 93L + 31L, 0); + } + + // TODO: support per IMSI state + private void setMobileRatTypeAndWaitForIdle(int ratType) { + when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString())) + .thenReturn(ratType); + mService.handleOnCollapsedRatTypeChanged(); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + } + + @Test + public void testSummaryForAllUid() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some traffic for two apps + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L)); + mService.incrementOperationCount(UID_RED, 0xF00D, 1); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 1); + assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 8L, 512L, 4L, 0); + + + // now create more traffic in next hour, but only for one app + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, + 2048L, 16L, 1024L, 8L, 0L)); + forcePollAndWaitForIdle(); + + // first verify entire history present + NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(3, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 50L, 5L, 50L, 5L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 10L, 1L, 10L, 1L, 1); + assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 2048L, 16L, 1024L, 8L, 0); + + // now verify that recent history only contains one uid + final long currentTime = currentTimeMillis(); + stats = mSession.getSummaryForAllUid( + sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0); + } + + @Test + public void testDetailedUidStats() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + NetworkStats.Entry entry1 = new NetworkStats.Entry( + TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); + NetworkStats.Entry entry2 = new NetworkStats.Entry( + TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 50L, 5L, 50L, 5L, 0L); + NetworkStats.Entry entry3 = new NetworkStats.Entry( + TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, 1024L, 8L, 512L, 4L, 0L); + + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) + .insertEntry(entry1) + .insertEntry(entry2) + .insertEntry(entry3)); + mService.incrementOperationCount(UID_RED, 0xF00D, 1); + + NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL); + + assertEquals(3, stats.size()); + entry1.operations = 1; + assertEquals(entry1, stats.getValues(0, null)); + entry2.operations = 1; + assertEquals(entry2, stats.getValues(1, null)); + assertEquals(entry3, stats.getValues(2, null)); + } + + @Test + public void testDetailedUidStats_Filtered() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + + final String stackedIface = "stacked-test0"; + final LinkProperties stackedProp = new LinkProperties(); + stackedProp.setInterfaceName(stackedIface); + final NetworkStateSnapshot wifiState = buildWifiState(); + wifiState.getLinkProperties().addStackedLink(stackedProp); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {wifiState}; + + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + NetworkStats.Entry uidStats = new NetworkStats.Entry( + TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); + // Stacked on matching interface + NetworkStats.Entry tetheredStats1 = new NetworkStats.Entry( + stackedIface, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); + // Different interface + NetworkStats.Entry tetheredStats2 = new NetworkStats.Entry( + "otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); + + final String[] ifaceFilter = new String[] { TEST_IFACE }; + final String[] augmentedIfaceFilter = new String[] { stackedIface, TEST_IFACE }; + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + when(mStatsFactory.augmentWithStackedInterfaces(eq(ifaceFilter))) + .thenReturn(augmentedIfaceFilter); + when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL))) + .thenReturn(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(uidStats)); + when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)) + .thenReturn(new NetworkStats(getElapsedRealtime(), 2) + .insertEntry(tetheredStats1) + .insertEntry(tetheredStats2)); + + NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); + + // mStatsFactory#readNetworkStatsDetail() has the following invocations: + // 1) NetworkStatsService#systemReady from #setUp. + // 2) mService#forceUpdateIfaces in the test above. + // + // Additionally, we should have one call from the above call to mService#getDetailedUidStats + // with the augmented ifaceFilter. + verify(mStatsFactory, times(2)).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL); + verify(mStatsFactory, times(1)).readNetworkStatsDetail( + eq(UID_ALL), + eq(augmentedIfaceFilter), + eq(TAG_ALL)); + assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE)); + assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface)); + assertEquals(2, stats.size()); + assertEquals(uidStats, stats.getValues(0, null)); + assertEquals(tetheredStats1, stats.getValues(1, null)); + } + + @Test + public void testForegroundBackground() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some initial traffic + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)); + mService.incrementOperationCount(UID_RED, 0xF00D, 1); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); + + + // now switch to foreground + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L)); + mService.setUidForeground(UID_RED, true); + mService.incrementOperationCount(UID_RED, 0xFAAD, 1); + + forcePollAndWaitForIdle(); + + // test that we combined correctly + assertUidTotal(sTemplateWifi, UID_RED, 160L, 4L, 160L, 4L, 2); + + // verify entire history present + final NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(4, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 32L, 2L, 32L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 1L, 1L, 1L, 1); + } + + @Test + public void testMetered() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = + new NetworkStateSnapshot[] {buildWifiState(true /* isMetered */, TEST_IFACE)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some initial traffic + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO + // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer. + // We layer them on top by inspecting the iface properties. + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); + mService.incrementOperationCount(UID_RED, 0xF00D, 1); + + forcePollAndWaitForIdle(); + + // verify service recorded history + assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); + // verify entire history present + final NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(2, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); + } + + @Test + public void testRoaming() throws Exception { + // pretend that network comes online + expectDefaultSettings(); + NetworkStateSnapshot[] states = + new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1, true /* isRoaming */)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + // Note that all traffic from NetworkManagementService is tagged as METERED_NO and + // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it + // on top by inspecting the iface properties. + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0); + + // verify entire history present + final NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(2, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0); + } + + @Test + public void testTethering() throws Exception { + // pretend first mobile network comes online + expectDefaultSettings(); + final NetworkStateSnapshot[] states = + new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // create some tethering traffic + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST-TETHERING-OFFLOAD", provider); + assertNotNull(cb); + final long now = getElapsedRealtime(); + + // Traffic seen by kernel counters (includes software tethering). + final NetworkStats swIfaceStats = new NetworkStats(now, 1) + .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L); + // Hardware tethering traffic, not seen by kernel counters. + final NetworkStats tetherHwIfaceStats = new NetworkStats(now, 1) + .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_ALL, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 512L, 4L, 128L, 1L, 0L)); + final NetworkStats tetherHwUidStats = new NetworkStats(now, 1) + .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 512L, 4L, 128L, 1L, 0L)); + cb.notifyStatsUpdated(0 /* unused */, tetherHwIfaceStats, tetherHwUidStats); + + // Fake some traffic done by apps on the device (as opposed to tethering), and record it + // into UID stats (as opposed to iface stats). + final NetworkStats localUidStats = new NetworkStats(now, 1) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L); + // Software per-uid tethering traffic. + final NetworkStats tetherSwUidStats = new NetworkStats(now, 1) + .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1408L, 10L, 256L, 1L, + 0L); + + expectNetworkStatsSummary(swIfaceStats); + expectNetworkStatsUidDetail(localUidStats, tetherSwUidStats); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); + assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0); + assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0); + } + + @Test + public void testRegisterUsageCallback() throws Exception { + // pretend that wifi network comes online; service should ask about full + // network state, and poll any existing interfaces before updating. + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // verify service has empty history for wifi + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + long thresholdInBytes = 1L; // very small; should be overriden by framework + DataUsageRequest inputRequest = new DataUsageRequest( + DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdInBytes); + + // Create a messenger that waits for callback activity + ConditionVariable cv = new ConditionVariable(false); + LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv); + Messenger messenger = new Messenger(latchedHandler); + + // Force poll + expectDefaultSettings(); + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register and verify request and that binder was called + DataUsageRequest request = + mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest, + messenger, mBinder); + assertTrue(request.requestId > 0); + assertTrue(Objects.equals(sTemplateWifi, request.template)); + long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB + assertEquals(minThresholdInBytes, request.thresholdInBytes); + + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + + // Make sure that the caller binder gets connected + verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); + + // modify some number on wifi, and trigger poll event + // not enough traffic to call data usage callback + incrementCurrentTime(HOUR_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + + // make sure callback has not being called + assertEquals(INVALID_TYPE, latchedHandler.lastMessageType); + + // and bump forward again, with counters going higher. this is + // important, since it will trigger the data usage callback + incrementCurrentTime(DAY_IN_MILLIS); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) + .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L)); + expectNetworkStatsUidDetail(buildEmptyStats()); + forcePollAndWaitForIdle(); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 4096000L, 4L, 8192000L, 8L, 0); + + + // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED + assertTrue(cv.block(WAIT_TIMEOUT)); + assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.lastMessageType); + cv.close(); + + // Allow binder to disconnect + when(mBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt())).thenReturn(true); + + // Unregister request + mService.unregisterUsageRequest(request); + + // Wait for the caller to ack receipt of CALLBACK_RELEASED + assertTrue(cv.block(WAIT_TIMEOUT)); + assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.lastMessageType); + + // Make sure that the caller binder gets disconnected + verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()); + } + + @Test + public void testUnregisterUsageCallback_unknown_noop() throws Exception { + String callingPackage = "the.calling.package"; + long thresholdInBytes = 10 * 1024 * 1024; // 10 MB + DataUsageRequest unknownRequest = new DataUsageRequest( + 2 /* requestId */, sTemplateImsi1, thresholdInBytes); + + mService.unregisterUsageRequest(unknownRequest); + } + + @Test + public void testStatsProviderUpdateStats() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + final NetworkStateSnapshot[] states = + new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Verifies that one requestStatsUpdate will be called during iface update. + provider.expectOnRequestStatsUpdate(0 /* unused */); + + // Create some initial traffic and report to the service. + incrementCurrentTime(HOUR_IN_MILLIS); + final NetworkStats expectedStats = new NetworkStats(0L, 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 128L, 2L, 128L, 2L, 1L)) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, + 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 64L, 1L, 64L, 1L, 1L)); + cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); + + // Make another empty mutable stats object. This is necessary since the new NetworkStats + // object will be used to compare with the old one in NetworkStatsRecoder, two of them + // cannot be the same object. + expectNetworkStatsUidDetail(buildEmptyStats()); + + forcePollAndWaitForIdle(); + + // Verifies that one requestStatsUpdate and setAlert will be called during polling. + provider.expectOnRequestStatsUpdate(0 /* unused */); + provider.expectOnSetAlert(MB_IN_BYTES); + + // Verifies that service recorded history, does not verify uid tag part. + assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); + + // Verifies that onStatsUpdated updates the stats accordingly. + final NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(2, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); + + // Verifies that unregister the callback will remove the provider from service. + cb.unregister(); + forcePollAndWaitForIdle(); + provider.assertNoCallback(); + } + + @Test + public void testDualVilteProviderStats() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + final int subId1 = 1; + final int subId2 = 2; + final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ + buildImsState(IMSI_1, subId1, TEST_IFACE), + buildImsState(IMSI_2, subId2, TEST_IFACE2)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Verifies that one requestStatsUpdate will be called during iface update. + provider.expectOnRequestStatsUpdate(0 /* unused */); + + // Create some initial traffic and report to the service. + incrementCurrentTime(HOUR_IN_MILLIS); + final String vtIface1 = NetworkStats.IFACE_VT + subId1; + final String vtIface2 = NetworkStats.IFACE_VT + subId2; + final NetworkStats expectedStats = new NetworkStats(0L, 1) + .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 128L, 2L, 128L, 2L, 1L)) + .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 64L, 1L, 64L, 1L, 1L)); + cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); + + // Make another empty mutable stats object. This is necessary since the new NetworkStats + // object will be used to compare with the old one in NetworkStatsRecoder, two of them + // cannot be the same object. + expectNetworkStatsUidDetail(buildEmptyStats()); + + forcePollAndWaitForIdle(); + + // Verifies that one requestStatsUpdate and setAlert will be called during polling. + provider.expectOnRequestStatsUpdate(0 /* unused */); + provider.expectOnSetAlert(MB_IN_BYTES); + + // Verifies that service recorded history, does not verify uid tag part. + assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1); + + // Verifies that onStatsUpdated updates the stats accordingly. + NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); + + stats = mSession.getSummaryForAllUid( + sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); + + // Verifies that unregister the callback will remove the provider from service. + cb.unregister(); + forcePollAndWaitForIdle(); + provider.assertNoCallback(); + } + + @Test + public void testStatsProviderSetAlert() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + NetworkStateSnapshot[] states = + new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + // Simulates alert quota of the provider has been reached. + cb.notifyAlertReached(); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + + // Verifies that polling is triggered by alert reached. + provider.expectOnRequestStatsUpdate(0 /* unused */); + // Verifies that global alert will be re-armed. + provider.expectOnSetAlert(MB_IN_BYTES); + } + + private void setCombineSubtypeEnabled(boolean enable) { + when(mSettings.getCombineSubtypeEnabled()).thenReturn(enable); + mHandler.post(() -> mContentObserver.onChange(false, Settings.Global + .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED))); + waitForIdle(); + if (enable) { + verify(mNetworkStatsSubscriptionsMonitor).stop(); + } else { + verify(mNetworkStatsSubscriptionsMonitor).start(); + } + } + + @Test + public void testDynamicWatchForNetworkRatTypeChanges() throws Exception { + // Build 3G template, type unknown template to get stats while network type is unknown + // and type all template to get the sum of all network type stats. + final NetworkTemplate template3g = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); + final NetworkTemplate templateUnknown = + buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN); + final NetworkTemplate templateAll = + buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL); + final NetworkStateSnapshot[] states = + new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; + + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // 3G network comes online. + setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 12L, 18L, 14L, 1L, 0L))); + forcePollAndWaitForIdle(); + + // Since CombineSubtypeEnabled is false by default in unit test, the generated traffic + // will be split by RAT type. Verify 3G templates gets stats, while template with unknown + // RAT type gets nothing, and template with NETWORK_TYPE_ALL gets all stats. + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(templateUnknown, UID_RED, 0L, 0L, 0L, 0L, 0); + assertUidTotal(templateAll, UID_RED, 12L, 18L, 14L, 1L, 0); + + // Stop monitoring data usage per RAT type changes NetworkStatsService records data + // to {@link TelephonyManager#NETWORK_TYPE_UNKNOWN}. + setCombineSubtypeEnabled(true); + + // Call handleOnCollapsedRatTypeChanged manually to simulate the callback fired + // when stopping monitor, this is needed by NetworkStatsService to trigger updateIfaces. + mService.handleOnCollapsedRatTypeChanged(); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + // Append more traffic on existing snapshot. + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 12L + 4L, 18L + 4L, 14L + 3L, 1L + 1L, 0L)) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 35L, 29L, 7L, 11L, 1L))); + forcePollAndWaitForIdle(); + + // Verify 3G counters do not increase, while template with unknown RAT type gets new + // traffic and template with NETWORK_TYPE_ALL gets all stats. + assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); + assertUidTotal(templateUnknown, UID_RED, 4L + 35L, 4L + 29L, 3L + 7L, 1L + 11L, 1); + assertUidTotal(templateAll, UID_RED, 16L + 35L, 22L + 29L, 17L + 7L, 2L + 11L, 1); + + // Start monitoring data usage per RAT type changes and NetworkStatsService records data + // by a granular subtype representative of the actual subtype + setCombineSubtypeEnabled(false); + + mService.handleOnCollapsedRatTypeChanged(); + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + // Append more traffic on existing snapshot. + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 22L, 26L, 19L, 5L, 0L)) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, + 35L, 29L, 7L, 11L, 1L))); + forcePollAndWaitForIdle(); + + // Verify traffic is split by RAT type, no increase on template with unknown RAT type + // and template with NETWORK_TYPE_ALL gets all stats. + assertUidTotal(template3g, UID_RED, 6L + 12L , 4L + 18L, 2L + 14L, 3L + 1L, 0); + assertUidTotal(templateUnknown, UID_RED, 4L + 35L, 4L + 29L, 3L + 7L, 1L + 11L, 1); + assertUidTotal(templateAll, UID_RED, 22L + 35L, 26L + 29L, 19L + 7L, 5L + 11L, 1); + } + + @Test + public void testOperationCount_nonDefault_traffic() throws Exception { + // Pretend mobile network comes online, but wifi is the default network. + expectDefaultSettings(); + NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ + buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)}; + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic on mobile network. + incrementCurrentTime(HOUR_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_NO, 2L, 1L, 3L, 4L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, + DEFAULT_NETWORK_YES, 1L, 3L, 2L, 1L, 0L) + .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 5L, 4L, 1L, 4L, 0L)); + // Increment operation count, which must have a specific tag. + mService.incrementOperationCount(UID_RED, 0xF00D, 2); + forcePollAndWaitForIdle(); + + // Verify mobile summary is not changed by the operation count. + final NetworkTemplate templateMobile = + buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL); + final NetworkStats statsMobile = mSession.getSummaryForAllUid( + templateMobile, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 3L, 4L, 5L, 5L, 0); + assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, 0xF00D, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 5L, 4L, 1L, 4L, 0); + + // Verify the operation count is blamed onto the default network. + // TODO: Blame onto the default network is not very reasonable. Consider blame onto the + // network that generates the traffic. + final NetworkTemplate templateWifi = buildTemplateWifiWildcard(); + final NetworkStats statsWifi = mSession.getSummaryForAllUid( + templateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertValues(statsWifi, IFACE_ALL, UID_RED, SET_ALL, 0xF00D, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 2); + } + + private static File getBaseDir(File statsDir) { + File baseDir = new File(statsDir, "netstats"); + baseDir.mkdirs(); + return baseDir; + } + + private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets, + long txBytes, long txPackets, int operations) throws Exception { + assertNetworkTotal(template, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, + txPackets, operations); + } + + private void assertNetworkTotal(NetworkTemplate template, long start, long end, long rxBytes, + long rxPackets, long txBytes, long txPackets, int operations) throws Exception { + // verify history API + final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELD_ALL); + assertValues(history, start, end, rxBytes, rxPackets, txBytes, txPackets, operations); + + // verify summary API + final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end); + assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, rxBytes, rxPackets, txBytes, txPackets, operations); + } + + private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets, + long txBytes, long txPackets, int operations) throws Exception { + assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + rxBytes, rxPackets, txBytes, txPackets, operations); + } + + private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered, + int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, + long txPackets, int operations) throws Exception { + // verify history API + final NetworkStatsHistory history = mSession.getHistoryForUid( + template, uid, set, TAG_NONE, FIELD_ALL); + assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, + txPackets, operations); + + // verify summary API + final NetworkStats stats = mSession.getSummaryForAllUid( + template, Long.MIN_VALUE, Long.MAX_VALUE, false); + assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, defaultNetwork, + rxBytes, rxPackets, txBytes, txPackets, operations); + } + + private void expectSystemReady() throws Exception { + expectNetworkStatsSummary(buildEmptyStats()); + } + + private String getActiveIface(NetworkStateSnapshot... states) throws Exception { + if (states == null || states.length == 0 || states[0].getLinkProperties() == null) { + return null; + } + return states[0].getLinkProperties().getInterfaceName(); + } + + private void expectNetworkStatsSummary(NetworkStats summary) throws Exception { + expectNetworkStatsSummaryDev(summary.clone()); + expectNetworkStatsSummaryXt(summary.clone()); + } + + private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception { + when(mStatsFactory.readNetworkStatsSummaryDev()).thenReturn(summary); + } + + private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception { + when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary); + } + + private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { + expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0)); + } + + private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats) + throws Exception { + when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL)) + .thenReturn(detail); + + // also include tethering details, since they are folded into UID + when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats); + } + + private void expectDefaultSettings() throws Exception { + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + } + + private void expectSettings(long persistBytes, long bucketDuration, long deleteAge) + throws Exception { + when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS); + when(mSettings.getPollDelay()).thenReturn(0L); + when(mSettings.getSampleEnabled()).thenReturn(true); + when(mSettings.getCombineSubtypeEnabled()).thenReturn(false); + + final Config config = new Config(bucketDuration, deleteAge, deleteAge); + when(mSettings.getDevConfig()).thenReturn(config); + when(mSettings.getXtConfig()).thenReturn(config); + when(mSettings.getUidConfig()).thenReturn(config); + when(mSettings.getUidTagConfig()).thenReturn(config); + + when(mSettings.getGlobalAlertBytes(anyLong())).thenReturn(MB_IN_BYTES); + when(mSettings.getDevPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); + when(mSettings.getXtPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); + when(mSettings.getUidPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); + when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); + } + + private void assertStatsFilesExist(boolean exist) { + final File basePath = new File(mStatsDir, "netstats"); + if (exist) { + assertTrue(basePath.list().length > 0); + } else { + assertTrue(basePath.list().length == 0); + } + } + + private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, + long rxPackets, long txBytes, long txPackets, int operations) { + final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } + + private static NetworkStateSnapshot buildWifiState() { + return buildWifiState(false, TEST_IFACE); + } + + private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(iface); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + capabilities.setSSID(TEST_SSID); + return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI); + } + + private static NetworkStateSnapshot buildMobile3gState(String subscriberId) { + return buildMobile3gState(subscriberId, false /* isRoaming */); + } + + private static NetworkStateSnapshot buildMobile3gState(String subscriberId, boolean isRoaming) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_IFACE); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + return new NetworkStateSnapshot( + MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); + } + + private NetworkStats buildEmptyStats() { + return new NetworkStats(getElapsedRealtime(), 0); + } + + private static NetworkStateSnapshot buildOemManagedMobileState( + String subscriberId, boolean isRoaming, int[] oemNetCapabilities) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_IFACE); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); + for (int nc : oemNetCapabilities) { + capabilities.setCapability(nc, true); + } + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + return new NetworkStateSnapshot(MOBILE_NETWORK, capabilities, prop, subscriberId, + TYPE_MOBILE); + } + + private static NetworkStateSnapshot buildImsState( + String subscriberId, int subId, String ifaceName) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(ifaceName); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)); + return new NetworkStateSnapshot( + MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); + } + + private long getElapsedRealtime() { + return mElapsedRealtime; + } + + private long startTimeMillis() { + return TEST_START; + } + + private long currentTimeMillis() { + return startTimeMillis() + mElapsedRealtime; + } + + private void incrementCurrentTime(long duration) { + mElapsedRealtime += duration; + } + + private void forcePollAndWaitForIdle() { + mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); + waitForIdle(); + } + + private void waitForIdle() { + HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); + } + + static class LatchedHandler extends Handler { + private final ConditionVariable mCv; + int lastMessageType = INVALID_TYPE; + + LatchedHandler(Looper looper, ConditionVariable cv) { + super(looper); + mCv = cv; + } + + @Override + public void handleMessage(Message msg) { + lastMessageType = msg.what; + mCv.open(); + super.handleMessage(msg); + } + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java new file mode 100644 index 000000000000..6d2c7dc39ffd --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java @@ -0,0 +1,386 @@ +/* + * 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.net; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.net.NetworkTemplate; +import android.os.test.TestLooper; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import com.android.internal.util.CollectionUtils; +import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +@RunWith(JUnit4.class) +public final class NetworkStatsSubscriptionsMonitorTest { + private static final int TEST_SUBID1 = 3; + private static final int TEST_SUBID2 = 5; + private static final String TEST_IMSI1 = "466921234567890"; + private static final String TEST_IMSI2 = "466920987654321"; + private static final String TEST_IMSI3 = "466929999999999"; + + @Mock private Context mContext; + @Mock private SubscriptionManager mSubscriptionManager; + @Mock private TelephonyManager mTelephonyManager; + @Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate; + private final List mTestSubList = new ArrayList<>(); + + private final Executor mExecutor = Executors.newSingleThreadExecutor(); + private NetworkStatsSubscriptionsMonitor mMonitor; + private TestLooper mTestLooper = new TestLooper(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + + when(mContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE))) + .thenReturn(mSubscriptionManager); + when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))) + .thenReturn(mTelephonyManager); + + mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mTestLooper.getLooper(), + mExecutor, mDelegate); + } + + @Test + public void testStartStop() { + // Verify that addOnSubscriptionsChangedListener() is never called before start(). + verify(mSubscriptionManager, never()) + .addOnSubscriptionsChangedListener(mExecutor, mMonitor); + mMonitor.start(); + verify(mSubscriptionManager).addOnSubscriptionsChangedListener(mExecutor, mMonitor); + + // Verify that removeOnSubscriptionsChangedListener() is never called before stop() + verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(mMonitor); + mMonitor.stop(); + verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mMonitor); + } + + @NonNull + private static int[] convertArrayListToIntArray(@NonNull List arrayList) { + final int[] list = new int[arrayList.size()]; + for (int i = 0; i < arrayList.size(); i++) { + list[i] = arrayList.get(i); + } + return list; + } + + private void setRatTypeForSub(List listeners, + int subId, int type) { + final ServiceState serviceState = mock(ServiceState.class); + when(serviceState.getDataNetworkType()).thenReturn(type); + final RatTypeListener match = CollectionUtils + .find(listeners, it -> it.getSubId() == subId); + if (match == null) { + fail("Could not find listener with subId: " + subId); + } + match.onServiceStateChanged(serviceState); + } + + private void addTestSub(int subId, String subscriberId) { + // add SubId to TestSubList. + if (mTestSubList.contains(subId)) fail("The subscriber list already contains this ID"); + + mTestSubList.add(subId); + + final int[] subList = convertArrayListToIntArray(mTestSubList); + when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList); + when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId); + mMonitor.onSubscriptionsChanged(); + } + + private void updateSubscriberIdForTestSub(int subId, @Nullable final String subscriberId) { + when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId); + mMonitor.onSubscriptionsChanged(); + } + + private void removeTestSub(int subId) { + // Remove subId from TestSubList. + mTestSubList.removeIf(it -> it == subId); + final int[] subList = convertArrayListToIntArray(mTestSubList); + when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList); + mMonitor.onSubscriptionsChanged(); + } + + private void assertRatTypeChangedForSub(String subscriberId, int ratType) { + assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId)); + final ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(Integer.class); + // Verify callback with the subscriberId and the RAT type should be as expected. + // It will fail if get a callback with an unexpected RAT type. + verify(mDelegate).onCollapsedRatTypeChanged(eq(subscriberId), typeCaptor.capture()); + final int type = typeCaptor.getValue(); + assertEquals(ratType, type); + } + + private void assertRatTypeNotChangedForSub(String subscriberId, int ratType) { + assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType); + // Should never get callback with any RAT type. + verify(mDelegate, never()).onCollapsedRatTypeChanged(eq(subscriberId), anyInt()); + } + + @Test + public void testSubChangedAndRatTypeChanged() { + final ArgumentCaptor ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. + addTestSub(TEST_SUBID1, TEST_IMSI1); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + + // Insert sim2. + addTestSub(TEST_SUBID2, TEST_IMSI2); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + verify(mTelephonyManager, times(2)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + reset(mDelegate); + + // Set RAT type of sim1 to UMTS. + // Verify RAT type of sim1 after subscription gets onCollapsedRatTypeChanged() callback + // and others remain untouched. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + // Set RAT type of sim2 to LTE. + // Verify RAT type of sim2 after subscription gets onCollapsedRatTypeChanged() callback + // and others remain untouched. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID2, + TelephonyManager.NETWORK_TYPE_LTE); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_LTE); + assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + // Remove sim2 and verify that callbacks are fired and RAT type is correct for sim2. + // while the other two remain untouched. + removeTestSub(TEST_SUBID2); + verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + // Set RAT type of sim1 to UNKNOWN. Then stop monitoring subscription changes + // and verify that the listener for sim1 is removed. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + mMonitor.stop(); + verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + } + + + @Test + public void test5g() { + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. Also capture listener for later use. + addTestSub(TEST_SUBID1, TEST_IMSI1); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + final ArgumentCaptor ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + final RatTypeListener listener = CollectionUtils + .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1); + assertNotNull(listener); + + // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs + // NETWORK_TYPE_5G_NSA. + final ServiceState serviceState = mock(ServiceState.class); + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA); + reset(mDelegate); + + // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE. + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); + reset(mDelegate); + + // Verify NR connected with other RAT type does not take effect. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); + listener.onServiceStateChanged(serviceState); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + + // Set RAT type to 5G standalone mode, the RAT type should be NR. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_NR); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + reset(mDelegate); + + // Set NR state to none in standalone mode does not change anything. + when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR); + when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); + listener.onServiceStateChanged(serviceState); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); + } + + @Test + public void testSubscriberIdUnavailable() { + final ArgumentCaptor ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + + mMonitor.start(); + // Insert sim1, set subscriberId to null which is normal in SIM PIN locked case. + // Verify RAT type is NETWORK_TYPE_UNKNOWN and service will not perform listener + // registration. + addTestSub(TEST_SUBID1, null); + verify(mTelephonyManager, never()).listen(any(), anyInt()); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + + // Set IMSI for sim1, verify the listener will be registered. + updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + reset(mTelephonyManager); + when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); + + // Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + + // Set IMSI to null again to simulate somehow IMSI is not available, such as + // modem crash. Verify service should unregister listener. + updateSubscriberIdForTestSub(TEST_SUBID1, null); + verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), + eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + clearInvocations(mTelephonyManager); + + // Simulate somehow IMSI is back. Verify service will register with + // another listener and fire callback accordingly. + final ArgumentCaptor ratTypeListenerCaptor2 = + ArgumentCaptor.forClass(RatTypeListener.class); + updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + clearInvocations(mTelephonyManager); + + // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works. + setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_LTE); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); + reset(mDelegate); + + mMonitor.stop(); + verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()), + eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + } + + /** + * Verify that when IMSI suddenly changed for a given subId, the service will register a new + * listener and unregister the old one, and report changes on updated IMSI. This is for modem + * feature that may be enabled for certain carrier, which changes to use a different IMSI while + * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same. + */ + @Test + public void testSubscriberIdChanged() { + mMonitor.start(); + // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback + // before changing RAT type. + addTestSub(TEST_SUBID1, TEST_IMSI1); + final ArgumentCaptor ratTypeListenerCaptor = + ArgumentCaptor.forClass(RatTypeListener.class); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + + // Set RAT type of sim1 to UMTS. + // Verify RAT type of sim1 changes accordingly. + setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + clearInvocations(mTelephonyManager); + + // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with + // another listener and remove the old one. The RAT type of new IMSI stays at + // NETWORK_TYPE_UNKNOWN until received initial callback from telephony. + final ArgumentCaptor ratTypeListenerCaptor2 = + ArgumentCaptor.forClass(RatTypeListener.class); + updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2); + verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), + eq(PhoneStateListener.LISTEN_SERVICE_STATE)); + verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), + eq(PhoneStateListener.LISTEN_NONE)); + assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); + reset(mDelegate); + + // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received + // from telephony after registration. Verify RAT type of sim1 changes with IMSI2 + // accordingly. + setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, + TelephonyManager.NETWORK_TYPE_UMTS); + assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); + assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS); + reset(mDelegate); + } +} diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java new file mode 100644 index 000000000000..ebbc0ef62548 --- /dev/null +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 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.net.ipmemorystore; + +import static org.junit.Assert.assertEquals; + +import android.net.ipmemorystore.NetworkAttributes; +import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; +import java.net.Inet4Address; +import java.net.UnknownHostException; +import java.util.Arrays; + +/** Unit tests for {@link NetworkAttributes}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NetworkAttributesTest { + private static final String WEIGHT_FIELD_NAME_PREFIX = "WEIGHT_"; + private static final float EPSILON = 0.0001f; + + // This is running two tests to make sure the total weight is the sum of all weights. To be + // sure this is not fireproof, but you'd kind of need to do it on purpose to pass. + @Test + public void testTotalWeight() throws IllegalAccessException, UnknownHostException { + // Make sure that TOTAL_WEIGHT is equal to the sum of the fields starting with WEIGHT_ + float sum = 0f; + final Field[] fieldList = NetworkAttributes.class.getDeclaredFields(); + for (final Field field : fieldList) { + if (!field.getName().startsWith(WEIGHT_FIELD_NAME_PREFIX)) continue; + field.setAccessible(true); + sum += (float) field.get(null); + } + assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON); + + final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk = + new IPv6ProvisioningLossQuirk(3, System.currentTimeMillis() + 7_200_000); + // Use directly the constructor with all attributes, and make sure that when compared + // to itself the score is a clean 1.0f. + final NetworkAttributes na = + new NetworkAttributes( + (Inet4Address) Inet4Address.getByAddress(new byte[] {1, 2, 3, 4}), + System.currentTimeMillis() + 7_200_000, + "some hint", + Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}), + Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})), + 98, ipv6ProvisioningLossQuirk); + assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON); + } +} diff --git a/packages/Connectivity/tests/unit/jni/Android.bp b/packages/Connectivity/tests/unit/jni/Android.bp new file mode 100644 index 000000000000..22a04f5c0945 --- /dev/null +++ b/packages/Connectivity/tests/unit/jni/Android.bp @@ -0,0 +1,32 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_library_shared { + name: "libnetworkstatsfactorytestjni", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], + + srcs: [ + ":lib_networkStatsFactory_native", + "test_onload.cpp", + ], + + shared_libs: [ + "libbpf_android", + "liblog", + "libnativehelper", + "libnetdbpf", + "libnetdutils", + ], +} diff --git a/packages/Connectivity/tests/unit/jni/test_onload.cpp b/packages/Connectivity/tests/unit/jni/test_onload.cpp new file mode 100644 index 000000000000..5194ddb0d882 --- /dev/null +++ b/packages/Connectivity/tests/unit/jni/test_onload.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 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. + */ + +/* + * this is a mini native libaray for NetworkStatsFactoryTest to run properly. It + * load all the native method related to NetworkStatsFactory when test run + */ +#include +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_net_NetworkStatsFactory(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + ALOGE("GetEnv failed!"); + return result; + } + ALOG_ASSERT(env, "Could not retrieve the env!"); + register_android_server_net_NetworkStatsFactory(env); + return JNI_VERSION_1_4; +} diff --git a/packages/Connectivity/tests/unit/res/raw/history_v1 b/packages/Connectivity/tests/unit/res/raw/history_v1 new file mode 100644 index 000000000000..de79491c032e Binary files /dev/null and b/packages/Connectivity/tests/unit/res/raw/history_v1 differ diff --git a/packages/Connectivity/tests/unit/res/raw/net_dev_typical b/packages/Connectivity/tests/unit/res/raw/net_dev_typical new file mode 100644 index 000000000000..290bf03eb9b4 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/net_dev_typical @@ -0,0 +1,8 @@ +Inter-| Receive | Transmit + face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0 +rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0 + ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 + ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 + sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0 +ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0 diff --git a/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 new file mode 100644 index 000000000000..e75fc1ca5c2e Binary files /dev/null and b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 differ diff --git a/packages/Connectivity/tests/unit/res/raw/netstats_v1 b/packages/Connectivity/tests/unit/res/raw/netstats_v1 new file mode 100644 index 000000000000..e80860a6b959 Binary files /dev/null and b/packages/Connectivity/tests/unit/res/raw/netstats_v1 differ diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical new file mode 100644 index 000000000000..656d5bb82da4 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical @@ -0,0 +1,4 @@ +ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets +rmnet2 4968 35 3081 39 +rmnet1 11153922 8051 190226 2468 +rmnet0 6824 16 5692 10 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical new file mode 100644 index 000000000000..610723aef2f2 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical @@ -0,0 +1,6 @@ +rmnet3 1 0 0 0 0 20822 501 1149991 815 +rmnet2 1 0 0 0 0 1594 15 1313 15 +rmnet1 1 0 0 0 0 207398 458 166918 565 +rmnet0 1 0 0 0 0 2112 24 700 10 +test1 1 1 2 3 4 5 6 7 8 +test2 0 1 2 3 4 5 6 7 8 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical new file mode 100644 index 000000000000..c1b0d259955c --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical @@ -0,0 +1,71 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 wlan0 0x0 0 0 18621 96 2898 44 312 6 15897 58 2412 32 312 6 1010 16 1576 22 +3 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 wlan0 0x0 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +5 wlan0 0x0 1000 1 1949 13 1078 14 0 0 1600 10 349 3 0 0 600 10 478 4 +6 wlan0 0x0 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +7 wlan0 0x0 10005 1 32081 38 5315 50 32081 38 0 0 0 0 5315 50 0 0 0 0 +8 wlan0 0x0 10011 0 35777 53 5718 57 0 0 0 0 35777 53 0 0 0 0 5718 57 +9 wlan0 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +10 wlan0 0x0 10014 0 0 0 1098 13 0 0 0 0 0 0 0 0 0 0 1098 13 +11 wlan0 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +12 wlan0 0x0 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549 +13 wlan0 0x0 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +14 wlan0 0x0 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6 +15 wlan0 0x0 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +16 wlan0 0x7fffff0100000000 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549 +17 wlan0 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +18 wlan0 0x7fffff0100000000 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6 +19 wlan0 0x7fffff0100000000 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +20 rmnet2 0x0 0 0 547 5 118 2 40 1 243 1 264 3 0 0 62 1 56 1 +21 rmnet2 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +22 rmnet2 0x0 10001 0 1125899906842624 5 984 11 632 5 0 0 0 0 984 11 0 0 0 0 +23 rmnet2 0x0 10001 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +24 rmnet1 0x0 0 0 26736 174 7098 130 7210 97 18382 64 1144 13 2932 64 4054 64 112 2 +25 rmnet1 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +26 rmnet1 0x0 1000 0 75774 77 18038 78 75335 72 439 5 0 0 17668 73 370 5 0 0 +27 rmnet1 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +28 rmnet1 0x0 10007 0 269945 578 111632 586 269945 578 0 0 0 0 111632 586 0 0 0 0 +29 rmnet1 0x0 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +30 rmnet1 0x0 10011 0 1741256 6918 769778 7019 1741256 6918 0 0 0 0 769778 7019 0 0 0 0 +31 rmnet1 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +32 rmnet1 0x0 10014 0 0 0 786 12 0 0 0 0 0 0 786 12 0 0 0 0 +33 rmnet1 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +34 rmnet1 0x0 10021 0 433533 1454 393420 1604 433533 1454 0 0 0 0 393420 1604 0 0 0 0 +35 rmnet1 0x0 10021 1 21215 33 10278 33 21215 33 0 0 0 0 10278 33 0 0 0 0 +36 rmnet1 0x0 10036 0 6310 25 3284 29 6310 25 0 0 0 0 3284 29 0 0 0 0 +37 rmnet1 0x0 10036 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +38 rmnet1 0x0 10047 0 34264 47 3936 34 34264 47 0 0 0 0 3936 34 0 0 0 0 +39 rmnet1 0x0 10047 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +40 rmnet1 0x4e7700000000 10011 0 9187 27 4248 33 9187 27 0 0 0 0 4248 33 0 0 0 0 +41 rmnet1 0x4e7700000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +42 rmnet1 0x1000000000000000 10007 0 2109 4 791 4 2109 4 0 0 0 0 791 4 0 0 0 0 +43 rmnet1 0x1000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +44 rmnet1 0x1000000400000000 10007 0 9811 22 6286 22 9811 22 0 0 0 0 6286 22 0 0 0 0 +45 rmnet1 0x1000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +46 rmnet1 0x1010000000000000 10021 0 164833 426 135392 527 164833 426 0 0 0 0 135392 527 0 0 0 0 +47 rmnet1 0x1010000000000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +48 rmnet1 0x1144000400000000 10011 0 10112 18 3334 17 10112 18 0 0 0 0 3334 17 0 0 0 0 +49 rmnet1 0x1144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +50 rmnet1 0x1244000400000000 10011 0 1300 3 848 2 1300 3 0 0 0 0 848 2 0 0 0 0 +51 rmnet1 0x1244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +52 rmnet1 0x3000000000000000 10007 0 10389 14 1521 12 10389 14 0 0 0 0 1521 12 0 0 0 0 +53 rmnet1 0x3000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +54 rmnet1 0x3000000400000000 10007 0 238070 380 93938 404 238070 380 0 0 0 0 93938 404 0 0 0 0 +55 rmnet1 0x3000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +56 rmnet1 0x3010000000000000 10021 0 219110 578 227423 676 219110 578 0 0 0 0 227423 676 0 0 0 0 +57 rmnet1 0x3010000000000000 10021 1 742 3 1265 3 742 3 0 0 0 0 1265 3 0 0 0 0 +58 rmnet1 0x3020000000000000 10021 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +59 rmnet1 0x3020000000000000 10021 1 20473 30 9013 30 20473 30 0 0 0 0 9013 30 0 0 0 0 +60 rmnet1 0x3144000400000000 10011 0 43963 92 34414 116 43963 92 0 0 0 0 34414 116 0 0 0 0 +61 rmnet1 0x3144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +62 rmnet1 0x3244000400000000 10011 0 3486 8 1520 9 3486 8 0 0 0 0 1520 9 0 0 0 0 +63 rmnet1 0x3244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +64 rmnet1 0x7fffff0100000000 10021 0 29102 56 8865 60 29102 56 0 0 0 0 8865 60 0 0 0 0 +65 rmnet1 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +66 rmnet1 0x7fffff0300000000 1000 0 995 13 14145 14 995 13 0 0 0 0 14145 14 0 0 0 0 +67 rmnet1 0x7fffff0300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +68 rmnet0 0x0 0 0 4312 49 1288 23 0 0 0 0 4312 49 0 0 0 0 1288 23 +69 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +70 rmnet0 0x0 10080 0 22266 30 20976 30 0 0 0 0 22266 30 0 0 0 0 20976 30 +71 rmnet0 0x0 10080 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface new file mode 100644 index 000000000000..fc92715253ed --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface @@ -0,0 +1,3 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying new file mode 100644 index 000000000000..1ef18894b669 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying @@ -0,0 +1,5 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression new file mode 100644 index 000000000000..6d6bf550bbfa --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic new file mode 100644 index 000000000000..2c2e5d2555f6 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic @@ -0,0 +1,6 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn new file mode 100644 index 000000000000..eb0513b10049 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn @@ -0,0 +1,9 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test_nss_tun1 0x0 1001 0 3000 300 700 70 0 0 0 0 0 0 0 0 0 0 0 0 +5 test_nss_tun1 0x0 1002 0 500 50 250 25 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +7 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 +8 test1 0x0 1004 0 3850 350 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self new file mode 100644 index 000000000000..afcdd7199026 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self @@ -0,0 +1,6 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication new file mode 100644 index 000000000000..d7c7eb9f4ae8 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication @@ -0,0 +1,5 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0 +5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split new file mode 100644 index 000000000000..38a3dce4a834 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0 +4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression new file mode 100644 index 000000000000..d35244b3b4f2 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0 +4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat new file mode 100644 index 000000000000..0d893d515ca2 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat @@ -0,0 +1,8 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 v4-test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat new file mode 100644 index 000000000000..f04b32f08332 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat @@ -0,0 +1,43 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 v4-wlan0 0x0 0 0 256 5 196 4 256 5 0 0 0 0 196 4 0 0 0 0 +3 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 v4-wlan0 0x0 1000 0 30312 25 1770 27 30236 24 76 1 0 0 1694 26 76 1 0 0 +5 v4-wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 v4-wlan0 0x0 10060 0 9398432 6717 169412 4235 9398432 6717 0 0 0 0 169412 4235 0 0 0 0 +7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0 +8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0 +9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +10 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0 +13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +14 wlan0 0x0 10013 0 0 0 144 2 0 0 0 0 0 0 144 2 0 0 0 0 +15 wlan0 0x0 10013 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +16 wlan0 0x0 10018 0 5980263 4715 167667 1922 5972583 4709 0 0 7680 6 167667 1922 0 0 0 0 +17 wlan0 0x0 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0 +18 wlan0 0x0 10060 0 134356 133 8705 74 134356 133 0 0 0 0 8705 74 0 0 0 0 +19 wlan0 0x0 10060 1 294709 326 26448 256 294709 326 0 0 0 0 26448 256 0 0 0 0 +20 wlan0 0x0 10079 0 10926 13 1507 13 10926 13 0 0 0 0 1507 13 0 0 0 0 +21 wlan0 0x0 10079 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +22 wlan0 0x0 10102 0 25038 42 8245 57 25038 42 0 0 0 0 8245 57 0 0 0 0 +23 wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +24 wlan0 0x0 10103 0 0 0 192 2 0 0 0 0 0 0 0 0 192 2 0 0 +25 wlan0 0x0 10103 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +26 wlan0 0x1000040700000000 10018 0 831 6 655 5 831 6 0 0 0 0 655 5 0 0 0 0 +27 wlan0 0x1000040700000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +28 wlan0 0x1000040b00000000 10018 0 1714 8 1561 7 1714 8 0 0 0 0 1561 7 0 0 0 0 +29 wlan0 0x1000040b00000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +30 wlan0 0x1000120300000000 10018 0 8243 11 2234 12 8243 11 0 0 0 0 2234 12 0 0 0 0 +31 wlan0 0x1000120300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +32 wlan0 0x1000180300000000 10018 0 56368 49 4790 39 56368 49 0 0 0 0 4790 39 0 0 0 0 +33 wlan0 0x1000180300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +34 wlan0 0x1000300000000000 10018 0 9488 17 18813 25 1808 11 0 0 7680 6 18813 25 0 0 0 0 +35 wlan0 0x1000300000000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +36 wlan0 0x3000180400000000 10018 0 131262 103 7416 103 131262 103 0 0 0 0 7416 103 0 0 0 0 +37 wlan0 0x3000180400000000 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0 +38 wlan0 0xffffff0100000000 10018 0 5771986 4518 131190 1725 5771986 4518 0 0 0 0 131190 1725 0 0 0 0 +39 wlan0 0xffffff0100000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +40 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 +41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8 +43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after new file mode 100644 index 000000000000..12d98ca29f57 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after @@ -0,0 +1,189 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6 +3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 v4-wlan0 0x0 0 0 58952 2072 2888 65 264 6 0 0 58688 2066 132 3 0 0 2756 62 +5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0 +7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0 +10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0 +11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0 +12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0 +13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0 +15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0 +16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 +17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0 +19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0 +21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +22 wlan0 0x0 10020 0 21163552 34395 2351650 15326 21162947 34390 605 5 0 0 2351045 15321 605 5 0 0 +23 wlan0 0x0 10020 1 13835740 12938 1548795 6365 13833754 12920 1986 18 0 0 1546809 6347 1986 18 0 0 +24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0 +25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0 +27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0 +28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0 +29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0 +30 wlan0 0x0 10057 0 12312074 9339 436098 5450 12248060 9263 64014 76 0 0 414224 5388 21874 62 0 0 +31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0 +32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0 +33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0 +34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0 +35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0 +37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0 +39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0 +40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0 +41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0 +43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0 +45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0 +47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 +48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0 +49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0 +50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0 +51 wlan0 0x0 10106 1 88923475 64963 1606962 35612 88917201 64886 3586 29 2688 48 1602032 35535 4930 77 0 0 +52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0 +53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0 +54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0 +55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0 +57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0 +59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0 +61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 +62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0 +63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0 +65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0 +67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0 +69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 +70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0 +71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0 +73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0 +75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 +76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0 +77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0 +79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0 +80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0 +81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0 +83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0 +84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0 +85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0 +86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0 +87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0 +89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0 +90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0 +91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0 +92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0 +93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0 +95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0 +97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0 +100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0 +101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0 +102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0 +103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0 +104 wlan0 0x3000040100000000 10020 0 113809 342 135666 308 113809 342 0 0 0 0 135666 308 0 0 0 0 +105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0 +106 wlan0 0x3000040700000000 10020 0 365815 5119 213340 2733 365815 5119 0 0 0 0 213340 2733 0 0 0 0 +107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0 +108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0 +109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0 +110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0 +111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0 +112 wlan0 0x3000090000000000 10020 0 11826 67 7309 52 11826 67 0 0 0 0 7309 52 0 0 0 0 +113 wlan0 0x3000090000000000 10020 1 24805 41 4785 41 24805 41 0 0 0 0 4785 41 0 0 0 0 +114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0 +116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0 +117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0 +118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0 +119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0 +120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0 +121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0 +122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0 +123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0 +124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0 +125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0 +126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0 +127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0 +128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 +129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0 +130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0 +131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0 +132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0 +133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0 +134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 +135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0 +136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0 +138 wlan0 0x3000320100000000 10020 0 6844 14 3745 13 6844 14 0 0 0 0 3745 13 0 0 0 0 +139 wlan0 0x3000320100000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +140 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0 +141 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0 +142 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0 +143 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +144 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0 +145 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0 +146 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +147 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0 +148 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0 +149 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +150 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +151 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0 +152 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +153 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0 +154 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +155 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0 +156 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +157 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0 +158 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0 +159 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +160 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +161 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0 +162 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0 +163 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0 +164 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0 +165 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0 +166 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 +167 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +168 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0 +169 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0 +170 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0 +171 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0 +172 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0 +173 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +174 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0 +175 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +176 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0 +177 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +178 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +179 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0 +180 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0 +181 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0 +182 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0 +183 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0 +184 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0 +185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 +187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285 +189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before new file mode 100644 index 000000000000..ce4bcc3a3b43 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before @@ -0,0 +1,187 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6 +3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 v4-wlan0 0x0 0 0 58848 2070 2836 64 160 4 0 0 58688 2066 80 2 0 0 2756 62 +5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0 +7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0 +10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0 +11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0 +12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0 +13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0 +15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0 +16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 +17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0 +19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0 +21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +22 wlan0 0x0 10020 0 21141412 34316 2329881 15262 21140807 34311 605 5 0 0 2329276 15257 605 5 0 0 +23 wlan0 0x0 10020 1 13835740 12938 1548555 6362 13833754 12920 1986 18 0 0 1546569 6344 1986 18 0 0 +24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0 +25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0 +27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0 +28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0 +29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0 +30 wlan0 0x0 10057 0 12311735 9335 435954 5448 12247721 9259 64014 76 0 0 414080 5386 21874 62 0 0 +31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0 +32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0 +33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0 +34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0 +35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0 +37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0 +39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0 +40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0 +41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0 +43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0 +45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0 +47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 +48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0 +49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0 +50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0 +51 wlan0 0x0 10106 1 88922349 64952 1605126 35599 88916075 64875 3586 29 2688 48 1600196 35522 4930 77 0 0 +52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0 +53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0 +54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0 +55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0 +57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0 +59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0 +61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 +62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0 +63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0 +65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0 +67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0 +69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 +70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0 +71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0 +73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0 +75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 +76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0 +77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0 +79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0 +80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0 +81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0 +83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0 +84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0 +85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0 +86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0 +87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0 +89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0 +90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0 +91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0 +92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0 +93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0 +95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0 +97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0 +100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0 +101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0 +102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0 +103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0 +104 wlan0 0x3000040100000000 10020 0 106718 322 121557 287 106718 322 0 0 0 0 121557 287 0 0 0 0 +105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0 +106 wlan0 0x3000040700000000 10020 0 365419 5113 213124 2730 365419 5113 0 0 0 0 213124 2730 0 0 0 0 +107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0 +108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0 +109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0 +110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0 +111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0 +112 wlan0 0x3000090000000000 10020 0 4017 28 3610 25 4017 28 0 0 0 0 3610 25 0 0 0 0 +113 wlan0 0x3000090000000000 10020 1 24805 41 4545 38 24805 41 0 0 0 0 4545 38 0 0 0 0 +114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0 +116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0 +117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0 +118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0 +119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0 +120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0 +121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0 +122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0 +123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0 +124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0 +125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0 +126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0 +127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0 +128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 +129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0 +130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0 +131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0 +132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0 +133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0 +134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 +135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0 +136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0 +138 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0 +139 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0 +140 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0 +141 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +142 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0 +143 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0 +144 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +145 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0 +146 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0 +147 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +148 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +149 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0 +150 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +151 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0 +152 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +153 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0 +154 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +155 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0 +156 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0 +157 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +158 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +159 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0 +160 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0 +161 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0 +162 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0 +163 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0 +164 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 +165 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +166 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0 +167 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0 +168 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0 +169 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0 +170 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0 +171 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +172 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0 +173 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +174 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0 +175 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +176 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +177 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0 +178 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0 +179 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0 +180 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0 +181 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0 +182 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0 +183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 +185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283 +187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple new file mode 100644 index 000000000000..a1d6d411bad8 --- /dev/null +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0 +3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +4 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/Android.bp b/tests/net/Android.bp deleted file mode 100644 index 6f503ace037f..000000000000 --- a/tests/net/Android.bp +++ /dev/null @@ -1,89 +0,0 @@ -//######################################################################## -// Build FrameworksNetTests package -//######################################################################## -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_defaults { - name: "FrameworksNetTests-jni-defaults", - jni_libs: [ - "ld-android", - "libbacktrace", - "libbase", - "libbinder", - "libbpf", - "libbpf_android", - "libc++", - "libcgrouprc", - "libcrypto", - "libcutils", - "libdl_android", - "libhidl-gen-utils", - "libhidlbase", - "libjsoncpp", - "liblog", - "liblzma", - "libnativehelper", - "libnetdbpf", - "libnetdutils", - "libnetworkstatsfactorytestjni", - "libpackagelistparser", - "libpcre2", - "libprocessgroup", - "libselinux", - "libtinyxml2", - "libui", - "libunwindstack", - "libutils", - "libutilscallstack", - "libvndksupport", - "libziparchive", - "libz", - "netd_aidl_interface-V5-cpp", - ], -} - -android_test { - name: "FrameworksNetTests", - defaults: [ - "framework-connectivity-test-defaults", - "FrameworksNetTests-jni-defaults", - ], - srcs: [ - "java/**/*.java", - "java/**/*.kt", - ], - test_suites: ["device-tests"], - certificate: "platform", - jarjar_rules: "jarjar-rules.txt", - static_libs: [ - "androidx.test.rules", - "bouncycastle-repackaged-unbundled", - "FrameworksNetCommonTests", - "frameworks-base-testutils", - "frameworks-net-integration-testutils", - "framework-protos", - "mockito-target-minus-junit4", - "net-tests-utils", - "platform-test-annotations", - "service-connectivity-pre-jarjar", - "services.core", - "services.net", - ], - libs: [ - "android.net.ipsec.ike.stubs.module_lib", - "android.test.runner", - "android.test.base", - "android.test.mock", - "ServiceConnectivityResources", - ], - jni_libs: [ - "libservice-connectivity", - ], -} diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml deleted file mode 100644 index 4c60ccf60615..000000000000 --- a/tests/net/AndroidManifest.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/net/AndroidTest.xml b/tests/net/AndroidTest.xml deleted file mode 100644 index 939ae493b280..000000000000 --- a/tests/net/AndroidTest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/tests/net/OWNERS b/tests/net/OWNERS deleted file mode 100644 index d3836d4c6c57..000000000000 --- a/tests/net/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING deleted file mode 100644 index 502f885ceb78..000000000000 --- a/tests/net/TEST_MAPPING +++ /dev/null @@ -1,34 +0,0 @@ -{ - "presubmit": [ - { - "name": "FrameworksNetIntegrationTests" - } - ], - "postsubmit": [ - { - "name": "FrameworksNetDeflakeTest" - } - ], - "auto-postsubmit": [ - // Test tag for automotive targets. These are only running in postsubmit so as to harden the - // automotive targets to avoid introducing additional test flake and build time. The plan for - // presubmit testing for auto is to augment the existing tests to cover auto use cases as well. - // Additionally, this tag is used in targeted test suites to limit resource usage on the test - // infra during the hardening phase. - // TODO: this tag to be removed once the above is no longer an issue. - { - "name": "FrameworksNetTests" - }, - { - "name": "FrameworksNetIntegrationTests" - }, - { - "name": "FrameworksNetDeflakeTest" - } - ], - "imports": [ - { - "path": "packages/modules/Connectivity" - } - ] -} \ No newline at end of file diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp deleted file mode 100644 index 439665b67ab7..000000000000 --- a/tests/net/common/Android.bp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (C) 2019 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. -// - -// Tests in this folder are included both in unit tests and CTS. -// They must be fast and stable, and exercise public or test APIs. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library { - name: "FrameworksNetCommonTests", - defaults: ["framework-connectivity-test-defaults"], - srcs: [ - "java/**/*.java", - "java/**/*.kt", - ], - static_libs: [ - "androidx.core_core", - "androidx.test.rules", - "junit", - "mockito-target-minus-junit4", - "modules-utils-build", - "net-tests-utils", - "net-utils-framework-common", - "platform-test-annotations", - ], - libs: [ - "android.test.base.stubs", - ], -} diff --git a/tests/net/common/java/ParseExceptionTest.kt b/tests/net/common/java/ParseExceptionTest.kt deleted file mode 100644 index b702d61a9fe1..000000000000 --- a/tests/net/common/java/ParseExceptionTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2021 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. - */ - -import android.net.ParseException -import android.os.Build -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.testutils.DevSdkIgnoreRule -import junit.framework.Assert.assertEquals -import junit.framework.Assert.assertNull -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class ParseExceptionTest { - @get:Rule - val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.R) - - @Test - fun testConstructor_WithCause() { - val testMessage = "Test message" - val base = Exception("Test") - val exception = ParseException(testMessage, base) - - assertEquals(testMessage, exception.response) - assertEquals(base, exception.cause) - } - - @Test - fun testConstructor_NoCause() { - val testMessage = "Test message" - val exception = ParseException(testMessage) - - assertEquals(testMessage, exception.response) - assertNull(exception.cause) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt deleted file mode 100644 index 18a93319b271..000000000000 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2019 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 - -import android.os.Build -import androidx.test.filters.SmallTest -import com.android.modules.utils.build.SdkLevel -import com.android.testutils.assertParcelSane -import com.android.testutils.assertParcelingIsLossless -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import com.android.testutils.DevSdkIgnoreRunner -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -@SmallTest -@RunWith(DevSdkIgnoreRunner::class) -@IgnoreUpTo(Build.VERSION_CODES.Q) -class CaptivePortalDataTest { - @Rule @JvmField - val ignoreRule = DevSdkIgnoreRule() - - private val data = CaptivePortalData.Builder() - .setRefreshTime(123L) - .setUserPortalUrl(Uri.parse("https://portal.example.com/test")) - .setVenueInfoUrl(Uri.parse("https://venue.example.com/test")) - .setSessionExtendable(true) - .setBytesRemaining(456L) - .setExpiryTime(789L) - .setCaptive(true) - .apply { - if (SdkLevel.isAtLeastS()) { - setVenueFriendlyName("venue friendly name") - } - } - .build() - - private val dataFromPasspoint = CaptivePortalData.Builder() - .setCaptive(true) - .apply { - if (SdkLevel.isAtLeastS()) { - setVenueFriendlyName("venue friendly name") - setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - } - } - .build() - - private fun makeBuilder() = CaptivePortalData.Builder(data) - - @Test - fun testParcelUnparcel() { - val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7 - assertParcelSane(data, fieldCount) - assertParcelSane(dataFromPasspoint, fieldCount) - - assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) - assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) - } - - @Test - fun testEquals() { - assertEquals(data, makeBuilder().build()) - - assertNotEqualsAfterChange { it.setRefreshTime(456L) } - assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) } - assertNotEqualsAfterChange { it.setUserPortalUrl(null) } - assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) } - assertNotEqualsAfterChange { it.setVenueInfoUrl(null) } - assertNotEqualsAfterChange { it.setSessionExtendable(false) } - assertNotEqualsAfterChange { it.setBytesRemaining(789L) } - assertNotEqualsAfterChange { it.setExpiryTime(12L) } - assertNotEqualsAfterChange { it.setCaptive(false) } - - if (SdkLevel.isAtLeastS()) { - assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } - assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } - - assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint")) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/other"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/passpoint")) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/other"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } - } - } - - @Test - fun testUserPortalUrl() { - assertEquals(Uri.parse("https://portal.example.com/test"), data.userPortalUrl) - } - - @Test - fun testVenueInfoUrl() { - assertEquals(Uri.parse("https://venue.example.com/test"), data.venueInfoUrl) - } - - @Test - fun testIsSessionExtendable() { - assertTrue(data.isSessionExtendable) - } - - @Test - fun testByteLimit() { - assertEquals(456L, data.byteLimit) - // Test byteLimit unset. - assertEquals(-1L, CaptivePortalData.Builder(null).build().byteLimit) - } - - @Test - fun testRefreshTimeMillis() { - assertEquals(123L, data.refreshTimeMillis) - } - - @Test - fun testExpiryTimeMillis() { - assertEquals(789L, data.expiryTimeMillis) - // Test expiryTimeMillis unset. - assertEquals(-1L, CaptivePortalData.Builder(null).build().expiryTimeMillis) - } - - @Test - fun testIsCaptive() { - assertTrue(data.isCaptive) - assertFalse(makeBuilder().setCaptive(false).build().isCaptive) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - fun testVenueFriendlyName() { - assertEquals("venue friendly name", data.venueFriendlyName) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - fun testGetVenueInfoUrlSource() { - assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, - data.venueInfoUrlSource) - assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, - dataFromPasspoint.venueInfoUrlSource) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - fun testGetUserPortalUrlSource() { - assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, - data.userPortalUrlSource) - assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, - dataFromPasspoint.userPortalUrlSource) - } - - private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = - CaptivePortalData.Builder(this).apply { mutator(this) }.build() - - private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) { - assertNotEquals(data, data.mutate(mutator)) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/tests/net/common/java/android/net/CaptivePortalTest.java deleted file mode 100644 index 15d3398d43c0..000000000000 --- a/tests/net/common/java/android/net/CaptivePortalTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2019 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; - -import static org.junit.Assert.assertEquals; - -import android.os.Build; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class CaptivePortalTest { - @Rule - public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); - - private static final int DEFAULT_TIMEOUT_MS = 5000; - private static final String TEST_PACKAGE_NAME = "com.google.android.test"; - - private final class MyCaptivePortalImpl extends ICaptivePortal.Stub { - int mCode = -1; - String mPackageName = null; - - @Override - public void appResponse(final int response) throws RemoteException { - mCode = response; - } - - @Override - public void appRequest(final int request) throws RemoteException { - mCode = request; - } - - // This is only @Override on R- - public void logEvent(int eventId, String packageName) throws RemoteException { - mCode = eventId; - mPackageName = packageName; - } - } - - private interface TestFunctor { - void useCaptivePortal(CaptivePortal o); - } - - private MyCaptivePortalImpl runCaptivePortalTest(TestFunctor f) { - final MyCaptivePortalImpl cp = new MyCaptivePortalImpl(); - f.useCaptivePortal(new CaptivePortal(cp.asBinder())); - return cp; - } - - @Test - public void testReportCaptivePortalDismissed() { - final MyCaptivePortalImpl result = - runCaptivePortalTest(c -> c.reportCaptivePortalDismissed()); - assertEquals(result.mCode, CaptivePortal.APP_RETURN_DISMISSED); - } - - @Test - public void testIgnoreNetwork() { - final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.ignoreNetwork()); - assertEquals(result.mCode, CaptivePortal.APP_RETURN_UNWANTED); - } - - @Test - public void testUseNetwork() { - final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.useNetwork()); - assertEquals(result.mCode, CaptivePortal.APP_RETURN_WANTED_AS_IS); - } - - @IgnoreUpTo(Build.VERSION_CODES.Q) - @Test - public void testReevaluateNetwork() { - final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.reevaluateNetwork()); - assertEquals(result.mCode, CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED); - } - - @IgnoreUpTo(Build.VERSION_CODES.R) - @Test - public void testLogEvent() { - /** - * From S testLogEvent is expected to do nothing but shouldn't crash (the API - * logEvent has been deprecated). - */ - final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent( - 0, - TEST_PACKAGE_NAME)); - } - - @IgnoreAfter(Build.VERSION_CODES.R) - @Test - public void testLogEvent_UntilR() { - final MyCaptivePortalImpl result = runCaptivePortalTest(c -> c.logEvent( - 42, TEST_PACKAGE_NAME)); - assertEquals(result.mCode, 42); - assertEquals(result.mPackageName, TEST_PACKAGE_NAME); - } -} diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/tests/net/common/java/android/net/DependenciesTest.java deleted file mode 100644 index ac1c28a45462..000000000000 --- a/tests/net/common/java/android/net/DependenciesTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.concurrent.TimeUnit; - -/** - * A simple class that tests dependencies to java standard tools from the - * Network stack. These tests are not meant to be comprehensive tests of - * the relevant APIs : such tests belong in the relevant test suite for - * these dependencies. Instead, this just makes sure coverage is present - * by calling the methods in the exact way (or a representative way of how) - * they are called in the network stack. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DependenciesTest { - // Used to in ipmemorystore's RegularMaintenanceJobService to convert - // 24 hours into seconds - @Test - public void testTimeUnit() { - final int hours = 24; - final long inSeconds = TimeUnit.HOURS.toMillis(hours); - assertEquals(inSeconds, hours * 60 * 60 * 1000); - } - - private byte[] makeTrivialArray(final int size) { - final byte[] src = new byte[size]; - for (int i = 0; i < size; ++i) { - src[i] = (byte) i; - } - return src; - } - - // Used in ApfFilter to find an IP address from a byte array - @Test - public void testArrays() { - final int size = 128; - final byte[] src = makeTrivialArray(size); - - // Test copy - final int copySize = 16; - final int offset = 24; - final byte[] expected = new byte[copySize]; - for (int i = 0; i < copySize; ++i) { - expected[i] = (byte) (offset + i); - } - - final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize); - assertArrayEquals(expected, copy); - assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size)); - } - - // Used mainly in the Dhcp code - @Test - public void testCopyOf() { - final byte[] src = makeTrivialArray(128); - final byte[] copy = Arrays.copyOf(src, src.length); - assertArrayEquals(src, copy); - assertFalse(src == copy); - - assertArrayEquals(new byte[0], Arrays.copyOf(src, 0)); - - final int excess = 16; - final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess); - for (int i = src.length; i < src.length + excess; ++i) { - assertEquals(0, biggerCopy[i]); - } - for (int i = src.length - 1; i >= 0; --i) { - assertEquals(src[i], biggerCopy[i]); - } - } - - // Used mainly in DnsUtils but also various other places - @Test - public void testAsList() { - final int size = 24; - final Object[] src = new Object[size]; - final ArrayList expected = new ArrayList<>(size); - for (int i = 0; i < size; ++i) { - final Object o = new Object(); - src[i] = o; - expected.add(o); - } - assertEquals(expected, Arrays.asList(src)); - } -} diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java deleted file mode 100644 index ab4726bab573..000000000000 --- a/tests/net/common/java/android/net/DhcpInfoTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2009 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; - -import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL; -import static com.android.testutils.MiscAsserts.assertFieldCountEquals; -import static com.android.testutils.ParcelUtils.parcelingRoundTrip; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.annotation.Nullable; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.net.InetAddress; - -@RunWith(AndroidJUnit4.class) -public class DhcpInfoTest { - private static final String STR_ADDR1 = "255.255.255.255"; - private static final String STR_ADDR2 = "127.0.0.1"; - private static final String STR_ADDR3 = "192.168.1.1"; - private static final String STR_ADDR4 = "192.168.1.0"; - private static final int LEASE_TIME = 9999; - - private int ipToInteger(String ipString) throws Exception { - return inet4AddressToIntHTL((Inet4Address) InetAddress.getByName(ipString)); - } - - private DhcpInfo createDhcpInfoObject() throws Exception { - final DhcpInfo dhcpInfo = new DhcpInfo(); - dhcpInfo.ipAddress = ipToInteger(STR_ADDR1); - dhcpInfo.gateway = ipToInteger(STR_ADDR2); - dhcpInfo.netmask = ipToInteger(STR_ADDR3); - dhcpInfo.dns1 = ipToInteger(STR_ADDR4); - dhcpInfo.dns2 = ipToInteger(STR_ADDR4); - dhcpInfo.serverAddress = ipToInteger(STR_ADDR2); - dhcpInfo.leaseDuration = LEASE_TIME; - return dhcpInfo; - } - - @Test - public void testConstructor() { - new DhcpInfo(); - } - - @Test - public void testToString() throws Exception { - final String expectedDefault = "ipaddr 0.0.0.0 gateway 0.0.0.0 netmask 0.0.0.0 " - + "dns1 0.0.0.0 dns2 0.0.0.0 DHCP server 0.0.0.0 lease 0 seconds"; - - DhcpInfo dhcpInfo = new DhcpInfo(); - - // Test default string. - assertEquals(expectedDefault, dhcpInfo.toString()); - - dhcpInfo = createDhcpInfoObject(); - - final String expected = "ipaddr " + STR_ADDR1 + " gateway " + STR_ADDR2 + " netmask " - + STR_ADDR3 + " dns1 " + STR_ADDR4 + " dns2 " + STR_ADDR4 + " DHCP server " - + STR_ADDR2 + " lease " + LEASE_TIME + " seconds"; - // Test with new values - assertEquals(expected, dhcpInfo.toString()); - } - - private boolean dhcpInfoEquals(@Nullable DhcpInfo left, @Nullable DhcpInfo right) { - if (left == null && right == null) return true; - - if (left == null || right == null) return false; - - return left.ipAddress == right.ipAddress - && left.gateway == right.gateway - && left.netmask == right.netmask - && left.dns1 == right.dns1 - && left.dns2 == right.dns2 - && left.serverAddress == right.serverAddress - && left.leaseDuration == right.leaseDuration; - } - - @Test - public void testParcelDhcpInfo() throws Exception { - // Cannot use assertParcelSane() here because this requires .equals() to work as - // defined, but DhcpInfo has a different legacy behavior that we cannot change. - final DhcpInfo dhcpInfo = createDhcpInfoObject(); - assertFieldCountEquals(7, DhcpInfo.class); - - final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo); - assertTrue(dhcpInfoEquals(null, null)); - assertFalse(dhcpInfoEquals(null, dhcpInfoRoundTrip)); - assertFalse(dhcpInfoEquals(dhcpInfo, null)); - assertTrue(dhcpInfoEquals(dhcpInfo, dhcpInfoRoundTrip)); - } -} diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java deleted file mode 100644 index 50ecb428359e..000000000000 --- a/tests/net/common/java/android/net/IpPrefixTest.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2014 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; - -import static com.android.testutils.MiscAsserts.assertEqualBothWays; -import static com.android.testutils.MiscAsserts.assertFieldCountEquals; -import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -import static org.junit.Assert.assertArrayEquals; -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 androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.util.Random; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpPrefixTest { - - private static InetAddress address(String addr) { - return InetAddress.parseNumericAddress(addr); - } - - // Explicitly cast everything to byte because "error: possible loss of precision". - private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; - private static final byte[] IPV6_BYTES = { - (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, - (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, - (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0 - }; - - @Test - public void testConstructor() { - IpPrefix p; - try { - p = new IpPrefix((byte[]) null, 9); - fail("Expected NullPointerException: null byte array"); - } catch (RuntimeException expected) { } - - try { - p = new IpPrefix((InetAddress) null, 10); - fail("Expected NullPointerException: null InetAddress"); - } catch (RuntimeException expected) { } - - try { - p = new IpPrefix((String) null); - fail("Expected NullPointerException: null String"); - } catch (RuntimeException expected) { } - - - try { - byte[] b2 = {1, 2, 3, 4, 5}; - p = new IpPrefix(b2, 29); - fail("Expected IllegalArgumentException: invalid array length"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("1.2.3.4"); - fail("Expected IllegalArgumentException: no prefix length"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("1.2.3.4/"); - fail("Expected IllegalArgumentException: empty prefix length"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("foo/32"); - fail("Expected IllegalArgumentException: invalid address"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("1/32"); - fail("Expected IllegalArgumentException: deprecated IPv4 format"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("1.2.3.256/32"); - fail("Expected IllegalArgumentException: invalid IPv4 address"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("foo/32"); - fail("Expected IllegalArgumentException: non-address"); - } catch (IllegalArgumentException expected) { } - - try { - p = new IpPrefix("f00:::/32"); - fail("Expected IllegalArgumentException: invalid IPv6 address"); - } catch (IllegalArgumentException expected) { } - - p = new IpPrefix("/64"); - assertEquals("::/64", p.toString()); - - p = new IpPrefix("/128"); - assertEquals("::1/128", p.toString()); - - p = new IpPrefix("[2001:db8::123]/64"); - assertEquals("2001:db8::/64", p.toString()); - } - - @Test - public void testTruncation() { - IpPrefix p; - - p = new IpPrefix(IPV4_BYTES, 32); - assertEquals("192.0.2.4/32", p.toString()); - - p = new IpPrefix(IPV4_BYTES, 29); - assertEquals("192.0.2.0/29", p.toString()); - - p = new IpPrefix(IPV4_BYTES, 8); - assertEquals("192.0.0.0/8", p.toString()); - - p = new IpPrefix(IPV4_BYTES, 0); - assertEquals("0.0.0.0/0", p.toString()); - - try { - p = new IpPrefix(IPV4_BYTES, 33); - fail("Expected IllegalArgumentException: invalid prefix length"); - } catch (RuntimeException expected) { } - - try { - p = new IpPrefix(IPV4_BYTES, 128); - fail("Expected IllegalArgumentException: invalid prefix length"); - } catch (RuntimeException expected) { } - - try { - p = new IpPrefix(IPV4_BYTES, -1); - fail("Expected IllegalArgumentException: negative prefix length"); - } catch (RuntimeException expected) { } - - p = new IpPrefix(IPV6_BYTES, 128); - assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString()); - - p = new IpPrefix(IPV6_BYTES, 122); - assertEquals("2001:db8:dead:beef:f00::80/122", p.toString()); - - p = new IpPrefix(IPV6_BYTES, 64); - assertEquals("2001:db8:dead:beef::/64", p.toString()); - - p = new IpPrefix(IPV6_BYTES, 3); - assertEquals("2000::/3", p.toString()); - - p = new IpPrefix(IPV6_BYTES, 0); - assertEquals("::/0", p.toString()); - - try { - p = new IpPrefix(IPV6_BYTES, -1); - fail("Expected IllegalArgumentException: negative prefix length"); - } catch (RuntimeException expected) { } - - try { - p = new IpPrefix(IPV6_BYTES, 129); - fail("Expected IllegalArgumentException: negative prefix length"); - } catch (RuntimeException expected) { } - - } - - @Test - public void testEquals() { - IpPrefix p1, p2; - - p1 = new IpPrefix("192.0.2.251/23"); - p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); - assertEqualBothWays(p1, p2); - - p1 = new IpPrefix("192.0.2.5/23"); - assertEqualBothWays(p1, p2); - - p1 = new IpPrefix("192.0.2.5/24"); - assertNotEqualEitherWay(p1, p2); - - p1 = new IpPrefix("192.0.4.5/23"); - assertNotEqualEitherWay(p1, p2); - - - p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); - p2 = new IpPrefix(IPV6_BYTES, 122); - assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); - assertEqualBothWays(p1, p2); - - p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); - assertEqualBothWays(p1, p2); - - p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); - assertNotEqualEitherWay(p1, p2); - - p1 = new IpPrefix("2001:db8:dead:beef::/122"); - assertNotEqualEitherWay(p1, p2); - - // 192.0.2.4/32 != c000:0204::/32. - byte[] ipv6bytes = new byte[16]; - System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); - p1 = new IpPrefix(ipv6bytes, 32); - assertEqualBothWays(p1, new IpPrefix("c000:0204::/32")); - - p2 = new IpPrefix(IPV4_BYTES, 32); - assertNotEqualEitherWay(p1, p2); - } - - @Test - public void testContainsInetAddress() { - IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127"); - assertTrue(p.contains(address("2001:db8:f00::ace:d00c"))); - assertTrue(p.contains(address("2001:db8:f00::ace:d00d"))); - assertFalse(p.contains(address("2001:db8:f00::ace:d00e"))); - assertFalse(p.contains(address("2001:db8:f00::bad:d00d"))); - assertFalse(p.contains(address("2001:4868:4860::8888"))); - assertFalse(p.contains(address("8.8.8.8"))); - - p = new IpPrefix("192.0.2.0/23"); - assertTrue(p.contains(address("192.0.2.43"))); - assertTrue(p.contains(address("192.0.3.21"))); - assertFalse(p.contains(address("192.0.0.21"))); - assertFalse(p.contains(address("8.8.8.8"))); - assertFalse(p.contains(address("2001:4868:4860::8888"))); - - IpPrefix ipv6Default = new IpPrefix("::/0"); - assertTrue(ipv6Default.contains(address("2001:db8::f00"))); - assertFalse(ipv6Default.contains(address("192.0.2.1"))); - - IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0"); - assertTrue(ipv4Default.contains(address("255.255.255.255"))); - assertTrue(ipv4Default.contains(address("192.0.2.1"))); - assertFalse(ipv4Default.contains(address("2001:db8::f00"))); - } - - @Test - public void testContainsIpPrefix() { - assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("0.0.0.0/0"))); - assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/0"))); - assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/8"))); - assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/24"))); - assertTrue(new IpPrefix("0.0.0.0/0").containsPrefix(new IpPrefix("1.2.3.4/23"))); - - assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.2.3.4/8"))); - assertTrue(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("1.254.12.9/8"))); - assertTrue(new IpPrefix("1.2.3.4/21").containsPrefix(new IpPrefix("1.2.3.4/21"))); - assertTrue(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.4/32"))); - - assertTrue(new IpPrefix("1.2.3.4/20").containsPrefix(new IpPrefix("1.2.3.0/24"))); - - assertFalse(new IpPrefix("1.2.3.4/32").containsPrefix(new IpPrefix("1.2.3.5/32"))); - assertFalse(new IpPrefix("1.2.3.4/8").containsPrefix(new IpPrefix("2.2.3.4/8"))); - assertFalse(new IpPrefix("0.0.0.0/16").containsPrefix(new IpPrefix("0.0.0.0/15"))); - assertFalse(new IpPrefix("100.0.0.0/8").containsPrefix(new IpPrefix("99.0.0.0/8"))); - - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("::/0"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/1"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("3d8a:661:a0::770/8"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/8"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/64"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/113"))); - assertTrue(new IpPrefix("::/0").containsPrefix(new IpPrefix("2001:db8::f00/128"))); - - assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( - new IpPrefix("2001:db8:f00::ace:d00d/64"))); - assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( - new IpPrefix("2001:db8:f00::ace:d00d/120"))); - assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( - new IpPrefix("2001:db8:f00::ace:d00d/32"))); - assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/64").containsPrefix( - new IpPrefix("2006:db8:f00::ace:d00d/96"))); - - assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( - new IpPrefix("2001:db8:f00::ace:d00d/128"))); - assertTrue(new IpPrefix("2001:db8:f00::ace:d00d/100").containsPrefix( - new IpPrefix("2001:db8:f00::ace:ccaf/110"))); - - assertFalse(new IpPrefix("2001:db8:f00::ace:d00d/128").containsPrefix( - new IpPrefix("2001:db8:f00::ace:d00e/128"))); - assertFalse(new IpPrefix("::/30").containsPrefix(new IpPrefix("::/29"))); - } - - @Test - public void testHashCode() { - IpPrefix p = new IpPrefix(new byte[4], 0); - Random random = new Random(); - for (int i = 0; i < 100; i++) { - final IpPrefix oldP = p; - if (random.nextBoolean()) { - // IPv4. - byte[] b = new byte[4]; - random.nextBytes(b); - p = new IpPrefix(b, random.nextInt(33)); - } else { - // IPv6. - byte[] b = new byte[16]; - random.nextBytes(b); - p = new IpPrefix(b, random.nextInt(129)); - } - if (p.equals(oldP)) { - assertEquals(p.hashCode(), oldP.hashCode()); - } - if (p.hashCode() != oldP.hashCode()) { - assertNotEquals(p, oldP); - } - } - } - - @Test - public void testHashCodeIsNotConstant() { - IpPrefix[] prefixes = { - new IpPrefix("2001:db8:f00::ace:d00d/127"), - new IpPrefix("192.0.2.0/23"), - new IpPrefix("::/0"), - new IpPrefix("0.0.0.0/0"), - }; - for (int i = 0; i < prefixes.length; i++) { - for (int j = i + 1; j < prefixes.length; j++) { - assertNotEquals(prefixes[i].hashCode(), prefixes[j].hashCode()); - } - } - } - - @Test - public void testMappedAddressesAreBroken() { - // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress, - // we are unable to comprehend that. - byte[] ipv6bytes = { - (byte) 0, (byte) 0, (byte) 0, (byte) 0, - (byte) 0, (byte) 0, (byte) 0, (byte) 0, - (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff, - (byte) 192, (byte) 0, (byte) 2, (byte) 0}; - IpPrefix p = new IpPrefix(ipv6bytes, 120); - assertEquals(16, p.getRawAddress().length); // Fine. - assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine. - - // Broken. - assertEquals("192.0.2.0/120", p.toString()); - assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); - } - - @Test - public void testParceling() { - IpPrefix p; - - p = new IpPrefix("2001:4860:db8::/64"); - assertParcelingIsLossless(p); - assertTrue(p.isIPv6()); - - p = new IpPrefix("192.0.2.0/25"); - assertParcelingIsLossless(p); - assertTrue(p.isIPv4()); - - assertFieldCountEquals(2, IpPrefix.class); - } -} diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt deleted file mode 100644 index f464ec6cf0e5..000000000000 --- a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 - -import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS -import android.net.InvalidPacketException.ERROR_INVALID_PORT -import android.os.Build -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import java.net.InetAddress -import java.util.Arrays -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class KeepalivePacketDataTest { - @Rule @JvmField - val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() - - private val INVALID_PORT = 65537 - private val TEST_DST_PORT = 4244 - private val TEST_SRC_PORT = 4243 - - private val TESTBYTES = byteArrayOf(12, 31, 22, 44) - private val TEST_SRC_ADDRV4 = "198.168.0.2".address() - private val TEST_DST_ADDRV4 = "198.168.0.1".address() - private val TEST_ADDRV6 = "2001:db8::1".address() - - private fun String.address() = InetAddresses.parseNumericAddress(this) - - // Add for test because constructor of KeepalivePacketData is protected. - private inner class TestKeepalivePacketData( - srcAddress: InetAddress? = TEST_SRC_ADDRV4, - srcPort: Int = TEST_SRC_PORT, - dstAddress: InetAddress? = TEST_DST_ADDRV4, - dstPort: Int = TEST_DST_PORT, - data: ByteArray = TESTBYTES - ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data) - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testConstructor() { - var data: TestKeepalivePacketData - - try { - data = TestKeepalivePacketData(srcAddress = null) - fail("Null src address should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) - } - - try { - data = TestKeepalivePacketData(dstAddress = null) - fail("Null dst address should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) - } - - try { - data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6) - fail("Ip family mismatched should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) - } - - try { - data = TestKeepalivePacketData(srcPort = INVALID_PORT) - fail("Invalid srcPort should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_PORT) - } - - try { - data = TestKeepalivePacketData(dstPort = INVALID_PORT) - fail("Invalid dstPort should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_PORT) - } - } - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress) - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress) - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort) - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort) - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet)) -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java deleted file mode 100644 index 2cf3cf9c11da..000000000000 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (C) 2013 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; - -import static android.system.OsConstants.IFA_F_DADFAILED; -import static android.system.OsConstants.IFA_F_DEPRECATED; -import static android.system.OsConstants.IFA_F_OPTIMISTIC; -import static android.system.OsConstants.IFA_F_PERMANENT; -import static android.system.OsConstants.IFA_F_TEMPORARY; -import static android.system.OsConstants.IFA_F_TENTATIVE; -import static android.system.OsConstants.RT_SCOPE_HOST; -import static android.system.OsConstants.RT_SCOPE_LINK; -import static android.system.OsConstants.RT_SCOPE_SITE; -import static android.system.OsConstants.RT_SCOPE_UNIVERSE; - -import static com.android.testutils.MiscAsserts.assertEqualBothWays; -import static com.android.testutils.MiscAsserts.assertFieldCountEquals; -import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -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 android.os.Build; -import android.os.SystemClock; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Arrays; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class LinkAddressTest { - @Rule - public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); - - private static final String V4 = "192.0.2.1"; - private static final String V6 = "2001:db8::1"; - private static final InetAddress V4_ADDRESS = InetAddresses.parseNumericAddress(V4); - private static final InetAddress V6_ADDRESS = InetAddresses.parseNumericAddress(V6); - - @Test - public void testConstants() { - // RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero. - assertNotEquals(0, RT_SCOPE_HOST); - assertNotEquals(0, RT_SCOPE_LINK); - assertNotEquals(0, RT_SCOPE_SITE); - - assertNotEquals(0, IFA_F_DEPRECATED); - assertNotEquals(0, IFA_F_PERMANENT); - assertNotEquals(0, IFA_F_TENTATIVE); - } - - @Test - public void testConstructors() throws SocketException { - LinkAddress address; - - // Valid addresses work as expected. - address = new LinkAddress(V4_ADDRESS, 25); - assertEquals(V4_ADDRESS, address.getAddress()); - assertEquals(25, address.getPrefixLength()); - assertEquals(0, address.getFlags()); - assertEquals(RT_SCOPE_UNIVERSE, address.getScope()); - assertTrue(address.isIpv4()); - - address = new LinkAddress(V6_ADDRESS, 127); - assertEquals(V6_ADDRESS, address.getAddress()); - assertEquals(127, address.getPrefixLength()); - assertEquals(0, address.getFlags()); - assertEquals(RT_SCOPE_UNIVERSE, address.getScope()); - assertTrue(address.isIpv6()); - - // Nonsensical flags/scopes or combinations thereof are acceptable. - address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK); - assertEquals(V6_ADDRESS, address.getAddress()); - assertEquals(64, address.getPrefixLength()); - assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags()); - assertEquals(RT_SCOPE_LINK, address.getScope()); - assertTrue(address.isIpv6()); - - address = new LinkAddress(V4 + "/23", 123, 456); - assertEquals(V4_ADDRESS, address.getAddress()); - assertEquals(23, address.getPrefixLength()); - assertEquals(123, address.getFlags()); - assertEquals(456, address.getScope()); - assertTrue(address.isIpv4()); - - address = new LinkAddress("/64", 1 /* flags */, 2 /* scope */); - assertEquals(Inet6Address.LOOPBACK, address.getAddress()); - assertEquals(64, address.getPrefixLength()); - assertEquals(1, address.getFlags()); - assertEquals(2, address.getScope()); - assertTrue(address.isIpv6()); - - address = new LinkAddress("[2001:db8::123]/64", 3 /* flags */, 4 /* scope */); - assertEquals(InetAddresses.parseNumericAddress("2001:db8::123"), address.getAddress()); - assertEquals(64, address.getPrefixLength()); - assertEquals(3, address.getFlags()); - assertEquals(4, address.getScope()); - assertTrue(address.isIpv6()); - - // InterfaceAddress doesn't have a constructor. Fetch some from an interface. - List addrs = NetworkInterface.getByName("lo").getInterfaceAddresses(); - - // We expect to find 127.0.0.1/8 and ::1/128, in any order. - LinkAddress ipv4Loopback, ipv6Loopback; - assertEquals(2, addrs.size()); - if (addrs.get(0).getAddress() instanceof Inet4Address) { - ipv4Loopback = new LinkAddress(addrs.get(0)); - ipv6Loopback = new LinkAddress(addrs.get(1)); - } else { - ipv4Loopback = new LinkAddress(addrs.get(1)); - ipv6Loopback = new LinkAddress(addrs.get(0)); - } - - assertEquals(InetAddresses.parseNumericAddress("127.0.0.1"), ipv4Loopback.getAddress()); - assertEquals(8, ipv4Loopback.getPrefixLength()); - - assertEquals(InetAddresses.parseNumericAddress("::1"), ipv6Loopback.getAddress()); - assertEquals(128, ipv6Loopback.getPrefixLength()); - - // Null addresses are rejected. - try { - address = new LinkAddress(null, 24); - fail("Null InetAddress should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress((String) null, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); - fail("Null string should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress((InterfaceAddress) null); - fail("Null string should cause NullPointerException"); - } catch(NullPointerException expected) {} - - // Invalid prefix lengths are rejected. - try { - address = new LinkAddress(V4_ADDRESS, -1); - fail("Negative IPv4 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress(V6_ADDRESS, -1); - fail("Negative IPv6 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress(V4_ADDRESS, 33); - fail("/33 IPv4 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress(V4 + "/33", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); - fail("/33 IPv4 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - - try { - address = new LinkAddress(V6_ADDRESS, 129, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); - fail("/129 IPv6 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress(V6 + "/129", IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); - fail("/129 IPv6 prefix length should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - // Multicast addresses are rejected. - try { - address = new LinkAddress("224.0.0.2/32"); - fail("IPv4 multicast address should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - - try { - address = new LinkAddress("ff02::1/128"); - fail("IPv6 multicast address should cause IllegalArgumentException"); - } catch(IllegalArgumentException expected) {} - } - - @Test - public void testAddressScopes() { - assertEquals(RT_SCOPE_HOST, new LinkAddress("::/128").getScope()); - assertEquals(RT_SCOPE_HOST, new LinkAddress("0.0.0.0/32").getScope()); - - assertEquals(RT_SCOPE_LINK, new LinkAddress("::1/128").getScope()); - assertEquals(RT_SCOPE_LINK, new LinkAddress("127.0.0.5/8").getScope()); - assertEquals(RT_SCOPE_LINK, new LinkAddress("fe80::ace:d00d/64").getScope()); - assertEquals(RT_SCOPE_LINK, new LinkAddress("169.254.5.12/16").getScope()); - - assertEquals(RT_SCOPE_SITE, new LinkAddress("fec0::dead/64").getScope()); - - assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("10.1.2.3/21").getScope()); - assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("192.0.2.1/25").getScope()); - assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("2001:db8::/64").getScope()); - assertEquals(RT_SCOPE_UNIVERSE, new LinkAddress("5000::/127").getScope()); - } - - private void assertIsSameAddressAs(LinkAddress l1, LinkAddress l2) { - assertTrue(l1 + " unexpectedly does not have same address as " + l2, - l1.isSameAddressAs(l2)); - assertTrue(l2 + " unexpectedly does not have same address as " + l1, - l2.isSameAddressAs(l1)); - } - - private void assertIsNotSameAddressAs(LinkAddress l1, LinkAddress l2) { - assertFalse(l1 + " unexpectedly has same address as " + l2, - l1.isSameAddressAs(l2)); - assertFalse(l2 + " unexpectedly has same address as " + l1, - l1.isSameAddressAs(l2)); - } - - @Test - public void testEqualsAndSameAddressAs() { - LinkAddress l1, l2, l3; - - l1 = new LinkAddress("2001:db8::1/64"); - l2 = new LinkAddress("2001:db8::1/64"); - assertEqualBothWays(l1, l2); - assertIsSameAddressAs(l1, l2); - - l2 = new LinkAddress("2001:db8::1/65"); - assertNotEqualEitherWay(l1, l2); - assertIsNotSameAddressAs(l1, l2); - - l2 = new LinkAddress("2001:db8::2/64"); - assertNotEqualEitherWay(l1, l2); - assertIsNotSameAddressAs(l1, l2); - - - l1 = new LinkAddress("192.0.2.1/24"); - l2 = new LinkAddress("192.0.2.1/24"); - assertEqualBothWays(l1, l2); - assertIsSameAddressAs(l1, l2); - - l2 = new LinkAddress("192.0.2.1/23"); - assertNotEqualEitherWay(l1, l2); - assertIsNotSameAddressAs(l1, l2); - - l2 = new LinkAddress("192.0.2.2/24"); - assertNotEqualEitherWay(l1, l2); - assertIsNotSameAddressAs(l1, l2); - - - // Check equals() and isSameAddressAs() on identical addresses with different flags. - l1 = new LinkAddress(V6_ADDRESS, 64); - l2 = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); - assertEqualBothWays(l1, l2); - assertIsSameAddressAs(l1, l2); - - l2 = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE); - assertNotEqualEitherWay(l1, l2); - assertIsSameAddressAs(l1, l2); - - // Check equals() and isSameAddressAs() on identical addresses with different scope. - l1 = new LinkAddress(V4_ADDRESS, 24); - l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_UNIVERSE); - assertEqualBothWays(l1, l2); - assertIsSameAddressAs(l1, l2); - - l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_HOST); - assertNotEqualEitherWay(l1, l2); - assertIsSameAddressAs(l1, l2); - - // Addresses with the same start or end bytes aren't equal between families. - l1 = new LinkAddress("32.1.13.184/24"); - l2 = new LinkAddress("2001:db8::1/24"); - l3 = new LinkAddress("::2001:db8/24"); - - byte[] ipv4Bytes = l1.getAddress().getAddress(); - byte[] l2FirstIPv6Bytes = Arrays.copyOf(l2.getAddress().getAddress(), 4); - byte[] l3LastIPv6Bytes = Arrays.copyOfRange(l3.getAddress().getAddress(), 12, 16); - assertTrue(Arrays.equals(ipv4Bytes, l2FirstIPv6Bytes)); - assertTrue(Arrays.equals(ipv4Bytes, l3LastIPv6Bytes)); - - assertNotEqualEitherWay(l1, l2); - assertIsNotSameAddressAs(l1, l2); - - assertNotEqualEitherWay(l1, l3); - assertIsNotSameAddressAs(l1, l3); - - // Because we use InetAddress, an IPv4 address is equal to its IPv4-mapped address. - // TODO: Investigate fixing this. - String addressString = V4 + "/24"; - l1 = new LinkAddress(addressString); - l2 = new LinkAddress("::ffff:" + addressString); - assertEqualBothWays(l1, l2); - assertIsSameAddressAs(l1, l2); - } - - @Test - public void testHashCode() { - LinkAddress l1, l2; - - l1 = new LinkAddress(V4_ADDRESS, 23); - l2 = new LinkAddress(V4_ADDRESS, 23, 0, RT_SCOPE_HOST); - assertNotEquals(l1.hashCode(), l2.hashCode()); - - l1 = new LinkAddress(V6_ADDRESS, 128); - l2 = new LinkAddress(V6_ADDRESS, 128, IFA_F_TENTATIVE, RT_SCOPE_UNIVERSE); - assertNotEquals(l1.hashCode(), l2.hashCode()); - } - - @Test - public void testParceling() { - LinkAddress l; - - l = new LinkAddress(V6_ADDRESS, 64, 123, 456); - assertParcelingIsLossless(l); - - l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); - assertParcelingIsLossless(l); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testLifetimeParceling() { - final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, 456, 1L, 3600000L); - assertParcelingIsLossless(l); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.Q) - public void testFieldCount_Q() { - assertFieldCountEquals(4, LinkAddress.class); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testFieldCount() { - // Make sure any new field is covered by the above parceling tests when changing this number - assertFieldCountEquals(6, LinkAddress.class); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testDeprecationTime() { - try { - new LinkAddress(V6_ADDRESS, 64, 0, 456, - LinkAddress.LIFETIME_UNKNOWN, 100000L); - fail("Only one time provided should cause exception"); - } catch (IllegalArgumentException expected) { } - - try { - new LinkAddress(V6_ADDRESS, 64, 0, 456, - 200000L, 100000L); - fail("deprecation time later than expiration time should cause exception"); - } catch (IllegalArgumentException expected) { } - - try { - new LinkAddress(V6_ADDRESS, 64, 0, 456, - -2, 100000L); - fail("negative deprecation time should cause exception"); - } catch (IllegalArgumentException expected) { } - - LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L); - assertEquals(100000L, addr.getDeprecationTime()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testExpirationTime() { - try { - new LinkAddress(V6_ADDRESS, 64, 0, 456, - 200000L, LinkAddress.LIFETIME_UNKNOWN); - fail("Only one time provided should cause exception"); - } catch (IllegalArgumentException expected) { } - - try { - new LinkAddress(V6_ADDRESS, 64, 0, 456, - 100000L, -2); - fail("negative expiration time should cause exception"); - } catch (IllegalArgumentException expected) { } - - LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L); - assertEquals(200000L, addr.getExpirationTime()); - } - - @Test - public void testGetFlags() { - LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST); - assertEquals(123, l.getFlags()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testGetFlags_Deprecation() { - // Test if deprecated bit was added/remove automatically based on the provided deprecation - // time - LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST, - 1L, LinkAddress.LIFETIME_PERMANENT); - // Check if the flag is added automatically. - assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, - SystemClock.elapsedRealtime() + 100000L, LinkAddress.LIFETIME_PERMANENT); - // Check if the flag is removed automatically. - assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, - LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT); - // Check if the permanent flag is added. - assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST, - 1000L, SystemClock.elapsedRealtime() + 100000L); - // Check if the permanent flag is removed - assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0); - } - - private void assertGlobalPreferred(LinkAddress l, String msg) { - assertTrue(msg, l.isGlobalPreferred()); - } - - private void assertNotGlobalPreferred(LinkAddress l, String msg) { - assertFalse(msg, l.isGlobalPreferred()); - } - - @Test - public void testIsGlobalPreferred() { - LinkAddress l; - - l = new LinkAddress(V4_ADDRESS, 32, 0, RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v4,global,noflags"); - - l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v4-rfc1918,global,noflags"); - - l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_SITE); - assertNotGlobalPreferred(l, "v4-rfc1918,site-local,noflags"); - - l = new LinkAddress("127.0.0.7/8", 0, RT_SCOPE_HOST); - assertNotGlobalPreferred(l, "v4-localhost,node-local,noflags"); - - l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v6,global,noflags"); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v6,global,permanent"); - - // IPv6 ULAs are not acceptable "global preferred" addresses. - l = new LinkAddress("fc12::1/64", 0, RT_SCOPE_UNIVERSE); - assertNotGlobalPreferred(l, "v6,ula1,noflags"); - - l = new LinkAddress("fd34::1/64", 0, RT_SCOPE_UNIVERSE); - assertNotGlobalPreferred(l, "v6,ula2,noflags"); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v6,global,tempaddr"); - - l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DADFAILED), - RT_SCOPE_UNIVERSE); - assertNotGlobalPreferred(l, "v6,global,tempaddr+dadfailed"); - - l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DEPRECATED), - RT_SCOPE_UNIVERSE); - assertNotGlobalPreferred(l, "v6,global,tempaddr+deprecated"); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_SITE); - assertNotGlobalPreferred(l, "v6,site-local,tempaddr"); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_LINK); - assertNotGlobalPreferred(l, "v6,link-local,tempaddr"); - - l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_HOST); - assertNotGlobalPreferred(l, "v6,node-local,tempaddr"); - - l = new LinkAddress("::1/128", IFA_F_PERMANENT, RT_SCOPE_HOST); - assertNotGlobalPreferred(l, "v6-localhost,node-local,permanent"); - - l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_TENTATIVE), - RT_SCOPE_UNIVERSE); - assertNotGlobalPreferred(l, "v6,global,tempaddr+tentative"); - - l = new LinkAddress(V6_ADDRESS, 64, - (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC), - RT_SCOPE_UNIVERSE); - assertGlobalPreferred(l, "v6,global,tempaddr+optimistic"); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testIsGlobalPreferred_DeprecatedInFuture() { - final LinkAddress l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, - RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000, - SystemClock.elapsedRealtime() + 200000); - // Although the deprecated bit is set, but the deprecation time is in the future, test - // if the flag is removed automatically. - assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future"); - } -} diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java deleted file mode 100644 index 550953d0612d..000000000000 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ /dev/null @@ -1,1271 +0,0 @@ -/* - * Copyright (C) 2010 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; - -import static android.net.RouteInfo.RTN_THROW; -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.RouteInfo.RTN_UNREACHABLE; - -import static com.android.testutils.ParcelUtils.assertParcelSane; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; -import static com.android.testutils.ParcelUtils.parcelingRoundTrip; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.LinkProperties.ProvisioningChange; -import android.os.Build; -import android.system.OsConstants; -import android.util.ArraySet; - -import androidx.core.os.BuildCompat; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.net.module.util.LinkPropertiesUtils.CompareResult; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class LinkPropertiesTest { - @Rule - public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); - - private static final InetAddress ADDRV4 = address("75.208.6.1"); - private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); - private static final InetAddress DNS1 = address("75.208.7.1"); - private static final InetAddress DNS2 = address("69.78.7.1"); - private static final InetAddress DNS6 = address("2001:4860:4860::8888"); - private static final InetAddress PRIVDNS1 = address("1.1.1.1"); - private static final InetAddress PRIVDNS2 = address("1.0.0.1"); - private static final InetAddress PRIVDNS6 = address("2606:4700:4700::1111"); - private static final InetAddress PCSCFV4 = address("10.77.25.37"); - private static final InetAddress PCSCFV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:1"); - private static final InetAddress GATEWAY1 = address("75.208.8.1"); - private static final InetAddress GATEWAY2 = address("69.78.8.1"); - private static final InetAddress GATEWAY61 = address("fe80::6:0000:613"); - private static final InetAddress GATEWAY62 = address("fe80::6:22%lo"); - private static final InetAddress TESTIPV4ADDR = address("192.168.47.42"); - private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43"); - private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1"); - private static final String NAME = "qmi0"; - private static final String DOMAINS = "google.com"; - private static final String PRIV_DNS_SERVER_NAME = "private.dns.com"; - private static final String TCP_BUFFER_SIZES = "524288,1048576,2097152,262144,524288,1048576"; - private static final int MTU = 1500; - private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32); - private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128); - private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64"); - private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi"); - - // CaptivePortalData cannot be in a constant as it does not exist on Q. - // The test runner also crashes when scanning for tests if it is a return type. - private static Object getCaptivePortalData() { - return new CaptivePortalData.Builder() - .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build(); - } - - private static InetAddress address(String addrString) { - return InetAddresses.parseNumericAddress(addrString); - } - - private static boolean isAtLeastR() { - // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) - return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); - } - - private void checkEmpty(final LinkProperties lp) { - assertEquals(0, lp.getAllInterfaceNames().size()); - assertEquals(0, lp.getAllAddresses().size()); - assertEquals(0, lp.getDnsServers().size()); - assertEquals(0, lp.getValidatedPrivateDnsServers().size()); - assertEquals(0, lp.getPcscfServers().size()); - assertEquals(0, lp.getAllRoutes().size()); - assertEquals(0, lp.getAllLinkAddresses().size()); - assertEquals(0, lp.getStackedLinks().size()); - assertEquals(0, lp.getMtu()); - assertNull(lp.getPrivateDnsServerName()); - assertNull(lp.getDomains()); - assertNull(lp.getHttpProxy()); - assertNull(lp.getTcpBufferSizes()); - assertNull(lp.getNat64Prefix()); - assertFalse(lp.isProvisioned()); - assertFalse(lp.isIpv4Provisioned()); - assertFalse(lp.isIpv6Provisioned()); - assertFalse(lp.isPrivateDnsActive()); - - if (isAtLeastR()) { - assertNull(lp.getDhcpServerAddress()); - assertFalse(lp.isWakeOnLanSupported()); - assertNull(lp.getCaptivePortalApiUrl()); - assertNull(lp.getCaptivePortalData()); - } - } - - private LinkProperties makeTestObject() { - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(NAME); - lp.addLinkAddress(LINKADDRV4); - lp.addLinkAddress(LINKADDRV6); - lp.addDnsServer(DNS1); - lp.addDnsServer(DNS2); - lp.addValidatedPrivateDnsServer(PRIVDNS1); - lp.addValidatedPrivateDnsServer(PRIVDNS2); - lp.setUsePrivateDns(true); - lp.setPrivateDnsServerName(PRIV_DNS_SERVER_NAME); - lp.addPcscfServer(PCSCFV6); - lp.setDomains(DOMAINS); - lp.addRoute(new RouteInfo(GATEWAY1)); - lp.addRoute(new RouteInfo(GATEWAY2)); - lp.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888)); - lp.setMtu(MTU); - lp.setTcpBufferSizes(TCP_BUFFER_SIZES); - lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); - if (isAtLeastR()) { - lp.setDhcpServerAddress(DHCPSERVER); - lp.setWakeOnLanSupported(true); - lp.setCaptivePortalApiUrl(CAPPORT_API_URL); - lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData()); - } - return lp; - } - - public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) { - // Check implementation of equals(), element by element. - assertTrue(source.isIdenticalInterfaceName(target)); - assertTrue(target.isIdenticalInterfaceName(source)); - - assertTrue(source.isIdenticalAddresses(target)); - assertTrue(target.isIdenticalAddresses(source)); - - assertTrue(source.isIdenticalDnses(target)); - assertTrue(target.isIdenticalDnses(source)); - - assertTrue(source.isIdenticalPrivateDns(target)); - assertTrue(target.isIdenticalPrivateDns(source)); - - assertTrue(source.isIdenticalValidatedPrivateDnses(target)); - assertTrue(target.isIdenticalValidatedPrivateDnses(source)); - - assertTrue(source.isIdenticalPcscfs(target)); - assertTrue(target.isIdenticalPcscfs(source)); - - assertTrue(source.isIdenticalRoutes(target)); - assertTrue(target.isIdenticalRoutes(source)); - - assertTrue(source.isIdenticalHttpProxy(target)); - assertTrue(target.isIdenticalHttpProxy(source)); - - assertTrue(source.isIdenticalStackedLinks(target)); - assertTrue(target.isIdenticalStackedLinks(source)); - - assertTrue(source.isIdenticalMtu(target)); - assertTrue(target.isIdenticalMtu(source)); - - assertTrue(source.isIdenticalTcpBufferSizes(target)); - assertTrue(target.isIdenticalTcpBufferSizes(source)); - - if (isAtLeastR()) { - assertTrue(source.isIdenticalDhcpServerAddress(target)); - assertTrue(source.isIdenticalDhcpServerAddress(source)); - - assertTrue(source.isIdenticalWakeOnLan(target)); - assertTrue(target.isIdenticalWakeOnLan(source)); - - assertTrue(source.isIdenticalCaptivePortalApiUrl(target)); - assertTrue(target.isIdenticalCaptivePortalApiUrl(source)); - - assertTrue(source.isIdenticalCaptivePortalData(target)); - assertTrue(target.isIdenticalCaptivePortalData(source)); - } - - // Check result of equals(). - assertTrue(source.equals(target)); - assertTrue(target.equals(source)); - - // Check hashCode. - assertEquals(source.hashCode(), target.hashCode()); - } - - @Test - public void testEqualsNull() { - LinkProperties source = new LinkProperties(); - LinkProperties target = new LinkProperties(); - - assertFalse(source == target); - assertLinkPropertiesEqual(source, target); - } - - @Test - public void testEqualsSameOrder() throws Exception { - LinkProperties source = new LinkProperties(); - source.setInterfaceName(NAME); - // set 2 link addresses - source.addLinkAddress(LINKADDRV4); - source.addLinkAddress(LINKADDRV6); - // set 2 dnses - source.addDnsServer(DNS1); - source.addDnsServer(DNS2); - // set 1 pcscf - source.addPcscfServer(PCSCFV6); - // set 2 gateways - source.addRoute(new RouteInfo(GATEWAY1)); - source.addRoute(new RouteInfo(GATEWAY2)); - source.setMtu(MTU); - - LinkProperties target = new LinkProperties(); - - // All fields are same - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(DNS1); - target.addDnsServer(DNS2); - target.addPcscfServer(PCSCFV6); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - target.setMtu(MTU); - - assertLinkPropertiesEqual(source, target); - - target.clear(); - // change Interface Name - target.setInterfaceName("qmi1"); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(DNS1); - target.addDnsServer(DNS2); - target.addPcscfServer(PCSCFV6); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - target.setMtu(MTU); - assertFalse(source.equals(target)); - - target.clear(); - target.setInterfaceName(NAME); - // change link addresses - target.addLinkAddress(new LinkAddress(address("75.208.6.2"), 32)); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(DNS1); - target.addDnsServer(DNS2); - target.addPcscfServer(PCSCFV6); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - target.setMtu(MTU); - assertFalse(source.equals(target)); - - target.clear(); - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - // change dnses - target.addDnsServer(address("75.208.7.2")); - target.addDnsServer(DNS2); - target.addPcscfServer(PCSCFV6); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - target.setMtu(MTU); - assertFalse(source.equals(target)); - - target.clear(); - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(address("75.208.7.2")); - target.addDnsServer(DNS2); - // change pcscf - target.addPcscfServer(address("2001::1")); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - target.setMtu(MTU); - assertFalse(source.equals(target)); - - target.clear(); - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(DNS1); - target.addDnsServer(DNS2); - // change gateway - target.addRoute(new RouteInfo(address("75.208.8.2"))); - target.setMtu(MTU); - target.addRoute(new RouteInfo(GATEWAY2)); - assertFalse(source.equals(target)); - - target.clear(); - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addDnsServer(DNS1); - target.addDnsServer(DNS2); - target.addRoute(new RouteInfo(GATEWAY1)); - target.addRoute(new RouteInfo(GATEWAY2)); - // change mtu - target.setMtu(1440); - assertFalse(source.equals(target)); - } - - @Test - public void testEqualsDifferentOrder() throws Exception { - LinkProperties source = new LinkProperties(); - source.setInterfaceName(NAME); - // set 2 link addresses - source.addLinkAddress(LINKADDRV4); - source.addLinkAddress(LINKADDRV6); - // set 2 dnses - source.addDnsServer(DNS1); - source.addDnsServer(DNS2); - // set 2 gateways - source.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1)); - source.addRoute(new RouteInfo(GATEWAY2)); - source.setMtu(MTU); - - LinkProperties target = new LinkProperties(); - // Exchange order - target.setInterfaceName(NAME); - target.addLinkAddress(LINKADDRV6); - target.addLinkAddress(LINKADDRV4); - target.addDnsServer(DNS2); - target.addDnsServer(DNS1); - target.addRoute(new RouteInfo(GATEWAY2)); - target.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1)); - target.setMtu(MTU); - - assertLinkPropertiesEqual(source, target); - } - - @Test - public void testEqualsDuplicated() throws Exception { - LinkProperties source = new LinkProperties(); - // set 3 link addresses, eg, [A, A, B] - source.addLinkAddress(LINKADDRV4); - source.addLinkAddress(LINKADDRV4); - source.addLinkAddress(LINKADDRV6); - - LinkProperties target = new LinkProperties(); - // set 3 link addresses, eg, [A, B, B] - target.addLinkAddress(LINKADDRV4); - target.addLinkAddress(LINKADDRV6); - target.addLinkAddress(LINKADDRV6); - - assertLinkPropertiesEqual(source, target); - } - - private void assertAllRoutesHaveInterface(String iface, LinkProperties lp) { - for (RouteInfo r : lp.getRoutes()) { - assertEquals(iface, r.getInterface()); - } - } - - private void assertAllRoutesNotHaveInterface(String iface, LinkProperties lp) { - for (RouteInfo r : lp.getRoutes()) { - assertNotEquals(iface, r.getInterface()); - } - } - - @Test - public void testRouteInterfaces() { - LinkAddress prefix1 = new LinkAddress(address("2001:db8:1::"), 48); - LinkAddress prefix2 = new LinkAddress(address("2001:db8:2::"), 48); - InetAddress address = ADDRV6; - - // Add a route with no interface to a LinkProperties with no interface. No errors. - LinkProperties lp = new LinkProperties(); - RouteInfo r = new RouteInfo(prefix1, address, null); - assertTrue(lp.addRoute(r)); - assertEquals(1, lp.getRoutes().size()); - assertAllRoutesHaveInterface(null, lp); - - // Adding the same route twice has no effect. - assertFalse(lp.addRoute(r)); - assertEquals(1, lp.getRoutes().size()); - - // Add a route with an interface. Expect an exception. - r = new RouteInfo(prefix2, address, "wlan0"); - try { - lp.addRoute(r); - fail("Adding wlan0 route to LP with no interface, expect exception"); - } catch (IllegalArgumentException expected) {} - - // Change the interface name. All the routes should change their interface name too. - lp.setInterfaceName("rmnet0"); - assertAllRoutesHaveInterface("rmnet0", lp); - assertAllRoutesNotHaveInterface(null, lp); - assertAllRoutesNotHaveInterface("wlan0", lp); - - // Now add a route with the wrong interface. This causes an exception too. - try { - lp.addRoute(r); - fail("Adding wlan0 route to rmnet0 LP, expect exception"); - } catch (IllegalArgumentException expected) {} - - // If the interface name matches, the route is added. - r = new RouteInfo(prefix2, null, "wlan0"); - lp.setInterfaceName("wlan0"); - lp.addRoute(r); - assertEquals(2, lp.getRoutes().size()); - assertAllRoutesHaveInterface("wlan0", lp); - assertAllRoutesNotHaveInterface("rmnet0", lp); - - // Routes with null interfaces are converted to wlan0. - r = RouteInfo.makeHostRoute(ADDRV6, null); - lp.addRoute(r); - assertEquals(3, lp.getRoutes().size()); - assertAllRoutesHaveInterface("wlan0", lp); - - // Check routes are updated correctly when calling setInterfaceName. - LinkProperties lp2 = new LinkProperties(lp); - assertAllRoutesHaveInterface("wlan0", lp2); - final CompareResult cr1 = - new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); - assertEquals(0, cr1.added.size()); - assertEquals(0, cr1.removed.size()); - - lp2.setInterfaceName("p2p0"); - assertAllRoutesHaveInterface("p2p0", lp2); - assertAllRoutesNotHaveInterface("wlan0", lp2); - final CompareResult cr2 = - new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); - assertEquals(3, cr2.added.size()); - assertEquals(3, cr2.removed.size()); - - // Remove route with incorrect interface, no route removed. - lp.removeRoute(new RouteInfo(prefix2, null, null)); - assertEquals(3, lp.getRoutes().size()); - - // Check remove works when interface is correct. - lp.removeRoute(new RouteInfo(prefix2, null, "wlan0")); - assertEquals(2, lp.getRoutes().size()); - assertAllRoutesHaveInterface("wlan0", lp); - assertAllRoutesNotHaveInterface("p2p0", lp); - } - - @Test - public void testStackedInterfaces() { - LinkProperties rmnet0 = new LinkProperties(); - rmnet0.setInterfaceName("rmnet0"); - rmnet0.addLinkAddress(LINKADDRV6); - - LinkProperties clat4 = new LinkProperties(); - clat4.setInterfaceName("clat4"); - clat4.addLinkAddress(LINKADDRV4); - - assertEquals(0, rmnet0.getStackedLinks().size()); - assertEquals(1, rmnet0.getAddresses().size()); - assertEquals(1, rmnet0.getLinkAddresses().size()); - assertEquals(1, rmnet0.getAllAddresses().size()); - assertEquals(1, rmnet0.getAllLinkAddresses().size()); - assertEquals(1, rmnet0.getAllInterfaceNames().size()); - assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); - - rmnet0.addStackedLink(clat4); - assertEquals(1, rmnet0.getStackedLinks().size()); - assertEquals(1, rmnet0.getAddresses().size()); - assertEquals(1, rmnet0.getLinkAddresses().size()); - assertEquals(2, rmnet0.getAllAddresses().size()); - assertEquals(2, rmnet0.getAllLinkAddresses().size()); - assertEquals(2, rmnet0.getAllInterfaceNames().size()); - assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); - assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); - - rmnet0.addStackedLink(clat4); - assertEquals(1, rmnet0.getStackedLinks().size()); - assertEquals(1, rmnet0.getAddresses().size()); - assertEquals(1, rmnet0.getLinkAddresses().size()); - assertEquals(2, rmnet0.getAllAddresses().size()); - assertEquals(2, rmnet0.getAllLinkAddresses().size()); - assertEquals(2, rmnet0.getAllInterfaceNames().size()); - assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); - assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); - - assertEquals(0, clat4.getStackedLinks().size()); - - // Modify an item in the returned collection to see what happens. - for (LinkProperties link : rmnet0.getStackedLinks()) { - if (link.getInterfaceName().equals("clat4")) { - link.setInterfaceName("newname"); - } - } - for (LinkProperties link : rmnet0.getStackedLinks()) { - assertFalse("newname".equals(link.getInterfaceName())); - } - - assertTrue(rmnet0.removeStackedLink("clat4")); - assertEquals(0, rmnet0.getStackedLinks().size()); - assertEquals(1, rmnet0.getAddresses().size()); - assertEquals(1, rmnet0.getLinkAddresses().size()); - assertEquals(1, rmnet0.getAllAddresses().size()); - assertEquals(1, rmnet0.getAllLinkAddresses().size()); - assertEquals(1, rmnet0.getAllInterfaceNames().size()); - assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); - - assertFalse(rmnet0.removeStackedLink("clat4")); - } - - private LinkAddress getFirstLinkAddress(LinkProperties lp) { - return lp.getLinkAddresses().iterator().next(); - } - - @Test - public void testAddressMethods() { - LinkProperties lp = new LinkProperties(); - - // No addresses. - assertFalse(lp.hasIpv4Address()); - assertFalse(lp.hasGlobalIpv6Address()); - - // Addresses on stacked links don't count. - LinkProperties stacked = new LinkProperties(); - stacked.setInterfaceName("stacked"); - lp.addStackedLink(stacked); - stacked.addLinkAddress(LINKADDRV4); - stacked.addLinkAddress(LINKADDRV6); - assertTrue(stacked.hasIpv4Address()); - assertTrue(stacked.hasGlobalIpv6Address()); - assertFalse(lp.hasIpv4Address()); - assertFalse(lp.hasGlobalIpv6Address()); - lp.removeStackedLink("stacked"); - assertFalse(lp.hasIpv4Address()); - assertFalse(lp.hasGlobalIpv6Address()); - - // Addresses on the base link. - // Check the return values of hasIpvXAddress and ensure the add/remove methods return true - // iff something changes. - assertEquals(0, lp.getLinkAddresses().size()); - assertTrue(lp.addLinkAddress(LINKADDRV6)); - assertEquals(1, lp.getLinkAddresses().size()); - assertFalse(lp.hasIpv4Address()); - assertTrue(lp.hasGlobalIpv6Address()); - - assertTrue(lp.removeLinkAddress(LINKADDRV6)); - assertEquals(0, lp.getLinkAddresses().size()); - - assertTrue(lp.addLinkAddress(LINKADDRV6LINKLOCAL)); - assertEquals(1, lp.getLinkAddresses().size()); - assertFalse(lp.hasGlobalIpv6Address()); - - assertTrue(lp.addLinkAddress(LINKADDRV4)); - assertEquals(2, lp.getLinkAddresses().size()); - assertTrue(lp.hasIpv4Address()); - assertFalse(lp.hasGlobalIpv6Address()); - - assertTrue(lp.addLinkAddress(LINKADDRV6)); - assertEquals(3, lp.getLinkAddresses().size()); - assertTrue(lp.hasIpv4Address()); - assertTrue(lp.hasGlobalIpv6Address()); - - assertTrue(lp.removeLinkAddress(LINKADDRV6LINKLOCAL)); - assertEquals(2, lp.getLinkAddresses().size()); - assertTrue(lp.hasIpv4Address()); - assertTrue(lp.hasGlobalIpv6Address()); - - // Adding an address twice has no effect. - // Removing an address that's not present has no effect. - assertFalse(lp.addLinkAddress(LINKADDRV4)); - assertEquals(2, lp.getLinkAddresses().size()); - assertTrue(lp.hasIpv4Address()); - assertTrue(lp.removeLinkAddress(LINKADDRV4)); - assertEquals(1, lp.getLinkAddresses().size()); - assertFalse(lp.hasIpv4Address()); - assertFalse(lp.removeLinkAddress(LINKADDRV4)); - assertEquals(1, lp.getLinkAddresses().size()); - - // Adding an address that's already present but with different properties causes the - // existing address to be updated and returns true. - // Start with only LINKADDRV6. - assertEquals(1, lp.getLinkAddresses().size()); - assertEquals(LINKADDRV6, getFirstLinkAddress(lp)); - - // Create a LinkAddress object for the same address, but with different flags. - LinkAddress deprecated = new LinkAddress(ADDRV6, 128, - OsConstants.IFA_F_DEPRECATED, OsConstants.RT_SCOPE_UNIVERSE); - assertTrue(deprecated.isSameAddressAs(LINKADDRV6)); - assertFalse(deprecated.equals(LINKADDRV6)); - - // Check that adding it updates the existing address instead of adding a new one. - assertTrue(lp.addLinkAddress(deprecated)); - assertEquals(1, lp.getLinkAddresses().size()); - assertEquals(deprecated, getFirstLinkAddress(lp)); - assertFalse(LINKADDRV6.equals(getFirstLinkAddress(lp))); - - // Removing LINKADDRV6 removes deprecated, because removing addresses ignores properties. - assertTrue(lp.removeLinkAddress(LINKADDRV6)); - assertEquals(0, lp.getLinkAddresses().size()); - } - - @Test - public void testLinkAddresses() { - final LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(LINKADDRV4); - lp.addLinkAddress(LINKADDRV6); - - final LinkProperties lp2 = new LinkProperties(); - lp2.addLinkAddress(LINKADDRV6); - - final LinkProperties lp3 = new LinkProperties(); - final List linkAddresses = Arrays.asList(LINKADDRV4); - lp3.setLinkAddresses(linkAddresses); - - assertFalse(lp.equals(lp2)); - assertFalse(lp2.equals(lp3)); - - lp.removeLinkAddress(LINKADDRV4); - assertTrue(lp.equals(lp2)); - - lp2.setLinkAddresses(lp3.getLinkAddresses()); - assertTrue(lp2.equals(lp3)); - } - - @Test - public void testNat64Prefix() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(LINKADDRV4); - lp.addLinkAddress(LINKADDRV6); - - assertNull(lp.getNat64Prefix()); - - IpPrefix p = new IpPrefix("64:ff9b::/96"); - lp.setNat64Prefix(p); - assertEquals(p, lp.getNat64Prefix()); - - p = new IpPrefix("2001:db8:a:b:1:2:3::/96"); - lp.setNat64Prefix(p); - assertEquals(p, lp.getNat64Prefix()); - - p = new IpPrefix("2001:db8:a:b:1:2::/80"); - try { - lp.setNat64Prefix(p); - } catch (IllegalArgumentException expected) { - } - - p = new IpPrefix("64:ff9b::/64"); - try { - lp.setNat64Prefix(p); - } catch (IllegalArgumentException expected) { - } - - assertEquals(new IpPrefix("2001:db8:a:b:1:2:3::/96"), lp.getNat64Prefix()); - - lp.setNat64Prefix(null); - assertNull(lp.getNat64Prefix()); - } - - @Test - public void testIsProvisioned() { - LinkProperties lp4 = new LinkProperties(); - assertFalse("v4only:empty", lp4.isProvisioned()); - lp4.addLinkAddress(LINKADDRV4); - assertFalse("v4only:addr-only", lp4.isProvisioned()); - lp4.addDnsServer(DNS1); - assertFalse("v4only:addr+dns", lp4.isProvisioned()); - lp4.addRoute(new RouteInfo(GATEWAY1)); - assertTrue("v4only:addr+dns+route", lp4.isProvisioned()); - assertTrue("v4only:addr+dns+route", lp4.isIpv4Provisioned()); - assertFalse("v4only:addr+dns+route", lp4.isIpv6Provisioned()); - - LinkProperties lp6 = new LinkProperties(); - assertFalse("v6only:empty", lp6.isProvisioned()); - lp6.addLinkAddress(LINKADDRV6LINKLOCAL); - assertFalse("v6only:fe80-only", lp6.isProvisioned()); - lp6.addDnsServer(DNS6); - assertFalse("v6only:fe80+dns", lp6.isProvisioned()); - lp6.addRoute(new RouteInfo(GATEWAY61)); - assertFalse("v6only:fe80+dns+route", lp6.isProvisioned()); - lp6.addLinkAddress(LINKADDRV6); - assertTrue("v6only:fe80+global+dns+route", lp6.isIpv6Provisioned()); - assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned()); - lp6.removeLinkAddress(LINKADDRV6LINKLOCAL); - assertFalse("v6only:global+dns+route", lp6.isIpv4Provisioned()); - assertTrue("v6only:global+dns+route", lp6.isIpv6Provisioned()); - assertTrue("v6only:global+dns+route", lp6.isProvisioned()); - - LinkProperties lp46 = new LinkProperties(); - lp46.addLinkAddress(LINKADDRV4); - lp46.addLinkAddress(LINKADDRV6); - lp46.addDnsServer(DNS1); - lp46.addDnsServer(DNS6); - assertFalse("dualstack:missing-routes", lp46.isProvisioned()); - lp46.addRoute(new RouteInfo(GATEWAY1)); - assertTrue("dualstack:v4-provisioned", lp46.isIpv4Provisioned()); - assertFalse("dualstack:v4-provisioned", lp46.isIpv6Provisioned()); - assertTrue("dualstack:v4-provisioned", lp46.isProvisioned()); - lp46.addRoute(new RouteInfo(GATEWAY61)); - assertTrue("dualstack:both-provisioned", lp46.isIpv4Provisioned()); - assertTrue("dualstack:both-provisioned", lp46.isIpv6Provisioned()); - assertTrue("dualstack:both-provisioned", lp46.isProvisioned()); - - // A link with an IPv6 address and default route, but IPv4 DNS server. - LinkProperties mixed = new LinkProperties(); - mixed.addLinkAddress(LINKADDRV6); - mixed.addDnsServer(DNS1); - mixed.addRoute(new RouteInfo(GATEWAY61)); - assertFalse("mixed:addr6+route6+dns4", mixed.isIpv4Provisioned()); - assertFalse("mixed:addr6+route6+dns4", mixed.isIpv6Provisioned()); - assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned()); - } - - @Test - public void testCompareProvisioning() { - LinkProperties v4lp = new LinkProperties(); - v4lp.addLinkAddress(LINKADDRV4); - v4lp.addRoute(new RouteInfo(GATEWAY1)); - v4lp.addDnsServer(DNS1); - assertTrue(v4lp.isProvisioned()); - - LinkProperties v4r = new LinkProperties(v4lp); - v4r.removeDnsServer(DNS1); - assertFalse(v4r.isProvisioned()); - - assertEquals(ProvisioningChange.STILL_NOT_PROVISIONED, - LinkProperties.compareProvisioning(v4r, v4r)); - assertEquals(ProvisioningChange.LOST_PROVISIONING, - LinkProperties.compareProvisioning(v4lp, v4r)); - assertEquals(ProvisioningChange.GAINED_PROVISIONING, - LinkProperties.compareProvisioning(v4r, v4lp)); - assertEquals(ProvisioningChange.STILL_PROVISIONED, - LinkProperties.compareProvisioning(v4lp, v4lp)); - - // Check that losing IPv4 provisioning on a dualstack network is - // seen as a total loss of provisioning. - LinkProperties v6lp = new LinkProperties(); - v6lp.addLinkAddress(LINKADDRV6); - v6lp.addRoute(new RouteInfo(GATEWAY61)); - v6lp.addDnsServer(DNS6); - assertFalse(v6lp.isIpv4Provisioned()); - assertTrue(v6lp.isIpv6Provisioned()); - assertTrue(v6lp.isProvisioned()); - - LinkProperties v46lp = new LinkProperties(v6lp); - v46lp.addLinkAddress(LINKADDRV4); - v46lp.addRoute(new RouteInfo(GATEWAY1)); - v46lp.addDnsServer(DNS1); - assertTrue(v46lp.isIpv4Provisioned()); - assertTrue(v46lp.isIpv6Provisioned()); - assertTrue(v46lp.isProvisioned()); - - assertEquals(ProvisioningChange.STILL_PROVISIONED, - LinkProperties.compareProvisioning(v4lp, v46lp)); - assertEquals(ProvisioningChange.STILL_PROVISIONED, - LinkProperties.compareProvisioning(v6lp, v46lp)); - assertEquals(ProvisioningChange.LOST_PROVISIONING, - LinkProperties.compareProvisioning(v46lp, v6lp)); - assertEquals(ProvisioningChange.LOST_PROVISIONING, - LinkProperties.compareProvisioning(v46lp, v4lp)); - - // Check that losing and gaining a secondary router does not change - // the provisioning status. - LinkProperties v6lp2 = new LinkProperties(v6lp); - v6lp2.addRoute(new RouteInfo(GATEWAY62)); - assertTrue(v6lp2.isProvisioned()); - - assertEquals(ProvisioningChange.STILL_PROVISIONED, - LinkProperties.compareProvisioning(v6lp2, v6lp)); - assertEquals(ProvisioningChange.STILL_PROVISIONED, - LinkProperties.compareProvisioning(v6lp, v6lp2)); - } - - @Test - public void testIsReachable() { - final LinkProperties v4lp = new LinkProperties(); - assertFalse(v4lp.isReachable(DNS1)); - assertFalse(v4lp.isReachable(DNS2)); - - // Add an on-link route, making the on-link DNS server reachable, - // but there is still no IPv4 address. - assertTrue(v4lp.addRoute(new RouteInfo(new IpPrefix(address("75.208.0.0"), 16)))); - assertFalse(v4lp.isReachable(DNS1)); - assertFalse(v4lp.isReachable(DNS2)); - - // Adding an IPv4 address (right now, any IPv4 address) means we use - // the routes to compute likely reachability. - assertTrue(v4lp.addLinkAddress(new LinkAddress(ADDRV4, 16))); - assertTrue(v4lp.isReachable(DNS1)); - assertFalse(v4lp.isReachable(DNS2)); - - // Adding a default route makes the off-link DNS server reachable. - assertTrue(v4lp.addRoute(new RouteInfo(GATEWAY1))); - assertTrue(v4lp.isReachable(DNS1)); - assertTrue(v4lp.isReachable(DNS2)); - - final LinkProperties v6lp = new LinkProperties(); - final InetAddress kLinkLocalDns = address("fe80::6:1"); - final InetAddress kLinkLocalDnsWithScope = address("fe80::6:2%43"); - final InetAddress kOnLinkDns = address("2001:db8:85a3::53"); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertFalse(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertFalse(v6lp.isReachable(kOnLinkDns)); - assertFalse(v6lp.isReachable(DNS6)); - - // Add a link-local route, making the link-local DNS servers reachable. Because - // we assume the presence of an IPv6 link-local address, link-local DNS servers - // are considered reachable, but only those with a non-zero scope identifier. - assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(address("fe80::"), 64)))); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertFalse(v6lp.isReachable(kOnLinkDns)); - assertFalse(v6lp.isReachable(DNS6)); - - // Add a link-local address--nothing changes. - assertTrue(v6lp.addLinkAddress(LINKADDRV6LINKLOCAL)); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertFalse(v6lp.isReachable(kOnLinkDns)); - assertFalse(v6lp.isReachable(DNS6)); - - // Add a global route on link, but no global address yet. DNS servers reachable - // via a route that doesn't require a gateway: give them the benefit of the - // doubt and hope the link-local source address suffices for communication. - assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(address("2001:db8:85a3::"), 64)))); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertTrue(v6lp.isReachable(kOnLinkDns)); - assertFalse(v6lp.isReachable(DNS6)); - - // Add a global address; the on-link global address DNS server is (still) - // presumed reachable. - assertTrue(v6lp.addLinkAddress(new LinkAddress(ADDRV6, 64))); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertTrue(v6lp.isReachable(kOnLinkDns)); - assertFalse(v6lp.isReachable(DNS6)); - - // Adding a default route makes the off-link DNS server reachable. - assertTrue(v6lp.addRoute(new RouteInfo(GATEWAY62))); - assertFalse(v6lp.isReachable(kLinkLocalDns)); - assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope)); - assertTrue(v6lp.isReachable(kOnLinkDns)); - assertTrue(v6lp.isReachable(DNS6)); - - // Check isReachable on stacked links. This requires that the source IP address be assigned - // on the interface returned by the route lookup. - LinkProperties stacked = new LinkProperties(); - - // Can't add a stacked link without an interface name. - stacked.setInterfaceName("v4-test0"); - v6lp.addStackedLink(stacked); - - InetAddress stackedAddress = address("192.0.0.4"); - LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32); - assertFalse(v6lp.isReachable(stackedAddress)); - stacked.addLinkAddress(stackedLinkAddress); - assertFalse(v6lp.isReachable(stackedAddress)); - stacked.addRoute(new RouteInfo(stackedLinkAddress)); - assertTrue(stacked.isReachable(stackedAddress)); - assertTrue(v6lp.isReachable(stackedAddress)); - - assertFalse(v6lp.isReachable(DNS1)); - stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress)); - assertTrue(v6lp.isReachable(DNS1)); - } - - @Test - public void testLinkPropertiesEnsureDirectlyConnectedRoutes() { - // IPv4 case: no route added initially - LinkProperties rmnet0 = new LinkProperties(); - rmnet0.setInterfaceName("rmnet0"); - rmnet0.addLinkAddress(new LinkAddress("10.0.0.2/8")); - RouteInfo directRoute0 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null, - rmnet0.getInterfaceName()); - - // Since no routes is added explicitly, getAllRoutes() should return empty. - assertTrue(rmnet0.getAllRoutes().isEmpty()); - rmnet0.ensureDirectlyConnectedRoutes(); - // ensureDirectlyConnectedRoutes() should have added the missing local route. - assertEqualRoutes(Collections.singletonList(directRoute0), rmnet0.getAllRoutes()); - - // IPv4 case: both direct and default routes added initially - LinkProperties rmnet1 = new LinkProperties(); - rmnet1.setInterfaceName("rmnet1"); - rmnet1.addLinkAddress(new LinkAddress("10.0.0.3/8")); - RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null, address("10.0.0.1"), - rmnet1.getInterfaceName()); - RouteInfo directRoute1 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null, - rmnet1.getInterfaceName()); - rmnet1.addRoute(defaultRoute1); - rmnet1.addRoute(directRoute1); - - // Check added routes - assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes()); - // ensureDirectlyConnectedRoutes() shouldn't change the routes since direct connected - // route is already part of the configuration. - rmnet1.ensureDirectlyConnectedRoutes(); - assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes()); - - // IPv6 case: only default routes added initially - LinkProperties rmnet2 = new LinkProperties(); - rmnet2.setInterfaceName("rmnet2"); - rmnet2.addLinkAddress(new LinkAddress("fe80::cafe/64")); - rmnet2.addLinkAddress(new LinkAddress("2001:db8::2/64")); - RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null, address("2001:db8::1"), - rmnet2.getInterfaceName()); - RouteInfo directRoute2 = new RouteInfo(new IpPrefix("2001:db8::/64"), null, - rmnet2.getInterfaceName()); - RouteInfo linkLocalRoute2 = new RouteInfo(new IpPrefix("fe80::/64"), null, - rmnet2.getInterfaceName()); - rmnet2.addRoute(defaultRoute2); - - assertEqualRoutes(Arrays.asList(defaultRoute2), rmnet2.getAllRoutes()); - rmnet2.ensureDirectlyConnectedRoutes(); - assertEqualRoutes(Arrays.asList(defaultRoute2, directRoute2, linkLocalRoute2), - rmnet2.getAllRoutes()); - - // Corner case: no interface name - LinkProperties rmnet3 = new LinkProperties(); - rmnet3.addLinkAddress(new LinkAddress("192.168.0.2/24")); - RouteInfo directRoute3 = new RouteInfo(new IpPrefix("192.168.0.0/24"), null, - rmnet3.getInterfaceName()); - - assertTrue(rmnet3.getAllRoutes().isEmpty()); - rmnet3.ensureDirectlyConnectedRoutes(); - assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes()); - } - - private void assertEqualRoutes(Collection expected, Collection actual) { - Set expectedSet = new ArraySet<>(expected); - Set actualSet = new ArraySet<>(actual); - // Duplicated entries in actual routes are considered failures - assertEquals(actual.size(), actualSet.size()); - - assertEquals(expectedSet, actualSet); - } - - private static LinkProperties makeLinkPropertiesForParceling() { - LinkProperties source = new LinkProperties(); - source.setInterfaceName(NAME); - - source.addLinkAddress(LINKADDRV4); - source.addLinkAddress(LINKADDRV6); - - source.addDnsServer(DNS1); - source.addDnsServer(DNS2); - source.addDnsServer(GATEWAY62); - - source.addPcscfServer(TESTIPV4ADDR); - source.addPcscfServer(TESTIPV6ADDR); - - source.setUsePrivateDns(true); - source.setPrivateDnsServerName(PRIV_DNS_SERVER_NAME); - - source.setDomains(DOMAINS); - - source.addRoute(new RouteInfo(GATEWAY1)); - source.addRoute(new RouteInfo(GATEWAY2)); - - source.addValidatedPrivateDnsServer(DNS6); - source.addValidatedPrivateDnsServer(GATEWAY61); - source.addValidatedPrivateDnsServer(TESTIPV6ADDR); - - source.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888)); - - source.setMtu(MTU); - - source.setTcpBufferSizes(TCP_BUFFER_SIZES); - - source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - - final LinkProperties stacked = new LinkProperties(); - stacked.setInterfaceName("test-stacked"); - source.addStackedLink(stacked); - - return source; - } - - @Test @IgnoreAfter(Build.VERSION_CODES.Q) - public void testLinkPropertiesParcelable_Q() throws Exception { - final LinkProperties source = makeLinkPropertiesForParceling(); - assertParcelSane(source, 14 /* fieldCount */); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testLinkPropertiesParcelable() throws Exception { - final LinkProperties source = makeLinkPropertiesForParceling(); - - source.setWakeOnLanSupported(true); - source.setCaptivePortalApiUrl(CAPPORT_API_URL); - source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData()); - source.setDhcpServerAddress((Inet4Address) GATEWAY1); - assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */), - 18 /* fieldCount */); - - // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared. - final LinkProperties sanitized = new LinkProperties(source); - sanitized.setCaptivePortalApiUrl(null); - sanitized.setCaptivePortalData(null); - assertEquals(sanitized, parcelingRoundTrip(source)); - } - - // Parceling of the scope was broken until Q-QPR2 - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testLinkLocalDnsServerParceling() throws Exception { - final String strAddress = "fe80::1%lo"; - final LinkProperties lp = new LinkProperties(); - lp.addDnsServer(address(strAddress)); - final LinkProperties unparceled = parcelingRoundTrip(lp); - // Inet6Address#equals does not test for the scope id - assertEquals(strAddress, unparceled.getDnsServers().get(0).getHostAddress()); - } - - @Test - public void testParcelUninitialized() throws Exception { - LinkProperties empty = new LinkProperties(); - assertParcelingIsLossless(empty); - } - - @Test - public void testConstructor() { - LinkProperties lp = new LinkProperties(); - checkEmpty(lp); - assertLinkPropertiesEqual(lp, new LinkProperties(lp)); - assertLinkPropertiesEqual(lp, new LinkProperties()); - - lp = makeTestObject(); - assertLinkPropertiesEqual(lp, new LinkProperties(lp)); - } - - @Test - public void testDnsServers() { - final LinkProperties lp = new LinkProperties(); - final List dnsServers = Arrays.asList(DNS1, DNS2); - lp.setDnsServers(dnsServers); - assertEquals(2, lp.getDnsServers().size()); - assertEquals(DNS1, lp.getDnsServers().get(0)); - assertEquals(DNS2, lp.getDnsServers().get(1)); - - lp.removeDnsServer(DNS1); - assertEquals(1, lp.getDnsServers().size()); - assertEquals(DNS2, lp.getDnsServers().get(0)); - - lp.addDnsServer(DNS6); - assertEquals(2, lp.getDnsServers().size()); - assertEquals(DNS2, lp.getDnsServers().get(0)); - assertEquals(DNS6, lp.getDnsServers().get(1)); - } - - @Test - public void testValidatedPrivateDnsServers() { - final LinkProperties lp = new LinkProperties(); - final List privDnsServers = Arrays.asList(PRIVDNS1, PRIVDNS2); - lp.setValidatedPrivateDnsServers(privDnsServers); - assertEquals(2, lp.getValidatedPrivateDnsServers().size()); - assertEquals(PRIVDNS1, lp.getValidatedPrivateDnsServers().get(0)); - assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(1)); - - lp.removeValidatedPrivateDnsServer(PRIVDNS1); - assertEquals(1, lp.getValidatedPrivateDnsServers().size()); - assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0)); - - lp.addValidatedPrivateDnsServer(PRIVDNS6); - assertEquals(2, lp.getValidatedPrivateDnsServers().size()); - assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0)); - assertEquals(PRIVDNS6, lp.getValidatedPrivateDnsServers().get(1)); - } - - @Test - public void testPcscfServers() { - final LinkProperties lp = new LinkProperties(); - final List pcscfServers = Arrays.asList(PCSCFV4); - lp.setPcscfServers(pcscfServers); - assertEquals(1, lp.getPcscfServers().size()); - assertEquals(PCSCFV4, lp.getPcscfServers().get(0)); - - lp.removePcscfServer(PCSCFV4); - assertEquals(0, lp.getPcscfServers().size()); - - lp.addPcscfServer(PCSCFV6); - assertEquals(1, lp.getPcscfServers().size()); - assertEquals(PCSCFV6, lp.getPcscfServers().get(0)); - } - - @Test - public void testTcpBufferSizes() { - final LinkProperties lp = makeTestObject(); - assertEquals(TCP_BUFFER_SIZES, lp.getTcpBufferSizes()); - - lp.setTcpBufferSizes(null); - assertNull(lp.getTcpBufferSizes()); - } - - @Test - public void testHasIpv6DefaultRoute() { - final LinkProperties lp = makeTestObject(); - assertFalse(lp.hasIPv6DefaultRoute()); - - lp.addRoute(new RouteInfo(GATEWAY61)); - assertTrue(lp.hasIPv6DefaultRoute()); - } - - @Test - public void testHttpProxy() { - final LinkProperties lp = makeTestObject(); - assertTrue(lp.getHttpProxy().equals(ProxyInfo.buildDirectProxy("test", 8888))); - } - - @Test - public void testPrivateDnsServerName() { - final LinkProperties lp = makeTestObject(); - assertEquals(PRIV_DNS_SERVER_NAME, lp.getPrivateDnsServerName()); - - lp.setPrivateDnsServerName(null); - assertNull(lp.getPrivateDnsServerName()); - } - - @Test - public void testUsePrivateDns() { - final LinkProperties lp = makeTestObject(); - assertTrue(lp.isPrivateDnsActive()); - - lp.clear(); - assertFalse(lp.isPrivateDnsActive()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testDhcpServerAddress() { - final LinkProperties lp = makeTestObject(); - assertEquals(DHCPSERVER, lp.getDhcpServerAddress()); - - lp.clear(); - assertNull(lp.getDhcpServerAddress()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testWakeOnLanSupported() { - final LinkProperties lp = makeTestObject(); - assertTrue(lp.isWakeOnLanSupported()); - - lp.clear(); - assertFalse(lp.isWakeOnLanSupported()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testCaptivePortalApiUrl() { - final LinkProperties lp = makeTestObject(); - assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl()); - - lp.clear(); - assertNull(lp.getCaptivePortalApiUrl()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testCaptivePortalData() { - final LinkProperties lp = makeTestObject(); - assertEquals(getCaptivePortalData(), lp.getCaptivePortalData()); - - lp.clear(); - assertNull(lp.getCaptivePortalData()); - } - - private LinkProperties makeIpv4LinkProperties() { - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setInterfaceName(NAME); - linkProperties.addLinkAddress(LINKADDRV4); - linkProperties.addDnsServer(DNS1); - linkProperties.addRoute(new RouteInfo(GATEWAY1)); - linkProperties.addRoute(new RouteInfo(GATEWAY2)); - return linkProperties; - } - - private LinkProperties makeIpv6LinkProperties() { - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setInterfaceName(NAME); - linkProperties.addLinkAddress(LINKADDRV6); - linkProperties.addDnsServer(DNS6); - linkProperties.addRoute(new RouteInfo(GATEWAY61)); - linkProperties.addRoute(new RouteInfo(GATEWAY62)); - return linkProperties; - } - - @Test - public void testHasIpv4DefaultRoute() { - final LinkProperties Ipv4 = makeIpv4LinkProperties(); - assertTrue(Ipv4.hasIpv4DefaultRoute()); - final LinkProperties Ipv6 = makeIpv6LinkProperties(); - assertFalse(Ipv6.hasIpv4DefaultRoute()); - } - - @Test - public void testHasIpv4DnsServer() { - final LinkProperties Ipv4 = makeIpv4LinkProperties(); - assertTrue(Ipv4.hasIpv4DnsServer()); - final LinkProperties Ipv6 = makeIpv6LinkProperties(); - assertFalse(Ipv6.hasIpv4DnsServer()); - } - - @Test - public void testHasIpv6DnsServer() { - final LinkProperties Ipv4 = makeIpv4LinkProperties(); - assertFalse(Ipv4.hasIpv6DnsServer()); - final LinkProperties Ipv6 = makeIpv6LinkProperties(); - assertTrue(Ipv6.hasIpv6DnsServer()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testHasIpv4UnreachableDefaultRoute() { - final LinkProperties lp = makeTestObject(); - assertFalse(lp.hasIpv4UnreachableDefaultRoute()); - assertFalse(lp.hasIpv6UnreachableDefaultRoute()); - - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); - assertTrue(lp.hasIpv4UnreachableDefaultRoute()); - assertFalse(lp.hasIpv6UnreachableDefaultRoute()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testHasIpv6UnreachableDefaultRoute() { - final LinkProperties lp = makeTestObject(); - assertFalse(lp.hasIpv6UnreachableDefaultRoute()); - assertFalse(lp.hasIpv4UnreachableDefaultRoute()); - - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); - assertTrue(lp.hasIpv6UnreachableDefaultRoute()); - assertFalse(lp.hasIpv4UnreachableDefaultRoute()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testRouteAddWithSameKey() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("wlan0"); - final IpPrefix v6 = new IpPrefix("64:ff9b::/96"); - lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1280)); - assertEquals(1, lp.getRoutes().size()); - lp.addRoute(new RouteInfo(v6, address("fe80::1"), "wlan0", RTN_UNICAST, 1500)); - assertEquals(1, lp.getRoutes().size()); - final IpPrefix v4 = new IpPrefix("192.0.2.128/25"); - lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_UNICAST, 1460)); - assertEquals(2, lp.getRoutes().size()); - lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_THROW, 1460)); - assertEquals(2, lp.getRoutes().size()); - } -} diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt deleted file mode 100644 index a5e44d59fcab..000000000000 --- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 - -import android.net.wifi.aware.DiscoverySession -import android.net.wifi.aware.PeerHandle -import android.net.wifi.aware.WifiAwareNetworkSpecifier -import android.os.Build -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 - -import com.android.testutils.assertParcelSane -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo - -import java.lang.IllegalStateException - -import org.junit.Assert.assertFalse -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito - -@RunWith(AndroidJUnit4::class) -@SmallTest -class MatchAllNetworkSpecifierTest { - @Rule @JvmField - val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() - - private val specifier = MatchAllNetworkSpecifier() - private val discoverySession = Mockito.mock(DiscoverySession::class.java) - private val peerHandle = Mockito.mock(PeerHandle::class.java) - private val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, - peerHandle).build() - - @Test - fun testParcel() { - assertParcelSane(MatchAllNetworkSpecifier(), 0) - } - - @Test - @IgnoreUpTo(Build.VERSION_CODES.Q) - @IgnoreAfter(Build.VERSION_CODES.R) - // Only run this test on Android R. - // The method - satisfiedBy() has changed to canBeSatisfiedBy() starting from Android R, so the - // method - canBeSatisfiedBy() cannot be found when running this test on Android Q. - fun testCanBeSatisfiedBy_OnlyForR() { - // MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to - // canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the - // NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned. - // Although it's not meeting the expectation, the behavior still needs to be verified. - assertFalse(specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier)) - } - - @Test(expected = IllegalStateException::class) - @IgnoreUpTo(Build.VERSION_CODES.R) - fun testCanBeSatisfiedBy() { - specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier) - } -} diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt deleted file mode 100644 index 46f39dd016fd..000000000000 --- a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 - -import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS -import android.net.InvalidPacketException.ERROR_INVALID_PORT -import android.net.NattSocketKeepalive.NATT_PORT -import android.os.Build -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertEqualBothWays -import com.android.testutils.assertFieldCountEquals -import com.android.testutils.assertParcelSane -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import com.android.testutils.parcelingRoundTrip -import java.net.InetAddress -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotEquals -import org.junit.Assert.fail -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NattKeepalivePacketDataTest { - @Rule @JvmField - val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule() - - /* Refer to the definition in {@code NattKeepalivePacketData} */ - private val IPV4_HEADER_LENGTH = 20 - private val UDP_HEADER_LENGTH = 8 - - private val TEST_PORT = 4243 - private val TEST_PORT2 = 4244 - private val TEST_SRC_ADDRV4 = "198.168.0.2".address() - private val TEST_DST_ADDRV4 = "198.168.0.1".address() - private val TEST_ADDRV6 = "2001:db8::1".address() - - private fun String.address() = InetAddresses.parseNumericAddress(this) - private fun nattKeepalivePacket( - srcAddress: InetAddress? = TEST_SRC_ADDRV4, - srcPort: Int = TEST_PORT, - dstAddress: InetAddress? = TEST_DST_ADDRV4, - dstPort: Int = NATT_PORT - ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort) - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testConstructor() { - try { - nattKeepalivePacket(dstPort = TEST_PORT) - fail("Dst port is not NATT port should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_PORT) - } - - try { - nattKeepalivePacket(srcAddress = TEST_ADDRV6) - fail("A v6 srcAddress should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) - } - - try { - nattKeepalivePacket(dstAddress = TEST_ADDRV6) - fail("A v6 dstAddress should cause exception") - } catch (e: InvalidPacketException) { - assertEquals(e.error, ERROR_INVALID_IP_ADDRESS) - } - - try { - parcelingRoundTrip( - NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT, - byteArrayOf(12, 31, 22, 44))) - fail("Invalid data should cause exception") - } catch (e: IllegalArgumentException) { } - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testParcel() { - assertParcelSane(nattKeepalivePacket(), 0) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testEquals() { - assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket()) - assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket()) - assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket()) - // Test src port only because dst port have to be NATT_PORT - assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket()) - // Make sure the parceling test is updated if fields are added in the base class. - assertFieldCountEquals(5, KeepalivePacketData::class.java) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testHashCode() { - assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode()) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt deleted file mode 100644 index 2b45b3d69ce9..000000000000 --- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 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 - -import android.os.Build -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.modules.utils.build.SdkLevel.isAtLeastS -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NetworkAgentConfigTest { - @Rule @JvmField - val ignoreRule = DevSdkIgnoreRule() - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testParcelNetworkAgentConfig() { - val config = NetworkAgentConfig.Builder().apply { - setExplicitlySelected(true) - setLegacyType(ConnectivityManager.TYPE_ETHERNET) - setSubscriberId("MySubId") - setPartialConnectivityAcceptable(false) - setUnvalidatedConnectivityAcceptable(true) - if (isAtLeastS()) { - setBypassableVpn(true) - } - }.build() - if (isAtLeastS()) { - // From S, the config will have 12 items - assertParcelSane(config, 12) - } else { - // For R or below, the config will have 10 items - assertParcelSane(config, 10) - } - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testBuilder() { - val config = NetworkAgentConfig.Builder().apply { - setExplicitlySelected(true) - setLegacyType(ConnectivityManager.TYPE_ETHERNET) - setSubscriberId("MySubId") - setPartialConnectivityAcceptable(false) - setUnvalidatedConnectivityAcceptable(true) - setLegacyTypeName("TEST_NETWORK") - if (isAtLeastS()) { - setNat64DetectionEnabled(false) - setProvisioningNotificationEnabled(false) - setBypassableVpn(true) - } - }.build() - - assertTrue(config.isExplicitlySelected()) - assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType()) - assertEquals("MySubId", config.getSubscriberId()) - assertFalse(config.isPartialConnectivityAcceptable()) - assertTrue(config.isUnvalidatedConnectivityAcceptable()) - assertEquals("TEST_NETWORK", config.getLegacyTypeName()) - if (isAtLeastS()) { - assertFalse(config.isNat64DetectionEnabled()) - assertFalse(config.isProvisioningNotificationEnabled()) - assertTrue(config.isBypassableVpn()) - } else { - assertTrue(config.isNat64DetectionEnabled()) - assertTrue(config.isProvisioningNotificationEnabled()) - } - } -} diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java deleted file mode 100644 index 9efdde4da0c0..000000000000 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ /dev/null @@ -1,1158 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; -import static android.net.NetworkCapabilities.MAX_TRANSPORT; -import static android.net.NetworkCapabilities.MIN_TRANSPORT; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; -import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; -import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; -import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; -import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; -import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_TEST; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; -import static android.os.Process.INVALID_UID; - -import static com.android.modules.utils.build.SdkLevel.isAtLeastR; -import static com.android.modules.utils.build.SdkLevel.isAtLeastS; -import static com.android.net.module.util.NetworkCapabilitiesUtils.TRANSPORT_USB; -import static com.android.testutils.MiscAsserts.assertEmpty; -import static com.android.testutils.MiscAsserts.assertThrows; -import static com.android.testutils.ParcelUtils.assertParcelSane; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -import android.net.wifi.aware.DiscoverySession; -import android.net.wifi.aware.PeerHandle; -import android.net.wifi.aware.WifiAwareNetworkSpecifier; -import android.os.Build; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.ArraySet; -import android.util.Range; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.CompatUtil; -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.Mockito; - -import java.util.Arrays; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkCapabilitiesTest { - private static final String TEST_SSID = "TEST_SSID"; - private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID"; - private static final int TEST_SUBID1 = 1; - private static final int TEST_SUBID2 = 2; - private static final int TEST_SUBID3 = 3; - - @Rule - public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); - - private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class); - private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class); - - @Test - public void testMaybeMarkCapabilitiesRestricted() { - // check that internet does not get restricted - NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.maybeMarkCapabilitiesRestricted(); - assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // metered-ness shouldn't matter - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.addCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.removeCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // add EIMS - bundled with unrestricted means it's unrestricted - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.addCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.removeCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // just a restricted cap should be restricted regardless of meteredness - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.addCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.removeCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // try 2 restricted caps - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_CBS); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.addCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - netCap = new NetworkCapabilities(); - netCap.addCapability(NET_CAPABILITY_CBS); - netCap.addCapability(NET_CAPABILITY_EIMS); - netCap.removeCapability(NET_CAPABILITY_NOT_METERED); - netCap.maybeMarkCapabilitiesRestricted(); - assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - } - - @Test - public void testDescribeImmutableDifferences() { - NetworkCapabilities nc1; - NetworkCapabilities nc2; - - // Transports changing - nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR); - nc2 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); - assertNotEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - - // Mutable capability changing - nc1 = new NetworkCapabilities().addCapability(NET_CAPABILITY_VALIDATED); - nc2 = new NetworkCapabilities(); - assertEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - - // NOT_METERED changing (http://b/63326103) - nc1 = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_NOT_METERED) - .addCapability(NET_CAPABILITY_INTERNET); - nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET); - assertEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - - // Immutable capability changing - nc1 = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET); - assertNotEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - - // Specifier changing - nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); - nc2 = new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth42")); - assertNotEquals("", nc1.describeImmutableDifferences(nc2)); - assertEquals("", nc1.describeImmutableDifferences(nc1)); - } - - @Test - public void testLinkBandwidthUtils() { - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities - .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); - assertEquals(10, NetworkCapabilities - .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); - assertEquals(10, NetworkCapabilities - .minBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); - assertEquals(10, NetworkCapabilities - .minBandwidth(10, 20)); - - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities - .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED)); - assertEquals(10, NetworkCapabilities - .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10)); - assertEquals(10, NetworkCapabilities - .maxBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED)); - assertEquals(20, NetworkCapabilities - .maxBandwidth(10, 20)); - } - - @Test - public void testSetUids() { - final NetworkCapabilities netCap = new NetworkCapabilities(); - // Null uids match all UIDs - netCap.setUids(null); - assertTrue(netCap.appliesToUid(10)); - assertTrue(netCap.appliesToUid(200)); - assertTrue(netCap.appliesToUid(3000)); - assertTrue(netCap.appliesToUid(10010)); - assertTrue(netCap.appliesToUidRange(new UidRange(50, 100))); - assertTrue(netCap.appliesToUidRange(new UidRange(70, 72))); - assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912))); - assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000))); - - if (isAtLeastS()) { - final Set> uids = new ArraySet<>(); - uids.add(uidRange(50, 100)); - uids.add(uidRange(3000, 4000)); - netCap.setUids(uids); - assertTrue(netCap.appliesToUid(50)); - assertTrue(netCap.appliesToUid(80)); - assertTrue(netCap.appliesToUid(100)); - assertTrue(netCap.appliesToUid(3000)); - assertTrue(netCap.appliesToUid(3001)); - assertFalse(netCap.appliesToUid(10)); - assertFalse(netCap.appliesToUid(25)); - assertFalse(netCap.appliesToUid(49)); - assertFalse(netCap.appliesToUid(101)); - assertFalse(netCap.appliesToUid(2000)); - assertFalse(netCap.appliesToUid(100000)); - - assertTrue(netCap.appliesToUidRange(new UidRange(50, 100))); - assertTrue(netCap.appliesToUidRange(new UidRange(70, 72))); - assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912))); - assertFalse(netCap.appliesToUidRange(new UidRange(1, 100))); - assertFalse(netCap.appliesToUidRange(new UidRange(49, 100))); - assertFalse(netCap.appliesToUidRange(new UidRange(1, 10))); - assertFalse(netCap.appliesToUidRange(new UidRange(60, 101))); - assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400))); - - NetworkCapabilities netCap2 = new NetworkCapabilities(); - // A new netcap object has null UIDs, so anything will satisfy it. - assertTrue(netCap2.satisfiedByUids(netCap)); - // Still not equal though. - assertFalse(netCap2.equalsUids(netCap)); - netCap2.setUids(uids); - assertTrue(netCap2.satisfiedByUids(netCap)); - assertTrue(netCap.equalsUids(netCap2)); - assertTrue(netCap2.equalsUids(netCap)); - - uids.add(uidRange(600, 700)); - netCap2.setUids(uids); - assertFalse(netCap2.satisfiedByUids(netCap)); - assertFalse(netCap.appliesToUid(650)); - assertTrue(netCap2.appliesToUid(650)); - netCap.combineCapabilities(netCap2); - assertTrue(netCap2.satisfiedByUids(netCap)); - assertTrue(netCap.appliesToUid(650)); - assertFalse(netCap.appliesToUid(500)); - - assertTrue(new NetworkCapabilities().satisfiedByUids(netCap)); - netCap.combineCapabilities(new NetworkCapabilities()); - assertTrue(netCap.appliesToUid(500)); - assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000))); - assertFalse(netCap2.appliesToUid(500)); - assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000))); - assertTrue(new NetworkCapabilities().satisfiedByUids(netCap)); - - // Null uids satisfies everything. - netCap.setUids(null); - assertTrue(netCap2.satisfiedByUids(netCap)); - assertTrue(netCap.satisfiedByUids(netCap2)); - netCap2.setUids(null); - assertTrue(netCap2.satisfiedByUids(netCap)); - assertTrue(netCap.satisfiedByUids(netCap2)); - } - } - - @Test - public void testParcelNetworkCapabilities() { - final Set> uids = new ArraySet<>(); - uids.add(uidRange(50, 100)); - uids.add(uidRange(3000, 4000)); - final NetworkCapabilities netCap = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_EIMS) - .addCapability(NET_CAPABILITY_NOT_METERED); - if (isAtLeastS()) { - netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)); - netCap.setUids(uids); - } - if (isAtLeastR()) { - netCap.setOwnerUid(123); - netCap.setAdministratorUids(new int[] {5, 11}); - } - assertParcelingIsLossless(netCap); - netCap.setSSID(TEST_SSID); - testParcelSane(netCap); - } - - @Test - public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() { - final NetworkCapabilities netCap = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_EIMS) - .addCapability(NET_CAPABILITY_NOT_METERED); - if (isAtLeastR()) { - netCap.setRequestorPackageName("com.android.test"); - netCap.setRequestorUid(9304); - } - assertParcelingIsLossless(netCap); - netCap.setSSID(TEST_SSID); - testParcelSane(netCap); - } - - private void testParcelSane(NetworkCapabilities cap) { - if (isAtLeastS()) { - assertParcelSane(cap, 16); - } else if (isAtLeastR()) { - assertParcelSane(cap, 15); - } else { - assertParcelSane(cap, 11); - } - } - - private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() { - return new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_EIMS) - .addCapability(NET_CAPABILITY_NOT_METERED) - .setSSID(TEST_SSID) - .setTransportInfo(new TestTransportInfo()) - .setRequestorPackageName("com.android.test") - .setRequestorUid(9304); - } - - @Test - public void testNetworkCapabilitiesCopyWithNoRedactions() { - assumeTrue(isAtLeastS()); - - final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo(); - final NetworkCapabilities netCapWithNoRedactions = - new NetworkCapabilities(netCap, NetworkCapabilities.REDACT_NONE); - TestTransportInfo testTransportInfo = - (TestTransportInfo) netCapWithNoRedactions.getTransportInfo(); - assertFalse(testTransportInfo.locationRedacted); - assertFalse(testTransportInfo.localMacAddressRedacted); - assertFalse(testTransportInfo.settingsRedacted); - } - - @Test - public void testNetworkCapabilitiesCopyWithoutLocationSensitiveFields() { - assumeTrue(isAtLeastS()); - - final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo(); - final NetworkCapabilities netCapWithNoRedactions = - new NetworkCapabilities(netCap, REDACT_FOR_ACCESS_FINE_LOCATION); - TestTransportInfo testTransportInfo = - (TestTransportInfo) netCapWithNoRedactions.getTransportInfo(); - assertTrue(testTransportInfo.locationRedacted); - assertFalse(testTransportInfo.localMacAddressRedacted); - assertFalse(testTransportInfo.settingsRedacted); - } - - @Test - public void testOemPaid() { - NetworkCapabilities nc = new NetworkCapabilities(); - // By default OEM_PAID is neither in the required or forbidden lists and the network is not - // restricted. - if (isAtLeastS()) { - assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PAID)); - } - assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID)); - nc.maybeMarkCapabilitiesRestricted(); - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Adding OEM_PAID to capability list should make network restricted. - nc.addCapability(NET_CAPABILITY_OEM_PAID); - nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability. - nc.maybeMarkCapabilitiesRestricted(); - assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PAID)); - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Now let's make request for OEM_PAID network. - NetworkCapabilities nr = new NetworkCapabilities(); - nr.addCapability(NET_CAPABILITY_OEM_PAID); - nr.maybeMarkCapabilitiesRestricted(); - assertTrue(nr.satisfiedByNetworkCapabilities(nc)); - - // Request fails for network with the default capabilities. - assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities())); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testOemPrivate() { - NetworkCapabilities nc = new NetworkCapabilities(); - // By default OEM_PRIVATE is neither in the required or forbidden lists and the network is - // not restricted. - assertFalse(nc.hasForbiddenCapability(NET_CAPABILITY_OEM_PRIVATE)); - assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE)); - nc.maybeMarkCapabilitiesRestricted(); - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Adding OEM_PRIVATE to capability list should make network restricted. - nc.addCapability(NET_CAPABILITY_OEM_PRIVATE); - nc.addCapability(NET_CAPABILITY_INTERNET); // Combine with unrestricted capability. - nc.maybeMarkCapabilitiesRestricted(); - assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PRIVATE)); - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Now let's make request for OEM_PRIVATE network. - NetworkCapabilities nr = new NetworkCapabilities(); - nr.addCapability(NET_CAPABILITY_OEM_PRIVATE); - nr.maybeMarkCapabilitiesRestricted(); - assertTrue(nr.satisfiedByNetworkCapabilities(nc)); - - // Request fails for network with the default capabilities. - assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities())); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testForbiddenCapabilities() { - NetworkCapabilities network = new NetworkCapabilities(); - - NetworkCapabilities request = new NetworkCapabilities(); - assertTrue("Request: " + request + ", Network:" + network, - request.satisfiedByNetworkCapabilities(network)); - - // Requesting absence of capabilities that network doesn't have. Request should satisfy. - request.addForbiddenCapability(NET_CAPABILITY_WIFI_P2P); - request.addForbiddenCapability(NET_CAPABILITY_NOT_METERED); - assertTrue(request.satisfiedByNetworkCapabilities(network)); - assertArrayEquals(new int[]{NET_CAPABILITY_WIFI_P2P, - NET_CAPABILITY_NOT_METERED}, - request.getForbiddenCapabilities()); - - // This is a default capability, just want to make sure its there because we use it below. - assertTrue(network.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Verify that adding forbidden capability will effectively remove it from capability list. - request.addForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED); - assertTrue(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED)); - assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - // Now this request won't be satisfied because network contains NOT_RESTRICTED. - assertFalse(request.satisfiedByNetworkCapabilities(network)); - network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - assertTrue(request.satisfiedByNetworkCapabilities(network)); - - // Verify that adding capability will effectively remove it from forbidden list - request.addCapability(NET_CAPABILITY_NOT_RESTRICTED); - assertTrue(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - assertFalse(request.hasForbiddenCapability(NET_CAPABILITY_NOT_RESTRICTED)); - - assertFalse(request.satisfiedByNetworkCapabilities(network)); - network.addCapability(NET_CAPABILITY_NOT_RESTRICTED); - assertTrue(request.satisfiedByNetworkCapabilities(network)); - } - - @Test - public void testConnectivityManagedCapabilities() { - NetworkCapabilities nc = new NetworkCapabilities(); - assertFalse(nc.hasConnectivityManagedCapability()); - // Check every single system managed capability. - nc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); - assertTrue(nc.hasConnectivityManagedCapability()); - nc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL); - nc.addCapability(NET_CAPABILITY_FOREGROUND); - assertTrue(nc.hasConnectivityManagedCapability()); - nc.removeCapability(NET_CAPABILITY_FOREGROUND); - nc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); - assertTrue(nc.hasConnectivityManagedCapability()); - nc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY); - nc.addCapability(NET_CAPABILITY_VALIDATED); - assertTrue(nc.hasConnectivityManagedCapability()); - } - - @Test - public void testEqualsNetCapabilities() { - NetworkCapabilities nc1 = new NetworkCapabilities(); - NetworkCapabilities nc2 = new NetworkCapabilities(); - assertTrue(nc1.equalsNetCapabilities(nc2)); - assertEquals(nc1, nc2); - - nc1.addCapability(NET_CAPABILITY_MMS); - assertFalse(nc1.equalsNetCapabilities(nc2)); - assertNotEquals(nc1, nc2); - nc2.addCapability(NET_CAPABILITY_MMS); - assertTrue(nc1.equalsNetCapabilities(nc2)); - assertEquals(nc1, nc2); - - if (isAtLeastS()) { - nc1.addForbiddenCapability(NET_CAPABILITY_INTERNET); - assertFalse(nc1.equalsNetCapabilities(nc2)); - nc2.addForbiddenCapability(NET_CAPABILITY_INTERNET); - assertTrue(nc1.equalsNetCapabilities(nc2)); - - // Remove a required capability doesn't affect forbidden capabilities. - // This is a behaviour change from R to S. - nc1.removeCapability(NET_CAPABILITY_INTERNET); - assertTrue(nc1.equalsNetCapabilities(nc2)); - - nc1.removeForbiddenCapability(NET_CAPABILITY_INTERNET); - assertFalse(nc1.equalsNetCapabilities(nc2)); - nc2.removeForbiddenCapability(NET_CAPABILITY_INTERNET); - assertTrue(nc1.equalsNetCapabilities(nc2)); - } - } - - @Test - public void testSSID() { - NetworkCapabilities nc1 = new NetworkCapabilities(); - NetworkCapabilities nc2 = new NetworkCapabilities(); - assertTrue(nc2.satisfiedBySSID(nc1)); - - nc1.setSSID(TEST_SSID); - assertTrue(nc2.satisfiedBySSID(nc1)); - nc2.setSSID("different " + TEST_SSID); - assertFalse(nc2.satisfiedBySSID(nc1)); - - assertTrue(nc1.satisfiedByImmutableNetworkCapabilities(nc2)); - assertFalse(nc1.satisfiedByNetworkCapabilities(nc2)); - } - - private ArraySet> uidRanges(int from, int to) { - final ArraySet> range = new ArraySet<>(1); - range.add(uidRange(from, to)); - return range; - } - - private Range uidRange(int from, int to) { - return new Range(from, to); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testSetAdministratorUids() { - NetworkCapabilities nc = - new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3}); - - assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testSetAdministratorUidsWithDuplicates() { - try { - new NetworkCapabilities().setAdministratorUids(new int[] {1, 1}); - fail("Expected IllegalArgumentException for duplicate uids"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testCombineCapabilities() { - NetworkCapabilities nc1 = new NetworkCapabilities(); - NetworkCapabilities nc2 = new NetworkCapabilities(); - - if (isAtLeastS()) { - nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL); - } - nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); - assertNotEquals(nc1, nc2); - nc2.combineCapabilities(nc1); - assertEquals(nc1, nc2); - assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - if (isAtLeastS()) { - assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); - } - - if (isAtLeastS()) { - // This will effectively move NOT_ROAMING capability from required to forbidden for nc1. - nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); - // It is not allowed to have the same capability in both wanted and forbidden list. - assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1)); - // Remove forbidden capability to continue other tests. - nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); - } - - nc1.setSSID(TEST_SSID); - nc2.combineCapabilities(nc1); - if (isAtLeastR()) { - assertTrue(TEST_SSID.equals(nc2.getSsid())); - } - - // Because they now have the same SSID, the following call should not throw - nc2.combineCapabilities(nc1); - - nc1.setSSID(DIFFERENT_TEST_SSID); - try { - nc2.combineCapabilities(nc1); - fail("Expected IllegalStateException: can't combine different SSIDs"); - } catch (IllegalStateException expected) {} - nc1.setSSID(TEST_SSID); - - if (isAtLeastS()) { - nc1.setUids(uidRanges(10, 13)); - assertNotEquals(nc1, nc2); - nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything. - assertNotEquals(nc1, nc2); - nc1.combineCapabilities(nc2); // 10~13 + everything is everything. - assertEquals(nc1, nc2); - nc1.setUids(uidRanges(10, 13)); - nc2.setUids(uidRanges(20, 23)); - assertNotEquals(nc1, nc2); - nc1.combineCapabilities(nc2); - assertTrue(nc1.appliesToUid(12)); - assertFalse(nc2.appliesToUid(12)); - assertTrue(nc1.appliesToUid(22)); - assertTrue(nc2.appliesToUid(22)); - - // Verify the subscription id list can be combined only when they are equal. - nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)); - nc2.setSubscriptionIds(Set.of(TEST_SUBID2)); - assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); - - nc2.setSubscriptionIds(Set.of()); - assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); - - nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1)); - nc2.combineCapabilities(nc1); - assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds()); - } - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testCombineCapabilities_AdministratorUids() { - final NetworkCapabilities nc1 = new NetworkCapabilities(); - final NetworkCapabilities nc2 = new NetworkCapabilities(); - - final int[] adminUids = {3, 6, 12}; - nc1.setAdministratorUids(adminUids); - nc2.combineCapabilities(nc1); - assertTrue(nc2.equalsAdministratorUids(nc1)); - assertArrayEquals(nc2.getAdministratorUids(), adminUids); - - final int[] adminUidsOtherOrder = {3, 12, 6}; - nc1.setAdministratorUids(adminUidsOtherOrder); - assertTrue(nc2.equalsAdministratorUids(nc1)); - - final int[] adminUids2 = {11, 1, 12, 3, 6}; - nc1.setAdministratorUids(adminUids2); - assertFalse(nc2.equalsAdministratorUids(nc1)); - assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2)); - try { - nc2.combineCapabilities(nc1); - fail("Shouldn't be able to combine different lists of admin UIDs"); - } catch (IllegalStateException expected) { } - } - - @Test - public void testSetCapabilities() { - final int[] REQUIRED_CAPABILITIES = new int[] { - NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN }; - - NetworkCapabilities nc1 = new NetworkCapabilities(); - NetworkCapabilities nc2 = new NetworkCapabilities(); - - nc1.setCapabilities(REQUIRED_CAPABILITIES); - assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities()); - - // Verify that setting and adding capabilities leads to the same object state. - nc2.clearAll(); - for (int cap : REQUIRED_CAPABILITIES) { - nc2.addCapability(cap); - } - assertEquals(nc1, nc2); - - if (isAtLeastS()) { - final int[] forbiddenCapabilities = new int[]{ - NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_RESTRICTED }; - - nc1.setCapabilities(REQUIRED_CAPABILITIES, forbiddenCapabilities); - assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities()); - assertArrayEquals(forbiddenCapabilities, nc1.getForbiddenCapabilities()); - - nc2.clearAll(); - for (int cap : REQUIRED_CAPABILITIES) { - nc2.addCapability(cap); - } - for (int cap : forbiddenCapabilities) { - nc2.addForbiddenCapability(cap); - } - assertEquals(nc1, nc2); - } - } - - @Test - public void testSetNetworkSpecifierOnMultiTransportNc() { - // Sequence 1: Transport + Transport + NetworkSpecifier - NetworkCapabilities nc1 = new NetworkCapabilities(); - nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI); - try { - nc1.setNetworkSpecifier(CompatUtil.makeEthernetNetworkSpecifier("eth0")); - fail("Cannot set NetworkSpecifier on a NetworkCapability with multiple transports!"); - } catch (IllegalStateException expected) { - // empty - } - - // Sequence 2: Transport + NetworkSpecifier + Transport - NetworkCapabilities nc2 = new NetworkCapabilities(); - nc2.addTransportType(TRANSPORT_CELLULAR).setNetworkSpecifier( - CompatUtil.makeEthernetNetworkSpecifier("testtap3")); - try { - nc2.addTransportType(TRANSPORT_WIFI); - fail("Cannot set a second TransportType of a network which has a NetworkSpecifier!"); - } catch (IllegalStateException expected) { - // empty - } - } - - @Test - public void testSetTransportInfoOnMultiTransportNc() { - // Sequence 1: Transport + Transport + TransportInfo - NetworkCapabilities nc1 = new NetworkCapabilities(); - nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI) - .setTransportInfo(new TestTransportInfo()); - - // Sequence 2: Transport + NetworkSpecifier + Transport - NetworkCapabilities nc2 = new NetworkCapabilities(); - nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo()) - .addTransportType(TRANSPORT_WIFI); - } - - @Test - public void testCombineTransportInfo() { - NetworkCapabilities nc1 = new NetworkCapabilities(); - nc1.setTransportInfo(new TestTransportInfo()); - - NetworkCapabilities nc2 = new NetworkCapabilities(); - // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where - // combine fails) - nc2.setTransportInfo(new TestTransportInfo()); - - try { - nc1.combineCapabilities(nc2); - fail("Should not be able to combine NetworkCabilities which contain TransportInfos"); - } catch (IllegalStateException expected) { - // empty - } - - // verify that can combine with identical TransportInfo objects - NetworkCapabilities nc3 = new NetworkCapabilities(); - nc3.setTransportInfo(nc1.getTransportInfo()); - nc1.combineCapabilities(nc3); - } - - @Test - public void testSet() { - NetworkCapabilities nc1 = new NetworkCapabilities(); - NetworkCapabilities nc2 = new NetworkCapabilities(); - - if (isAtLeastS()) { - nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL); - } - nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); - assertNotEquals(nc1, nc2); - nc2.set(nc1); - assertEquals(nc1, nc2); - assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - if (isAtLeastS()) { - assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); - } - - if (isAtLeastS()) { - // This will effectively move NOT_ROAMING capability from required to forbidden for nc1. - nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING); - } - nc1.setSSID(TEST_SSID); - nc2.set(nc1); - assertEquals(nc1, nc2); - if (isAtLeastS()) { - // Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability - // from nc2. - assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_NOT_ROAMING)); - } - - if (isAtLeastR()) { - assertTrue(TEST_SSID.equals(nc2.getSsid())); - } - - nc1.setSSID(DIFFERENT_TEST_SSID); - nc2.set(nc1); - assertEquals(nc1, nc2); - if (isAtLeastR()) { - assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid())); - } - if (isAtLeastS()) { - nc1.setUids(uidRanges(10, 13)); - } else { - nc1.setUids(null); - } - nc2.set(nc1); // Overwrites, as opposed to combineCapabilities - assertEquals(nc1, nc2); - - if (isAtLeastS()) { - assertThrows(NullPointerException.class, () -> nc1.setSubscriptionIds(null)); - nc1.setSubscriptionIds(Set.of()); - nc2.set(nc1); - assertEquals(nc1, nc2); - - nc1.setSubscriptionIds(Set.of(TEST_SUBID1)); - nc2.set(nc1); - assertEquals(nc1, nc2); - - nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1)); - nc2.set(nc1); - assertEquals(nc1, nc2); - - nc2.setSubscriptionIds(Set.of(TEST_SUBID3, TEST_SUBID2)); - assertNotEquals(nc1, nc2); - } - } - - @Test - public void testGetTransportTypes() { - final NetworkCapabilities nc = new NetworkCapabilities(); - nc.addTransportType(TRANSPORT_CELLULAR); - nc.addTransportType(TRANSPORT_WIFI); - nc.addTransportType(TRANSPORT_VPN); - nc.addTransportType(TRANSPORT_TEST); - - final int[] transportTypes = nc.getTransportTypes(); - assertEquals(4, transportTypes.length); - assertEquals(TRANSPORT_CELLULAR, transportTypes[0]); - assertEquals(TRANSPORT_WIFI, transportTypes[1]); - assertEquals(TRANSPORT_VPN, transportTypes[2]); - assertEquals(TRANSPORT_TEST, transportTypes[3]); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testTelephonyNetworkSpecifier() { - final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1); - final NetworkCapabilities nc1 = new NetworkCapabilities.Builder() - .addTransportType(TRANSPORT_WIFI) - .setNetworkSpecifier(specifier) - .build(); - assertEquals(specifier, nc1.getNetworkSpecifier()); - try { - final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() - .setNetworkSpecifier(specifier) - .build(); - fail("Must have a single transport type. Without transport type or multiple transport" - + " types is invalid."); - } catch (IllegalStateException expected) { } - } - - @Test - public void testWifiAwareNetworkSpecifier() { - final NetworkCapabilities nc = new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI_AWARE); - // If NetworkSpecifier is not set, the default value is null. - assertNull(nc.getNetworkSpecifier()); - final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder( - mDiscoverySession, mPeerHandle).build(); - nc.setNetworkSpecifier(specifier); - assertEquals(specifier, nc.getNetworkSpecifier()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testAdministratorUidsAndOwnerUid() { - // Test default owner uid. - // If the owner uid is not set, the default value should be Process.INVALID_UID. - final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build(); - assertEquals(INVALID_UID, nc1.getOwnerUid()); - // Test setAdministratorUids and getAdministratorUids. - final int[] administratorUids = {1001, 10001}; - final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() - .setAdministratorUids(administratorUids) - .build(); - assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids())); - // Test setOwnerUid and getOwnerUid. - // The owner UID must be included in administrator UIDs, or throw IllegalStateException. - try { - final NetworkCapabilities nc3 = new NetworkCapabilities.Builder() - .setOwnerUid(1001) - .build(); - fail("The owner UID must be included in administrator UIDs."); - } catch (IllegalStateException expected) { } - final NetworkCapabilities nc4 = new NetworkCapabilities.Builder() - .setAdministratorUids(administratorUids) - .setOwnerUid(1001) - .build(); - assertEquals(1001, nc4.getOwnerUid()); - try { - final NetworkCapabilities nc5 = new NetworkCapabilities.Builder() - .setAdministratorUids(null) - .build(); - fail("Should not set null into setAdministratorUids"); - } catch (NullPointerException expected) { } - } - - private static NetworkCapabilities capsWithSubIds(Integer ... subIds) { - // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for - // every NetworkCapabilities that simulates networks needs to add it too in order to - // satisfy these requests. - final NetworkCapabilities nc = new NetworkCapabilities.Builder() - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .setSubscriptionIds(new ArraySet<>(subIds)).build(); - assertEquals(new ArraySet<>(subIds), nc.getSubscriptionIds()); - return nc; - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testSubIds() throws Exception { - final NetworkCapabilities ncWithoutId = capsWithSubIds(); - final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1); - final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3); - final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3); - - final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build(); - assertEmpty(requestWithoutId.networkCapabilities.getSubscriptionIds()); - final NetworkRequest requestWithIds = new NetworkRequest.Builder() - .setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build(); - assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2), - requestWithIds.networkCapabilities.getSubscriptionIds()); - - assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId)); - assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds)); - assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds)); - assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId)); - assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId)); - assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId)); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testEqualsSubIds() throws Exception { - assertEquals(capsWithSubIds(), capsWithSubIds()); - assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1)); - assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1)); - assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2)); - assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); - assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2), - capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); - } - - @Test - public void testLinkBandwidthKbps() { - final NetworkCapabilities nc = new NetworkCapabilities(); - // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED. - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps()); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps()); - nc.setLinkDownstreamBandwidthKbps(512); - nc.setLinkUpstreamBandwidthKbps(128); - assertEquals(512, nc.getLinkDownstreamBandwidthKbps()); - assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps()); - assertEquals(128, nc.getLinkUpstreamBandwidthKbps()); - assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps()); - } - - private int getMaxTransport() { - if (!isAtLeastS() && MAX_TRANSPORT == TRANSPORT_USB) return MAX_TRANSPORT - 1; - return MAX_TRANSPORT; - } - - @Test - public void testSignalStrength() { - final NetworkCapabilities nc = new NetworkCapabilities(); - // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED. - assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength()); - nc.setSignalStrength(-80); - assertEquals(-80, nc.getSignalStrength()); - assertNotEquals(-50, nc.getSignalStrength()); - } - - private void assertNoTransport(NetworkCapabilities nc) { - for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { - assertFalse(nc.hasTransport(i)); - } - } - - // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all - // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence - // is true. If positiveSequence is false, then the check sequence is opposite. - private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType, - boolean positiveSequence) { - for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) { - if (positiveSequence) { - assertTrue(nc.hasTransport(i)); - } else { - assertFalse(nc.hasTransport(i)); - } - } - for (int i = getMaxTransport(); i > maxTransportType; i--) { - if (positiveSequence) { - assertFalse(nc.hasTransport(i)); - } else { - assertTrue(nc.hasTransport(i)); - } - } - } - - @Test - public void testMultipleTransportTypes() { - final NetworkCapabilities nc = new NetworkCapabilities(); - assertNoTransport(nc); - // Test adding multiple transport types. - for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { - nc.addTransportType(i); - checkCurrentTransportTypes(nc, i, true /* positiveSequence */); - } - // Test removing multiple transport types. - for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) { - nc.removeTransportType(i); - checkCurrentTransportTypes(nc, i, false /* positiveSequence */); - } - assertNoTransport(nc); - nc.addTransportType(TRANSPORT_WIFI); - assertTrue(nc.hasTransport(TRANSPORT_WIFI)); - assertFalse(nc.hasTransport(TRANSPORT_VPN)); - nc.addTransportType(TRANSPORT_VPN); - assertTrue(nc.hasTransport(TRANSPORT_WIFI)); - assertTrue(nc.hasTransport(TRANSPORT_VPN)); - nc.removeTransportType(TRANSPORT_WIFI); - assertFalse(nc.hasTransport(TRANSPORT_WIFI)); - assertTrue(nc.hasTransport(TRANSPORT_VPN)); - nc.removeTransportType(TRANSPORT_VPN); - assertFalse(nc.hasTransport(TRANSPORT_WIFI)); - assertFalse(nc.hasTransport(TRANSPORT_VPN)); - assertNoTransport(nc); - } - - @Test - public void testAddAndRemoveTransportType() { - final NetworkCapabilities nc = new NetworkCapabilities(); - try { - nc.addTransportType(-1); - fail("Should not set invalid transport type into addTransportType"); - } catch (IllegalArgumentException expected) { } - try { - nc.removeTransportType(-1); - fail("Should not set invalid transport type into removeTransportType"); - } catch (IllegalArgumentException e) { } - } - - /** - * Test TransportInfo to verify redaction mechanism. - */ - private static class TestTransportInfo implements TransportInfo { - public final boolean locationRedacted; - public final boolean localMacAddressRedacted; - public final boolean settingsRedacted; - - TestTransportInfo() { - locationRedacted = false; - localMacAddressRedacted = false; - settingsRedacted = false; - } - - TestTransportInfo(boolean locationRedacted, - boolean localMacAddressRedacted, - boolean settingsRedacted) { - this.locationRedacted = locationRedacted; - this.localMacAddressRedacted = - localMacAddressRedacted; - this.settingsRedacted = settingsRedacted; - } - - @Override - public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { - return new TestTransportInfo( - (redactions & NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION) != 0, - (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, - (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 - ); - } - - @Override - public @NetworkCapabilities.RedactionType long getApplicableRedactions() { - return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS - | REDACT_FOR_NETWORK_SETTINGS; - } - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testBuilder() { - final int ownerUid = 1001; - final int signalStrength = -80; - final int requestUid = 10100; - final int[] administratorUids = {ownerUid, 10001}; - final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1); - final TransportInfo transportInfo = new TransportInfo() {}; - final String ssid = "TEST_SSID"; - final String packageName = "com.google.test.networkcapabilities"; - final NetworkCapabilities nc = new NetworkCapabilities.Builder() - .addTransportType(TRANSPORT_WIFI) - .addTransportType(TRANSPORT_CELLULAR) - .removeTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_EIMS) - .addCapability(NET_CAPABILITY_CBS) - .removeCapability(NET_CAPABILITY_CBS) - .setAdministratorUids(administratorUids) - .setOwnerUid(ownerUid) - .setLinkDownstreamBandwidthKbps(512) - .setLinkUpstreamBandwidthKbps(128) - .setNetworkSpecifier(specifier) - .setTransportInfo(transportInfo) - .setSignalStrength(signalStrength) - .setSsid(ssid) - .setRequestorUid(requestUid) - .setRequestorPackageName(packageName) - .build(); - assertEquals(1, nc.getTransportTypes().length); - assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]); - assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS)); - assertFalse(nc.hasCapability(NET_CAPABILITY_CBS)); - assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids())); - assertEquals(ownerUid, nc.getOwnerUid()); - assertEquals(512, nc.getLinkDownstreamBandwidthKbps()); - assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps()); - assertEquals(128, nc.getLinkUpstreamBandwidthKbps()); - assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps()); - assertEquals(specifier, nc.getNetworkSpecifier()); - assertEquals(transportInfo, nc.getTransportInfo()); - assertEquals(signalStrength, nc.getSignalStrength()); - assertNotEquals(-50, nc.getSignalStrength()); - assertEquals(ssid, nc.getSsid()); - assertEquals(requestUid, nc.getRequestorUid()); - assertEquals(packageName, nc.getRequestorPackageName()); - // Cannot assign null into NetworkCapabilities.Builder - try { - final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null); - fail("Should not set null into NetworkCapabilities.Builder"); - } catch (NullPointerException expected) { } - assertEquals(nc, new NetworkCapabilities.Builder(nc).build()); - - if (isAtLeastS()) { - final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() - .setSubscriptionIds(Set.of(TEST_SUBID1)).build(); - assertEquals(Set.of(TEST_SUBID1), nc2.getSubscriptionIds()); - } - } -} diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt deleted file mode 100644 index 7424157bea74..000000000000 --- a/tests/net/common/java/android/net/NetworkProviderTest.kt +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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 - -import android.app.Instrumentation -import android.content.Context -import android.net.NetworkCapabilities.TRANSPORT_TEST -import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable -import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn -import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested -import android.os.Build -import android.os.HandlerThread -import android.os.Looper -import androidx.test.InstrumentationRegistry -import com.android.net.module.util.ArrayTrackRecord -import com.android.testutils.CompatUtil -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import com.android.testutils.DevSdkIgnoreRunner -import com.android.testutils.isDevSdkInRange -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.verifyNoMoreInteractions -import java.util.UUID -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -private const val DEFAULT_TIMEOUT_MS = 5000L -private val instrumentation: Instrumentation - get() = InstrumentationRegistry.getInstrumentation() -private val context: Context get() = InstrumentationRegistry.getContext() -private val PROVIDER_NAME = "NetworkProviderTest" - -@RunWith(DevSdkIgnoreRunner::class) -@IgnoreUpTo(Build.VERSION_CODES.Q) -class NetworkProviderTest { - private val mCm = context.getSystemService(ConnectivityManager::class.java) - private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread") - - @Before - fun setUp() { - instrumentation.getUiAutomation().adoptShellPermissionIdentity() - mHandlerThread.start() - } - - @After - fun tearDown() { - mHandlerThread.quitSafely() - instrumentation.getUiAutomation().dropShellPermissionIdentity() - } - - private class TestNetworkProvider(context: Context, looper: Looper) : - NetworkProvider(context, looper, PROVIDER_NAME) { - private val seenEvents = ArrayTrackRecord().newReadHead() - - sealed class CallbackEntry { - data class OnNetworkRequested( - val request: NetworkRequest, - val score: Int, - val id: Int - ) : CallbackEntry() - data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry() - } - - override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) { - seenEvents.add(OnNetworkRequested(request, score, id)) - } - - override fun onNetworkRequestWithdrawn(request: NetworkRequest) { - seenEvents.add(OnNetworkRequestWithdrawn(request)) - } - - inline fun expectCallback( - crossinline predicate: (T) -> Boolean - ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } - } - - private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider { - return TestNetworkProvider(ctx, mHandlerThread.looper) - } - - @Test - fun testOnNetworkRequested() { - val provider = createNetworkProvider() - assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) - mCm.registerNetworkProvider(provider) - assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE) - - val specifier = CompatUtil.makeTestNetworkSpecifier( - UUID.randomUUID().toString()) - val nr: NetworkRequest = NetworkRequest.Builder() - .addTransportType(TRANSPORT_TEST) - .setNetworkSpecifier(specifier) - .build() - val cb = ConnectivityManager.NetworkCallback() - mCm.requestNetwork(nr, cb) - provider.expectCallback() { callback -> - callback.request.getNetworkSpecifier() == specifier && - callback.request.hasTransport(TRANSPORT_TEST) - } - - val initialScore = 40 - val updatedScore = 60 - val nc = NetworkCapabilities().apply { - addTransportType(NetworkCapabilities.TRANSPORT_TEST) - removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED) - addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING) - addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - setNetworkSpecifier(specifier) - } - val lp = LinkProperties() - val config = NetworkAgentConfig.Builder().build() - val agent = object : NetworkAgent(context, mHandlerThread.looper, "TestAgent", nc, lp, - initialScore, config, provider) {} - - provider.expectCallback() { callback -> - callback.request.getNetworkSpecifier() == specifier && - callback.score == initialScore && - callback.id == agent.providerId - } - - agent.sendNetworkScore(updatedScore) - provider.expectCallback() { callback -> - callback.request.getNetworkSpecifier() == specifier && - callback.score == updatedScore && - callback.id == agent.providerId - } - - mCm.unregisterNetworkCallback(cb) - provider.expectCallback() { callback -> - callback.request.getNetworkSpecifier() == specifier && - callback.request.hasTransport(TRANSPORT_TEST) - } - mCm.unregisterNetworkProvider(provider) - // Provider id should be ID_NONE after unregister network provider - assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) - // unregisterNetworkProvider should not crash even if it's called on an - // already unregistered provider. - mCm.unregisterNetworkProvider(provider) - } - - private class TestNetworkCallback : ConnectivityManager.NetworkCallback() { - private val seenEvents = ArrayTrackRecord().newReadHead() - sealed class CallbackEntry { - object OnUnavailable : CallbackEntry() - } - - override fun onUnavailable() { - seenEvents.add(OnUnavailable) - } - - inline fun expectCallback( - crossinline predicate: (T) -> Boolean - ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } - } - - @Test - fun testDeclareNetworkRequestUnfulfillable() { - val mockContext = mock(Context::class.java) - doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE) - val provider = createNetworkProvider(mockContext) - // ConnectivityManager not required at creation time after R - if (!isDevSdkInRange(0, Build.VERSION_CODES.R)) { - verifyNoMoreInteractions(mockContext) - } - - mCm.registerNetworkProvider(provider) - - val specifier = CompatUtil.makeTestNetworkSpecifier( - UUID.randomUUID().toString()) - val nr: NetworkRequest = NetworkRequest.Builder() - .addTransportType(TRANSPORT_TEST) - .setNetworkSpecifier(specifier) - .build() - - val cb = TestNetworkCallback() - mCm.requestNetwork(nr, cb) - provider.declareNetworkRequestUnfulfillable(nr) - cb.expectCallback() { nr.getNetworkSpecifier() == specifier } - mCm.unregisterNetworkProvider(provider) - } -} diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/tests/net/common/java/android/net/NetworkSpecifierTest.kt deleted file mode 100644 index f3409f53596f..000000000000 --- a/tests/net/common/java/android/net/NetworkSpecifierTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 - -import android.os.Build -import androidx.test.filters.SmallTest -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import com.android.testutils.DevSdkIgnoreRunner -import kotlin.test.assertTrue -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotEquals -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(DevSdkIgnoreRunner::class) -@IgnoreUpTo(Build.VERSION_CODES.Q) -class NetworkSpecifierTest { - private class TestNetworkSpecifier( - val intData: Int = 123, - val stringData: String = "init" - ) : NetworkSpecifier() { - override fun canBeSatisfiedBy(other: NetworkSpecifier?): Boolean = - other != null && - other is TestNetworkSpecifier && - other.intData >= intData && - stringData.equals(other.stringData) - - override fun redact(): NetworkSpecifier = TestNetworkSpecifier(intData, "redact") - } - - @Test - fun testRedact() { - val ns: TestNetworkSpecifier = TestNetworkSpecifier() - val redactNs = ns.redact() - assertTrue(redactNs is TestNetworkSpecifier) - assertEquals(ns.intData, redactNs.intData) - assertNotEquals(ns.stringData, redactNs.stringData) - assertTrue("redact".equals(redactNs.stringData)) - } - - @Test - fun testcanBeSatisfiedBy() { - val target: TestNetworkSpecifier = TestNetworkSpecifier() - assertFalse(target.canBeSatisfiedBy(null)) - assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier())) - val otherNs = TelephonyNetworkSpecifier.Builder().setSubscriptionId(123).build() - assertFalse(target.canBeSatisfiedBy(otherNs)) - assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 999))) - assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 1))) - assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(stringData = "diff"))) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java deleted file mode 100644 index f8f9c72374ad..000000000000 --- a/tests/net/common/java/android/net/NetworkStackTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 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; - -import static org.junit.Assert.assertEquals; - -import android.os.Build; -import android.os.IBinder; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class NetworkStackTest { - @Rule - public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); - - @Mock private IBinder mConnectorBinder; - - @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testGetService() { - NetworkStack.setServiceForTest(mConnectorBinder); - assertEquals(NetworkStack.getService(), mConnectorBinder); - } -} diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt deleted file mode 100644 index 0ca4d9551f39..000000000000 --- a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2021 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 - -import android.net.ConnectivityManager.TYPE_NONE -import android.net.ConnectivityManager.TYPE_WIFI -import android.net.InetAddresses.parseNumericAddress -import android.net.NetworkCapabilities.TRANSPORT_WIFI -import android.os.Build -import androidx.test.filters.SmallTest -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRunner -import com.android.testutils.assertParcelSane -import org.junit.Test -import org.junit.runner.RunWith -import java.net.Inet4Address -import java.net.Inet6Address - -private const val TEST_IMSI = "imsi1" -private const val TEST_SSID = "SSID1" -private const val TEST_NETID = 123 - -private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address -private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address -private val TEST_IPV4_LINKADDR = LinkAddress("192.168.222.123/24") -private val TEST_IPV6_LINKADDR = LinkAddress("2001:db8::123/64") -private val TEST_IFACE = "fake0" -private val TEST_LINK_PROPERTIES = LinkProperties().apply { - interfaceName = TEST_IFACE - addLinkAddress(TEST_IPV4_LINKADDR) - addLinkAddress(TEST_IPV6_LINKADDR) - - // Add default routes - addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY)) - addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_GATEWAY)) -} - -private val TEST_CAPABILITIES = NetworkCapabilities().apply { - addTransportType(TRANSPORT_WIFI) - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) - setSSID(TEST_SSID) -} - -@SmallTest -@RunWith(DevSdkIgnoreRunner::class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) -class NetworkStateSnapshotTest { - - @Test - fun testParcelUnparcel() { - val emptySnapshot = NetworkStateSnapshot(Network(TEST_NETID), NetworkCapabilities(), - LinkProperties(), null, TYPE_NONE) - val snapshot = NetworkStateSnapshot( - Network(TEST_NETID), TEST_CAPABILITIES, TEST_LINK_PROPERTIES, TEST_IMSI, TYPE_WIFI) - assertParcelSane(emptySnapshot, 5) - assertParcelSane(snapshot, 5) - } -} diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java deleted file mode 100644 index 7423c733c6eb..000000000000 --- a/tests/net/common/java/android/net/NetworkTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2015 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; - -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 android.os.Build; -import android.platform.test.annotations.AppModeFull; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.net.DatagramSocket; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.SocketException; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkTest { - final Network mNetwork = new Network(99); - - @Rule - public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); - - @Test - public void testBindSocketOfInvalidFdThrows() throws Exception { - - final FileDescriptor fd = new FileDescriptor(); - assertFalse(fd.valid()); - - try { - mNetwork.bindSocket(fd); - fail("SocketException not thrown"); - } catch (SocketException expected) {} - } - - @Test - public void testBindSocketOfNonSocketFdThrows() throws Exception { - final File devNull = new File("/dev/null"); - assertTrue(devNull.canRead()); - - final FileInputStream fis = new FileInputStream(devNull); - assertTrue(null != fis.getFD()); - assertTrue(fis.getFD().valid()); - - try { - mNetwork.bindSocket(fis.getFD()); - fail("SocketException not thrown"); - } catch (SocketException expected) {} - } - - @Test - @AppModeFull(reason = "Socket cannot bind in instant app mode") - public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception { - final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY); - mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53); - assertTrue(mDgramSocket.isConnected()); - - try { - mNetwork.bindSocket(mDgramSocket); - fail("SocketException not thrown"); - } catch (SocketException expected) {} - } - - @Test - public void testBindSocketOfLocalSocketThrows() throws Exception { - final LocalSocket mLocalClient = new LocalSocket(); - mLocalClient.bind(new LocalSocketAddress("testClient")); - assertTrue(mLocalClient.getFileDescriptor().valid()); - - try { - mNetwork.bindSocket(mLocalClient.getFileDescriptor()); - fail("SocketException not thrown"); - } catch (SocketException expected) {} - - final LocalServerSocket mLocalServer = new LocalServerSocket("testServer"); - mLocalClient.connect(mLocalServer.getLocalSocketAddress()); - assertTrue(mLocalClient.isConnected()); - - try { - mNetwork.bindSocket(mLocalClient.getFileDescriptor()); - fail("SocketException not thrown"); - } catch (SocketException expected) {} - } - - @Test - public void testZeroIsObviousForDebugging() { - Network zero = new Network(0); - assertEquals(0, zero.hashCode()); - assertEquals(0, zero.getNetworkHandle()); - assertEquals("0", zero.toString()); - } - - @Test - public void testGetNetworkHandle() { - Network one = new Network(1); - Network two = new Network(2); - Network three = new Network(3); - - // None of the hashcodes are zero. - assertNotEquals(0, one.hashCode()); - assertNotEquals(0, two.hashCode()); - assertNotEquals(0, three.hashCode()); - - // All the hashcodes are distinct. - assertNotEquals(one.hashCode(), two.hashCode()); - assertNotEquals(one.hashCode(), three.hashCode()); - assertNotEquals(two.hashCode(), three.hashCode()); - - // None of the handles are zero. - assertNotEquals(0, one.getNetworkHandle()); - assertNotEquals(0, two.getNetworkHandle()); - assertNotEquals(0, three.getNetworkHandle()); - - // All the handles are distinct. - assertNotEquals(one.getNetworkHandle(), two.getNetworkHandle()); - assertNotEquals(one.getNetworkHandle(), three.getNetworkHandle()); - assertNotEquals(two.getNetworkHandle(), three.getNetworkHandle()); - - // The handles are not equal to the hashcodes. - assertNotEquals(one.hashCode(), one.getNetworkHandle()); - assertNotEquals(two.hashCode(), two.getNetworkHandle()); - assertNotEquals(three.hashCode(), three.getNetworkHandle()); - - // Adjust as necessary to test an implementation's specific constants. - // When running with runtest, "adb logcat -s TestRunner" can be useful. - assertEquals(7700664333L, one.getNetworkHandle()); - assertEquals(11995631629L, two.getNetworkHandle()); - assertEquals(16290598925L, three.getNetworkHandle()); - } - - // getNetId() did not exist in Q - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testGetNetId() { - assertEquals(1234, new Network(1234).getNetId()); - assertEquals(2345, new Network(2345, true).getNetId()); - } - - @Test - public void testFromNetworkHandle() { - final Network network = new Network(1234); - assertEquals(network.netId, Network.fromNetworkHandle(network.getNetworkHandle()).netId); - } - - // Parsing private DNS bypassing handle was not supported until S - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testFromNetworkHandlePrivateDnsBypass_S() { - final Network network = new Network(1234, true); - - final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle()); - assertEquals(network.netId, recreatedNetwork.netId); - assertEquals(network.getNetIdForResolv(), recreatedNetwork.getNetIdForResolv()); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.R) - public void testFromNetworkHandlePrivateDnsBypass_R() { - final Network network = new Network(1234, true); - - final Network recreatedNetwork = Network.fromNetworkHandle(network.getNetworkHandle()); - assertEquals(network.netId, recreatedNetwork.netId); - // Until R included, fromNetworkHandle would not parse the private DNS bypass flag - assertEquals(network.netId, recreatedNetwork.getNetIdForResolv()); - } - - @Test - public void testGetPrivateDnsBypassingCopy() { - final Network copy = mNetwork.getPrivateDnsBypassingCopy(); - assertEquals(mNetwork.netId, copy.netId); - assertNotEquals(copy.netId, copy.getNetIdForResolv()); - assertNotEquals(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv()); - } -} diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java deleted file mode 100644 index fd29a9539de8..000000000000 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2021 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; - -import static com.android.testutils.MiscAsserts.assertThrows; -import static com.android.testutils.ParcelUtils.assertParcelSane; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.os.Build; - -import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; -import com.android.testutils.DevSdkIgnoreRunner; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Map; - -@IgnoreUpTo(Build.VERSION_CODES.R) -@RunWith(DevSdkIgnoreRunner.class) -@SmallTest -public class OemNetworkPreferencesTest { - - private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; - private static final String TEST_PACKAGE = "com.google.apps.contacts"; - - private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder(); - - @Test - public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() { - assertThrows(NullPointerException.class, - () -> mBuilder.addNetworkPreference(null, TEST_PREF)); - } - - @Test - public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { - assertThrows(NullPointerException.class, - () -> mBuilder.clearNetworkPreference(null)); - } - - @Test - public void testGetNetworkPreferenceReturnsCorrectValue() { - final int expectedNumberOfMappings = 1; - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - - final Map networkPreferences = - mBuilder.build().getNetworkPreferences(); - - assertEquals(expectedNumberOfMappings, networkPreferences.size()); - assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - } - - @Test - public void testGetNetworkPreferenceReturnsUnmodifiableValue() { - final String newPackage = "new.com.google.apps.contacts"; - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - - final Map networkPreferences = - mBuilder.build().getNetworkPreferences(); - - assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.put(newPackage, TEST_PREF)); - - assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.remove(TEST_PACKAGE)); - - } - - @Test - public void testToStringReturnsCorrectValue() { - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - - final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString(); - - assertTrue(networkPreferencesString.contains(Integer.toString(TEST_PREF))); - assertTrue(networkPreferencesString.contains(TEST_PACKAGE)); - } - - @Test - public void testOemNetworkPreferencesParcelable() { - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - - final OemNetworkPreferences prefs = mBuilder.build(); - - assertParcelSane(prefs, 1 /* fieldCount */); - } - - @Test - public void testAddNetworkPreferenceOverwritesPriorPreference() { - final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - Map networkPreferences = - mBuilder.build().getNetworkPreferences(); - - assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); - - mBuilder.addNetworkPreference(TEST_PACKAGE, newPref); - networkPreferences = mBuilder.build().getNetworkPreferences(); - - assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref); - } - - @Test - public void testRemoveNetworkPreferenceRemovesValue() { - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - Map networkPreferences = - mBuilder.build().getNetworkPreferences(); - - assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - - mBuilder.clearNetworkPreference(TEST_PACKAGE); - networkPreferences = mBuilder.build().getNetworkPreferences(); - - assertFalse(networkPreferences.containsKey(TEST_PACKAGE)); - } - - @Test - public void testConstructorByOemNetworkPreferencesSetsValue() { - mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - OemNetworkPreferences networkPreference = mBuilder.build(); - - final Map networkPreferences = - new OemNetworkPreferences - .Builder(networkPreference) - .build() - .getNetworkPreferences(); - - assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); - } -} diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java deleted file mode 100644 index 71689f919726..000000000000 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (C) 2010 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; - -import static android.net.RouteInfo.RTN_UNREACHABLE; - -import static com.android.testutils.MiscAsserts.assertEqualBothWays; -import static com.android.testutils.MiscAsserts.assertFieldCountEquals; -import static com.android.testutils.MiscAsserts.assertNotEqualEitherWay; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Build; - -import androidx.core.os.BuildCompat; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class RouteInfoTest { - @Rule - public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); - - private static final int INVALID_ROUTE_TYPE = -1; - - private InetAddress Address(String addr) { - return InetAddresses.parseNumericAddress(addr); - } - - private IpPrefix Prefix(String prefix) { - return new IpPrefix(prefix); - } - - private static boolean isAtLeastR() { - // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) - return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); - } - - @Test - public void testConstructor() { - RouteInfo r; - // Invalid input. - try { - r = new RouteInfo((IpPrefix) null, null, "rmnet0"); - fail("Expected RuntimeException: destination and gateway null"); - } catch (RuntimeException e) { } - - try { - r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "rmnet0", - INVALID_ROUTE_TYPE); - fail("Invalid route type should cause exception"); - } catch (IllegalArgumentException e) { } - - try { - r = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("192.0.2.1"), "rmnet0", - RTN_UNREACHABLE); - fail("Address family mismatch should cause exception"); - } catch (IllegalArgumentException e) { } - - try { - r = new RouteInfo(Prefix("0.0.0.0/0"), Address("2001:db8::1"), "rmnet0", - RTN_UNREACHABLE); - fail("Address family mismatch should cause exception"); - } catch (IllegalArgumentException e) { } - - // Null destination is default route. - r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); - assertEquals(Prefix("::/0"), r.getDestination()); - assertEquals(Address("2001:db8::1"), r.getGateway()); - assertNull(r.getInterface()); - - r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0"); - assertEquals(Prefix("0.0.0.0/0"), r.getDestination()); - assertEquals(Address("192.0.2.1"), r.getGateway()); - assertEquals("wlan0", r.getInterface()); - - // Null gateway sets gateway to unspecified address (why?). - r = new RouteInfo(Prefix("2001:db8:beef:cafe::/48"), null, "lo"); - assertEquals(Prefix("2001:db8:beef::/48"), r.getDestination()); - assertEquals(Address("::"), r.getGateway()); - assertEquals("lo", r.getInterface()); - - r = new RouteInfo(Prefix("192.0.2.5/24"), null); - assertEquals(Prefix("192.0.2.0/24"), r.getDestination()); - assertEquals(Address("0.0.0.0"), r.getGateway()); - assertNull(r.getInterface()); - } - - @Test - public void testMatches() { - class PatchedRouteInfo { - private final RouteInfo mRouteInfo; - - public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) { - mRouteInfo = new RouteInfo(destination, gateway, iface); - } - - public boolean matches(InetAddress destination) { - return mRouteInfo.matches(destination); - } - } - - PatchedRouteInfo r; - - r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0"); - assertTrue(r.matches(Address("2001:db8:f00::ace:d00c"))); - assertTrue(r.matches(Address("2001:db8:f00::ace:d00d"))); - assertFalse(r.matches(Address("2001:db8:f00::ace:d00e"))); - assertFalse(r.matches(Address("2001:db8:f00::bad:d00d"))); - assertFalse(r.matches(Address("2001:4868:4860::8888"))); - assertFalse(r.matches(Address("8.8.8.8"))); - - r = new PatchedRouteInfo(Prefix("192.0.2.0/23"), null, "wlan0"); - assertTrue(r.matches(Address("192.0.2.43"))); - assertTrue(r.matches(Address("192.0.3.21"))); - assertFalse(r.matches(Address("192.0.0.21"))); - assertFalse(r.matches(Address("8.8.8.8"))); - - PatchedRouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0"); - assertTrue(ipv6Default.matches(Address("2001:db8::f00"))); - assertFalse(ipv6Default.matches(Address("192.0.2.1"))); - - PatchedRouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0"); - assertTrue(ipv4Default.matches(Address("255.255.255.255"))); - assertTrue(ipv4Default.matches(Address("192.0.2.1"))); - assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); - } - - @Test - public void testEquals() { - // IPv4 - RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); - RouteInfo r2 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); - assertEqualBothWays(r1, r2); - - RouteInfo r3 = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "wlan0"); - RouteInfo r4 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::2"), "wlan0"); - RouteInfo r5 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "rmnet0"); - assertNotEqualEitherWay(r1, r3); - assertNotEqualEitherWay(r1, r4); - assertNotEqualEitherWay(r1, r5); - - // IPv6 - r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); - r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); - assertEqualBothWays(r1, r2); - - r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); - r4 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.2"), "wlan0"); - r5 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "rmnet0"); - assertNotEqualEitherWay(r1, r3); - assertNotEqualEitherWay(r1, r4); - assertNotEqualEitherWay(r1, r5); - - // Interfaces (but not destinations or gateways) can be null. - r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); - r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); - r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); - assertEqualBothWays(r1, r2); - assertNotEqualEitherWay(r1, r3); - } - - @Test - public void testHostAndDefaultRoutes() { - RouteInfo r; - - r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0"); - assertFalse(r.isHostRoute()); - assertTrue(r.isDefaultRoute()); - assertTrue(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0"); - assertFalse(r.isHostRoute()); - assertTrue(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertTrue(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertFalse(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0"); - assertFalse(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); - assertTrue(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE); - assertFalse(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertTrue(r.isIPv4UnreachableDefault()); - assertFalse(r.isIPv6UnreachableDefault()); - } - - r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE); - assertFalse(r.isHostRoute()); - assertFalse(r.isDefaultRoute()); - assertFalse(r.isIPv4Default()); - assertFalse(r.isIPv6Default()); - if (isAtLeastR()) { - assertFalse(r.isIPv4UnreachableDefault()); - assertTrue(r.isIPv6UnreachableDefault()); - } - } - - @Test - public void testTruncation() { - LinkAddress l; - RouteInfo r; - - l = new LinkAddress("192.0.2.5/30"); - r = new RouteInfo(l, Address("192.0.2.1"), "wlan0"); - assertEquals("192.0.2.4", r.getDestination().getAddress().getHostAddress()); - - l = new LinkAddress("2001:db8:1:f::5/63"); - r = new RouteInfo(l, Address("2001:db8:5::1"), "wlan0"); - assertEquals("2001:db8:1:e::", r.getDestination().getAddress().getHostAddress()); - } - - // Make sure that creating routes to multicast addresses doesn't throw an exception. Even though - // there's nothing we can do with them, we don't want to crash if, e.g., someone calls - // requestRouteToHostAddress("230.0.0.0", MOBILE_HIPRI); - @Test - public void testMulticastRoute() { - RouteInfo r; - r = new RouteInfo(Prefix("230.0.0.0/32"), Address("192.0.2.1"), "wlan0"); - r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), "wlan0"); - // No exceptions? Good. - } - - @Test - public void testParceling() { - RouteInfo r; - r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), null); - assertParcelingIsLossless(r); - r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertParcelingIsLossless(r); - r = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", RTN_UNREACHABLE); - assertParcelingIsLossless(r); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testMtuParceling() { - final RouteInfo r = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::"), "testiface", - RTN_UNREACHABLE, 1450 /* mtu */); - assertParcelingIsLossless(r); - } - - @Test @IgnoreAfter(Build.VERSION_CODES.Q) - public void testFieldCount_Q() { - assertFieldCountEquals(6, RouteInfo.class); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testFieldCount() { - // Make sure any new field is covered by the above parceling tests when changing this number - assertFieldCountEquals(7, RouteInfo.class); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testMtu() { - RouteInfo r; - r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0", - RouteInfo.RTN_UNICAST, 1500); - assertEquals(1500, r.getMtu()); - - r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0"); - assertEquals(0, r.getMtu()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testRouteKey() { - RouteInfo.RouteKey k1, k2; - // Only prefix, null gateway and null interface - k1 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey(); - k2 = new RouteInfo(Prefix("2001:db8::/128"), null).getRouteKey(); - assertEquals(k1, k2); - assertEquals(k1.hashCode(), k2.hashCode()); - - // With prefix, gateway and interface. Type and MTU does not affect RouteKey equality - k1 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", - RTN_UNREACHABLE, 1450).getRouteKey(); - k2 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0", - RouteInfo.RTN_UNICAST, 1400).getRouteKey(); - assertEquals(k1, k2); - assertEquals(k1.hashCode(), k2.hashCode()); - - // Different scope IDs are ignored by the kernel, so we consider them equal here too. - k1 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%1"), "wlan0").getRouteKey(); - k2 = new RouteInfo(Prefix("2001:db8::/64"), Address("fe80::1%2"), "wlan0").getRouteKey(); - assertEquals(k1, k2); - assertEquals(k1.hashCode(), k2.hashCode()); - - // Different prefix - k1 = new RouteInfo(Prefix("192.0.2.0/24"), null).getRouteKey(); - k2 = new RouteInfo(Prefix("192.0.3.0/24"), null).getRouteKey(); - assertNotEquals(k1, k2); - - // Different gateway - k1 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::1"), null).getRouteKey(); - k2 = new RouteInfo(Prefix("ff02::1/128"), Address("2001:db8::2"), null).getRouteKey(); - assertNotEquals(k1, k2); - - // Different interface - k1 = new RouteInfo(Prefix("ff02::1/128"), null, "tun0").getRouteKey(); - k2 = new RouteInfo(Prefix("ff02::1/128"), null, "tun1").getRouteKey(); - assertNotEquals(k1, k2); - } -} diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java deleted file mode 100644 index b5f23bf19a3c..000000000000 --- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2014 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class StaticIpConfigurationTest { - - private static final String ADDRSTR = "192.0.2.2/25"; - private static final LinkAddress ADDR = new LinkAddress(ADDRSTR); - private static final InetAddress GATEWAY = IpAddress("192.0.2.1"); - private static final InetAddress OFFLINKGATEWAY = IpAddress("192.0.2.129"); - private static final InetAddress DNS1 = IpAddress("8.8.8.8"); - private static final InetAddress DNS2 = IpAddress("8.8.4.4"); - private static final InetAddress DNS3 = IpAddress("4.2.2.2"); - private static final String IFACE = "eth0"; - private static final String FAKE_DOMAINS = "google.com"; - - private static InetAddress IpAddress(String addr) { - return InetAddress.parseNumericAddress(addr); - } - - private void checkEmpty(StaticIpConfiguration s) { - assertNull(s.ipAddress); - assertNull(s.gateway); - assertNull(s.domains); - assertEquals(0, s.dnsServers.size()); - } - - private StaticIpConfiguration makeTestObject() { - StaticIpConfiguration s = new StaticIpConfiguration(); - s.ipAddress = ADDR; - s.gateway = GATEWAY; - s.dnsServers.add(DNS1); - s.dnsServers.add(DNS2); - s.dnsServers.add(DNS3); - s.domains = FAKE_DOMAINS; - return s; - } - - @Test - public void testConstructor() { - StaticIpConfiguration s = new StaticIpConfiguration(); - checkEmpty(s); - } - - @Test - public void testCopyAndClear() { - StaticIpConfiguration empty = new StaticIpConfiguration((StaticIpConfiguration) null); - checkEmpty(empty); - - StaticIpConfiguration s1 = makeTestObject(); - StaticIpConfiguration s2 = new StaticIpConfiguration(s1); - assertEquals(s1, s2); - s2.clear(); - assertEquals(empty, s2); - } - - @Test - public void testHashCodeAndEquals() { - HashSet hashCodes = new HashSet(); - hashCodes.add(0); - - StaticIpConfiguration s = new StaticIpConfiguration(); - // Check that this hash code is nonzero and different from all the ones seen so far. - assertTrue(hashCodes.add(s.hashCode())); - - s.ipAddress = ADDR; - assertTrue(hashCodes.add(s.hashCode())); - - s.gateway = GATEWAY; - assertTrue(hashCodes.add(s.hashCode())); - - s.dnsServers.add(DNS1); - assertTrue(hashCodes.add(s.hashCode())); - - s.dnsServers.add(DNS2); - assertTrue(hashCodes.add(s.hashCode())); - - s.dnsServers.add(DNS3); - assertTrue(hashCodes.add(s.hashCode())); - - s.domains = "example.com"; - assertTrue(hashCodes.add(s.hashCode())); - - assertFalse(s.equals(null)); - assertEquals(s, s); - - StaticIpConfiguration s2 = new StaticIpConfiguration(s); - assertEquals(s, s2); - - s.ipAddress = new LinkAddress(DNS1, 32); - assertNotEquals(s, s2); - - s2 = new StaticIpConfiguration(s); - s.domains = "foo"; - assertNotEquals(s, s2); - - s2 = new StaticIpConfiguration(s); - s.gateway = DNS2; - assertNotEquals(s, s2); - - s2 = new StaticIpConfiguration(s); - s.dnsServers.add(DNS3); - assertNotEquals(s, s2); - } - - @Test - public void testToLinkProperties() { - LinkProperties expected = new LinkProperties(); - expected.setInterfaceName(IFACE); - - StaticIpConfiguration s = new StaticIpConfiguration(); - assertEquals(expected, s.toLinkProperties(IFACE)); - - final RouteInfo connectedRoute = new RouteInfo(new IpPrefix(ADDRSTR), null, IFACE); - s.ipAddress = ADDR; - expected.addLinkAddress(ADDR); - expected.addRoute(connectedRoute); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.gateway = GATEWAY; - RouteInfo defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), GATEWAY, IFACE); - expected.addRoute(defaultRoute); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.gateway = OFFLINKGATEWAY; - expected.removeRoute(defaultRoute); - defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), OFFLINKGATEWAY, IFACE); - expected.addRoute(defaultRoute); - - RouteInfo gatewayRoute = new RouteInfo(new IpPrefix("192.0.2.129/32"), null, IFACE); - expected.addRoute(gatewayRoute); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.dnsServers.add(DNS1); - expected.addDnsServer(DNS1); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.dnsServers.add(DNS2); - s.dnsServers.add(DNS3); - expected.addDnsServer(DNS2); - expected.addDnsServer(DNS3); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.domains = FAKE_DOMAINS; - expected.setDomains(FAKE_DOMAINS); - assertEquals(expected, s.toLinkProperties(IFACE)); - - s.gateway = null; - expected.removeRoute(defaultRoute); - expected.removeRoute(gatewayRoute); - assertEquals(expected, s.toLinkProperties(IFACE)); - - // Without knowing the IP address, we don't have a directly-connected route, so we can't - // tell if the gateway is off-link or not and we don't add a host route. This isn't a real - // configuration, but we should at least not crash. - s.gateway = OFFLINKGATEWAY; - s.ipAddress = null; - expected.removeLinkAddress(ADDR); - expected.removeRoute(connectedRoute); - expected.addRoute(defaultRoute); - assertEquals(expected, s.toLinkProperties(IFACE)); - } - - private StaticIpConfiguration passThroughParcel(StaticIpConfiguration s) { - Parcel p = Parcel.obtain(); - StaticIpConfiguration s2 = null; - try { - s.writeToParcel(p, 0); - p.setDataPosition(0); - s2 = StaticIpConfiguration.readFromParcel(p); - } finally { - p.recycle(); - } - assertNotNull(s2); - return s2; - } - - @Test - public void testParceling() { - StaticIpConfiguration s = makeTestObject(); - StaticIpConfiguration s2 = passThroughParcel(s); - assertEquals(s, s2); - } - - @Test - public void testBuilder() { - final ArrayList dnsServers = new ArrayList<>(); - dnsServers.add(DNS1); - - final StaticIpConfiguration s = new StaticIpConfiguration.Builder() - .setIpAddress(ADDR) - .setGateway(GATEWAY) - .setDomains(FAKE_DOMAINS) - .setDnsServers(dnsServers) - .build(); - - assertEquals(s.ipAddress, s.getIpAddress()); - assertEquals(ADDR, s.getIpAddress()); - assertEquals(s.gateway, s.getGateway()); - assertEquals(GATEWAY, s.getGateway()); - assertEquals(s.domains, s.getDomains()); - assertEquals(FAKE_DOMAINS, s.getDomains()); - assertTrue(s.dnsServers.equals(s.getDnsServers())); - assertEquals(1, s.getDnsServers().size()); - assertEquals(DNS1, s.getDnsServers().get(0)); - } - - @Test - public void testAddDnsServers() { - final StaticIpConfiguration s = new StaticIpConfiguration((StaticIpConfiguration) null); - checkEmpty(s); - - s.addDnsServer(DNS1); - assertEquals(1, s.getDnsServers().size()); - assertEquals(DNS1, s.getDnsServers().get(0)); - - s.addDnsServer(DNS2); - s.addDnsServer(DNS3); - assertEquals(3, s.getDnsServers().size()); - assertEquals(DNS2, s.getDnsServers().get(1)); - assertEquals(DNS3, s.getDnsServers().get(2)); - } - - @Test - public void testGetRoutes() { - final StaticIpConfiguration s = makeTestObject(); - final List routeInfoList = s.getRoutes(IFACE); - - assertEquals(2, routeInfoList.size()); - assertEquals(new RouteInfo(ADDR, (InetAddress) null, IFACE), routeInfoList.get(0)); - assertEquals(new RouteInfo((IpPrefix) null, GATEWAY, IFACE), routeInfoList.get(1)); - } -} diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt deleted file mode 100644 index 7a18bb08faa8..000000000000 --- a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 - -import android.net.InetAddresses.parseNumericAddress -import android.os.Build -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRunner -import com.android.testutils.assertFieldCountEquals -import com.android.testutils.assertParcelSane -import org.junit.Test -import org.junit.runner.RunWith -import java.net.InetAddress -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import kotlin.test.assertTrue - -@RunWith(DevSdkIgnoreRunner::class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) // TcpKeepalivePacketData added to SDK in S -class TcpKeepalivePacketDataTest { - private fun makeData( - srcAddress: InetAddress = parseNumericAddress("192.0.2.123"), - srcPort: Int = 1234, - dstAddress: InetAddress = parseNumericAddress("192.0.2.231"), - dstPort: Int = 4321, - data: ByteArray = byteArrayOf(1, 2, 3), - tcpSeq: Int = 135, - tcpAck: Int = 246, - tcpWnd: Int = 1234, - tcpWndScale: Int = 2, - ipTos: Int = 0x12, - ipTtl: Int = 10 - ) = TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data, tcpSeq, tcpAck, - tcpWnd, tcpWndScale, ipTos, ipTtl) - - @Test - fun testEquals() { - val data1 = makeData() - val data2 = makeData() - assertEquals(data1, data2) - assertEquals(data1.hashCode(), data2.hashCode()) - } - - @Test - fun testNotEquals() { - assertNotEquals(makeData(srcAddress = parseNumericAddress("192.0.2.124")), makeData()) - assertNotEquals(makeData(srcPort = 1235), makeData()) - assertNotEquals(makeData(dstAddress = parseNumericAddress("192.0.2.232")), makeData()) - assertNotEquals(makeData(dstPort = 4322), makeData()) - // .equals does not test .packet, as it should be generated from the other fields - assertNotEquals(makeData(tcpSeq = 136), makeData()) - assertNotEquals(makeData(tcpAck = 247), makeData()) - assertNotEquals(makeData(tcpWnd = 1235), makeData()) - assertNotEquals(makeData(tcpWndScale = 3), makeData()) - assertNotEquals(makeData(ipTos = 0x14), makeData()) - assertNotEquals(makeData(ipTtl = 11), makeData()) - - // Update above assertions if field is added - assertFieldCountEquals(5, KeepalivePacketData::class.java) - assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) - } - - @Test - fun testParcelUnparcel() { - assertParcelSane(makeData(), fieldCount = 6) { a, b -> - // .equals() does not verify .packet - a == b && a.packet contentEquals b.packet - } - } - - @Test - fun testToString() { - val data = makeData() - val str = data.toString() - - assertTrue(str.contains(data.srcAddress.hostAddress)) - assertTrue(str.contains(data.srcPort.toString())) - assertTrue(str.contains(data.dstAddress.hostAddress)) - assertTrue(str.contains(data.dstPort.toString())) - // .packet not included in toString() - assertTrue(str.contains(data.getTcpSeq().toString())) - assertTrue(str.contains(data.getTcpAck().toString())) - assertTrue(str.contains(data.getTcpWindow().toString())) - assertTrue(str.contains(data.getTcpWindowScale().toString())) - assertTrue(str.contains(data.getIpTos().toString())) - assertTrue(str.contains(data.getIpTtl().toString())) - - // Update above assertions if field is added - assertFieldCountEquals(5, KeepalivePacketData::class.java) - assertFieldCountEquals(6, TcpKeepalivePacketData::class.java) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/UidRangeTest.java b/tests/net/common/java/android/net/UidRangeTest.java deleted file mode 100644 index 1b1c95431d6f..000000000000 --- a/tests/net/common/java/android/net/UidRangeTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2016 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; - -import static android.os.UserHandle.MIN_SECONDARY_USER_ID; -import static android.os.UserHandle.SYSTEM; -import static android.os.UserHandle.USER_SYSTEM; -import static android.os.UserHandle.getUid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Build; -import android.os.UserHandle; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class UidRangeTest { - - /* - * UidRange is no longer passed to netd. UID ranges between the framework and netd are passed as - * UidRangeParcel objects. - */ - - @Rule - public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); - - @Test - public void testSingleItemUidRangeAllowed() { - new UidRange(123, 123); - new UidRange(0, 0); - new UidRange(Integer.MAX_VALUE, Integer.MAX_VALUE); - } - - @Test - public void testNegativeUidsDisallowed() { - try { - new UidRange(-2, 100); - fail("Exception not thrown for negative start UID"); - } catch (IllegalArgumentException expected) { - } - - try { - new UidRange(-200, -100); - fail("Exception not thrown for negative stop UID"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testStopLessThanStartDisallowed() { - final int x = 4195000; - try { - new UidRange(x, x - 1); - fail("Exception not thrown for negative-length UID range"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testGetStartAndEndUser() throws Exception { - final UidRange uidRangeOfPrimaryUser = new UidRange( - getUid(USER_SYSTEM, 10000), getUid(USER_SYSTEM, 10100)); - final UidRange uidRangeOfSecondaryUser = new UidRange( - getUid(MIN_SECONDARY_USER_ID, 10000), getUid(MIN_SECONDARY_USER_ID, 10100)); - assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); - assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser()); - assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getStartUser()); - assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser()); - - final UidRange uidRangeForDifferentUsers = new UidRange( - getUid(USER_SYSTEM, 10000), getUid(MIN_SECONDARY_USER_ID, 10100)); - assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); - assertEquals(MIN_SECONDARY_USER_ID, uidRangeOfSecondaryUser.getEndUser()); - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.R) - public void testCreateForUser() throws Exception { - final UidRange uidRangeOfPrimaryUser = UidRange.createForUser(SYSTEM); - final UidRange uidRangeOfSecondaryUser = UidRange.createForUser( - UserHandle.of(USER_SYSTEM + 1)); - assertTrue(uidRangeOfPrimaryUser.stop < uidRangeOfSecondaryUser.start); - assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getStartUser()); - assertEquals(USER_SYSTEM, uidRangeOfPrimaryUser.getEndUser()); - assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getStartUser()); - assertEquals(USER_SYSTEM + 1, uidRangeOfSecondaryUser.getEndUser()); - } -} diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt deleted file mode 100644 index f23ba26d0039..000000000000 --- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2021 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 - -import android.os.Build -import androidx.test.filters.SmallTest -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRunner -import com.android.testutils.assertParcelSane -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals - -private const val TEST_OWNER_UID = 123 -private const val TEST_IFACE = "test_tun0" -private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0") - -@SmallTest -@RunWith(DevSdkIgnoreRunner::class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) -class UnderlyingNetworkInfoTest { - @Test - fun testParcelUnparcel() { - val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST) - assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid()) - assertEquals(TEST_IFACE, testInfo.getInterface()) - assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces()) - assertParcelSane(testInfo, 3) - - val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf()) - assertEquals(0, emptyInfo.getOwnerUid()) - assertEquals(String(), emptyInfo.getInterface()) - assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces()) - assertParcelSane(emptyInfo, 3) - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java deleted file mode 100644 index d50406fd3a1c..000000000000 --- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2019 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.apf; - -import static com.android.testutils.ParcelUtils.assertParcelSane; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.content.Context; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Arrays; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ApfCapabilitiesTest { - private Context mContext; - - @Before - public void setUp() { - mContext = InstrumentationRegistry.getContext(); - } - - @Test - public void testConstructAndParcel() { - final ApfCapabilities caps = new ApfCapabilities(123, 456, 789); - assertEquals(123, caps.apfVersionSupported); - assertEquals(456, caps.maximumApfProgramSize); - assertEquals(789, caps.apfPacketFormat); - - assertParcelSane(caps, 3); - } - - @Test - public void testEquals() { - assertEquals(new ApfCapabilities(1, 2, 3), new ApfCapabilities(1, 2, 3)); - assertNotEquals(new ApfCapabilities(2, 2, 3), new ApfCapabilities(1, 2, 3)); - assertNotEquals(new ApfCapabilities(1, 3, 3), new ApfCapabilities(1, 2, 3)); - assertNotEquals(new ApfCapabilities(1, 2, 4), new ApfCapabilities(1, 2, 3)); - } - - @Test - public void testHasDataAccess() { - //hasDataAccess is only supported starting at apf version 4. - ApfCapabilities caps = new ApfCapabilities(1 /* apfVersionSupported */, 2, 3); - assertFalse(caps.hasDataAccess()); - - caps = new ApfCapabilities(4 /* apfVersionSupported */, 5, 6); - assertTrue(caps.hasDataAccess()); - } - - @Test - public void testGetApfDrop8023Frames() { - // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly - // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID. - final int resId = mContext.getResources().getIdentifier("config_apfDrop802_3Frames", - "bool", "android"); - final boolean shouldDrop8023Frames = mContext.getResources().getBoolean(resId); - final boolean actual = ApfCapabilities.getApfDrop8023Frames(); - assertEquals(shouldDrop8023Frames, actual); - } - - @Test - public void testGetApfEtherTypeBlackList() { - // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly - // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID. - final int resId = mContext.getResources().getIdentifier("config_apfEthTypeBlackList", - "array", "android"); - final int[] blacklistedEtherTypeArray = mContext.getResources().getIntArray(resId); - final int[] actual = ApfCapabilities.getApfEtherTypeBlackList(); - assertNotNull(actual); - assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual)); - } -} diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt deleted file mode 100644 index 0b7b74097cc6..000000000000 --- a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics; - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class ApfProgramEventTest { - private infix fun Int.hasFlag(flag: Int) = (this and (1 shl flag)) != 0 - - @Test - fun testBuilderAndParcel() { - val apfProgramEvent = ApfProgramEvent.Builder() - .setLifetime(1) - .setActualLifetime(2) - .setFilteredRas(3) - .setCurrentRas(4) - .setProgramLength(5) - .setFlags(true, true) - .build() - - assertEquals(1, apfProgramEvent.lifetime) - assertEquals(2, apfProgramEvent.actualLifetime) - assertEquals(3, apfProgramEvent.filteredRas) - assertEquals(4, apfProgramEvent.currentRas) - assertEquals(5, apfProgramEvent.programLength) - assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags) - - assertParcelSane(apfProgramEvent, 6) - } - - @Test - fun testFlagsFor() { - var flags = ApfProgramEvent.flagsFor(false, false) - assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) - assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) - - flags = ApfProgramEvent.flagsFor(true, false) - assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) - assertFalse(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) - - flags = ApfProgramEvent.flagsFor(false, true) - assertFalse(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) - assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) - - flags = ApfProgramEvent.flagsFor(true, true) - assertTrue(flags hasFlag ApfProgramEvent.FLAG_HAS_IPV4_ADDRESS) - assertTrue(flags hasFlag ApfProgramEvent.FLAG_MULTICAST_FILTER_ON) - } -} diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt deleted file mode 100644 index 46a8c8e5b509..000000000000 --- a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class ApfStatsTest { - @Test - fun testBuilderAndParcel() { - val apfStats = ApfStats.Builder() - .setDurationMs(Long.MAX_VALUE) - .setReceivedRas(1) - .setMatchingRas(2) - .setDroppedRas(3) - .setZeroLifetimeRas(4) - .setParseErrors(5) - .setProgramUpdates(6) - .setProgramUpdatesAll(7) - .setProgramUpdatesAllowingMulticast(8) - .setMaxProgramSize(9) - .build() - - assertEquals(Long.MAX_VALUE, apfStats.durationMs) - assertEquals(1, apfStats.receivedRas) - assertEquals(2, apfStats.matchingRas) - assertEquals(3, apfStats.droppedRas) - assertEquals(4, apfStats.zeroLifetimeRas) - assertEquals(5, apfStats.parseErrors) - assertEquals(6, apfStats.programUpdates) - assertEquals(7, apfStats.programUpdatesAll) - assertEquals(8, apfStats.programUpdatesAllowingMulticast) - assertEquals(9, apfStats.maxProgramSize) - - assertParcelSane(apfStats, 10) - } -} diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt deleted file mode 100644 index 8d7a9c405024..000000000000 --- a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -private const val FAKE_MESSAGE = "test" - -@RunWith(AndroidJUnit4::class) -@SmallTest -class DhcpClientEventTest { - @Test - fun testBuilderAndParcel() { - val dhcpClientEvent = DhcpClientEvent.Builder() - .setMsg(FAKE_MESSAGE) - .setDurationMs(Integer.MAX_VALUE) - .build() - - assertEquals(FAKE_MESSAGE, dhcpClientEvent.msg) - assertEquals(Integer.MAX_VALUE, dhcpClientEvent.durationMs) - - assertParcelSane(dhcpClientEvent, 2) - } -} diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt deleted file mode 100644 index 236f72eafbdc..000000000000 --- a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -package android.net.metrics - -import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH -import android.net.metrics.DhcpErrorEvent.errorCodeWithOption -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.parcelingRoundTrip -import java.lang.reflect.Modifier -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith - -private const val TEST_ERROR_CODE = 12345 -//DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java due to it's protected) -private const val DHCP_SUBNET_MASK = 1 - -@RunWith(AndroidJUnit4::class) -@SmallTest -class DhcpErrorEventTest { - - @Test - fun testConstructor() { - val event = DhcpErrorEvent(TEST_ERROR_CODE) - assertEquals(TEST_ERROR_CODE, event.errorCode) - } - - @Test - fun testParcelUnparcel() { - val event = DhcpErrorEvent(TEST_ERROR_CODE) - val parceled = parcelingRoundTrip(event) - assertEquals(TEST_ERROR_CODE, parceled.errorCode) - } - - @Test - fun testErrorCodeWithOption() { - val errorCode = errorCodeWithOption(DHCP_INVALID_OPTION_LENGTH, DHCP_SUBNET_MASK); - assertTrue((DHCP_INVALID_OPTION_LENGTH and errorCode) == DHCP_INVALID_OPTION_LENGTH); - assertTrue((DHCP_SUBNET_MASK and errorCode) == DHCP_SUBNET_MASK); - } - - @Test - fun testToString() { - val names = listOf("L2_ERROR", "L3_ERROR", "L4_ERROR", "DHCP_ERROR", "MISC_ERROR") - val errorFields = DhcpErrorEvent::class.java.declaredFields.filter { - it.type == Int::class.javaPrimitiveType - && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) - && it.name !in names - } - - errorFields.forEach { - val intValue = it.getInt(null) - val stringValue = DhcpErrorEvent(intValue).toString() - assertTrue("Invalid string for error 0x%08X (field %s): %s".format(intValue, it.name, - stringValue), - stringValue.contains(it.name)) - } - } - - @Test - fun testToString_InvalidErrorCode() { - assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString()) - } -} diff --git a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java deleted file mode 100644 index d4780d3a5d7b..000000000000 --- a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics; - -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; - -import android.net.ConnectivityMetricsEvent; -import android.net.IIpConnectivityMetrics; -import android.net.Network; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.BitUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpConnectivityLogTest { - private static final int FAKE_NET_ID = 100; - private static final int[] FAKE_TRANSPORT_TYPES = BitUtils.unpackBits(TRANSPORT_WIFI); - private static final long FAKE_TIME_STAMP = System.currentTimeMillis(); - private static final String FAKE_INTERFACE_NAME = "test"; - private static final IpReachabilityEvent FAKE_EV = - new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); - - @Mock IIpConnectivityMetrics mMockService; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testLoggingEvents() throws Exception { - IpConnectivityLog logger = new IpConnectivityLog(mMockService); - - assertTrue(logger.log(FAKE_EV)); - assertTrue(logger.log(FAKE_TIME_STAMP, FAKE_EV)); - assertTrue(logger.log(FAKE_NET_ID, FAKE_TRANSPORT_TYPES, FAKE_EV)); - assertTrue(logger.log(new Network(FAKE_NET_ID), FAKE_TRANSPORT_TYPES, FAKE_EV)); - assertTrue(logger.log(FAKE_INTERFACE_NAME, FAKE_EV)); - assertTrue(logger.log(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, TRANSPORT_WIFI, - FAKE_INTERFACE_NAME))); - - List got = verifyEvents(6); - assertEventsEqual(makeExpectedEvent(got.get(0).timestamp, 0, 0, null), got.get(0)); - assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, 0, 0, null), got.get(1)); - assertEventsEqual(makeExpectedEvent(got.get(2).timestamp, FAKE_NET_ID, - TRANSPORT_WIFI, null), got.get(2)); - assertEventsEqual(makeExpectedEvent(got.get(3).timestamp, FAKE_NET_ID, - TRANSPORT_WIFI, null), got.get(3)); - assertEventsEqual(makeExpectedEvent(got.get(4).timestamp, 0, 0, FAKE_INTERFACE_NAME), - got.get(4)); - assertEventsEqual(makeExpectedEvent(FAKE_TIME_STAMP, FAKE_NET_ID, - TRANSPORT_WIFI, FAKE_INTERFACE_NAME), got.get(5)); - } - - @Test - public void testLoggingEventsWithMultipleCallers() throws Exception { - IpConnectivityLog logger = new IpConnectivityLog(mMockService); - - final int nCallers = 10; - final int nEvents = 10; - for (int n = 0; n < nCallers; n++) { - final int i = n; - new Thread() { - public void run() { - for (int j = 0; j < nEvents; j++) { - assertTrue(logger.log(makeExpectedEvent( - FAKE_TIME_STAMP + i * 100 + j, - FAKE_NET_ID + i * 100 + j, - ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR, - FAKE_INTERFACE_NAME))); - } - } - }.start(); - } - - List got = verifyEvents(nCallers * nEvents, 200); - Collections.sort(got, EVENT_COMPARATOR); - Iterator iter = got.iterator(); - for (int i = 0; i < nCallers; i++) { - for (int j = 0; j < nEvents; j++) { - final long expectedTimestamp = FAKE_TIME_STAMP + i * 100 + j; - final int expectedNetId = FAKE_NET_ID + i * 100 + j; - final long expectedTransports = - ((i + j) % 2 == 0) ? TRANSPORT_WIFI : TRANSPORT_CELLULAR; - assertEventsEqual(makeExpectedEvent(expectedTimestamp, expectedNetId, - expectedTransports, FAKE_INTERFACE_NAME), iter.next()); - } - } - } - - private List verifyEvents(int n, int timeoutMs) throws Exception { - ArgumentCaptor captor = - ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); - verify(mMockService, timeout(timeoutMs).times(n)).logEvent(captor.capture()); - return captor.getAllValues(); - } - - private List verifyEvents(int n) throws Exception { - return verifyEvents(n, 10); - } - - - private ConnectivityMetricsEvent makeExpectedEvent(long timestamp, int netId, long transports, - String ifname) { - ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); - ev.timestamp = timestamp; - ev.data = FAKE_EV; - ev.netId = netId; - ev.transports = transports; - ev.ifname = ifname; - return ev; - } - - /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ - private void assertEventsEqual(ConnectivityMetricsEvent expected, - ConnectivityMetricsEvent got) { - assertEquals(expected.data, got.data); - assertEquals(expected.timestamp, got.timestamp); - assertEquals(expected.netId, got.netId); - assertEquals(expected.transports, got.transports); - assertEquals(expected.ifname, got.ifname); - } - - static final Comparator EVENT_COMPARATOR = - Comparator.comparingLong((ev) -> ev.timestamp); -} diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt deleted file mode 100644 index 64be50837fc9..000000000000 --- a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class IpManagerEventTest { - @Test - fun testConstructorAndParcel() { - (IpManagerEvent.PROVISIONING_OK..IpManagerEvent.ERROR_INTERFACE_NOT_FOUND).forEach { - val ipManagerEvent = IpManagerEvent(it, Long.MAX_VALUE) - assertEquals(it, ipManagerEvent.eventType) - assertEquals(Long.MAX_VALUE, ipManagerEvent.durationMs) - - assertParcelSane(ipManagerEvent, 2) - } - } -} diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt deleted file mode 100644 index 55b5e492dd47..000000000000 --- a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class IpReachabilityEventTest { - @Test - fun testConstructorAndParcel() { - (IpReachabilityEvent.PROBE..IpReachabilityEvent.PROVISIONING_LOST_ORGANIC).forEach { - val ipReachabilityEvent = IpReachabilityEvent(it) - assertEquals(it, ipReachabilityEvent.eventType) - - assertParcelSane(ipReachabilityEvent, 1) - } - } -} diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/tests/net/common/java/android/net/metrics/NetworkEventTest.kt deleted file mode 100644 index 41430b03a1eb..000000000000 --- a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NetworkEventTest { - @Test - fun testConstructorAndParcel() { - (NetworkEvent.NETWORK_CONNECTED..NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY).forEach { - var networkEvent = NetworkEvent(it) - assertEquals(it, networkEvent.eventType) - assertEquals(0, networkEvent.durationMs) - - networkEvent = NetworkEvent(it, Long.MAX_VALUE) - assertEquals(it, networkEvent.eventType) - assertEquals(Long.MAX_VALUE, networkEvent.durationMs) - - assertParcelSane(networkEvent, 2) - } - } -} diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/tests/net/common/java/android/net/metrics/RaEventTest.kt deleted file mode 100644 index d9b720332fbe..000000000000 --- a/tests/net/common/java/android/net/metrics/RaEventTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - -private const val NO_LIFETIME: Long = -1L - -@RunWith(AndroidJUnit4::class) -@SmallTest -class RaEventTest { - @Test - fun testConstructorAndParcel() { - var raEvent = RaEvent.Builder().build() - assertEquals(NO_LIFETIME, raEvent.routerLifetime) - assertEquals(NO_LIFETIME, raEvent.prefixValidLifetime) - assertEquals(NO_LIFETIME, raEvent.prefixPreferredLifetime) - assertEquals(NO_LIFETIME, raEvent.routeInfoLifetime) - assertEquals(NO_LIFETIME, raEvent.rdnssLifetime) - assertEquals(NO_LIFETIME, raEvent.dnsslLifetime) - - raEvent = RaEvent.Builder() - .updateRouterLifetime(1) - .updatePrefixValidLifetime(2) - .updatePrefixPreferredLifetime(3) - .updateRouteInfoLifetime(4) - .updateRdnssLifetime(5) - .updateDnsslLifetime(6) - .build() - assertEquals(1, raEvent.routerLifetime) - assertEquals(2, raEvent.prefixValidLifetime) - assertEquals(3, raEvent.prefixPreferredLifetime) - assertEquals(4, raEvent.routeInfoLifetime) - assertEquals(5, raEvent.rdnssLifetime) - assertEquals(6, raEvent.dnsslLifetime) - - raEvent = RaEvent.Builder() - .updateRouterLifetime(Long.MIN_VALUE) - .updateRouterLifetime(Long.MAX_VALUE) - .build() - assertEquals(Long.MIN_VALUE, raEvent.routerLifetime) - - raEvent = RaEvent(1, 2, 3, 4, 5, 6) - assertEquals(1, raEvent.routerLifetime) - assertEquals(2, raEvent.prefixValidLifetime) - assertEquals(3, raEvent.prefixPreferredLifetime) - assertEquals(4, raEvent.routeInfoLifetime) - assertEquals(5, raEvent.rdnssLifetime) - assertEquals(6, raEvent.dnsslLifetime) - - assertParcelSane(raEvent, 6) - } -} diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt deleted file mode 100644 index 51c0d41bf4d5..000000000000 --- a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 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.metrics - -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.assertParcelSane -import java.lang.reflect.Modifier -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith - -private const val FIRST_VALIDATION: Int = 1 shl 8 -private const val REVALIDATION: Int = 2 shl 8 - -@RunWith(AndroidJUnit4::class) -@SmallTest -class ValidationProbeEventTest { - private infix fun Int.hasType(type: Int) = (type and this) == type - - @Test - fun testBuilderAndParcel() { - var validationProbeEvent = ValidationProbeEvent.Builder() - .setProbeType(ValidationProbeEvent.PROBE_DNS, false).build() - - assertTrue(validationProbeEvent.probeType hasType REVALIDATION) - - validationProbeEvent = ValidationProbeEvent.Builder() - .setDurationMs(Long.MAX_VALUE) - .setProbeType(ValidationProbeEvent.PROBE_DNS, true) - .setReturnCode(ValidationProbeEvent.DNS_SUCCESS) - .build() - - assertEquals(Long.MAX_VALUE, validationProbeEvent.durationMs) - assertTrue(validationProbeEvent.probeType hasType ValidationProbeEvent.PROBE_DNS) - assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION) - assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode) - - assertParcelSane(validationProbeEvent, 3) - } - - @Test - fun testGetProbeName() { - val probeFields = ValidationProbeEvent::class.java.declaredFields.filter { - it.type == Int::class.javaPrimitiveType - && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) - && it.name.contains("PROBE") - } - - probeFields.forEach { - val intValue = it.getInt(null) - val stringValue = ValidationProbeEvent.getProbeName(intValue) - assertEquals(it.name, stringValue) - } - - } -} diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt deleted file mode 100644 index 7b22e45db90a..000000000000 --- a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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.netstats - -import android.net.NetworkStats -import android.net.NetworkStats.DEFAULT_NETWORK_NO -import android.net.NetworkStats.DEFAULT_NETWORK_YES -import android.net.NetworkStats.Entry -import android.net.NetworkStats.IFACE_VT -import android.net.NetworkStats.METERED_NO -import android.net.NetworkStats.METERED_YES -import android.net.NetworkStats.ROAMING_NO -import android.net.NetworkStats.ROAMING_YES -import android.net.NetworkStats.SET_DEFAULT -import android.net.NetworkStats.SET_FOREGROUND -import android.net.NetworkStats.TAG_NONE -import android.os.Build -import androidx.test.filters.SmallTest -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.assertFieldCountEquals -import com.android.testutils.assertNetworkStatsEquals -import com.android.testutils.assertParcelingIsLossless -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import kotlin.test.assertEquals - -@RunWith(JUnit4::class) -@SmallTest -class NetworkStatsApiTest { - @Rule - @JvmField - val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q) - - private val testStatsEmpty = NetworkStats(0L, 0) - - // Note that these variables need to be initialized outside of constructor, initialize - // here with methods that don't exist in Q devices will result in crash. - - // stats1 and stats2 will have some entries with common keys, which are expected to - // be merged if performing add on these 2 stats. - private lateinit var testStats1: NetworkStats - private lateinit var testStats2: NetworkStats - - // This is a result of adding stats1 and stats2, while the merging of common key items is - // subject to test later, this should not be initialized with for a loop to add stats1 - // and stats2 above. - private lateinit var testStats3: NetworkStats - - companion object { - private const val TEST_IFACE = "test0" - private const val TEST_UID1 = 1001 - private const val TEST_UID2 = 1002 - } - - @Before - fun setUp() { - testStats1 = NetworkStats(0L, 0) - // Entries which only appear in set1. - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4)) - // Entries which are common for set1 and set2. - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0)) - .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8)) - .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0)) - assertEquals(8, testStats1.size()) - - testStats2 = NetworkStats(0L, 0) - // Entries which are common for set1 and set2. - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45)) - .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7)) - .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0)) - // Entry which only appears in set2. - .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) - assertEquals(5, testStats2.size()) - - testStats3 = NetworkStats(0L, 9) - // Entries which are unique either in stats1 or stats2. - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2)) - .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) - // Entries which are common for stats1 and stats2 are being merged. - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1)) - .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49)) - .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15)) - .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0)) - assertEquals(9, testStats3.size()) - } - - @Test - fun testAddEntry() { - val expectedEntriesInStats2 = arrayOf( - Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1), - Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45), - Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7), - Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0), - Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0)) - - // While testStats* are already initialized with addEntry, verify content added - // matches expectation. - for (i in expectedEntriesInStats2.indices) { - val entry = testStats2.getValues(i, null) - assertEquals(expectedEntriesInStats2[i], entry) - } - - // Verify entry updated with addEntry. - val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9)) - assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9), - stats.getValues(3, null)) - } - - @Test - fun testAdd() { - var stats = NetworkStats(0L, 0) - assertNetworkStatsEquals(testStatsEmpty, stats) - stats = stats.add(testStats2) - assertNetworkStatsEquals(testStats2, stats) - stats = stats.add(testStats1) - // EMPTY + STATS2 + STATS1 = STATS3 - assertNetworkStatsEquals(testStats3, stats) - } - - @Test - fun testParcelUnparcel() { - assertParcelingIsLossless(testStatsEmpty) - assertParcelingIsLossless(testStats1) - assertParcelingIsLossless(testStats2) - assertFieldCountEquals(15, NetworkStats::class.java) - } - - @Test - fun testDescribeContents() { - assertEquals(0, testStatsEmpty.describeContents()) - assertEquals(0, testStats1.describeContents()) - assertEquals(0, testStats2.describeContents()) - assertEquals(0, testStats3.describeContents()) - } - - @Test - fun testSubtract() { - // STATS3 - STATS2 = STATS1 - assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2)) - // STATS3 - STATS1 = STATS2 - assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1)) - } - - @Test - fun testMethodsDontModifyReceiver() { - listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach { - val origStats = it.clone() - it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45)) - it.add(testStats3) - it.subtract(testStats1) - assertNetworkStatsEquals(origStats, it) - } - } -} \ No newline at end of file diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt deleted file mode 100644 index aaf97f36889b..000000000000 --- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2019 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.util - -import android.os.Build -import android.system.NetlinkSocketAddress -import android.system.Os -import android.system.OsConstants.AF_INET -import android.system.OsConstants.ETH_P_ALL -import android.system.OsConstants.IPPROTO_UDP -import android.system.OsConstants.RTMGRP_NEIGH -import android.system.OsConstants.SOCK_DGRAM -import android.system.PacketSocketAddress -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.testutils.DevSdkIgnoreRule -import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -private const val TEST_INDEX = 123 -private const val TEST_PORT = 555 -private const val FF_BYTE = 0xff.toByte() - -@RunWith(AndroidJUnit4::class) -@SmallTest -class SocketUtilsTest { - @Rule @JvmField - val ignoreRule = DevSdkIgnoreRule() - - @Test - fun testMakeNetlinkSocketAddress() { - val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH) - if (nlAddress is NetlinkSocketAddress) { - assertEquals(TEST_PORT, nlAddress.getPortId()) - assertEquals(RTMGRP_NEIGH, nlAddress.getGroupsMask()) - } else { - fail("Not NetlinkSocketAddress object") - } - } - - @Test - fun testMakePacketSocketAddress_Q() { - val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX) - assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) - - val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE }) - assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress) - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - fun testMakePacketSocketAddress() { - val pkAddress = SocketUtils.makePacketSocketAddress( - ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE }) - assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) - } - - @Test - fun testCloseSocket() { - // Expect no exception happening with null object. - SocketUtils.closeSocket(null) - - val fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) - assertTrue(fd.valid()) - SocketUtils.closeSocket(fd) - assertFalse(fd.valid()) - // Expecting socket should be still invalid even closed socket again. - SocketUtils.closeSocket(fd) - assertFalse(fd.valid()) - } -} diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp deleted file mode 100644 index 58ece37ef647..000000000000 --- a/tests/net/deflake/Android.bp +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright (C) 2019 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_test_host { - name: "FrameworksNetDeflakeTest", - srcs: ["src/**/*.kt"], - libs: [ - "junit", - "tradefed", - ], - static_libs: [ - "kotlin-test", - "net-host-tests-utils", - ], - data: [":FrameworksNetTests"], - test_suites: ["device-tests"], -} diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt deleted file mode 100644 index 62855255fec2..000000000000 --- a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2019 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.net - -import com.android.testutils.host.DeflakeHostTestBase -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner -import org.junit.runner.RunWith - -@RunWith(DeviceJUnit4ClassRunner::class) -class FrameworksNetDeflakeTest: DeflakeHostTestBase() { - override val runCount = 20 - override val testApkFilename = "FrameworksNetTests.apk" - override val testClasses = listOf("com.android.server.ConnectivityServiceTest") -} \ No newline at end of file diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp deleted file mode 100644 index 39c424e31f0e..000000000000 --- a/tests/net/integration/Android.bp +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (C) 2019 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "FrameworksNetIntegrationTests", - defaults: ["framework-connectivity-test-defaults"], - platform_apis: true, - certificate: "platform", - srcs: [ - "src/**/*.kt", - "src/**/*.aidl", - ], - libs: [ - "android.test.mock", - ], - static_libs: [ - "NetworkStackApiStableLib", - "androidx.test.ext.junit", - "frameworks-net-integration-testutils", - "kotlin-reflect", - "mockito-target-extended-minus-junit4", - "net-tests-utils", - "service-connectivity", - "services.core", - "services.net", - "testables", - ], - test_suites: ["device-tests"], - use_embedded_native_libs: true, - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - // android_library does not include JNI libs: include NetworkStack dependencies here - "libnativehelper_compat_libc++", - "libnetworkstackutilsjni", - ], -} - -// Utilities for testing framework code both in integration and unit tests. -java_library { - name: "frameworks-net-integration-testutils", - defaults: ["framework-connectivity-test-defaults"], - srcs: ["util/**/*.java", "util/**/*.kt"], - static_libs: [ - "androidx.annotation_annotation", - "androidx.test.rules", - "junit", - "net-tests-utils", - ], - libs: [ - "service-connectivity", - "services.core", - "services.net", - ], -} diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml deleted file mode 100644 index 2e1368935759..000000000000 --- a/tests/net/integration/AndroidManifest.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/net/integration/res/values/config.xml b/tests/net/integration/res/values/config.xml deleted file mode 100644 index 2c8046ffd781..000000000000 --- a/tests/net/integration/res/values/config.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - 12500 - http://test.android.com - https://secure.test.android.com - - http://fallback1.android.com - http://fallback2.android.com - - - - diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/tests/net/integration/src/android/net/TestNetworkStackClient.kt deleted file mode 100644 index 61ef5bdca487..000000000000 --- a/tests/net/integration/src/android/net/TestNetworkStackClient.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 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 - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.net.networkstack.NetworkStackClientBase -import android.os.IBinder -import com.android.server.net.integrationtests.TestNetworkStackService -import org.mockito.Mockito.any -import org.mockito.Mockito.spy -import org.mockito.Mockito.timeout -import org.mockito.Mockito.verify -import kotlin.test.fail - -const val TEST_ACTION_SUFFIX = ".Test" - -class TestNetworkStackClient(private val context: Context) : NetworkStackClientBase() { - // TODO: consider switching to TrackRecord for more expressive checks - private val lastCallbacks = HashMap() - private val moduleConnector = ConnectivityModuleConnector { _, action, _, _ -> - val intent = Intent(action) - val serviceName = TestNetworkStackService::class.qualifiedName - ?: fail("TestNetworkStackService name not found") - intent.component = ComponentName(context.packageName, serviceName) - return@ConnectivityModuleConnector intent - }.also { it.init(context) } - - fun start() { - moduleConnector.startModuleService( - INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX, - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) { connector -> - onNetworkStackConnected(INetworkStackConnector.Stub.asInterface(connector)) - } - } - - // base may be an instance of an inaccessible subclass, so non-spyable. - // Use a known open class that delegates to the original instance for all methods except - // asBinder. asBinder needs to use its own non-delegated implementation as otherwise it would - // return a binder token to a class that is not spied on. - open class NetworkMonitorCallbacksWrapper(private val base: INetworkMonitorCallbacks) : - INetworkMonitorCallbacks.Stub(), INetworkMonitorCallbacks by base { - // asBinder is implemented by both base class and delegate: specify explicitly - override fun asBinder(): IBinder { - return super.asBinder() - } - } - - override fun makeNetworkMonitor(network: Network, name: String?, cb: INetworkMonitorCallbacks) { - val cbSpy = spy(NetworkMonitorCallbacksWrapper(cb)) - lastCallbacks[network] = cbSpy - super.makeNetworkMonitor(network, name, cbSpy) - } - - fun verifyNetworkMonitorCreated(network: Network, timeoutMs: Long) { - val cb = lastCallbacks[network] - ?: fail("NetworkMonitor for network $network not requested") - verify(cb, timeout(timeoutMs)).onNetworkMonitorCreated(any()) - } -} \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt deleted file mode 100644 index e039ef072542..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests - -import android.app.usage.NetworkStatsManager -import android.content.ComponentName -import android.content.Context -import android.content.Context.BIND_AUTO_CREATE -import android.content.Context.BIND_IMPORTANT -import android.content.Intent -import android.content.ServiceConnection -import android.net.ConnectivityManager -import android.net.IDnsResolver -import android.net.INetd -import android.net.LinkProperties -import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL -import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET -import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED -import android.net.NetworkCapabilities.TRANSPORT_CELLULAR -import android.net.NetworkRequest -import android.net.TestNetworkStackClient -import android.net.Uri -import android.net.metrics.IpConnectivityLog -import android.os.ConditionVariable -import android.os.IBinder -import android.os.SystemConfigManager -import android.os.UserHandle -import android.testing.TestableContext -import android.util.Log -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.ConnectivityService -import com.android.server.NetworkAgentWrapper -import com.android.server.TestNetIdManager -import com.android.server.connectivity.MockableSystemProperties -import com.android.server.connectivity.ProxyTracker -import com.android.testutils.TestableNetworkCallback -import org.junit.After -import org.junit.Before -import org.junit.BeforeClass -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.AdditionalAnswers -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock -import org.mockito.Mockito.any -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.doNothing -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.eq -import org.mockito.Mockito.mock -import org.mockito.Mockito.spy -import org.mockito.MockitoAnnotations -import org.mockito.Spy -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotNull -import kotlin.test.assertTrue -import kotlin.test.fail - -const val SERVICE_BIND_TIMEOUT_MS = 5_000L -const val TEST_TIMEOUT_MS = 10_000L - -/** - * Test that exercises an instrumented version of ConnectivityService against an instrumented - * NetworkStack in a different test process. - */ -@RunWith(AndroidJUnit4::class) -class ConnectivityServiceIntegrationTest { - // lateinit used here for mocks as they need to be reinitialized between each test and the test - // should crash if they are used before being initialized. - @Mock - private lateinit var statsManager: NetworkStatsManager - @Mock - private lateinit var log: IpConnectivityLog - @Mock - private lateinit var netd: INetd - @Mock - private lateinit var dnsResolver: IDnsResolver - @Mock - private lateinit var systemConfigManager: SystemConfigManager - @Spy - private var context = TestableContext(realContext) - - // lateinit for these three classes under test, as they should be reset to a different instance - // for every test but should always be initialized before use (or the test should crash). - private lateinit var networkStackClient: TestNetworkStackClient - private lateinit var service: ConnectivityService - private lateinit var cm: ConnectivityManager - - companion object { - // lateinit for this binder token, as it must be initialized before any test code is run - // and use of it before init should crash the test. - private lateinit var nsInstrumentation: INetworkStackInstrumentation - private val bindingCondition = ConditionVariable(false) - - private val realContext get() = InstrumentationRegistry.getInstrumentation().context - private val httpProbeUrl get() = - realContext.getResources().getString(R.string.config_captive_portal_http_url) - private val httpsProbeUrl get() = - realContext.getResources().getString(R.string.config_captive_portal_https_url) - - private class InstrumentationServiceConnection : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { - Log.i("TestNetworkStack", "Service connected") - try { - if (service == null) fail("Error binding to NetworkStack instrumentation") - if (::nsInstrumentation.isInitialized) fail("Service already connected") - nsInstrumentation = INetworkStackInstrumentation.Stub.asInterface(service) - } finally { - bindingCondition.open() - } - } - - override fun onServiceDisconnected(name: ComponentName?) = Unit - } - - @BeforeClass - @JvmStatic - fun setUpClass() { - val intent = Intent(realContext, NetworkStackInstrumentationService::class.java) - intent.action = INetworkStackInstrumentation::class.qualifiedName - assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(), - BIND_AUTO_CREATE or BIND_IMPORTANT), - "Error binding to instrumentation service") - assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS), - "Timed out binding to instrumentation service " + - "after $SERVICE_BIND_TIMEOUT_MS ms") - } - } - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo(context)) - doReturn(UserHandle.ALL).`when`(asUserCtx).user - doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt()) - doNothing().`when`(context).sendStickyBroadcast(any(), any()) - doReturn(Context.SYSTEM_CONFIG_SERVICE).`when`(context) - .getSystemServiceName(SystemConfigManager::class.java) - doReturn(systemConfigManager).`when`(context) - .getSystemService(Context.SYSTEM_CONFIG_SERVICE) - doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString()) - - networkStackClient = TestNetworkStackClient(realContext) - networkStackClient.start() - - service = TestConnectivityService(makeDependencies()) - cm = ConnectivityManager(context, service) - context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm) - context.addMockSystemService(Context.NETWORK_STATS_SERVICE, statsManager) - - service.systemReadyInternal() - } - - private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService( - context, dnsResolver, log, netd, deps) - - private fun makeDependencies(): ConnectivityService.Dependencies { - val deps = spy(ConnectivityService.Dependencies()) - doReturn(networkStackClient).`when`(deps).networkStack - doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any()) - doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties - doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager() - return deps - } - - @After - fun tearDown() { - nsInstrumentation.clearAllState() - } - - @Test - fun testValidation() { - val request = NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .build() - val testCallback = TestableNetworkCallback() - - cm.registerNetworkCallback(request, testCallback) - nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204)) - nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) - - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */, - context) - networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) - - na.addCapability(NET_CAPABILITY_INTERNET) - na.connect() - - testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS) - assertEquals(2, nsInstrumentation.getRequestUrls().size) - } - - @Test - fun testCapportApi() { - val request = NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .build() - val testCb = TestableNetworkCallback() - val apiUrl = "https://capport.android.com" - - cm.registerNetworkCallback(request, testCb) - nsInstrumentation.addHttpResponse(HttpResponse( - apiUrl, - """ - |{ - | "captive": true, - | "user-portal-url": "https://login.capport.android.com", - | "venue-info-url": "https://venueinfo.capport.android.com" - |} - """.trimMargin())) - - // Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the - // HTTP probe as it should not be sent. - // Even if the HTTPS probe succeeds, a portal should be detected as the API takes precedence - // in that case. - nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) - - val lp = LinkProperties() - lp.captivePortalApiUrl = Uri.parse(apiUrl) - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context) - networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) - - na.addCapability(NET_CAPABILITY_INTERNET) - na.connect() - - testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS) - - val capportData = testCb.expectLinkPropertiesThat(na, TEST_TIMEOUT_MS) { - it.captivePortalData != null - }.lp.captivePortalData - assertNotNull(capportData) - assertTrue(capportData.isCaptive) - assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl) - assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl) - - val nc = testCb.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, na, TEST_TIMEOUT_MS) - assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)) - } -} \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl deleted file mode 100644 index 9a2bcfea7641..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests; - -parcelable HttpResponse; \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt deleted file mode 100644 index e2063138fef1..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests - -import android.os.Parcel -import android.os.Parcelable - -data class HttpResponse( - val requestUrl: String, - val responseCode: Int, - val content: String = "", - val redirectUrl: String? = null -) : Parcelable { - constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString()) - constructor(requestUrl: String, contentBody: String): this( - requestUrl, - responseCode = 200, - content = contentBody, - redirectUrl = null) - - override fun writeToParcel(dest: Parcel, flags: Int) { - with(dest) { - writeString(requestUrl) - writeInt(responseCode) - writeString(content) - writeString(redirectUrl) - } - } - - override fun describeContents() = 0 - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(source: Parcel) = HttpResponse(source) - override fun newArray(size: Int) = arrayOfNulls(size) - } -} \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl b/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl deleted file mode 100644 index efc58add9cf5..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests; - -import com.android.server.net.integrationtests.HttpResponse; - -interface INetworkStackInstrumentation { - void clearAllState(); - void addHttpResponse(in HttpResponse response); - List getRequestUrls(); -} \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt deleted file mode 100644 index e807952cec11..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests - -import android.app.Service -import android.content.Intent -import java.net.URL -import java.util.Collections -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedQueue -import kotlin.collections.ArrayList -import kotlin.test.fail - -/** - * An instrumentation interface for the NetworkStack that allows controlling behavior to - * facilitate integration tests. - */ -class NetworkStackInstrumentationService : Service() { - override fun onBind(intent: Intent) = InstrumentationConnector.asBinder() - - object InstrumentationConnector : INetworkStackInstrumentation.Stub() { - private val httpResponses = ConcurrentHashMap>() - .run { - withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } } - } - private val httpRequestUrls = Collections.synchronizedList(ArrayList()) - - /** - * Called when an HTTP request is being processed by NetworkMonitor. Returns the response - * that should be simulated. - */ - fun processRequest(url: URL): HttpResponse { - val strUrl = url.toString() - httpRequestUrls.add(strUrl) - return httpResponses[strUrl]?.poll() - ?: fail("No mocked response for request: $strUrl. " + - "Mocked URL keys are: ${httpResponses.keys}") - } - - /** - * Clear all state of this connector. This is intended for use between two tests, so all - * state should be reset as if the connector was just created. - */ - override fun clearAllState() { - httpResponses.clear() - httpRequestUrls.clear() - } - - /** - * Add a response to a future HTTP request. - * - *

For any subsequent HTTP/HTTPS query, the first response with a matching URL will be - * used to mock the query response. - * - *

All requests that are expected to be sent must have a mock response: if an unexpected - * request is seen, the test will fail. - */ - override fun addHttpResponse(response: HttpResponse) { - httpResponses.getValue(response.requestUrl).add(response) - } - - /** - * Get the ordered list of request URLs that have been sent by NetworkMonitor, and were - * answered based on mock responses. - */ - override fun getRequestUrls(): List { - return ArrayList(httpRequestUrls) - } - } -} \ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt deleted file mode 100644 index eff66584d6c1..000000000000 --- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2019 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.net.integrationtests - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.net.INetworkMonitorCallbacks -import android.net.Network -import android.net.metrics.IpConnectivityLog -import android.net.util.SharedLog -import android.os.IBinder -import com.android.networkstack.netlink.TcpSocketTracker -import com.android.server.NetworkStackService -import com.android.server.NetworkStackService.NetworkMonitorConnector -import com.android.server.NetworkStackService.NetworkStackConnector -import com.android.server.connectivity.NetworkMonitor -import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.spy -import java.io.ByteArrayInputStream -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection -import java.nio.charset.StandardCharsets - -private const val TEST_NETID = 42 - -/** - * Android service that can return an [android.net.INetworkStackConnector] which can be instrumented - * through [NetworkStackInstrumentationService]. - * Useful in tests to create test instrumented NetworkStack components that can receive - * instrumentation commands through [NetworkStackInstrumentationService]. - */ -class TestNetworkStackService : Service() { - override fun onBind(intent: Intent): IBinder = TestNetworkStackConnector(makeTestContext()) - - private fun makeTestContext() = spy(applicationContext).also { - doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE) - } - - private class TestPermissionChecker : NetworkStackService.PermissionChecker() { - override fun enforceNetworkStackCallingPermission() = Unit - } - - private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) : - NetworkMonitor.Dependencies() { - override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork - } - - private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector( - context, TestPermissionChecker(), NetworkStackService.Dependencies()) { - - private val network = Network(TEST_NETID) - private val privateDnsBypassNetwork = TestNetwork(TEST_NETID) - - private inner class TestNetwork(netId: Int) : Network(netId) { - override fun openConnection(url: URL): URLConnection { - val response = InstrumentationConnector.processRequest(url) - val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8) - - val connection = mock(HttpURLConnection::class.java) - doReturn(response.responseCode).`when`(connection).responseCode - doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong - doReturn(response.redirectUrl).`when`(connection).getHeaderField("location") - doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream - return connection - } - } - - override fun makeNetworkMonitor( - network: Network, - name: String?, - cb: INetworkMonitorCallbacks - ) { - val nm = NetworkMonitor(this@TestNetworkStackService, cb, - this.network, - mock(IpConnectivityLog::class.java), mock(SharedLog::class.java), - mock(NetworkStackService.NetworkStackServiceManager::class.java), - NetworkMonitorDeps(privateDnsBypassNetwork), - mock(TcpSocketTracker::class.java)) - cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker())) - } - } -} diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt deleted file mode 100644 index 165fd3728281..000000000000 --- a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 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 - */ - -@file:JvmName("ConnectivityServiceTestUtils") - -package com.android.server - -import android.net.ConnectivityManager.TYPE_BLUETOOTH -import android.net.ConnectivityManager.TYPE_ETHERNET -import android.net.ConnectivityManager.TYPE_MOBILE -import android.net.ConnectivityManager.TYPE_NONE -import android.net.ConnectivityManager.TYPE_TEST -import android.net.ConnectivityManager.TYPE_VPN -import android.net.ConnectivityManager.TYPE_WIFI -import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH -import android.net.NetworkCapabilities.TRANSPORT_CELLULAR -import android.net.NetworkCapabilities.TRANSPORT_ETHERNET -import android.net.NetworkCapabilities.TRANSPORT_TEST -import android.net.NetworkCapabilities.TRANSPORT_VPN -import android.net.NetworkCapabilities.TRANSPORT_WIFI - -fun transportToLegacyType(transport: Int) = when (transport) { - TRANSPORT_BLUETOOTH -> TYPE_BLUETOOTH - TRANSPORT_CELLULAR -> TYPE_MOBILE - TRANSPORT_ETHERNET -> TYPE_ETHERNET - TRANSPORT_TEST -> TYPE_TEST - TRANSPORT_VPN -> TYPE_VPN - TRANSPORT_WIFI -> TYPE_WIFI - else -> TYPE_NONE -} \ No newline at end of file diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java deleted file mode 100644 index 17db17923f4d..000000000000 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2019 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 static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; - -import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; - -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.net.NetworkSpecifier; -import android.net.QosFilter; -import android.net.SocketKeepalive; -import android.os.ConditionVariable; -import android.os.HandlerThread; -import android.os.Message; -import android.util.Log; -import android.util.Range; - -import com.android.net.module.util.ArrayTrackRecord; -import com.android.testutils.HandlerUtils; -import com.android.testutils.TestableNetworkCallback; - -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { - private final NetworkCapabilities mNetworkCapabilities; - private final HandlerThread mHandlerThread; - private final Context mContext; - private final String mLogTag; - private final NetworkAgentConfig mNetworkAgentConfig; - - private final ConditionVariable mDisconnected = new ConditionVariable(); - private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); - private final AtomicBoolean mConnected = new AtomicBoolean(false); - private NetworkScore mScore; - private NetworkAgent mNetworkAgent; - private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED; - private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE; - // Controls how test network agent is going to wait before responding to keepalive - // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem. - private long mKeepaliveResponseDelay = 0L; - private Integer mExpectedKeepaliveSlot = null; - private final ArrayTrackRecord.ReadHead mCallbackHistory = - new ArrayTrackRecord().newReadHead(); - - public NetworkAgentWrapper(int transport, LinkProperties linkProperties, - NetworkCapabilities ncTemplate, Context context) throws Exception { - final int type = transportToLegacyType(transport); - final String typeName = ConnectivityManager.getNetworkTypeName(type); - mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); - mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - mNetworkCapabilities.addTransportType(transport); - switch (transport) { - case TRANSPORT_ETHERNET: - mScore = new NetworkScore.Builder().setLegacyInt(70).build(); - break; - case TRANSPORT_WIFI: - mScore = new NetworkScore.Builder().setLegacyInt(60).build(); - break; - case TRANSPORT_CELLULAR: - mScore = new NetworkScore.Builder().setLegacyInt(50).build(); - break; - case TRANSPORT_WIFI_AWARE: - mScore = new NetworkScore.Builder().setLegacyInt(20).build(); - break; - case TRANSPORT_VPN: - mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN); - // VPNs deduce the SUSPENDED capability from their underlying networks and there - // is no public API to let VPN services set it. - mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - mScore = new NetworkScore.Builder().setLegacyInt(101).build(); - break; - default: - throw new UnsupportedOperationException("unimplemented network type"); - } - mContext = context; - mLogTag = "Mock-" + typeName; - mHandlerThread = new HandlerThread(mLogTag); - mHandlerThread.start(); - - // extraInfo is set to "" by default in NetworkAgentConfig. - final String extraInfo = (transport == TRANSPORT_CELLULAR) ? "internet.apn" : ""; - mNetworkAgentConfig = new NetworkAgentConfig.Builder() - .setLegacyType(type) - .setLegacyTypeName(typeName) - .setLegacyExtraInfo(extraInfo) - .build(); - mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig); - } - - protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties, - final NetworkAgentConfig nac) throws Exception { - return new InstrumentedNetworkAgent(this, linkProperties, nac); - } - - public static class InstrumentedNetworkAgent extends NetworkAgent { - private final NetworkAgentWrapper mWrapper; - private static final String PROVIDER_NAME = "InstrumentedNetworkAgentProvider"; - - public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp, - NetworkAgentConfig nac) { - super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag, - wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac, - new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(), - PROVIDER_NAME)); - mWrapper = wrapper; - register(); - } - - @Override - public void unwanted() { - mWrapper.mDisconnected.open(); - } - - @Override - public void startSocketKeepalive(Message msg) { - int slot = msg.arg1; - if (mWrapper.mExpectedKeepaliveSlot != null) { - assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot); - } - mWrapper.mHandlerThread.getThreadHandler().postDelayed( - () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError), - mWrapper.mKeepaliveResponseDelay); - } - - @Override - public void stopSocketKeepalive(Message msg) { - final int slot = msg.arg1; - mWrapper.mHandlerThread.getThreadHandler().postDelayed( - () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError), - mWrapper.mKeepaliveResponseDelay); - } - - @Override - public void onQosCallbackRegistered(final int qosCallbackId, - final @NonNull QosFilter filter) { - Log.i(mWrapper.mLogTag, "onQosCallbackRegistered"); - mWrapper.mCallbackHistory.add( - new CallbackType.OnQosCallbackRegister(qosCallbackId, filter)); - } - - @Override - public void onQosCallbackUnregistered(final int qosCallbackId) { - Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered"); - mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId)); - } - - @Override - protected void preventAutomaticReconnect() { - mWrapper.mPreventReconnectReceived.open(); - } - - @Override - protected void addKeepalivePacketFilter(Message msg) { - Log.i(mWrapper.mLogTag, "Add keepalive packet filter."); - } - - @Override - protected void removeKeepalivePacketFilter(Message msg) { - Log.i(mWrapper.mLogTag, "Remove keepalive packet filter."); - } - } - - public void setScore(@NonNull final NetworkScore score) { - mScore = score; - mNetworkAgent.sendNetworkScore(score); - } - - public void adjustScore(int change) { - final int newLegacyScore = mScore.getLegacyInt() + change; - final NetworkScore.Builder builder = new NetworkScore.Builder() - .setLegacyInt(newLegacyScore); - if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI) && newLegacyScore < 50) { - builder.setExiting(true); - } - mScore = builder.build(); - mNetworkAgent.sendNetworkScore(mScore); - } - - public NetworkScore getScore() { - return mScore; - } - - public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { - mNetworkAgent.explicitlySelected(explicitlySelected, acceptUnvalidated); - } - - public void addCapability(int capability) { - mNetworkCapabilities.addCapability(capability); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void removeCapability(int capability) { - mNetworkCapabilities.removeCapability(capability); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setUids(Set> uids) { - mNetworkCapabilities.setUids(uids); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setSignalStrength(int signalStrength) { - mNetworkCapabilities.setSignalStrength(signalStrength); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) { - mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - - public void setNetworkCapabilities(NetworkCapabilities nc, boolean sendToConnectivityService) { - mNetworkCapabilities.set(nc); - if (sendToConnectivityService) { - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); - } - } - - public void connect() { - if (!mConnected.compareAndSet(false /* expect */, true /* update */)) { - // compareAndSet returns false when the value couldn't be updated because it did not - // match the expected value. - fail("Test NetworkAgents can only be connected once"); - } - mNetworkAgent.markConnected(); - } - - public void suspend() { - removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - } - - public void resume() { - addCapability(NET_CAPABILITY_NOT_SUSPENDED); - } - - public void disconnect() { - mNetworkAgent.unregister(); - } - - @Override - public Network getNetwork() { - return mNetworkAgent.getNetwork(); - } - - public void expectPreventReconnectReceived(long timeoutMs) { - assertTrue(mPreventReconnectReceived.block(timeoutMs)); - } - - public void expectDisconnected(long timeoutMs) { - assertTrue(mDisconnected.block(timeoutMs)); - } - - public void sendLinkProperties(LinkProperties lp) { - mNetworkAgent.sendLinkProperties(lp); - } - - public void setStartKeepaliveEvent(int reason) { - mStartKeepaliveError = reason; - } - - public void setStopKeepaliveEvent(int reason) { - mStopKeepaliveError = reason; - } - - public void setKeepaliveResponseDelay(long delay) { - mKeepaliveResponseDelay = delay; - } - - public void setExpectedKeepaliveSlot(Integer slot) { - mExpectedKeepaliveSlot = slot; - } - - public NetworkAgent getNetworkAgent() { - return mNetworkAgent; - } - - public NetworkCapabilities getNetworkCapabilities() { - return mNetworkCapabilities; - } - - public int getLegacyType() { - return mNetworkAgentConfig.getLegacyType(); - } - - public String getExtraInfo() { - return mNetworkAgentConfig.getLegacyExtraInfo(); - } - - public @NonNull ArrayTrackRecord.ReadHead getCallbackHistory() { - return mCallbackHistory; - } - - public void waitForIdle(long timeoutMs) { - HandlerUtils.waitForIdle(mHandlerThread, timeoutMs); - } - - abstract static class CallbackType { - final int mQosCallbackId; - - protected CallbackType(final int qosCallbackId) { - mQosCallbackId = qosCallbackId; - } - - static class OnQosCallbackRegister extends CallbackType { - final QosFilter mFilter; - OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) { - super(qosCallbackId); - mFilter = filter; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final OnQosCallbackRegister that = (OnQosCallbackRegister) o; - return mQosCallbackId == that.mQosCallbackId - && Objects.equals(mFilter, that.mFilter); - } - - @Override - public int hashCode() { - return Objects.hash(mQosCallbackId, mFilter); - } - } - - static class OnQosCallbackUnregister extends CallbackType { - OnQosCallbackUnregister(final int qosCallbackId) { - super(qosCallbackId); - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o; - return mQosCallbackId == that.mQosCallbackId; - } - - @Override - public int hashCode() { - return Objects.hash(mQosCallbackId); - } - } - } - - public boolean isBypassableVpn() { - return mNetworkAgentConfig.isBypassableVpn(); - } -} diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt deleted file mode 100644 index 938a694e8ba9..000000000000 --- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 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 java.util.concurrent.atomic.AtomicInteger - -/** - * A [NetIdManager] that generates ID starting from [NetIdManager.MAX_NET_ID] and decreasing, rather - * than starting from [NetIdManager.MIN_NET_ID] and increasing. - * - * Useful for testing ConnectivityService, to minimize the risk of test ConnectivityService netIDs - * overlapping with netIDs used by the real ConnectivityService on the device. - * - * IDs may still overlap if many networks have been used on the device (so the "real" netIDs - * are close to MAX_NET_ID), but this is typically not the case when running unit tests. Also, there - * is no way to fully solve the overlap issue without altering ID allocation in non-test code, as - * the real ConnectivityService could start using netIds that have been used by the test in the - * past. - */ -class TestNetIdManager : NetIdManager() { - private val nextId = AtomicInteger(MAX_NET_ID) - override fun reserveNetId() = nextId.decrementAndGet() - override fun releaseNetId(id: Int) = Unit - fun peekNextNetId() = nextId.get() - 1 -} diff --git a/tests/net/jarjar-rules.txt b/tests/net/jarjar-rules.txt deleted file mode 100644 index ca8867206dda..000000000000 --- a/tests/net/jarjar-rules.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Module library in frameworks/libs/net -rule com.android.net.module.util.** android.net.frameworktests.util.@1 diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java deleted file mode 100644 index 899295a019d2..000000000000 --- a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2018 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.app.usage; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.net.ConnectivityManager; -import android.net.INetworkStatsService; -import android.net.INetworkStatsSession; -import android.net.NetworkStats.Entry; -import android.net.NetworkStatsHistory; -import android.net.NetworkTemplate; -import android.os.RemoteException; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsManagerTest { - - private @Mock INetworkStatsService mService; - private @Mock INetworkStatsSession mStatsSession; - - private NetworkStatsManager mManager; - - // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp. - private static final int MATCH_MOBILE_ALL = 1; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService); - } - - @Test - public void testQueryDetails() throws RemoteException { - final String subscriberId = "subid"; - final long startTime = 1; - final long endTime = 100; - final int uid1 = 10001; - final int uid2 = 10002; - final int uid3 = 10003; - - Entry uid1Entry1 = new Entry("if1", uid1, - android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, - 100, 10, 200, 20, 0); - - Entry uid1Entry2 = new Entry( - "if2", uid1, - android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, - 100, 10, 200, 20, 0); - - Entry uid2Entry1 = new Entry("if1", uid2, - android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, - 150, 10, 250, 20, 0); - - Entry uid2Entry2 = new Entry( - "if2", uid2, - android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, - 150, 10, 250, 20, 0); - - NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2); - history1.recordData(10, 20, uid1Entry1); - history1.recordData(20, 30, uid1Entry2); - - NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2); - history1.recordData(30, 40, uid2Entry1); - history1.recordData(35, 45, uid2Entry2); - - - when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); - when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 }); - - when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), - eq(uid1), eq(android.net.NetworkStats.SET_ALL), - eq(android.net.NetworkStats.TAG_NONE), - eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) - .then((InvocationOnMock inv) -> { - NetworkTemplate template = inv.getArgument(0); - assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); - assertEquals(subscriberId, template.getSubscriberId()); - return history1; - }); - - when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), - eq(uid2), eq(android.net.NetworkStats.SET_ALL), - eq(android.net.NetworkStats.TAG_NONE), - eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) - .then((InvocationOnMock inv) -> { - NetworkTemplate template = inv.getArgument(0); - assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); - assertEquals(subscriberId, template.getSubscriberId()); - return history2; - }); - - - NetworkStats stats = mManager.queryDetails( - ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime); - - NetworkStats.Bucket bucket = new NetworkStats.Bucket(); - - // First 2 buckets exactly match entry timings - assertTrue(stats.getNextBucket(bucket)); - assertEquals(10, bucket.getStartTimeStamp()); - assertEquals(20, bucket.getEndTimeStamp()); - assertBucketMatches(uid1Entry1, bucket); - - assertTrue(stats.getNextBucket(bucket)); - assertEquals(20, bucket.getStartTimeStamp()); - assertEquals(30, bucket.getEndTimeStamp()); - assertBucketMatches(uid1Entry2, bucket); - - // 30 -> 40: contains uid2Entry1 and half of uid2Entry2 - assertTrue(stats.getNextBucket(bucket)); - assertEquals(30, bucket.getStartTimeStamp()); - assertEquals(40, bucket.getEndTimeStamp()); - assertEquals(225, bucket.getRxBytes()); - assertEquals(15, bucket.getRxPackets()); - assertEquals(375, bucket.getTxBytes()); - assertEquals(30, bucket.getTxPackets()); - - // 40 -> 50: contains half of uid2Entry2 - assertTrue(stats.getNextBucket(bucket)); - assertEquals(40, bucket.getStartTimeStamp()); - assertEquals(50, bucket.getEndTimeStamp()); - assertEquals(75, bucket.getRxBytes()); - assertEquals(5, bucket.getRxPackets()); - assertEquals(125, bucket.getTxBytes()); - assertEquals(10, bucket.getTxPackets()); - - assertFalse(stats.hasNextBucket()); - } - - @Test - public void testQueryDetails_NoSubscriberId() throws RemoteException { - final long startTime = 1; - final long endTime = 100; - final int uid1 = 10001; - final int uid2 = 10002; - - when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); - when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 }); - - NetworkStats stats = mManager.queryDetails( - ConnectivityManager.TYPE_MOBILE, null, startTime, endTime); - - when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), - anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong())) - .thenReturn(new NetworkStatsHistory(10, 0)); - - verify(mStatsSession, times(1)).getHistoryIntervalForUid( - argThat((NetworkTemplate t) -> - // No subscriberId: MATCH_MOBILE_WILDCARD template - t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), - eq(uid1), eq(android.net.NetworkStats.SET_ALL), - eq(android.net.NetworkStats.TAG_NONE), - eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); - - verify(mStatsSession, times(1)).getHistoryIntervalForUid( - argThat((NetworkTemplate t) -> - // No subscriberId: MATCH_MOBILE_WILDCARD template - t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), - eq(uid2), eq(android.net.NetworkStats.SET_ALL), - eq(android.net.NetworkStats.TAG_NONE), - eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); - - assertFalse(stats.hasNextBucket()); - } - - private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) { - assertEquals(expected.uid, actual.getUid()); - assertEquals(expected.rxBytes, actual.getRxBytes()); - assertEquals(expected.rxPackets, actual.getRxPackets()); - assertEquals(expected.txBytes, actual.getTxBytes()); - assertEquals(expected.txPackets, actual.getTxPackets()); - } -} diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java deleted file mode 100644 index 06e9405a6a79..000000000000 --- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * 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; - -import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsBinder; -import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; -import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport; -import static android.net.ConnectivityDiagnosticsManager.DataStallReport; - -import static com.android.testutils.ParcelUtils.assertParcelSane; - -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 androidx.test.InstrumentationRegistry; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; - -import java.util.concurrent.Executor; - -@RunWith(JUnit4.class) -public class ConnectivityDiagnosticsManagerTest { - private static final int NET_ID = 1; - private static final int DETECTION_METHOD = 2; - private static final long TIMESTAMP = 10L; - private static final String INTERFACE_NAME = "interface"; - private static final String BUNDLE_KEY = "key"; - private static final String BUNDLE_VALUE = "value"; - - private static final Executor INLINE_EXECUTOR = x -> x.run(); - - @Mock private IConnectivityManager mService; - @Mock private ConnectivityDiagnosticsCallback mCb; - - private Context mContext; - private ConnectivityDiagnosticsBinder mBinder; - private ConnectivityDiagnosticsManager mManager; - - private String mPackageName; - - @Before - public void setUp() { - mContext = InstrumentationRegistry.getContext(); - - mService = mock(IConnectivityManager.class); - mCb = mock(ConnectivityDiagnosticsCallback.class); - - mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR); - mManager = new ConnectivityDiagnosticsManager(mContext, mService); - - mPackageName = mContext.getOpPackageName(); - } - - @After - public void tearDown() { - // clear ConnectivityDiagnosticsManager callbacks map - ConnectivityDiagnosticsManager.sCallbacks.clear(); - } - - private ConnectivityReport createSampleConnectivityReport() { - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setInterfaceName(INTERFACE_NAME); - - final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - - final PersistableBundle bundle = new PersistableBundle(); - bundle.putString(BUNDLE_KEY, BUNDLE_VALUE); - - return new ConnectivityReport( - new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle); - } - - private ConnectivityReport createDefaultConnectivityReport() { - return new ConnectivityReport( - new Network(0), - 0L, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY); - } - - @Test - public void testPersistableBundleEquals() { - assertFalse( - ConnectivityDiagnosticsManager.persistableBundleEquals( - null, PersistableBundle.EMPTY)); - assertFalse( - ConnectivityDiagnosticsManager.persistableBundleEquals( - PersistableBundle.EMPTY, null)); - assertTrue( - ConnectivityDiagnosticsManager.persistableBundleEquals( - PersistableBundle.EMPTY, PersistableBundle.EMPTY)); - - final PersistableBundle a = new PersistableBundle(); - a.putString(BUNDLE_KEY, BUNDLE_VALUE); - - final PersistableBundle b = new PersistableBundle(); - b.putString(BUNDLE_KEY, BUNDLE_VALUE); - - final PersistableBundle c = new PersistableBundle(); - c.putString(BUNDLE_KEY, null); - - assertFalse( - ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a)); - assertFalse( - ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY)); - - assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b)); - assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a)); - - assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c)); - assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a)); - } - - @Test - public void testConnectivityReportEquals() { - final ConnectivityReport defaultReport = createDefaultConnectivityReport(); - final ConnectivityReport sampleReport = createSampleConnectivityReport(); - assertEquals(sampleReport, createSampleConnectivityReport()); - assertEquals(defaultReport, createDefaultConnectivityReport()); - - final LinkProperties linkProperties = sampleReport.getLinkProperties(); - final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities(); - final PersistableBundle bundle = sampleReport.getAdditionalInfo(); - - assertNotEquals( - createDefaultConnectivityReport(), - new ConnectivityReport( - new Network(NET_ID), - 0L, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - createDefaultConnectivityReport(), - new ConnectivityReport( - new Network(0), - TIMESTAMP, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - createDefaultConnectivityReport(), - new ConnectivityReport( - new Network(0), - 0L, - linkProperties, - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - createDefaultConnectivityReport(), - new ConnectivityReport( - new Network(0), - TIMESTAMP, - new LinkProperties(), - networkCapabilities, - PersistableBundle.EMPTY)); - assertNotEquals( - createDefaultConnectivityReport(), - new ConnectivityReport( - new Network(0), - TIMESTAMP, - new LinkProperties(), - new NetworkCapabilities(), - bundle)); - } - - @Test - public void testConnectivityReportParcelUnparcel() { - assertParcelSane(createSampleConnectivityReport(), 5); - } - - private DataStallReport createSampleDataStallReport() { - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setInterfaceName(INTERFACE_NAME); - - final PersistableBundle bundle = new PersistableBundle(); - bundle.putString(BUNDLE_KEY, BUNDLE_VALUE); - - final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - - return new DataStallReport( - new Network(NET_ID), - TIMESTAMP, - DETECTION_METHOD, - linkProperties, - networkCapabilities, - bundle); - } - - private DataStallReport createDefaultDataStallReport() { - return new DataStallReport( - new Network(0), - 0L, - 0, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY); - } - - @Test - public void testDataStallReportEquals() { - final DataStallReport defaultReport = createDefaultDataStallReport(); - final DataStallReport sampleReport = createSampleDataStallReport(); - assertEquals(sampleReport, createSampleDataStallReport()); - assertEquals(defaultReport, createDefaultDataStallReport()); - - final LinkProperties linkProperties = sampleReport.getLinkProperties(); - final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities(); - final PersistableBundle bundle = sampleReport.getStallDetails(); - - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(NET_ID), - 0L, - 0, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(0), - TIMESTAMP, - 0, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(0), - 0L, - DETECTION_METHOD, - new LinkProperties(), - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(0), - 0L, - 0, - linkProperties, - new NetworkCapabilities(), - PersistableBundle.EMPTY)); - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(0), - 0L, - 0, - new LinkProperties(), - networkCapabilities, - PersistableBundle.EMPTY)); - assertNotEquals( - defaultReport, - new DataStallReport( - new Network(0), - 0L, - 0, - new LinkProperties(), - new NetworkCapabilities(), - bundle)); - } - - @Test - public void testDataStallReportParcelUnparcel() { - assertParcelSane(createSampleDataStallReport(), 6); - } - - @Test - public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() { - mBinder.onConnectivityReportAvailable(createSampleConnectivityReport()); - - // The callback will be invoked synchronously by inline executor. Immediately check the - // latch without waiting. - verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport())); - } - - @Test - public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() { - mBinder.onDataStallSuspected(createSampleDataStallReport()); - - // The callback will be invoked synchronously by inline executor. Immediately check the - // latch without waiting. - verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport())); - } - - @Test - public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() { - final Network n = new Network(NET_ID); - final boolean connectivity = true; - - mBinder.onNetworkConnectivityReported(n, connectivity); - - // The callback will be invoked synchronously by inline executor. Immediately check the - // 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), eq(mPackageName)); - 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), eq(mPackageName)); - assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb)); - } - - @Test - public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception { - mManager.unregisterConnectivityDiagnosticsCallback(mCb); - - verifyNoMoreInteractions(mService); - } -} diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java deleted file mode 100644 index 591e0cc3504e..000000000000 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static android.net.ConnectivityManager.TYPE_NONE; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; -import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; -import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; -import static android.net.NetworkRequest.Type.REQUEST; -import static android.net.NetworkRequest.Type.TRACK_DEFAULT; -import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.net.ConnectivityManager.NetworkCallback; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.os.Process; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ConnectivityManagerTest { - - @Mock Context mCtx; - @Mock IConnectivityManager mService; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - static NetworkCapabilities verifyNetworkCapabilities( - int legacyType, int transportType, int... capabilities) { - final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType); - assertNotNull(nc); - assertTrue(nc.hasTransport(transportType)); - for (int capability : capabilities) { - assertTrue(nc.hasCapability(capability)); - } - - return nc; - } - - static void verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType) { - verifyNetworkCapabilities( - legacyType, - transportType, - NET_CAPABILITY_INTERNET, - NET_CAPABILITY_NOT_RESTRICTED, - NET_CAPABILITY_NOT_VPN, - NET_CAPABILITY_TRUSTED); - } - - static void verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability) { - final NetworkCapabilities nc = verifyNetworkCapabilities( - legacyType, - TRANSPORT_CELLULAR, - capability, - NET_CAPABILITY_NOT_VPN, - NET_CAPABILITY_TRUSTED); - - assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - } - - @Test - public void testNetworkCapabilitiesForTypeMobile() { - verifyUnrestrictedNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE, TRANSPORT_CELLULAR); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileCbs() { - verifyRestrictedMobileNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_CBS, NET_CAPABILITY_CBS); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileDun() { - verifyRestrictedMobileNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_DUN, NET_CAPABILITY_DUN); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileFota() { - verifyRestrictedMobileNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_FOTA, NET_CAPABILITY_FOTA); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileHipri() { - verifyUnrestrictedNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_HIPRI, TRANSPORT_CELLULAR); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileIms() { - verifyRestrictedMobileNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_IMS, NET_CAPABILITY_IMS); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileMms() { - final NetworkCapabilities nc = verifyNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_MMS, - TRANSPORT_CELLULAR, - NET_CAPABILITY_MMS, - NET_CAPABILITY_NOT_VPN, - NET_CAPABILITY_TRUSTED); - - assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); - } - - @Test - public void testNetworkCapabilitiesForTypeMobileSupl() { - final NetworkCapabilities nc = verifyNetworkCapabilities( - ConnectivityManager.TYPE_MOBILE_SUPL, - TRANSPORT_CELLULAR, - NET_CAPABILITY_SUPL, - NET_CAPABILITY_NOT_VPN, - NET_CAPABILITY_TRUSTED); - - assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); - } - - @Test - public void testNetworkCapabilitiesForTypeWifi() { - verifyUnrestrictedNetworkCapabilities( - ConnectivityManager.TYPE_WIFI, TRANSPORT_WIFI); - } - - @Test - public void testNetworkCapabilitiesForTypeWifiP2p() { - final NetworkCapabilities nc = verifyNetworkCapabilities( - ConnectivityManager.TYPE_WIFI_P2P, - TRANSPORT_WIFI, - NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_VPN, - NET_CAPABILITY_TRUSTED, NET_CAPABILITY_WIFI_P2P); - - assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); - } - - @Test - public void testNetworkCapabilitiesForTypeBluetooth() { - verifyUnrestrictedNetworkCapabilities( - ConnectivityManager.TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH); - } - - @Test - public void testNetworkCapabilitiesForTypeEthernet() { - verifyUnrestrictedNetworkCapabilities( - ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET); - } - - @Test - public void testCallbackRelease() throws Exception { - ConnectivityManager manager = new ConnectivityManager(mCtx, mService); - NetworkRequest request = makeRequest(1); - NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class); - Handler handler = new Handler(Looper.getMainLooper()); - ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); - - // register callback - when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), - anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request); - manager.requestNetwork(request, callback, handler); - - // callback triggers - captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE)); - verify(callback, timeout(500).times(1)).onAvailable(any(Network.class), - any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean()); - - // unregister callback - manager.unregisterNetworkCallback(callback); - verify(mService, times(1)).releaseNetworkRequest(request); - - // callback does not trigger anymore. - captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING)); - verify(callback, timeout(500).times(0)).onLosing(any(), anyInt()); - } - - @Test - public void testCallbackRecycling() throws Exception { - ConnectivityManager manager = new ConnectivityManager(mCtx, mService); - NetworkRequest req1 = makeRequest(1); - NetworkRequest req2 = makeRequest(2); - NetworkCallback callback = mock(ConnectivityManager.NetworkCallback.class); - Handler handler = new Handler(Looper.getMainLooper()); - ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); - - // register callback - when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), - anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1); - manager.requestNetwork(req1, callback, handler); - - // callback triggers - captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE)); - verify(callback, timeout(100).times(1)).onAvailable(any(Network.class), - any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean()); - - // unregister callback - manager.unregisterNetworkCallback(callback); - verify(mService, times(1)).releaseNetworkRequest(req1); - - // callback does not trigger anymore. - captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING)); - verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); - - // callback can be registered again - when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), - anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2); - manager.requestNetwork(req2, callback, handler); - - // callback triggers - captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST)); - verify(callback, timeout(100).times(1)).onLost(any()); - - // unregister callback - manager.unregisterNetworkCallback(callback); - verify(mService, times(1)).releaseNetworkRequest(req2); - } - - // TODO: turn on this test when request callback 1:1 mapping is enforced - //@Test - private void noDoubleCallbackRegistration() throws Exception { - ConnectivityManager manager = new ConnectivityManager(mCtx, mService); - NetworkRequest request = makeRequest(1); - NetworkCallback callback = new ConnectivityManager.NetworkCallback(); - ApplicationInfo info = new ApplicationInfo(); - // TODO: update version when starting to enforce 1:1 mapping - info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; - - when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(request); - - Handler handler = new Handler(Looper.getMainLooper()); - manager.requestNetwork(request, callback, handler); - - // callback is already registered, reregistration should fail. - Class wantException = IllegalArgumentException.class; - expectThrowable(() -> manager.requestNetwork(request, callback), wantException); - - manager.unregisterNetworkCallback(callback); - verify(mService, times(1)).releaseNetworkRequest(request); - - // unregistering the callback should make it registrable again. - manager.requestNetwork(request, callback); - } - - @Test - public void testArgumentValidation() throws Exception { - ConnectivityManager manager = new ConnectivityManager(mCtx, mService); - - NetworkRequest request = mock(NetworkRequest.class); - NetworkCallback callback = mock(NetworkCallback.class); - Handler handler = mock(Handler.class); - NetworkCallback nullCallback = null; - PendingIntent nullIntent = null; - - mustFail(() -> { manager.requestNetwork(null, callback); }); - mustFail(() -> { manager.requestNetwork(request, nullCallback); }); - mustFail(() -> { manager.requestNetwork(request, callback, null); }); - mustFail(() -> { manager.requestNetwork(request, callback, -1); }); - mustFail(() -> { manager.requestNetwork(request, nullIntent); }); - - mustFail(() -> { manager.registerNetworkCallback(null, callback, handler); }); - mustFail(() -> { manager.registerNetworkCallback(request, null, handler); }); - mustFail(() -> { manager.registerNetworkCallback(request, callback, null); }); - mustFail(() -> { manager.registerNetworkCallback(request, nullIntent); }); - - mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); }); - mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); }); - - mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); }); - mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); }); - - mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); }); - mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); }); - mustFail(() -> { manager.releaseNetworkRequest(nullIntent); }); - } - - static void mustFail(Runnable fn) { - try { - fn.run(); - fail(); - } catch (Exception expected) { - } - } - - @Test - public void testRequestType() throws Exception { - final String testPkgName = "MyPackage"; - final String testAttributionTag = "MyTag"; - final ConnectivityManager manager = new ConnectivityManager(mCtx, mService); - when(mCtx.getOpPackageName()).thenReturn(testPkgName); - when(mCtx.getAttributionTag()).thenReturn(testAttributionTag); - final NetworkRequest request = makeRequest(1); - final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); - - manager.requestNetwork(request, callback); - verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), - eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - reset(mService); - - // Verify that register network callback does not calls requestNetwork at all. - manager.registerNetworkCallback(request, callback); - verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), - anyInt(), anyInt(), any(), any()); - verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - reset(mService); - - Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); - - manager.registerDefaultNetworkCallback(callback); - verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), - eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - reset(mService); - - manager.registerDefaultNetworkCallbackForUid(42, callback, handler); - verify(mService).requestNetwork(eq(42), eq(null), - eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - - manager.requestBackgroundNetwork(request, callback, handler); - verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), - eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - reset(mService); - - manager.registerSystemDefaultNetworkCallback(callback, handler); - verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), - eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), - eq(testPkgName), eq(testAttributionTag)); - reset(mService); - } - - static Message makeMessage(NetworkRequest req, int messageType) { - Bundle bundle = new Bundle(); - bundle.putParcelable(NetworkRequest.class.getSimpleName(), req); - // Pass default objects as we don't care which get passed here - bundle.putParcelable(Network.class.getSimpleName(), new Network(1)); - bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities()); - bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties()); - Message msg = Message.obtain(); - msg.what = messageType; - msg.setData(bundle); - return msg; - } - - static NetworkRequest makeRequest(int requestId) { - NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); - return new NetworkRequest(request.networkCapabilities, ConnectivityManager.TYPE_NONE, - requestId, NetworkRequest.Type.NONE); - } - - static void expectThrowable(Runnable block, Class throwableType) { - try { - block.run(); - } catch (Throwable t) { - if (t.getClass().equals(throwableType)) { - return; - } - fail("expected exception of type " + throwableType + ", but was " + t.getClass()); - } - fail("expected exception of type " + throwableType); - } -} diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java deleted file mode 100644 index 1abd39a32bdf..000000000000 --- a/tests/net/java/android/net/Ikev2VpnProfileTest.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright (C) 2019 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; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.test.mock.MockContext; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.net.VpnProfile; -import com.android.net.module.util.ProxyUtils; -import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.security.auth.x500.X500Principal; - -/** Unit tests for {@link Ikev2VpnProfile.Builder}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class Ikev2VpnProfileTest { - private static final String SERVER_ADDR_STRING = "1.2.3.4"; - private static final String IDENTITY_STRING = "Identity"; - private static final String USERNAME_STRING = "username"; - private static final String PASSWORD_STRING = "pa55w0rd"; - private static final String EXCL_LIST = "exclList"; - private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); - private static final int TEST_MTU = 1300; - - private final MockContext mMockContext = - new MockContext() { - @Override - public String getOpPackageName() { - return "fooPackage"; - } - }; - private final ProxyInfo mProxy = ProxyInfo.buildDirectProxy( - SERVER_ADDR_STRING, -1, ProxyUtils.exclusionStringAsList(EXCL_LIST)); - - private X509Certificate mUserCert; - private X509Certificate mServerRootCa; - private PrivateKey mPrivateKey; - - @Before - public void setUp() throws Exception { - mServerRootCa = generateRandomCertAndKeyPair().cert; - - final CertificateAndKey userCertKey = generateRandomCertAndKeyPair(); - mUserCert = userCertKey.cert; - mPrivateKey = userCertKey.key; - } - - private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() { - final Ikev2VpnProfile.Builder builder = - new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING); - - builder.setBypassable(true); - builder.setProxy(mProxy); - builder.setMaxMtu(TEST_MTU); - builder.setMetered(true); - - return builder; - } - - @Test - public void testBuildValidProfileWithOptions() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); - final Ikev2VpnProfile profile = builder.build(); - assertNotNull(profile); - - // Check non-auth parameters correctly stored - assertEquals(SERVER_ADDR_STRING, profile.getServerAddr()); - assertEquals(IDENTITY_STRING, profile.getUserIdentity()); - assertEquals(mProxy, profile.getProxyInfo()); - assertTrue(profile.isBypassable()); - assertTrue(profile.isMetered()); - assertEquals(TEST_MTU, profile.getMaxMtu()); - assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms()); - } - - @Test - public void testBuildUsernamePasswordProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); - final Ikev2VpnProfile profile = builder.build(); - assertNotNull(profile); - - assertEquals(USERNAME_STRING, profile.getUsername()); - assertEquals(PASSWORD_STRING, profile.getPassword()); - assertEquals(mServerRootCa, profile.getServerRootCaCert()); - - assertNull(profile.getPresharedKey()); - assertNull(profile.getRsaPrivateKey()); - assertNull(profile.getUserCert()); - } - - @Test - public void testBuildDigitalSignatureProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); - final Ikev2VpnProfile profile = builder.build(); - assertNotNull(profile); - - assertEquals(profile.getUserCert(), mUserCert); - assertEquals(mPrivateKey, profile.getRsaPrivateKey()); - assertEquals(profile.getServerRootCaCert(), mServerRootCa); - - assertNull(profile.getPresharedKey()); - assertNull(profile.getUsername()); - assertNull(profile.getPassword()); - } - - @Test - public void testBuildPresharedKeyProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthPsk(PSK_BYTES); - final Ikev2VpnProfile profile = builder.build(); - assertNotNull(profile); - - assertArrayEquals(PSK_BYTES, profile.getPresharedKey()); - - assertNull(profile.getServerRootCaCert()); - assertNull(profile.getUsername()); - assertNull(profile.getPassword()); - assertNull(profile.getRsaPrivateKey()); - assertNull(profile.getUserCert()); - } - - @Test - public void testBuildWithAllowedAlgorithmsAead() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - builder.setAuthPsk(PSK_BYTES); - - List allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM); - builder.setAllowedAlgorithms(allowedAlgorithms); - - final Ikev2VpnProfile profile = builder.build(); - assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); - } - - @Test - public void testBuildWithAllowedAlgorithmsNormal() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - builder.setAuthPsk(PSK_BYTES); - - List allowedAlgorithms = - Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC); - builder.setAllowedAlgorithms(allowedAlgorithms); - - final Ikev2VpnProfile profile = builder.build(); - assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms()); - } - - @Test - public void testSetAllowedAlgorithmsEmptyList() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - try { - builder.setAllowedAlgorithms(new ArrayList<>()); - fail("Expected exception due to no valid algorithm set"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testSetAllowedAlgorithmsInvalidList() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - List allowedAlgorithms = new ArrayList<>(); - - try { - builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256)); - fail("Expected exception due to missing encryption"); - } catch (IllegalArgumentException expected) { - } - - try { - builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC)); - fail("Expected exception due to missing authentication"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - List allowedAlgorithms = new ArrayList<>(); - - try { - builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5)); - fail("Expected exception due to insecure algorithm"); - } catch (IllegalArgumentException expected) { - } - - try { - builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1)); - fail("Expected exception due to insecure algorithm"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testBuildNoAuthMethodSet() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - try { - builder.build(); - fail("Expected exception due to lack of auth method"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testBuildInvalidMtu() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - try { - builder.setMaxMtu(500); - fail("Expected exception due to too-small MTU"); - } catch (IllegalArgumentException expected) { - } - } - - private void verifyVpnProfileCommon(VpnProfile profile) { - assertEquals(SERVER_ADDR_STRING, profile.server); - assertEquals(IDENTITY_STRING, profile.ipsecIdentifier); - assertEquals(mProxy, profile.proxy); - assertTrue(profile.isBypassable); - assertTrue(profile.isMetered); - assertEquals(TEST_MTU, profile.maxMtu); - } - - @Test - public void testPskConvertToVpnProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthPsk(PSK_BYTES); - final VpnProfile profile = builder.build().toVpnProfile(); - - verifyVpnProfileCommon(profile); - assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret); - - // Check nothing else is set - assertEquals("", profile.username); - assertEquals("", profile.password); - assertEquals("", profile.ipsecUserCert); - assertEquals("", profile.ipsecCaCert); - } - - @Test - public void testUsernamePasswordConvertToVpnProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); - final VpnProfile profile = builder.build().toVpnProfile(); - - verifyVpnProfileCommon(profile); - assertEquals(USERNAME_STRING, profile.username); - assertEquals(PASSWORD_STRING, profile.password); - assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); - - // Check nothing else is set - assertEquals("", profile.ipsecUserCert); - assertEquals("", profile.ipsecSecret); - } - - @Test - public void testRsaConvertToVpnProfile() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); - final VpnProfile profile = builder.build().toVpnProfile(); - - final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE - + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()); - verifyVpnProfileCommon(profile); - assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert); - assertEquals( - expectedSecret, - profile.ipsecSecret); - assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); - - // Check nothing else is set - assertEquals("", profile.username); - assertEquals("", profile.password); - } - - @Test - public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthPsk(PSK_BYTES); - final VpnProfile profile = builder.build().toVpnProfile(); - profile.username = USERNAME_STRING; - profile.password = PASSWORD_STRING; - profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa); - profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); - - final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); - assertNull(result.getUsername()); - assertNull(result.getPassword()); - assertNull(result.getUserCert()); - assertNull(result.getRsaPrivateKey()); - assertNull(result.getServerRootCaCert()); - } - - @Test - public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); - final VpnProfile profile = builder.build().toVpnProfile(); - profile.ipsecSecret = new String(PSK_BYTES); - profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); - - final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); - assertNull(result.getPresharedKey()); - assertNull(result.getUserCert()); - assertNull(result.getRsaPrivateKey()); - } - - @Test - public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); - final VpnProfile profile = builder.build().toVpnProfile(); - profile.username = USERNAME_STRING; - profile.password = PASSWORD_STRING; - - final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); - assertNull(result.getUsername()); - assertNull(result.getPassword()); - assertNull(result.getPresharedKey()); - } - - @Test - public void testPskConversionIsLossless() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthPsk(PSK_BYTES); - final Ikev2VpnProfile ikeProfile = builder.build(); - - assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); - } - - @Test - public void testUsernamePasswordConversionIsLossless() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); - final Ikev2VpnProfile ikeProfile = builder.build(); - - assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); - } - - @Test - public void testRsaConversionIsLossless() throws Exception { - final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); - - builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); - final Ikev2VpnProfile ikeProfile = builder.build(); - - assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); - } - - private static class CertificateAndKey { - public final X509Certificate cert; - public final PrivateKey key; - - CertificateAndKey(X509Certificate cert, PrivateKey key) { - this.cert = cert; - this.key = key; - } - } - - private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception { - final Date validityBeginDate = - new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L)); - final Date validityEndDate = - new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L)); - - // Generate a keypair - final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(512); - final KeyPair keyPair = keyPairGenerator.generateKeyPair(); - - final X500Principal dnName = new X500Principal("CN=test.android.com"); - final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); - certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); - certGen.setSubjectDN(dnName); - certGen.setIssuerDN(dnName); - certGen.setNotBefore(validityBeginDate); - certGen.setNotAfter(validityEndDate); - certGen.setPublicKey(keyPair.getPublic()); - certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); - - final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL"); - return new CertificateAndKey(cert, keyPair.getPrivate()); - } -} diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java deleted file mode 100644 index 0b13800bc5c9..000000000000 --- a/tests/net/java/android/net/IpMemoryStoreTest.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2018 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.net.ipmemorystore.Blob; -import android.net.ipmemorystore.IOnStatusListener; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.ipmemorystore.NetworkAttributesParcelable; -import android.net.ipmemorystore.Status; -import android.net.networkstack.ModuleNetworkStackClient; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.UnknownHostException; -import java.util.Arrays; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpMemoryStoreTest { - private static final String TAG = IpMemoryStoreTest.class.getSimpleName(); - private static final String TEST_CLIENT_ID = "testClientId"; - private static final String TEST_DATA_NAME = "testData"; - private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other"; - private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12, - -128, 0, 89, 112, 91, -34 }; - private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes( - "hint", 219); - - @Mock - Context mMockContext; - @Mock - ModuleNetworkStackClient mModuleNetworkStackClient; - @Mock - IIpMemoryStore mMockService; - @Mock - IOnStatusListener mIOnStatusListener; - IpMemoryStore mStore; - - @Captor - ArgumentCaptor mCbCaptor; - @Captor - ArgumentCaptor mNapCaptor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - private void startIpMemoryStore(boolean supplyService) { - if (supplyService) { - doAnswer(invocation -> { - ((IIpMemoryStoreCallbacks) invocation.getArgument(0)) - .onIpMemoryStoreFetched(mMockService); - return null; - }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any()); - } else { - doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture()); - } - mStore = new IpMemoryStore(mMockContext) { - @Override - protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) { - return mModuleNetworkStackClient; - } - }; - } - - private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) { - return new NetworkAttributes.Builder() - .setCluster(hint) - .setMtu(mtu) - .build(); - } - - @Test - public void testNetworkAttributes() throws Exception { - startIpMemoryStore(true /* supplyService */); - final String l2Key = "fakeKey"; - - mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key), - mNapCaptor.capture(), any()); - assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); - - mStore.retrieveNetworkAttributes(l2Key, - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(TEST_NETWORK_ATTRIBUTES, attr); - }); - - verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any()); - } - - @Test - public void testPrivateData() throws RemoteException { - startIpMemoryStore(true /* supplyService */); - final Blob b = new Blob(); - b.data = TEST_BLOB_DATA; - final String l2Key = "fakeKey"; - - mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - }); - verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), - eq(b), any()); - - mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data.data)); - }); - verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), - eq(TEST_OTHER_DATA_NAME), any()); - } - - @Test - public void testFindL2Key() - throws UnknownHostException, RemoteException, Exception { - startIpMemoryStore(true /* supplyService */); - final String l2Key = "fakeKey"; - - mStore.findL2Key(TEST_NETWORK_ATTRIBUTES, - (status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - }); - verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any()); - assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); - } - - @Test - public void testIsSameNetwork() throws UnknownHostException, RemoteException { - startIpMemoryStore(true /* supplyService */); - final String l2Key1 = "fakeKey1"; - final String l2Key2 = "fakeKey2"; - - mStore.isSameNetwork(l2Key1, l2Key2, - (status, answer) -> { - assertFalse("Retrieve network sameness suspiciously successful : " - + status.resultCode, status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - assertNull(answer); - }); - verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any()); - } - - @Test - public void testEnqueuedIpMsRequests() throws Exception { - startIpMemoryStore(false /* supplyService */); - - final Blob b = new Blob(); - b.data = TEST_BLOB_DATA; - final String l2Key = "fakeKey"; - - // enqueue multiple ipms requests - mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - mStore.retrieveNetworkAttributes(l2Key, - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(TEST_NETWORK_ATTRIBUTES, attr); - }); - mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data.data)); - }); - - // get ipms service ready - mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService); - - InOrder inOrder = inOrder(mMockService); - - inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); - inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); - inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), - eq(b), any()); - inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), - eq(TEST_OTHER_DATA_NAME), any()); - assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); - } - - @Test - public void testEnqueuedIpMsRequestsWithException() throws Exception { - startIpMemoryStore(true /* supplyService */); - doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any()); - - final Blob b = new Blob(); - b.data = TEST_BLOB_DATA; - final String l2Key = "fakeKey"; - - // enqueue multiple ipms requests - mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - mStore.retrieveNetworkAttributes(l2Key, - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(TEST_NETWORK_ATTRIBUTES, attr); - }); - mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data.data)); - }); - - // verify the rest of the queue is still processed in order even if the remote exception - // occurs when calling one or more requests - InOrder inOrder = inOrder(mMockService); - - inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any()); - inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), - eq(b), any()); - inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), - eq(TEST_OTHER_DATA_NAME), any()); - assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); - } - - @Test - public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception { - startIpMemoryStore(true /* supplyService */); - - final Blob b = new Blob(); - b.data = TEST_BLOB_DATA; - final String l2Key = "fakeKey"; - - // enqueue multiple ipms requests - mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES, - status -> assertTrue("Store not successful : " + status.resultCode, - status.isSuccess())); - mStore.retrieveNetworkAttributes(l2Key, - (status, key, attr) -> { - throw new RuntimeException("retrieveNetworkAttributes test"); - }); - mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - status -> { - throw new RuntimeException("storeBlob test"); - }); - mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME, - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data.data)); - }); - - // verify the rest of the queue is still processed in order even if when one or more - // callback throw the remote exception - InOrder inOrder = inOrder(mMockService); - - inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), - any()); - inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any()); - inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME), - eq(b), any()); - inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID), - eq(TEST_OTHER_DATA_NAME), any()); - assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); - } - - @Test - public void testFactoryReset() throws RemoteException { - startIpMemoryStore(true /* supplyService */); - mStore.factoryReset(); - verify(mMockService, times(1)).factoryReset(); - } -} diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java deleted file mode 100644 index 5bd221477412..000000000000 --- a/tests/net/java/android/net/IpSecAlgorithmTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import android.content.res.Resources; -import android.os.Build; -import android.os.Parcel; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.CollectionUtils; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; - -/** Unit tests for {@link IpSecAlgorithm}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpSecAlgorithmTest { - private static final byte[] KEY_MATERIAL; - - private final Resources mMockResources = mock(Resources.class); - - static { - KEY_MATERIAL = new byte[128]; - new Random().nextBytes(KEY_MATERIAL); - }; - - private static byte[] generateKey(int keyLenInBits) { - return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8); - } - - @Test - public void testNoTruncLen() throws Exception { - Entry[] authAndAeadList = - new Entry[] { - new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128), - new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160), - new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256), - new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384), - new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512), - new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224), - }; - - // Expect auth and aead algorithms to throw errors if trunclen is omitted. - for (Entry algData : authAndAeadList) { - try { - new IpSecAlgorithm( - algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8)); - fail("Expected exception on unprovided auth trunclen"); - } catch (IllegalArgumentException expected) { - } - } - - // Ensure crypt works with no truncation length supplied. - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); - } - - private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen) - throws Exception { - new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen); - - try { - new IpSecAlgorithm(algoName, generateKey(keyLen)); - fail("Expected exception on unprovided auth trunclen"); - } catch (IllegalArgumentException pass) { - } - - try { - new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen); - fail("Invalid key length not validated"); - } catch (IllegalArgumentException pass) { - } - - try { - new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1); - fail("Invalid truncation length not validated"); - } catch (IllegalArgumentException pass) { - } - } - - private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception { - new IpSecAlgorithm(algoName, generateKey(keyLen)); - - try { - new IpSecAlgorithm(algoName, generateKey(keyLen + 8)); - fail("Invalid key length not validated"); - } catch (IllegalArgumentException pass) { - } - } - - @Test - public void testValidationForAlgosAddedInS() throws Exception { - if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) { - return; - } - - for (int len : new int[] {160, 224, 288}) { - checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len); - } - checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96); - checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_CMAC, 128, 96); - checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128); - } - - @Test - public void testTruncLenValidation() throws Exception { - for (int truncLen : new int[] {256, 512}) { - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA512, - Arrays.copyOf(KEY_MATERIAL, 512 / 8), - truncLen); - } - - for (int truncLen : new int[] {255, 513}) { - try { - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA512, - Arrays.copyOf(KEY_MATERIAL, 512 / 8), - truncLen); - fail("Invalid truncation length not validated"); - } catch (IllegalArgumentException pass) { - } - } - } - - @Test - public void testLenValidation() throws Exception { - for (int len : new int[] {128, 192, 256}) { - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, len / 8)); - } - try { - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 384 / 8)); - fail("Invalid key length not validated"); - } catch (IllegalArgumentException pass) { - } - } - - @Test - public void testAlgoNameValidation() throws Exception { - try { - new IpSecAlgorithm("rot13", Arrays.copyOf(KEY_MATERIAL, 128 / 8)); - fail("Invalid algorithm name not validated"); - } catch (IllegalArgumentException pass) { - } - } - - @Test - public void testParcelUnparcel() throws Exception { - IpSecAlgorithm init = - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA512, Arrays.copyOf(KEY_MATERIAL, 512 / 8), 256); - - Parcel p = Parcel.obtain(); - p.setDataPosition(0); - init.writeToParcel(p, 0); - - p.setDataPosition(0); - IpSecAlgorithm fin = IpSecAlgorithm.CREATOR.createFromParcel(p); - assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin)); - p.recycle(); - } - - private static Set getMandatoryAlgos() { - return CollectionUtils.filter( - ALGO_TO_REQUIRED_FIRST_SDK.keySet(), - i -> Build.VERSION.DEVICE_INITIAL_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i)); - } - - private static Set getOptionalAlgos() { - return CollectionUtils.filter( - ALGO_TO_REQUIRED_FIRST_SDK.keySet(), - i -> Build.VERSION.DEVICE_INITIAL_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i)); - } - - @Test - public void testGetSupportedAlgorithms() throws Exception { - assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos())); - assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll( - IpSecAlgorithm.getSupportedAlgorithms())); - } - - @Test - public void testLoadAlgos() throws Exception { - final Set optionalAlgoSet = getOptionalAlgos(); - final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]); - - doReturn(optionalAlgos).when(mMockResources) - .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms); - - final Set enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources)); - final Set expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet(); - - assertEquals(expectedAlgos, enabledAlgos); - } -} diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java deleted file mode 100644 index 25e225ef303a..000000000000 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static com.android.testutils.ParcelUtils.assertParcelSane; -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link IpSecConfig}. */ -@SmallTest -@RunWith(JUnit4.class) -public class IpSecConfigTest { - - @Test - public void testDefaults() throws Exception { - IpSecConfig c = new IpSecConfig(); - assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode()); - assertEquals("", c.getSourceAddress()); - assertEquals("", c.getDestinationAddress()); - assertNull(c.getNetwork()); - assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType()); - assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId()); - assertEquals(0, c.getEncapRemotePort()); - assertEquals(0, c.getNattKeepaliveInterval()); - assertNull(c.getEncryption()); - assertNull(c.getAuthentication()); - assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId()); - assertEquals(0, c.getXfrmInterfaceId()); - } - - private IpSecConfig getSampleConfig() { - IpSecConfig c = new IpSecConfig(); - c.setMode(IpSecTransform.MODE_TUNNEL); - c.setSourceAddress("0.0.0.0"); - c.setDestinationAddress("1.2.3.4"); - c.setSpiResourceId(1984); - c.setEncryption( - new IpSecAlgorithm( - IpSecAlgorithm.CRYPT_AES_CBC, - new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF})); - c.setAuthentication( - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_MD5, - new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}, - 128)); - c.setAuthenticatedEncryption( - new IpSecAlgorithm( - IpSecAlgorithm.AUTH_CRYPT_AES_GCM, - new byte[] { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4 - }, - 128)); - c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP); - c.setEncapSocketResourceId(7); - c.setEncapRemotePort(22); - c.setNattKeepaliveInterval(42); - c.setMarkValue(12); - c.setMarkMask(23); - c.setXfrmInterfaceId(34); - - return c; - } - - @Test - public void testCopyConstructor() { - IpSecConfig original = getSampleConfig(); - IpSecConfig copy = new IpSecConfig(original); - - assertEquals(original, copy); - assertNotSame(original, copy); - } - - @Test - public void testParcelUnparcel() { - assertParcelingIsLossless(new IpSecConfig()); - - IpSecConfig c = getSampleConfig(); - assertParcelSane(c, 15); - } -} diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java deleted file mode 100644 index 730e2d56bd78..000000000000 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.system.Os; -import android.test.mock.MockContext; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.IpSecService; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -/** Unit tests for {@link IpSecManager}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpSecManagerTest { - - private static final int TEST_UDP_ENCAP_PORT = 34567; - private static final int DROID_SPI = 0xD1201D; - private static final int DUMMY_RESOURCE_ID = 0x1234; - - private static final InetAddress GOOGLE_DNS_4; - private static final String VTI_INTF_NAME = "ipsec_test"; - private static final InetAddress VTI_LOCAL_ADDRESS; - private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24"); - - static { - try { - // Google Public DNS Addresses; - GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8"); - VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4"); - } catch (UnknownHostException e) { - throw new RuntimeException("Could not resolve DNS Addresses", e); - } - } - - private IpSecService mMockIpSecService; - private IpSecManager mIpSecManager; - private MockContext mMockContext = new MockContext() { - @Override - public String getOpPackageName() { - return "fooPackage"; - } - }; - - @Before - public void setUp() throws Exception { - mMockIpSecService = mock(IpSecService.class); - mIpSecManager = new IpSecManager(mMockContext, mMockIpSecService); - } - - /* - * Allocate a specific SPI - * Close SPIs - */ - @Test - public void testAllocSpi() throws Exception { - IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); - when(mMockIpSecService.allocateSecurityParameterIndex( - eq(GOOGLE_DNS_4.getHostAddress()), - eq(DROID_SPI), - anyObject())) - .thenReturn(spiResp); - - IpSecManager.SecurityParameterIndex droidSpi = - mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, DROID_SPI); - assertEquals(DROID_SPI, droidSpi.getSpi()); - - droidSpi.close(); - - verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); - } - - @Test - public void testAllocRandomSpi() throws Exception { - IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI); - when(mMockIpSecService.allocateSecurityParameterIndex( - eq(GOOGLE_DNS_4.getHostAddress()), - eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX), - anyObject())) - .thenReturn(spiResp); - - IpSecManager.SecurityParameterIndex randomSpi = - mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); - - assertEquals(DROID_SPI, randomSpi.getSpi()); - - randomSpi.close(); - - verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID); - } - - /* - * Throws resource unavailable exception - */ - @Test - public void testAllocSpiResUnavailableException() throws Exception { - IpSecSpiResponse spiResp = - new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0); - when(mMockIpSecService.allocateSecurityParameterIndex( - anyString(), anyInt(), anyObject())) - .thenReturn(spiResp); - - try { - mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); - fail("ResourceUnavailableException was not thrown"); - } catch (IpSecManager.ResourceUnavailableException e) { - } - } - - /* - * Throws spi unavailable exception - */ - @Test - public void testAllocSpiSpiUnavailableException() throws Exception { - IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0); - when(mMockIpSecService.allocateSecurityParameterIndex( - anyString(), anyInt(), anyObject())) - .thenReturn(spiResp); - - try { - mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4); - fail("ResourceUnavailableException was not thrown"); - } catch (IpSecManager.ResourceUnavailableException e) { - } - } - - /* - * Should throw exception when request spi 0 in IpSecManager - */ - @Test - public void testRequestAllocInvalidSpi() throws Exception { - try { - mIpSecManager.allocateSecurityParameterIndex(GOOGLE_DNS_4, 0); - fail("Able to allocate invalid spi"); - } catch (IllegalArgumentException e) { - } - } - - @Test - public void testOpenEncapsulationSocket() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - new IpSecUdpEncapResponse( - IpSecManager.Status.OK, - DUMMY_RESOURCE_ID, - TEST_UDP_ENCAP_PORT, - Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); - when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject())) - .thenReturn(udpEncapResp); - - IpSecManager.UdpEncapsulationSocket encapSocket = - mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT); - assertNotNull(encapSocket.getFileDescriptor()); - assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); - - encapSocket.close(); - - verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); - } - - @Test - public void testApplyTransportModeTransformEnsuresSocketCreation() throws Exception { - Socket socket = new Socket(); - IpSecConfig dummyConfig = new IpSecConfig(); - IpSecTransform dummyTransform = new IpSecTransform(null, dummyConfig); - - // Even if underlying SocketImpl is not initalized, this should force the init, and - // thereby succeed. - mIpSecManager.applyTransportModeTransform( - socket, IpSecManager.DIRECTION_IN, dummyTransform); - - // Check to make sure the FileDescriptor is non-null - assertNotNull(socket.getFileDescriptor$()); - } - - @Test - public void testRemoveTransportModeTransformsForcesSocketCreation() throws Exception { - Socket socket = new Socket(); - - // Even if underlying SocketImpl is not initalized, this should force the init, and - // thereby succeed. - mIpSecManager.removeTransportModeTransforms(socket); - - // Check to make sure the FileDescriptor is non-null - assertNotNull(socket.getFileDescriptor$()); - } - - @Test - public void testOpenEncapsulationSocketOnRandomPort() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - new IpSecUdpEncapResponse( - IpSecManager.Status.OK, - DUMMY_RESOURCE_ID, - TEST_UDP_ENCAP_PORT, - Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); - - when(mMockIpSecService.openUdpEncapsulationSocket(eq(0), anyObject())) - .thenReturn(udpEncapResp); - - IpSecManager.UdpEncapsulationSocket encapSocket = - mIpSecManager.openUdpEncapsulationSocket(); - - assertNotNull(encapSocket.getFileDescriptor()); - assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort()); - - encapSocket.close(); - - verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID); - } - - @Test - public void testOpenEncapsulationSocketWithInvalidPort() throws Exception { - try { - mIpSecManager.openUdpEncapsulationSocket(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); - fail("IllegalArgumentException was not thrown"); - } catch (IllegalArgumentException e) { - } - } - - // TODO: add test when applicable transform builder interface is available - - private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName) - throws Exception { - IpSecTunnelInterfaceResponse dummyResponse = - new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); - when(mMockIpSecService.createTunnelInterface( - eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()), - anyObject(), anyObject(), anyString())) - .thenReturn(dummyResponse); - - IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface( - VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class)); - - assertNotNull(tunnelIntf); - return tunnelIntf; - } - - @Test - public void testCreateVti() throws Exception { - IpSecManager.IpSecTunnelInterface tunnelIntf = - createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); - - assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName()); - - tunnelIntf.close(); - verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID), anyString()); - } - - @Test - public void testAddRemoveAddressesFromVti() throws Exception { - IpSecManager.IpSecTunnelInterface tunnelIntf = - createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME); - - tunnelIntf.addAddress(VTI_INNER_ADDRESS.getAddress(), - VTI_INNER_ADDRESS.getPrefixLength()); - verify(mMockIpSecService) - .addAddressToTunnelInterface( - eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString()); - - tunnelIntf.removeAddress(VTI_INNER_ADDRESS.getAddress(), - VTI_INNER_ADDRESS.getPrefixLength()); - verify(mMockIpSecService) - .addAddressToTunnelInterface( - eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString()); - } -} diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java deleted file mode 100644 index 424f23dbbaf6..000000000000 --- a/tests/net/java/android/net/IpSecTransformTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Unit tests for {@link IpSecTransform}. */ -@SmallTest -@RunWith(JUnit4.class) -public class IpSecTransformTest { - - @Test - public void testCreateTransformCopiesConfig() { - // Create a config with a few parameters to make sure it's not empty - IpSecConfig config = new IpSecConfig(); - config.setSourceAddress("0.0.0.0"); - config.setDestinationAddress("1.2.3.4"); - config.setSpiResourceId(1984); - - IpSecTransform preModification = new IpSecTransform(null, config); - - config.setSpiResourceId(1985); - IpSecTransform postModification = new IpSecTransform(null, config); - - assertNotEquals(preModification, postModification); - } - - @Test - public void testCreateTransformsWithSameConfigEqual() { - // Create a config with a few parameters to make sure it's not empty - IpSecConfig config = new IpSecConfig(); - config.setSourceAddress("0.0.0.0"); - config.setDestinationAddress("1.2.3.4"); - config.setSpiResourceId(1984); - - IpSecTransform config1 = new IpSecTransform(null, config); - IpSecTransform config2 = new IpSecTransform(null, config); - - assertEquals(config1, config2); - } -} diff --git a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java b/tests/net/java/android/net/KeepalivePacketDataUtilTest.java deleted file mode 100644 index fc739fbfac61..000000000000 --- a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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; - -import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.util.KeepalivePacketDataUtil; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.net.InetAddress; -import java.nio.ByteBuffer; - -@RunWith(JUnit4.class) -public final class KeepalivePacketDataUtilTest { - private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1}; - private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5}; - - @Before - public void setUp() {} - - @Test - public void testFromTcpKeepaliveStableParcelable() throws Exception { - final int srcPort = 1234; - final int dstPort = 4321; - final int seq = 0x11111111; - final int ack = 0x22222222; - final int wnd = 8000; - final int wndScale = 2; - final int tos = 4; - final int ttl = 64; - TcpKeepalivePacketData resultData = null; - final TcpKeepalivePacketDataParcelable testInfo = new TcpKeepalivePacketDataParcelable(); - testInfo.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; - testInfo.srcPort = srcPort; - testInfo.dstAddress = IPV4_KEEPALIVE_DST_ADDR; - testInfo.dstPort = dstPort; - testInfo.seq = seq; - testInfo.ack = ack; - testInfo.rcvWnd = wnd; - testInfo.rcvWndScale = wndScale; - testInfo.tos = tos; - testInfo.ttl = ttl; - try { - resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); - } catch (InvalidPacketException e) { - fail("InvalidPacketException: " + e); - } - - assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.getSrcAddress()); - assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.getDstAddress()); - assertEquals(testInfo.srcPort, resultData.getSrcPort()); - assertEquals(testInfo.dstPort, resultData.getDstPort()); - assertEquals(testInfo.seq, resultData.tcpSeq); - assertEquals(testInfo.ack, resultData.tcpAck); - assertEquals(testInfo.rcvWnd, resultData.tcpWindow); - assertEquals(testInfo.rcvWndScale, resultData.tcpWindowScale); - assertEquals(testInfo.tos, resultData.ipTos); - assertEquals(testInfo.ttl, resultData.ipTtl); - - assertParcelingIsLossless(resultData); - - final byte[] packet = resultData.getPacket(); - // IP version and IHL - assertEquals(packet[0], 0x45); - // TOS - assertEquals(packet[1], tos); - // TTL - assertEquals(packet[8], ttl); - // Source IP address. - byte[] ip = new byte[4]; - ByteBuffer buf = ByteBuffer.wrap(packet, 12, 4); - buf.get(ip); - assertArrayEquals(ip, IPV4_KEEPALIVE_SRC_ADDR); - // Destination IP address. - buf = ByteBuffer.wrap(packet, 16, 4); - buf.get(ip); - assertArrayEquals(ip, IPV4_KEEPALIVE_DST_ADDR); - - buf = ByteBuffer.wrap(packet, 20, 12); - // Source port. - assertEquals(buf.getShort(), srcPort); - // Destination port. - assertEquals(buf.getShort(), dstPort); - // Sequence number. - assertEquals(buf.getInt(), seq); - // Ack. - assertEquals(buf.getInt(), ack); - // Window size. - buf = ByteBuffer.wrap(packet, 34, 2); - assertEquals(buf.getShort(), wnd >> wndScale); - } - - //TODO: add ipv6 test when ipv6 supported - - @Test - public void testToTcpKeepaliveStableParcelable() throws Exception { - final int srcPort = 1234; - final int dstPort = 4321; - final int sequence = 0x11111111; - final int ack = 0x22222222; - final int wnd = 48_000; - final int wndScale = 2; - final int tos = 4; - final int ttl = 64; - final TcpKeepalivePacketDataParcelable testInfo = new TcpKeepalivePacketDataParcelable(); - testInfo.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; - testInfo.srcPort = srcPort; - testInfo.dstAddress = IPV4_KEEPALIVE_DST_ADDR; - testInfo.dstPort = dstPort; - testInfo.seq = sequence; - testInfo.ack = ack; - testInfo.rcvWnd = wnd; - testInfo.rcvWndScale = wndScale; - testInfo.tos = tos; - testInfo.ttl = ttl; - TcpKeepalivePacketData testData = null; - TcpKeepalivePacketDataParcelable resultData = null; - testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo); - resultData = KeepalivePacketDataUtil.toStableParcelable(testData); - assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR); - assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR); - assertEquals(resultData.srcPort, srcPort); - assertEquals(resultData.dstPort, dstPort); - assertEquals(resultData.seq, sequence); - assertEquals(resultData.ack, ack); - assertEquals(resultData.rcvWnd, wnd); - assertEquals(resultData.rcvWndScale, wndScale); - assertEquals(resultData.tos, tos); - assertEquals(resultData.ttl, ttl); - - final String expected = "" - + "android.net.TcpKeepalivePacketDataParcelable{srcAddress: [10, 0, 0, 1]," - + " srcPort: 1234, dstAddress: [10, 0, 0, 5], dstPort: 4321, seq: 286331153," - + " ack: 572662306, rcvWnd: 48000, rcvWndScale: 2, tos: 4, ttl: 64}"; - assertEquals(expected, resultData.toString()); - } - - @Test - public void testParseTcpKeepalivePacketData() throws Exception { - final int srcPort = 1234; - final int dstPort = 4321; - final int sequence = 0x11111111; - final int ack = 0x22222222; - final int wnd = 4800; - final int wndScale = 2; - final int tos = 4; - final int ttl = 64; - final TcpKeepalivePacketDataParcelable testParcel = new TcpKeepalivePacketDataParcelable(); - testParcel.srcAddress = IPV4_KEEPALIVE_SRC_ADDR; - testParcel.srcPort = srcPort; - testParcel.dstAddress = IPV4_KEEPALIVE_DST_ADDR; - testParcel.dstPort = dstPort; - testParcel.seq = sequence; - testParcel.ack = ack; - testParcel.rcvWnd = wnd; - testParcel.rcvWndScale = wndScale; - testParcel.tos = tos; - testParcel.ttl = ttl; - - final KeepalivePacketData testData = - KeepalivePacketDataUtil.fromStableParcelable(testParcel); - final TcpKeepalivePacketDataParcelable parsedParcelable = - KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData); - final TcpKeepalivePacketData roundTripData = - KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable); - - // Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero - assertTrue(testData.getPacket().length > 0); - assertArrayEquals(testData.getPacket(), roundTripData.getPacket()); - - testParcel.rcvWndScale = 0; - final KeepalivePacketData noScaleTestData = - KeepalivePacketDataUtil.fromStableParcelable(testParcel); - final TcpKeepalivePacketDataParcelable noScaleParsedParcelable = - KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData); - final TcpKeepalivePacketData noScaleRoundTripData = - KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable); - assertEquals(noScaleTestData, noScaleRoundTripData); - assertTrue(noScaleTestData.getPacket().length > 0); - assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket()); - } -} diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java deleted file mode 100644 index 6de31f6b4be1..000000000000 --- a/tests/net/java/android/net/MacAddressTest.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2017 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; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.net.module.util.MacAddressUtils; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.Inet6Address; -import java.util.Arrays; -import java.util.Random; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class MacAddressTest { - - static class AddrTypeTestCase { - byte[] addr; - int expectedType; - - static AddrTypeTestCase of(int expectedType, int... addr) { - AddrTypeTestCase t = new AddrTypeTestCase(); - t.expectedType = expectedType; - t.addr = toByteArray(addr); - return t; - } - } - - @Test - public void testMacAddrTypes() { - AddrTypeTestCase[] testcases = { - AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN), - AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 0), - AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5), - AddrTypeTestCase.of(MacAddress.TYPE_UNKNOWN, 1, 2, 3, 4, 5, 6, 7), - AddrTypeTestCase.of(MacAddress.TYPE_UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0), - AddrTypeTestCase.of(MacAddress.TYPE_BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), - AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 1, 2, 3, 4, 5, 6), - AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 11, 22, 33, 44, 55, 66), - AddrTypeTestCase.of(MacAddress.TYPE_MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd) - }; - - for (AddrTypeTestCase t : testcases) { - int got = MacAddress.macAddressType(t.addr); - String msg = String.format("expected type of %s to be %s, but got %s", - Arrays.toString(t.addr), t.expectedType, got); - assertEquals(msg, t.expectedType, got); - - if (got != MacAddress.TYPE_UNKNOWN) { - assertEquals(got, MacAddress.fromBytes(t.addr).getAddressType()); - } - } - } - - @Test - public void testToOuiString() { - String[][] macs = { - {"07:00:d3:56:8a:c4", "07:00:d3"}, - {"33:33:aa:bb:cc:dd", "33:33:aa"}, - {"06:00:00:00:00:00", "06:00:00"}, - {"07:00:d3:56:8a:c4", "07:00:d3"} - }; - - for (String[] pair : macs) { - String mac = pair[0]; - String expected = pair[1]; - assertEquals(expected, MacAddress.fromString(mac).toOuiString()); - } - } - - @Test - public void testHexPaddingWhenPrinting() { - String[] macs = { - "07:00:d3:56:8a:c4", - "33:33:aa:bb:cc:dd", - "06:00:00:00:00:00", - "07:00:d3:56:8a:c4" - }; - - for (String mac : macs) { - assertEquals(mac, MacAddress.fromString(mac).toString()); - assertEquals(mac, - MacAddress.stringAddrFromByteAddr(MacAddress.byteAddrFromStringAddr(mac))); - } - } - - @Test - public void testIsMulticastAddress() { - MacAddress[] multicastAddresses = { - MacAddress.BROADCAST_ADDRESS, - MacAddress.fromString("07:00:d3:56:8a:c4"), - MacAddress.fromString("33:33:aa:bb:cc:dd"), - }; - MacAddress[] unicastAddresses = { - MacAddress.ALL_ZEROS_ADDRESS, - MacAddress.fromString("00:01:44:55:66:77"), - MacAddress.fromString("08:00:22:33:44:55"), - MacAddress.fromString("06:00:00:00:00:00"), - }; - - for (MacAddress mac : multicastAddresses) { - String msg = mac.toString() + " expected to be a multicast address"; - assertTrue(msg, MacAddressUtils.isMulticastAddress(mac)); - } - for (MacAddress mac : unicastAddresses) { - String msg = mac.toString() + " expected not to be a multicast address"; - assertFalse(msg, MacAddressUtils.isMulticastAddress(mac)); - } - } - - @Test - public void testIsLocallyAssignedAddress() { - MacAddress[] localAddresses = { - MacAddress.fromString("06:00:00:00:00:00"), - MacAddress.fromString("07:00:d3:56:8a:c4"), - MacAddress.fromString("33:33:aa:bb:cc:dd"), - }; - MacAddress[] universalAddresses = { - MacAddress.fromString("00:01:44:55:66:77"), - MacAddress.fromString("08:00:22:33:44:55"), - }; - - for (MacAddress mac : localAddresses) { - String msg = mac.toString() + " expected to be a locally assigned address"; - assertTrue(msg, mac.isLocallyAssigned()); - } - for (MacAddress mac : universalAddresses) { - String msg = mac.toString() + " expected not to be globally unique address"; - assertFalse(msg, mac.isLocallyAssigned()); - } - } - - @Test - public void testMacAddressConversions() { - final int iterations = 10000; - for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); - - String stringRepr = mac.toString(); - byte[] bytesRepr = mac.toByteArray(); - - assertEquals(mac, MacAddress.fromString(stringRepr)); - assertEquals(mac, MacAddress.fromBytes(bytesRepr)); - - assertEquals(mac, MacAddress.fromString(MacAddress.stringAddrFromByteAddr(bytesRepr))); - assertEquals(mac, MacAddress.fromBytes(MacAddress.byteAddrFromStringAddr(stringRepr))); - } - } - - @Test - public void testMacAddressRandomGeneration() { - final int iterations = 1000; - final String expectedAndroidOui = "da:a1:19"; - for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddress.createRandomUnicastAddressWithGoogleBase(); - String stringRepr = mac.toString(); - - assertTrue(stringRepr + " expected to be a locally assigned address", - mac.isLocallyAssigned()); - assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui, - stringRepr.startsWith(expectedAndroidOui)); - } - - final Random r = new Random(); - final String anotherOui = "24:5f:78"; - final String expectedLocalOui = "26:5f:78"; - final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0"); - for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r); - String stringRepr = mac.toString(); - - assertTrue(stringRepr + " expected to be a locally assigned address", - mac.isLocallyAssigned()); - assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); - assertTrue(stringRepr + " expected to begin with " + expectedLocalOui, - stringRepr.startsWith(expectedLocalOui)); - } - - for (int i = 0; i < iterations; i++) { - MacAddress mac = MacAddressUtils.createRandomUnicastAddress(); - String stringRepr = mac.toString(); - - assertTrue(stringRepr + " expected to be a locally assigned address", - mac.isLocallyAssigned()); - assertEquals(MacAddress.TYPE_UNICAST, mac.getAddressType()); - } - } - - @Test - public void testConstructorInputValidation() { - String[] invalidStringAddresses = { - "", - "abcd", - "1:2:3:4:5", - "1:2:3:4:5:6:7", - "10000:2:3:4:5:6", - }; - - for (String s : invalidStringAddresses) { - try { - MacAddress mac = MacAddress.fromString(s); - fail("MacAddress.fromString(" + s + ") should have failed, but returned " + mac); - } catch (IllegalArgumentException excepted) { - } - } - - try { - MacAddress mac = MacAddress.fromString(null); - fail("MacAddress.fromString(null) should have failed, but returned " + mac); - } catch (NullPointerException excepted) { - } - - byte[][] invalidBytesAddresses = { - {}, - {1,2,3,4,5}, - {1,2,3,4,5,6,7}, - }; - - for (byte[] b : invalidBytesAddresses) { - try { - MacAddress mac = MacAddress.fromBytes(b); - fail("MacAddress.fromBytes(" + Arrays.toString(b) - + ") should have failed, but returned " + mac); - } catch (IllegalArgumentException excepted) { - } - } - - try { - MacAddress mac = MacAddress.fromBytes(null); - fail("MacAddress.fromBytes(null) should have failed, but returned " + mac); - } catch (NullPointerException excepted) { - } - } - - @Test - public void testMatches() { - // match 4 bytes prefix - assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("aa:bb:cc:dd:00:00"), - MacAddress.fromString("ff:ff:ff:ff:00:00"))); - - // match bytes 0,1,2 and 5 - assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("aa:bb:cc:00:00:11"), - MacAddress.fromString("ff:ff:ff:00:00:ff"))); - - // match 34 bit prefix - assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("aa:bb:cc:dd:c0:00"), - MacAddress.fromString("ff:ff:ff:ff:c0:00"))); - - // fail to match 36 bit prefix - assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("aa:bb:cc:dd:40:00"), - MacAddress.fromString("ff:ff:ff:ff:f0:00"))); - - // match all 6 bytes - assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("aa:bb:cc:dd:ee:11"), - MacAddress.fromString("ff:ff:ff:ff:ff:ff"))); - - // match none of 6 bytes - assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches( - MacAddress.fromString("00:00:00:00:00:00"), - MacAddress.fromString("00:00:00:00:00:00"))); - } - - /** - * Tests that link-local address generation from MAC is valid. - */ - @Test - public void testLinkLocalFromMacGeneration() { - MacAddress mac = MacAddress.fromString("52:74:f2:b1:a8:7f"); - byte[] inet6ll = {(byte) 0xfe, (byte) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x74, - (byte) 0xf2, (byte) 0xff, (byte) 0xfe, (byte) 0xb1, (byte) 0xa8, 0x7f}; - Inet6Address llv6 = mac.getLinkLocalIpv6FromEui48Mac(); - assertTrue(llv6.isLinkLocalAddress()); - assertArrayEquals(inet6ll, llv6.getAddress()); - } - - static byte[] toByteArray(int... in) { - byte[] out = new byte[in.length]; - for (int i = 0; i < in.length; i++) { - out[i] = (byte) in[i]; - } - return out; - } -} diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/tests/net/java/android/net/NetworkIdentityTest.kt deleted file mode 100644 index eb2b85c14578..000000000000 --- a/tests/net/java/android/net/NetworkIdentityTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2021 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 - -import android.net.NetworkIdentity.OEM_NONE -import android.net.NetworkIdentity.OEM_PAID -import android.net.NetworkIdentity.OEM_PRIVATE -import android.net.NetworkIdentity.getOemBitfield -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import kotlin.test.assertEquals - -@RunWith(JUnit4::class) -class NetworkIdentityTest { - @Test - fun testGetOemBitfield() { - val oemNone = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) - } - val oemPaid = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) - } - val oemPrivate = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) - } - val oemAll = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) - } - - assertEquals(getOemBitfield(oemNone), OEM_NONE) - assertEquals(getOemBitfield(oemPaid), OEM_PAID) - assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE) - assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE) - } -} diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/tests/net/java/android/net/NetworkStatsHistoryTest.java deleted file mode 100644 index 13558cd51c28..000000000000 --- a/tests/net/java/android/net/NetworkStatsHistoryTest.java +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (C) 2011 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; - -import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong; -import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong; -import static android.net.NetworkStatsHistory.Entry.UNKNOWN; -import static android.net.NetworkStatsHistory.FIELD_ALL; -import static android.net.NetworkStatsHistory.FIELD_OPERATIONS; -import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; -import static android.net.NetworkStatsHistory.FIELD_RX_PACKETS; -import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; -import static android.net.TrafficStats.GB_IN_BYTES; -import static android.net.TrafficStats.MB_IN_BYTES; -import static android.text.format.DateUtils.DAY_IN_MILLIS; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; -import static android.text.format.DateUtils.SECOND_IN_MILLIS; -import static android.text.format.DateUtils.WEEK_IN_MILLIS; -import static android.text.format.DateUtils.YEAR_IN_MILLIS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.frameworks.tests.net.R; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.util.Random; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsHistoryTest { - private static final String TAG = "NetworkStatsHistoryTest"; - - private static final long TEST_START = 1194220800000L; - - private NetworkStatsHistory stats; - - @After - public void tearDown() throws Exception { - if (stats != null) { - assertConsistent(stats); - } - } - - @Test - public void testReadOriginalVersion() throws Exception { - final Context context = InstrumentationRegistry.getContext(); - final DataInputStream in = - new DataInputStream(context.getResources().openRawResource(R.raw.history_v1)); - - NetworkStatsHistory.Entry entry = null; - try { - final NetworkStatsHistory history = new NetworkStatsHistory(in); - assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration()); - - entry = history.getValues(0, entry); - assertEquals(29143L, entry.rxBytes); - assertEquals(6223L, entry.txBytes); - - entry = history.getValues(history.size() - 1, entry); - assertEquals(1476L, entry.rxBytes); - assertEquals(838L, entry.txBytes); - - entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry); - assertEquals(332401L, entry.rxBytes); - assertEquals(64314L, entry.txBytes); - - } finally { - in.close(); - } - } - - @Test - public void testRecordSingleBucket() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - // record data into narrow window to get single bucket - stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - - assertEquals(1, stats.size()); - assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L); - } - - @Test - public void testRecordEqualBuckets() throws Exception { - final long bucketDuration = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(bucketDuration); - - // split equally across two buckets - final long recordStart = TEST_START + (bucketDuration / 2); - stats.recordData(recordStart, recordStart + bucketDuration, - new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L)); - - assertEquals(2, stats.size()); - assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); - assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L); - } - - @Test - public void testRecordTouchingBuckets() throws Exception { - final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - // split almost completely into middle bucket, but with a few minutes - // overlap into neighboring buckets. total record is 20 minutes. - final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS; - final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4); - stats.recordData(recordStart, recordEnd, - new NetworkStats.Entry(1000L, 2000L, 5000L, 10000L, 100L)); - - assertEquals(3, stats.size()); - // first bucket should have (1/20 of value) - assertValues(stats, 0, MINUTE_IN_MILLIS, 50L, 100L, 250L, 500L, 5L); - // second bucket should have (15/20 of value) - assertValues(stats, 1, 15 * MINUTE_IN_MILLIS, 750L, 1500L, 3750L, 7500L, 75L); - // final bucket should have (4/20 of value) - assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L); - } - - @Test - public void testRecordGapBuckets() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - // record some data today and next week with large gap - final long firstStart = TEST_START; - final long lastStart = TEST_START + WEEK_IN_MILLIS; - stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, - new NetworkStats.Entry(128L, 2L, 256L, 4L, 1L)); - stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, - new NetworkStats.Entry(64L, 1L, 512L, 8L, 2L)); - - // we should have two buckets, far apart from each other - assertEquals(2, stats.size()); - assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); - assertValues(stats, 1, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); - - // now record something in middle, spread across two buckets - final long middleStart = TEST_START + DAY_IN_MILLIS; - final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2); - stats.recordData(middleStart, middleEnd, - new NetworkStats.Entry(2048L, 4L, 2048L, 4L, 2L)); - - // now should have four buckets, with new record in middle two buckets - assertEquals(4, stats.size()); - assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L); - assertValues(stats, 1, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); - assertValues(stats, 2, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L); - assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L); - } - - @Test - public void testRecordOverlapBuckets() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - // record some data in one bucket, and another overlapping buckets - stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, - new NetworkStats.Entry(256L, 2L, 256L, 2L, 1L)); - final long midStart = TEST_START + (HOUR_IN_MILLIS / 2); - stats.recordData(midStart, midStart + HOUR_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 1024L, 10L, 10L)); - - // should have two buckets, with some data mixed together - assertEquals(2, stats.size()); - assertValues(stats, 0, SECOND_IN_MILLIS + (HOUR_IN_MILLIS / 2), 768L, 7L, 768L, 7L, 6L); - assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L); - } - - @Test - public void testRecordEntireGapIdentical() throws Exception { - // first, create two separate histories far apart - final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); - stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L); - - final long TEST_START_2 = TEST_START + DAY_IN_MILLIS; - final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS); - stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L); - - // combine together with identical bucket size - stats = new NetworkStatsHistory(HOUR_IN_MILLIS); - stats.recordEntireHistory(stats1); - stats.recordEntireHistory(stats2); - - // first verify that totals match up - assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L); - - // now inspect internal buckets - assertValues(stats, 0, 1000L, 500L); - assertValues(stats, 1, 1000L, 500L); - assertValues(stats, 2, 500L, 250L); - assertValues(stats, 3, 500L, 250L); - } - - @Test - public void testRecordEntireOverlapVaryingBuckets() throws Exception { - // create history just over hour bucket boundary - final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); - stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L); - - final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS; - final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS); - stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L); - - // combine together with minute bucket size - stats = new NetworkStatsHistory(MINUTE_IN_MILLIS); - stats.recordEntireHistory(stats1); - stats.recordEntireHistory(stats2); - - // first verify that totals match up - assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); - - // now inspect internal buckets - assertValues(stats, 0, 10L, 10L); - assertValues(stats, 1, 20L, 20L); - assertValues(stats, 2, 20L, 20L); - assertValues(stats, 3, 20L, 20L); - assertValues(stats, 4, 20L, 20L); - assertValues(stats, 5, 20L, 20L); - assertValues(stats, 6, 10L, 10L); - - // now combine using 15min buckets - stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); - stats.recordEntireHistory(stats1); - stats.recordEntireHistory(stats2); - - // first verify that totals match up - assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L); - - // and inspect buckets - assertValues(stats, 0, 200L, 200L); - assertValues(stats, 1, 150L, 150L); - assertValues(stats, 2, 150L, 150L); - assertValues(stats, 3, 150L, 150L); - } - - @Test - public void testRemove() throws Exception { - stats = new NetworkStatsHistory(HOUR_IN_MILLIS); - - // record some data across 24 buckets - stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); - assertEquals(24, stats.size()); - - // try removing invalid data; should be no change - stats.removeBucketsBefore(0 - DAY_IN_MILLIS); - assertEquals(24, stats.size()); - - // try removing far before buckets; should be no change - stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS); - assertEquals(24, stats.size()); - - // try removing just moments into first bucket; should be no change - // since that bucket contains data beyond the cutoff - stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS); - assertEquals(24, stats.size()); - - // try removing single bucket - stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS); - assertEquals(23, stats.size()); - - // try removing multiple buckets - stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS)); - assertEquals(20, stats.size()); - - // try removing all buckets - stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS); - assertEquals(0, stats.size()); - } - - @Test - public void testTotalData() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - // record uniform data across day - stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L); - - // verify that total outside range is 0 - assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L); - - // verify total in first hour - assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L); - - // verify total across 1.5 hours - assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L); - - // verify total beyond end - assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L); - - // verify everything total - assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L); - - } - - @Test - public void testFuzzing() throws Exception { - try { - // fuzzing with random events, looking for crashes - final NetworkStats.Entry entry = new NetworkStats.Entry(); - final Random r = new Random(); - for (int i = 0; i < 500; i++) { - stats = new NetworkStatsHistory(r.nextLong()); - for (int j = 0; j < 10000; j++) { - if (r.nextBoolean()) { - // add range - final long start = r.nextLong(); - final long end = start + r.nextInt(); - entry.rxBytes = nextPositiveLong(r); - entry.rxPackets = nextPositiveLong(r); - entry.txBytes = nextPositiveLong(r); - entry.txPackets = nextPositiveLong(r); - entry.operations = nextPositiveLong(r); - stats.recordData(start, end, entry); - } else { - // trim something - stats.removeBucketsBefore(r.nextLong()); - } - } - assertConsistent(stats); - } - } catch (Throwable e) { - Log.e(TAG, String.valueOf(stats)); - throw new RuntimeException(e); - } - } - - private static long nextPositiveLong(Random r) { - final long value = r.nextLong(); - return value < 0 ? -value : value; - } - - @Test - public void testIgnoreFields() throws Exception { - final NetworkStatsHistory history = new NetworkStatsHistory( - MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES); - - history.recordData(0, MINUTE_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); - history.recordData(0, 2 * MINUTE_IN_MILLIS, - new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L)); - - assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN); - } - - @Test - public void testIgnoreFieldsRecordIn() throws Exception { - final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); - final NetworkStatsHistory partial = new NetworkStatsHistory( - MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); - - full.recordData(0, MINUTE_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); - partial.recordEntireHistory(full); - - assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L); - } - - @Test - public void testIgnoreFieldsRecordOut() throws Exception { - final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL); - final NetworkStatsHistory partial = new NetworkStatsHistory( - MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS); - - partial.recordData(0, MINUTE_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); - full.recordEntireHistory(partial); - - assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L); - } - - @Test - public void testSerialize() throws Exception { - final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL); - before.recordData(0, 4 * MINUTE_IN_MILLIS, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L)); - before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS, - new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L)); - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - before.writeToStream(new DataOutputStream(out)); - out.close(); - - final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in)); - - // must have identical totals before and after - assertFullValues(before, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); - assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L); - } - - @Test - public void testVarLong() throws Exception { - assertEquals(0L, performVarLong(0L)); - assertEquals(-1L, performVarLong(-1L)); - assertEquals(1024L, performVarLong(1024L)); - assertEquals(-1024L, performVarLong(-1024L)); - assertEquals(40 * MB_IN_BYTES, performVarLong(40 * MB_IN_BYTES)); - assertEquals(512 * GB_IN_BYTES, performVarLong(512 * GB_IN_BYTES)); - assertEquals(Long.MIN_VALUE, performVarLong(Long.MIN_VALUE)); - assertEquals(Long.MAX_VALUE, performVarLong(Long.MAX_VALUE)); - assertEquals(Long.MIN_VALUE + 40, performVarLong(Long.MIN_VALUE + 40)); - assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40)); - } - - @Test - public void testIndexBeforeAfter() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - final long FIRST_START = TEST_START; - final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS); - final long SECOND_START = TEST_START + WEEK_IN_MILLIS; - final long SECOND_END = SECOND_START + HOUR_IN_MILLIS; - final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS); - final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS); - - stats.recordData(FIRST_START, FIRST_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - stats.recordData(SECOND_START, SECOND_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - stats.recordData(THIRD_START, THIRD_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - - // should have buckets: 2+1+2 - assertEquals(5, stats.size()); - - assertIndexBeforeAfter(stats, 0, 0, Long.MIN_VALUE); - assertIndexBeforeAfter(stats, 0, 1, FIRST_START); - assertIndexBeforeAfter(stats, 0, 1, FIRST_START + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 0, 2, FIRST_START + HOUR_IN_MILLIS); - assertIndexBeforeAfter(stats, 1, 2, FIRST_START + HOUR_IN_MILLIS + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 1, 2, FIRST_END - MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 1, 2, FIRST_END); - assertIndexBeforeAfter(stats, 1, 2, FIRST_END + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 1, 2, SECOND_START - MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 1, 3, SECOND_START); - assertIndexBeforeAfter(stats, 2, 3, SECOND_END); - assertIndexBeforeAfter(stats, 2, 3, SECOND_END + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 2, 3, THIRD_START - MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 2, 4, THIRD_START); - assertIndexBeforeAfter(stats, 3, 4, THIRD_START + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 3, 4, THIRD_START + HOUR_IN_MILLIS); - assertIndexBeforeAfter(stats, 4, 4, THIRD_END); - assertIndexBeforeAfter(stats, 4, 4, THIRD_END + MINUTE_IN_MILLIS); - assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE); - } - - @Test - public void testIntersects() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = new NetworkStatsHistory(BUCKET_SIZE); - - final long FIRST_START = TEST_START; - final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS); - final long SECOND_START = TEST_START + WEEK_IN_MILLIS; - final long SECOND_END = SECOND_START + HOUR_IN_MILLIS; - final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS); - final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS); - - stats.recordData(FIRST_START, FIRST_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - stats.recordData(SECOND_START, SECOND_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - stats.recordData(THIRD_START, THIRD_END, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - - assertFalse(stats.intersects(10, 20)); - assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1)); - assertFalse(stats.intersects(Long.MAX_VALUE, Long.MIN_VALUE)); - - assertTrue(stats.intersects(Long.MIN_VALUE, Long.MAX_VALUE)); - assertTrue(stats.intersects(10, TEST_START + YEAR_IN_MILLIS)); - assertTrue(stats.intersects(TEST_START, TEST_START)); - assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, TEST_START + DAY_IN_MILLIS + 1)); - assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, Long.MAX_VALUE)); - assertTrue(stats.intersects(TEST_START + 1, Long.MAX_VALUE)); - - assertFalse(stats.intersects(Long.MIN_VALUE, TEST_START - 1)); - assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START)); - assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1)); - } - - @Test - public void testSetValues() throws Exception { - stats = new NetworkStatsHistory(HOUR_IN_MILLIS); - stats.recordData(TEST_START, TEST_START + 1, - new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); - - assertEquals(1024L + 2048L, stats.getTotalBytes()); - - final NetworkStatsHistory.Entry entry = stats.getValues(0, null); - entry.rxBytes /= 2; - entry.txBytes *= 2; - stats.setValues(0, entry); - - assertEquals(512L + 4096L, stats.getTotalBytes()); - } - - private static void assertIndexBeforeAfter( - NetworkStatsHistory stats, int before, int after, long time) { - assertEquals("unexpected before", before, stats.getIndexBefore(time)); - assertEquals("unexpected after", after, stats.getIndexAfter(time)); - } - - private static long performVarLong(long before) throws Exception { - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - writeVarLong(new DataOutputStream(out), before); - - final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - return readVarLong(new DataInputStream(in)); - } - - private static void assertConsistent(NetworkStatsHistory stats) { - // verify timestamps are monotonic - long lastStart = Long.MIN_VALUE; - NetworkStatsHistory.Entry entry = null; - for (int i = 0; i < stats.size(); i++) { - entry = stats.getValues(i, entry); - assertTrue(lastStart < entry.bucketStart); - lastStart = entry.bucketStart; - } - } - - private static void assertValues( - NetworkStatsHistory stats, int index, long rxBytes, long txBytes) { - final NetworkStatsHistory.Entry entry = stats.getValues(index, null); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - } - - private static void assertValues( - NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) { - final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - } - - private static void assertValues(NetworkStatsHistory stats, int index, long activeTime, - long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { - final NetworkStatsHistory.Entry entry = stats.getValues(index, null); - assertEquals("unexpected activeTime", activeTime, entry.activeTime); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - assertEquals("unexpected operations", operations, entry.operations); - } - - private static void assertFullValues(NetworkStatsHistory stats, long activeTime, long rxBytes, - long rxPackets, long txBytes, long txPackets, long operations) { - assertValues(stats, Long.MIN_VALUE, Long.MAX_VALUE, activeTime, rxBytes, rxPackets, txBytes, - txPackets, operations); - } - - private static void assertValues(NetworkStatsHistory stats, long start, long end, - long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets, - long operations) { - final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); - assertEquals("unexpected activeTime", activeTime, entry.activeTime); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - assertEquals("unexpected operations", operations, entry.operations); - } -} diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java deleted file mode 100644 index 23d5a7e5d5f8..000000000000 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Copyright (C) 2011 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; - -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.DEFAULT_NETWORK_YES; -import static android.net.NetworkStats.IFACE_ALL; -import static android.net.NetworkStats.INTERFACES_ALL; -import static android.net.NetworkStats.METERED_ALL; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.METERED_YES; -import static android.net.NetworkStats.ROAMING_ALL; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.ROAMING_YES; -import static android.net.NetworkStats.SET_ALL; -import static android.net.NetworkStats.SET_DBG_VPN_IN; -import static android.net.NetworkStats.SET_DBG_VPN_OUT; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.SET_FOREGROUND; -import static android.net.NetworkStats.TAG_ALL; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.os.Process; -import android.util.ArrayMap; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.google.android.collect.Sets; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Arrays; -import java.util.HashSet; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsTest { - - private static final String TEST_IFACE = "test0"; - private static final String TEST_IFACE2 = "test2"; - private static final int TEST_UID = 1001; - private static final long TEST_START = 1194220800000L; - - @Test - public void testFindIndex() throws Exception { - final NetworkStats stats = new NetworkStats(TEST_START, 5) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) - .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12) - .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12); - - assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES, DEFAULT_NETWORK_YES)); - assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO)); - assertEquals(2, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_NO, DEFAULT_NETWORK_YES)); - assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO)); - assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_YES)); - assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO)); - assertEquals(-1, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO)); - } - - @Test - public void testFindIndexHinted() { - final NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10) - .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11) - .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11) - .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12) - .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12); - - // verify that we correctly find across regardless of hinting - for (int hint = 0; hint < stats.size(); hint++) { - assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); - assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); - assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); - assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); - assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); - assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, - METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, hint)); - assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); - assertEquals(7, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, - METERED_YES, ROAMING_YES, DEFAULT_NETWORK_NO, hint)); - assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, - METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, hint)); - assertEquals(-1, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, - METERED_YES, ROAMING_YES, DEFAULT_NETWORK_YES, hint)); - } - } - - @Test - public void testAddEntryGrow() throws Exception { - final NetworkStats stats = new NetworkStats(TEST_START, 4); - - assertEquals(0, stats.size()); - assertEquals(4, stats.internalSize()); - - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); - - assertEquals(4, stats.size()); - assertEquals(4, stats.internalSize()); - - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); - stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); - - assertEquals(9, stats.size()); - assertTrue(stats.internalSize() >= 9); - - assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3); - assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4); - assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5); - assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES, DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5); - assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7); - assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8); - assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10); - assertValues(stats, 7, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11); - assertValues(stats, 8, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, - ROAMING_YES, DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11); - } - - @Test - public void testCombineExisting() throws Exception { - final NetworkStats stats = new NetworkStats(TEST_START, 10); - - stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10); - stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2); - stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L, - -128L, -1L, -1); - - assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 384L, 3L, 128L, 1L, 9); - assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 2); - - // now try combining that should create row - stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); - assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 128L, 1L, 128L, 1L, 3); - stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 3); - assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 256L, 2L, 256L, 2L, 6); - } - - @Test - public void testSubtractIdenticalData() throws Exception { - final NetworkStats before = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); - - final NetworkStats after = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); - - final NetworkStats result = after.subtract(before); - - // identical data should result in zero delta - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); - } - - @Test - public void testSubtractIdenticalRows() throws Exception { - final NetworkStats before = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); - - final NetworkStats after = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20); - - final NetworkStats result = after.subtract(before); - - // expect delta between measurements - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1L, 1L, 2L, 1L, 4); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 3L, 1L, 4L, 1L, 8); - } - - @Test - public void testSubtractNewRows() throws Exception { - final NetworkStats before = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12); - - final NetworkStats after = new NetworkStats(TEST_START, 3) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12) - .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20); - - final NetworkStats result = after.subtract(before); - - // its okay to have new rows - assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); - assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0); - assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 20); - } - - @Test - public void testSubtractMissingRows() throws Exception { - final NetworkStats before = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0) - .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0); - - final NetworkStats after = new NetworkStats(TEST_START, 1) - .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0); - - final NetworkStats result = after.subtract(before); - - // should silently drop omitted rows - assertEquals(1, result.size()); - assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 0); - assertEquals(4L, result.getTotalBytes()); - } - - @Test - public void testTotalBytes() throws Exception { - final NetworkStats iface = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L); - assertEquals(384L, iface.getTotalBytes()); - - final NetworkStats uidSet = new NetworkStats(TEST_START, 3) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L); - assertEquals(96L, uidSet.getTotalBytes()); - - final NetworkStats uidTag = new NetworkStats(TEST_START, 6) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L); - assertEquals(64L, uidTag.getTotalBytes()); - - final NetworkStats uidMetered = new NetworkStats(TEST_START, 3) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); - assertEquals(96L, uidMetered.getTotalBytes()); - - final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); - assertEquals(96L, uidRoaming.getTotalBytes()); - } - - @Test - public void testGroupedByIfaceEmpty() throws Exception { - final NetworkStats uidStats = new NetworkStats(TEST_START, 3); - final NetworkStats grouped = uidStats.groupedByIface(); - - assertEquals(0, uidStats.size()); - assertEquals(0, grouped.size()); - } - - @Test - public void testGroupedByIfaceAll() throws Exception { - final NetworkStats uidStats = new NetworkStats(TEST_START, 3) - .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L) - .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L); - final NetworkStats grouped = uidStats.groupedByIface(); - - assertEquals(3, uidStats.size()); - assertEquals(1, grouped.size()); - - assertValues(grouped, 0, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 6L, 0L); - } - - @Test - public void testGroupedByIface() throws Exception { - final NetworkStats uidStats = new NetworkStats(TEST_START, 7) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L); - - final NetworkStats grouped = uidStats.groupedByIface(); - - assertEquals(7, uidStats.size()); - - assertEquals(2, grouped.size()); - assertValues(grouped, 0, TEST_IFACE, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 384L, 24L, 0L, 2L, 0L); - assertValues(grouped, 1, TEST_IFACE2, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1024L, 64L, 0L, 0L, 0L); - } - - @Test - public void testAddAllValues() { - final NetworkStats first = new NetworkStats(TEST_START, 5) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); - - final NetworkStats second = new NetworkStats(TEST_START, 2) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L); - - first.combineAllValues(second); - - assertEquals(4, first.size()); - assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); - assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); - assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES, - DEFAULT_NETWORK_YES, 64L, 0L, 0L, 0L, 0L); - assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L); - } - - @Test - public void testGetTotal() { - final NetworkStats stats = new NetworkStats(TEST_START, 7) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES, - DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L); - - assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L); - assertValues(stats.getTotal(null, 100), 1280L, 80L, 0L, 2L, 20L); - assertValues(stats.getTotal(null, 101), 128L, 8L, 0L, 0L, 0L); - - final HashSet ifaces = Sets.newHashSet(); - assertValues(stats.getTotal(null, ifaces), 0L, 0L, 0L, 0L, 0L); - - ifaces.add(TEST_IFACE2); - assertValues(stats.getTotal(null, ifaces), 1024L, 64L, 0L, 0L, 0L); - } - - @Test - public void testRemoveUids() throws Exception { - final NetworkStats before = new NetworkStats(TEST_START, 3); - - // Test 0 item stats. - NetworkStats after = before.clone(); - after.removeUids(new int[0]); - assertEquals(0, after.size()); - after.removeUids(new int[] {100}); - assertEquals(0, after.size()); - - // Test 1 item stats. - before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L); - after = before.clone(); - after.removeUids(new int[0]); - assertEquals(1, after.size()); - assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); - after.removeUids(new int[] {99}); - assertEquals(0, after.size()); - - // Append remaining test items. - before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L); - assertEquals(7, before.size()); - - // Test remove with empty uid list. - after = before.clone(); - after.removeUids(new int[0]); - assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L); - - // Test remove uids don't exist in stats. - after.removeUids(new int[] {98, 0, Integer.MIN_VALUE, Integer.MAX_VALUE}); - assertValues(after.getTotalIncludingTags(null), 127L, 254L, 0L, 4L, 40L); - - // Test remove all uids. - after.removeUids(new int[] {99, 100, 100, 101}); - assertEquals(0, after.size()); - - // Test remove in the middle. - after = before.clone(); - after.removeUids(new int[] {100}); - assertEquals(3, after.size()); - assertValues(after, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); - assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 32L, 4L, 0L, 0L, 0L); - assertValues(after, 2, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 64L, 2L, 0L, 0L, 0L); - } - - @Test - public void testRemoveEmptyEntries() throws Exception { - // Test empty stats. - final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3); - assertEquals(0, statsEmpty.removeEmptyEntries().size()); - - // Test stats with non-zero entry. - final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1) - .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); - assertEquals(1, statsNonZero.size()); - final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries(); - assertEquals(1, expectedNonZero.size()); - assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L); - - // Test stats with empty entry. - final NetworkStats statsZero = new NetworkStats(TEST_START, 1) - .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - assertEquals(1, statsZero.size()); - final NetworkStats expectedZero = statsZero.removeEmptyEntries(); - assertEquals(1, statsZero.size()); // Assert immutable. - assertEquals(0, expectedZero.size()); - - // Test stats with multiple entries. - final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L) - .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L); - assertEquals(9, statsMultiple.size()); - final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries(); - assertEquals(9, statsMultiple.size()); // Assert immutable. - assertEquals(7, expectedMultiple.size()); - assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L); - - // Test stats with multiple empty entries. - assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size()); - assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size()); - } - - @Test - public void testClone() throws Exception { - final NetworkStats original = new NetworkStats(TEST_START, 5) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); - - // make clone and mutate original - final NetworkStats clone = original.clone(); - original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L); - - assertEquals(3, original.size()); - assertEquals(2, clone.size()); - - assertEquals(128L + 512L + 128L, original.getTotalBytes()); - assertEquals(128L + 512L, clone.getTotalBytes()); - } - - @Test - public void testAddWhenEmpty() throws Exception { - final NetworkStats red = new NetworkStats(TEST_START, -1); - final NetworkStats blue = new NetworkStats(TEST_START, 5) - .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L) - .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L); - - // We're mostly checking that we don't crash - red.combineAllValues(blue); - } - - @Test - public void testMigrateTun() throws Exception { - final int tunUid = 10030; - final String tunIface = "tun0"; - final String underlyingIface = "wlan0"; - final int testTag1 = 8888; - NetworkStats delta = new NetworkStats(TEST_START, 17) - .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L) - .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L) - .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L) - // VPN package also uses some traffic through unprotected network. - .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L) - .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - // Tag entries - .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L) - .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L) - // Irrelevant entries - .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L) - // Underlying Iface entries - .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L) - .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L) - .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */, - 299L /* smaller than sum(tun0) */, 0L) - .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - - delta.migrateTun(tunUid, tunIface, Arrays.asList(underlyingIface)); - assertEquals(20, delta.size()); - - // tunIface and TEST_IFACE entries are not changed. - assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L); - assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L); - assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L); - assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L); - assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L); - assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L); - assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L); - - // Existing underlying Iface entries are updated - assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 44783L, 54L, 14178L, 62L, 0L); - assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - - // VPN underlying Iface entries are updated - assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 28304L, 27L, 1L, 2L, 0L); - assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - - // New entries are added for new application's underlying Iface traffic - assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 72667L, 197L, 43123L, 227L, 0L); - assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 9297L, 17L, 4054, 19L, 0L); - assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 21691L, 41L, 13572L, 48L, 0L); - assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 1281L, 2L, 653L, 1L, 0L); - - // New entries are added for debug purpose - assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 39605L, 46L, 12039, 51, 0); - assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 81964, 214, 47177, 246, 0); - assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, 121569, 260, 59216, 297, 0); - - } - - // Tests a case where all of the data received by the tun0 interface is echo back into the tun0 - // interface by the vpn app before it's sent out of the underlying interface. The VPN app should - // not be charged for the echoed data but it should still be charged for any extra data it sends - // via the underlying interface. - @Test - public void testMigrateTun_VpnAsLoopback() { - final int tunUid = 10030; - final String tunIface = "tun0"; - final String underlyingIface = "wlan0"; - NetworkStats delta = new NetworkStats(TEST_START, 9) - // 2 different apps sent/receive data via tun0. - .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L) - .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L) - // VPN package resends data through the tunnel (with exaggerated overhead) - .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L) - // 1 app already has some traffic on the underlying interface, the other doesn't yet - .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L) - // Traffic through the underlying interface via the vpn app. - // This test should redistribute this data correctly. - .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); - - delta.migrateTun(tunUid, tunIface, Arrays.asList(underlyingIface)); - assertEquals(9, delta.size()); - - // tunIface entries should not be changed. - assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); - assertValues(delta, 2, tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 240000L, 100L, 120000L, 60L, 0L); - - // Existing underlying Iface entries are updated - assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L); - - // VPN underlying Iface entries are updated - assertValues(delta, 4, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 25000L, 10L, 29800L, 15L, 0L); - - // New entries are added for new application's underlying Iface traffic - assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L); - - // New entries are added for debug purpose - assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L); - assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0); - } - - @Test - public void testFilter_NoFilter() { - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3); - - stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL); - assertEquals(3, stats.size()); - assertEquals(entry1, stats.getValues(0, null)); - assertEquals(entry2, stats.getValues(1, null)); - assertEquals(entry3, stats.getValues(2, null)); - } - - @Test - public void testFilter_UidFilter() { - final int testUid = 10101; - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "test2", testUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - "test2", testUid, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3); - - stats.filter(testUid, INTERFACES_ALL, TAG_ALL); - assertEquals(2, stats.size()); - assertEquals(entry2, stats.getValues(0, null)); - assertEquals(entry3, stats.getValues(1, null)); - } - - @Test - public void testFilter_InterfaceFilter() { - final String testIf1 = "testif1"; - final String testIf2 = "testif2"; - NetworkStats.Entry entry1 = new NetworkStats.Entry( - testIf1, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "otherif", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - testIf1, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry4 = new NetworkStats.Entry( - testIf2, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 4) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3) - .insertEntry(entry4); - - stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL); - assertEquals(3, stats.size()); - assertEquals(entry1, stats.getValues(0, null)); - assertEquals(entry3, stats.getValues(1, null)); - assertEquals(entry4, stats.getValues(2, null)); - } - - @Test - public void testFilter_EmptyInterfaceFilter() { - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "if1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "if2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(entry1) - .insertEntry(entry2); - - stats.filter(UID_ALL, new String[] { }, TAG_ALL); - assertEquals(0, stats.size()); - } - - @Test - public void testFilter_TagFilter() { - final int testTag = 123; - final int otherTag = 456; - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "test1", 10100, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, otherTag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3); - - stats.filter(UID_ALL, INTERFACES_ALL, testTag); - assertEquals(2, stats.size()); - assertEquals(entry1, stats.getValues(0, null)); - assertEquals(entry2, stats.getValues(1, null)); - } - - @Test - public void testFilterDebugEntries() { - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "test2", 10101, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry4 = new NetworkStats.Entry( - "test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 4) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3) - .insertEntry(entry4); - - stats.filterDebugEntries(); - - assertEquals(2, stats.size()); - assertEquals(entry1, stats.getValues(0, null)); - assertEquals(entry3, stats.getValues(1, null)); - } - - @Test - public void testApply464xlatAdjustments() { - final String v4Iface = "v4-wlan0"; - final String baseIface = "wlan0"; - final String otherIface = "other"; - final int appUid = 10001; - final int rootUid = Process.ROOT_UID; - ArrayMap stackedIface = new ArrayMap<>(); - stackedIface.put(v4Iface, baseIface); - - // Ipv4 traffic sent/received by an app on stacked interface. - final NetworkStats.Entry appEntry = new NetworkStats.Entry( - v4Iface, appUid, SET_DEFAULT, TAG_NONE, - 30501490 /* rxBytes */, - 22401 /* rxPackets */, - 876235 /* txBytes */, - 13805 /* txPackets */, - 0 /* operations */); - - // Traffic measured for the root uid on the base interface. - final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry( - baseIface, rootUid, SET_DEFAULT, TAG_NONE, - 163577 /* rxBytes */, - 187 /* rxPackets */, - 17607 /* txBytes */, - 97 /* txPackets */, - 0 /* operations */); - - final NetworkStats.Entry otherEntry = new NetworkStats.Entry( - otherIface, appUid, SET_DEFAULT, TAG_NONE, - 2600 /* rxBytes */, - 2 /* rxPackets */, - 3800 /* txBytes */, - 3 /* txPackets */, - 0 /* operations */); - - final NetworkStats stats = new NetworkStats(TEST_START, 3) - .insertEntry(appEntry) - .insertEntry(rootUidEntry) - .insertEntry(otherEntry); - - stats.apply464xlatAdjustments(stackedIface); - - assertEquals(3, stats.size()); - final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry( - v4Iface, appUid, SET_DEFAULT, TAG_NONE, - 30949510, - 22401, - 1152335, - 13805, - 0); - final NetworkStats.Entry expectedRootUid = new NetworkStats.Entry( - baseIface, 0, SET_DEFAULT, TAG_NONE, - 163577, - 187, - 17607, - 97, - 0); - assertEquals(expectedAppUid, stats.getValues(0, null)); - assertEquals(expectedRootUid, stats.getValues(1, null)); - assertEquals(otherEntry, stats.getValues(2, null)); - } - - @Test - public void testApply464xlatAdjustments_noStackedIface() { - NetworkStats.Entry firstEntry = new NetworkStats.Entry( - "if1", 10002, SET_DEFAULT, TAG_NONE, - 2600 /* rxBytes */, - 2 /* rxPackets */, - 3800 /* txBytes */, - 3 /* txPackets */, - 0 /* operations */); - NetworkStats.Entry secondEntry = new NetworkStats.Entry( - "if2", 10002, SET_DEFAULT, TAG_NONE, - 5000 /* rxBytes */, - 3 /* rxPackets */, - 6000 /* txBytes */, - 4 /* txPackets */, - 0 /* operations */); - - NetworkStats stats = new NetworkStats(TEST_START, 2) - .insertEntry(firstEntry) - .insertEntry(secondEntry); - - // Empty map: no adjustment - stats.apply464xlatAdjustments(new ArrayMap<>()); - - assertEquals(2, stats.size()); - assertEquals(firstEntry, stats.getValues(0, null)); - assertEquals(secondEntry, stats.getValues(1, null)); - } - - private static void assertContains(NetworkStats stats, String iface, int uid, int set, - int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, - long txBytes, long txPackets, long operations) { - int index = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); - assertTrue(index != -1); - assertValues(stats, index, iface, uid, set, tag, metered, roaming, defaultNetwork, - rxBytes, rxPackets, txBytes, txPackets, operations); - } - - private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set, - int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, - long txBytes, long txPackets, long operations) { - final NetworkStats.Entry entry = stats.getValues(index, null); - assertValues(entry, iface, uid, set, tag, metered, roaming, defaultNetwork); - assertValues(entry, rxBytes, rxPackets, txBytes, txPackets, operations); - } - - private static void assertValues( - NetworkStats.Entry entry, String iface, int uid, int set, int tag, int metered, - int roaming, int defaultNetwork) { - assertEquals(iface, entry.iface); - assertEquals(uid, entry.uid); - assertEquals(set, entry.set); - assertEquals(tag, entry.tag); - assertEquals(metered, entry.metered); - assertEquals(roaming, entry.roaming); - assertEquals(defaultNetwork, entry.defaultNetwork); - } - - private static void assertValues(NetworkStats.Entry entry, long rxBytes, long rxPackets, - long txBytes, long txPackets, long operations) { - assertEquals(rxBytes, entry.rxBytes); - assertEquals(rxPackets, entry.rxPackets); - assertEquals(txBytes, entry.txBytes); - assertEquals(txPackets, entry.txPackets); - assertEquals(operations, entry.operations); - } - -} diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt deleted file mode 100644 index ab6b2f409867..000000000000 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ /dev/null @@ -1,351 +0,0 @@ -/* - * 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 - -import android.content.Context -import android.net.ConnectivityManager.TYPE_MOBILE -import android.net.ConnectivityManager.TYPE_WIFI -import android.net.NetworkIdentity.SUBTYPE_COMBINED -import android.net.NetworkIdentity.OEM_NONE -import android.net.NetworkIdentity.OEM_PAID -import android.net.NetworkIdentity.OEM_PRIVATE -import android.net.NetworkIdentity.buildNetworkIdentity -import android.net.NetworkStats.DEFAULT_NETWORK_ALL -import android.net.NetworkStats.METERED_ALL -import android.net.NetworkStats.ROAMING_ALL -import android.net.NetworkTemplate.MATCH_MOBILE -import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD -import android.net.NetworkTemplate.MATCH_WIFI -import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD -import android.net.NetworkTemplate.WIFI_NETWORKID_ALL -import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA -import android.net.NetworkTemplate.NETWORK_TYPE_ALL -import android.net.NetworkTemplate.OEM_MANAGED_ALL -import android.net.NetworkTemplate.OEM_MANAGED_NO -import android.net.NetworkTemplate.OEM_MANAGED_YES -import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT -import android.net.NetworkTemplate.buildTemplateWifi -import android.net.NetworkTemplate.buildTemplateWifiWildcard -import android.net.NetworkTemplate.buildTemplateCarrier -import android.net.NetworkTemplate.buildTemplateMobileWithRatType -import android.telephony.TelephonyManager -import com.android.testutils.assertParcelSane -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.mockito.Mockito.mock -import org.mockito.MockitoAnnotations -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotEquals -import kotlin.test.assertTrue - -private const val TEST_IMSI1 = "imsi1" -private const val TEST_IMSI2 = "imsi2" -private const val TEST_SSID1 = "ssid1" -private const val TEST_SSID2 = "ssid2" - -@RunWith(JUnit4::class) -class NetworkTemplateTest { - private val mockContext = mock(Context::class.java) - - private fun buildMobileNetworkState(subscriberId: String): NetworkStateSnapshot = - buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId) - private fun buildWifiNetworkState(subscriberId: String?, ssid: String?): NetworkStateSnapshot = - buildNetworkState(TYPE_WIFI, subscriberId = subscriberId, ssid = ssid) - - private fun buildNetworkState( - type: Int, - subscriberId: String? = null, - ssid: String? = null, - oemManaged: Int = OEM_NONE - ): NetworkStateSnapshot { - val lp = LinkProperties() - val caps = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) - setSSID(ssid) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, - (oemManaged and OEM_PAID) == OEM_PAID) - setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, - (oemManaged and OEM_PRIVATE) == OEM_PRIVATE) - } - return NetworkStateSnapshot(mock(Network::class.java), caps, lp, subscriberId, type) - } - - private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) = - assertTrue(matches(ident), "$this does not match $ident") - - private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) = - assertFalse(matches(ident), "$this should match $ident") - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - } - - @Test - fun testWifiWildcardMatches() { - val templateWifiWildcard = buildTemplateWifiWildcard() - - val identMobileImsi1 = buildNetworkIdentity(mockContext, - buildMobileNetworkState(TEST_IMSI1), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifiImsiNullSsid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - val identWifiImsi1Ssid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) - - templateWifiWildcard.assertDoesNotMatch(identMobileImsi1) - templateWifiWildcard.assertMatches(identWifiImsiNullSsid1) - templateWifiWildcard.assertMatches(identWifiImsi1Ssid1) - } - - @Test - fun testWifiMatches() { - val templateWifiSsid1 = buildTemplateWifi(TEST_SSID1) - val templateWifiSsid1ImsiNull = buildTemplateWifi(TEST_SSID1, null) - val templateWifiSsid1Imsi1 = buildTemplateWifi(TEST_SSID1, TEST_IMSI1) - val templateWifiSsidAllImsi1 = buildTemplateWifi(WIFI_NETWORKID_ALL, TEST_IMSI1) - - val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifiImsiNullSsid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - val identWifiImsi1Ssid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) - val identWifiImsi2Ssid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) - val identWifiImsi1Ssid2 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID2), true, 0) - - // Verify that template with SSID only matches any subscriberId and specific SSID. - templateWifiSsid1.assertDoesNotMatch(identMobile1) - templateWifiSsid1.assertMatches(identWifiImsiNullSsid1) - templateWifiSsid1.assertMatches(identWifiImsi1Ssid1) - templateWifiSsid1.assertMatches(identWifiImsi2Ssid1) - templateWifiSsid1.assertDoesNotMatch(identWifiImsi1Ssid2) - - // Verify that template with SSID1 and null imsi matches any network with - // SSID1 and null imsi. - templateWifiSsid1ImsiNull.assertDoesNotMatch(identMobile1) - templateWifiSsid1ImsiNull.assertMatches(identWifiImsiNullSsid1) - templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid1) - templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi2Ssid1) - templateWifiSsid1ImsiNull.assertDoesNotMatch(identWifiImsi1Ssid2) - - // Verify that template with SSID1 and imsi1 matches any network with - // SSID1 and imsi1. - templateWifiSsid1Imsi1.assertDoesNotMatch(identMobile1) - templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsiNullSsid1) - templateWifiSsid1Imsi1.assertMatches(identWifiImsi1Ssid1) - templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi2Ssid1) - templateWifiSsid1Imsi1.assertDoesNotMatch(identWifiImsi1Ssid2) - - // Verify that template with SSID all and imsi1 matches any network with - // any SSID and imsi1. - templateWifiSsidAllImsi1.assertDoesNotMatch(identMobile1) - templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsiNullSsid1) - templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid1) - templateWifiSsidAllImsi1.assertDoesNotMatch(identWifiImsi2Ssid1) - templateWifiSsidAllImsi1.assertMatches(identWifiImsi1Ssid2) - } - - @Test - fun testCarrierMatches() { - val templateCarrierImsi1 = buildTemplateCarrier(TEST_IMSI1) - - val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identMobile2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifiSsid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - val identCarrierWifiImsi1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) - val identCarrierWifiImsi2 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) - - templateCarrierImsi1.assertMatches(identCarrierWifiImsi1) - templateCarrierImsi1.assertDoesNotMatch(identCarrierWifiImsi2) - templateCarrierImsi1.assertDoesNotMatch(identWifiSsid1) - templateCarrierImsi1.assertMatches(identMobile1) - templateCarrierImsi1.assertDoesNotMatch(identMobile2) - } - - @Test - fun testRatTypeGroupMatches() { - val stateMobile = buildMobileNetworkState(TEST_IMSI1) - // Build UMTS template that matches mobile identities with RAT in the same - // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}. - val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS) - // Build normal template that matches mobile identities with any RAT and IMSI. - val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL) - // Build template with UNKNOWN RAT that matches mobile identities with RAT that - // cannot be determined. - val templateUnknown = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN) - - val identUmts = buildNetworkIdentity( - mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS) - val identHsdpa = buildNetworkIdentity( - mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA) - val identLte = buildNetworkIdentity( - mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE) - val identCombined = buildNetworkIdentity( - mockContext, stateMobile, false, SUBTYPE_COMBINED) - val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifi = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - - // Assert that identity with the same RAT matches. - templateUmts.assertMatches(identUmts) - templateAll.assertMatches(identUmts) - templateUnknown.assertDoesNotMatch(identUmts) - // Assert that identity with the RAT within the same group matches. - templateUmts.assertMatches(identHsdpa) - templateAll.assertMatches(identHsdpa) - templateUnknown.assertDoesNotMatch(identHsdpa) - // Assert that identity with the RAT out of the same group only matches template with - // NETWORK_TYPE_ALL. - templateUmts.assertDoesNotMatch(identLte) - templateAll.assertMatches(identLte) - templateUnknown.assertDoesNotMatch(identLte) - // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL - // and NETWORK_TYPE_UNKNOWN. - templateUmts.assertDoesNotMatch(identCombined) - templateAll.assertMatches(identCombined) - templateUnknown.assertMatches(identCombined) - // Assert that identity with different IMSI matches. - templateUmts.assertMatches(identImsi2) - templateAll.assertMatches(identImsi2) - templateUnknown.assertDoesNotMatch(identImsi2) - // Assert that wifi identity does not match. - templateUmts.assertDoesNotMatch(identWifi) - templateAll.assertDoesNotMatch(identWifi) - templateUnknown.assertDoesNotMatch(identWifi) - } - - @Test - fun testParcelUnparcel() { - val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE, - OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT) - val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_ALL, - SUBSCRIBER_ID_MATCH_RULE_EXACT) - val templateOem = NetworkTemplate(MATCH_MOBILE, null, null, null, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_YES, - SUBSCRIBER_ID_MATCH_RULE_EXACT) - assertParcelSane(templateMobile, 10) - assertParcelSane(templateWifi, 10) - assertParcelSane(templateOem, 10) - } - - // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with - // TelephonyManager#NETWORK_TYPE_* constants. - @Test - fun testNetworkTypeConstants() { - for (ratType in TelephonyManager.getAllNetworkTypes()) { - assertNotEquals(NETWORK_TYPE_ALL, ratType) - assertNotEquals(NETWORK_TYPE_5G_NSA, ratType) - } - } - - @Test - fun testOemNetworkConstants() { - val constantValues = arrayOf(OEM_MANAGED_YES, OEM_MANAGED_ALL, OEM_MANAGED_NO, - OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) - - // Verify that "not OEM managed network" constants are equal. - assertEquals(OEM_MANAGED_NO, OEM_NONE) - - // Verify the constants don't conflict. - assertEquals(constantValues.size, constantValues.distinct().count()) - } - - /** - * Helper to enumerate and assert OEM managed wifi and mobile {@code NetworkTemplate}s match - * their the appropriate OEM managed {@code NetworkIdentity}s. - * - * @param networkType {@code TYPE_MOBILE} or {@code TYPE_WIFI} - * @param matchType A match rule from {@code NetworkTemplate.MATCH_*} corresponding to the - * networkType. - * @param subscriberId To be populated with {@code TEST_IMSI*} only if networkType is - * {@code TYPE_MOBILE}. May be left as null when matchType is - * {@link NetworkTemplate.MATCH_MOBILE_WILDCARD}. - * @param templateSsid Top be populated with {@code TEST_SSID*} only if networkType is - * {@code TYPE_WIFI}. May be left as null when matchType is - * {@link NetworkTemplate.MATCH_WIFI_WILDCARD}. - * @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide - * one of {@code TEST_SSID*}. - */ - private fun matchOemManagedIdent( - networkType: Int, - matchType: Int, - subscriberId: String? = null, - templateSsid: String? = null, - identSsid: String? = null - ) { - val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) - val matchSubscriberIds = arrayOf(subscriberId) - - val templateOemYes = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, - templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_YES, SUBSCRIBER_ID_MATCH_RULE_EXACT) - val templateOemAll = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, - templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT) - - for (identityOemManagedState in oemManagedStates) { - val ident = buildNetworkIdentity(mockContext, buildNetworkState(networkType, - subscriberId, identSsid, identityOemManagedState), /*defaultNetwork=*/false, - /*subType=*/0) - - // Create a template with each OEM managed type and match it against the NetworkIdentity - for (templateOemManagedState in oemManagedStates) { - val template = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, - templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, - NETWORK_TYPE_ALL, templateOemManagedState, SUBSCRIBER_ID_MATCH_RULE_EXACT) - if (identityOemManagedState == templateOemManagedState) { - template.assertMatches(ident) - } else { - template.assertDoesNotMatch(ident) - } - } - // OEM_MANAGED_ALL ignores OEM state. - templateOemAll.assertMatches(ident) - if (identityOemManagedState == OEM_NONE) { - // OEM_MANAGED_YES matches everything except OEM_NONE. - templateOemYes.assertDoesNotMatch(ident) - } else { - templateOemYes.assertMatches(ident) - } - } - } - - @Test - fun testOemManagedMatchesIdent() { - matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE, subscriberId = TEST_IMSI1) - matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE_WILDCARD) - matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateSsid = TEST_SSID1, - identSsid = TEST_SSID1) - matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1) - } -} diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java deleted file mode 100644 index 7748288aeb05..000000000000 --- a/tests/net/java/android/net/NetworkUtilsTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2015 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; - -import static junit.framework.Assert.assertEquals; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.math.BigInteger; -import java.util.TreeSet; - -@RunWith(AndroidJUnit4.class) -@androidx.test.filters.SmallTest -public class NetworkUtilsTest { - @Test - public void testRoutedIPv4AddressCount() { - final TreeSet set = new TreeSet<>(IpPrefix.lengthComparator()); - // No routes routes to no addresses. - assertEquals(0, NetworkUtils.routedIPv4AddressCount(set)); - - set.add(new IpPrefix("0.0.0.0/0")); - assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); - - set.add(new IpPrefix("20.18.0.0/16")); - set.add(new IpPrefix("20.18.0.0/24")); - set.add(new IpPrefix("20.18.0.0/8")); - // There is a default route, still covers everything - assertEquals(1l << 32, NetworkUtils.routedIPv4AddressCount(set)); - - set.clear(); - set.add(new IpPrefix("20.18.0.0/24")); - set.add(new IpPrefix("20.18.0.0/8")); - // The 8-length includes the 24-length prefix - assertEquals(1l << 24, NetworkUtils.routedIPv4AddressCount(set)); - - set.add(new IpPrefix("10.10.10.126/25")); - // The 8-length does not include this 25-length prefix - assertEquals((1l << 24) + (1 << 7), NetworkUtils.routedIPv4AddressCount(set)); - - set.clear(); - set.add(new IpPrefix("1.2.3.4/32")); - set.add(new IpPrefix("1.2.3.4/32")); - set.add(new IpPrefix("1.2.3.4/32")); - set.add(new IpPrefix("1.2.3.4/32")); - assertEquals(1l, NetworkUtils.routedIPv4AddressCount(set)); - - set.add(new IpPrefix("1.2.3.5/32")); - set.add(new IpPrefix("1.2.3.6/32")); - - set.add(new IpPrefix("1.2.3.7/32")); - set.add(new IpPrefix("1.2.3.8/32")); - set.add(new IpPrefix("1.2.3.9/32")); - set.add(new IpPrefix("1.2.3.0/32")); - assertEquals(7l, NetworkUtils.routedIPv4AddressCount(set)); - - // 1.2.3.4/30 eats 1.2.3.{4-7}/32 - set.add(new IpPrefix("1.2.3.4/30")); - set.add(new IpPrefix("6.2.3.4/28")); - set.add(new IpPrefix("120.2.3.4/16")); - assertEquals(7l - 4 + 4 + 16 + 65536, NetworkUtils.routedIPv4AddressCount(set)); - } - - @Test - public void testRoutedIPv6AddressCount() { - final TreeSet set = new TreeSet<>(IpPrefix.lengthComparator()); - // No routes routes to no addresses. - assertEquals(BigInteger.ZERO, NetworkUtils.routedIPv6AddressCount(set)); - - set.add(new IpPrefix("::/0")); - assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); - - set.add(new IpPrefix("1234:622a::18/64")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); - // There is a default route, still covers everything - assertEquals(BigInteger.ONE.shiftLeft(128), NetworkUtils.routedIPv6AddressCount(set)); - - set.clear(); - set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/96")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6adb/8")); - // The 8-length includes the 96-length prefix - assertEquals(BigInteger.ONE.shiftLeft(120), NetworkUtils.routedIPv6AddressCount(set)); - - set.add(new IpPrefix("10::26/64")); - // The 8-length does not include this 64-length prefix - assertEquals(BigInteger.ONE.shiftLeft(120).add(BigInteger.ONE.shiftLeft(64)), - NetworkUtils.routedIPv6AddressCount(set)); - - set.clear(); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/128")); - assertEquals(BigInteger.ONE, NetworkUtils.routedIPv6AddressCount(set)); - - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad5/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad6/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad7/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad8/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad9/128")); - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad0/128")); - assertEquals(BigInteger.valueOf(7), NetworkUtils.routedIPv6AddressCount(set)); - - // add4:f00:80:f7:1111::6ad4/126 eats add4:f00:8[:f7:1111::6ad{4-7}/128 - set.add(new IpPrefix("add4:f00:80:f7:1111::6ad4/126")); - set.add(new IpPrefix("d00d:f00:80:f7:1111::6ade/124")); - set.add(new IpPrefix("f00b:a33::/112")); - assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), - NetworkUtils.routedIPv6AddressCount(set)); - } -} diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/tests/net/java/android/net/QosSocketFilterTest.java deleted file mode 100644 index ad58960eaadd..000000000000 --- a/tests/net/java/android/net/QosSocketFilterTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.net.InetSocketAddress; - -@RunWith(AndroidJUnit4.class) -@androidx.test.filters.SmallTest -public class QosSocketFilterTest { - - @Test - public void testPortExactMatch() { - final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); - final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesLocalAddress( - new InetSocketAddress(addressA, 10), addressB, 10, 10)); - - } - - @Test - public void testPortLessThanStart() { - final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); - final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesLocalAddress( - new InetSocketAddress(addressA, 8), addressB, 10, 10)); - } - - @Test - public void testPortGreaterThanEnd() { - final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); - final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertFalse(QosSocketFilter.matchesLocalAddress( - new InetSocketAddress(addressA, 18), addressB, 10, 10)); - } - - @Test - public void testPortBetweenStartAndEnd() { - final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); - final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); - assertTrue(QosSocketFilter.matchesLocalAddress( - new InetSocketAddress(addressA, 10), addressB, 8, 18)); - } - - @Test - public void testAddressesDontMatch() { - final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); - final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5"); - assertFalse(QosSocketFilter.matchesLocalAddress( - new InetSocketAddress(addressA, 10), addressB, 10, 10)); - } -} - diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java deleted file mode 100644 index 6714bb1abbe6..000000000000 --- a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2018 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; - -import static com.android.testutils.ParcelUtils.assertParcelSane; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.net.wifi.WifiNetworkSpecifier; -import android.telephony.SubscriptionManager; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Unit test for {@link android.net.TelephonyNetworkSpecifier}. - */ -@SmallTest -public class TelephonyNetworkSpecifierTest { - private static final int TEST_SUBID = 5; - private static final String TEST_SSID = "Test123"; - - /** - * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier - * without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. - */ - @Test - public void testBuilderBuildWithDefault() { - try { - new TelephonyNetworkSpecifier.Builder().build(); - } catch (IllegalArgumentException iae) { - // expected, test pass - } - } - - /** - * Validate that no exception will be thrown even if pass invalid subscription id to - * {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. - */ - @Test - public void testBuilderBuildWithInvalidSubId() { - TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID) - .build(); - assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID); - } - - /** - * Validate the correctness of TelephonyNetworkSpecifier when provide valid subId. - */ - @Test - public void testBuilderBuildWithValidSubId() { - final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(TEST_SUBID) - .build(); - assertEquals(TEST_SUBID, specifier.getSubscriptionId()); - } - - /** - * Validate that parcel marshalling/unmarshalling works. - */ - @Test - public void testParcel() { - TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(TEST_SUBID) - .build(); - assertParcelSane(specifier, 1 /* fieldCount */); - } - - /** - * Validate the behavior of method canBeSatisfiedBy(). - */ - @Test - public void testCanBeSatisfiedBy() { - final TelephonyNetworkSpecifier tns1 = new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(TEST_SUBID) - .build(); - final TelephonyNetworkSpecifier tns2 = new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(TEST_SUBID) - .build(); - final WifiNetworkSpecifier wns = new WifiNetworkSpecifier.Builder() - .setSsid(TEST_SSID) - .build(); - final MatchAllNetworkSpecifier mans = new MatchAllNetworkSpecifier(); - - // Test equality - assertEquals(tns1, tns2); - assertTrue(tns1.canBeSatisfiedBy(tns1)); - assertTrue(tns1.canBeSatisfiedBy(tns2)); - - // Test other edge cases. - assertFalse(tns1.canBeSatisfiedBy(null)); - assertFalse(tns1.canBeSatisfiedBy(wns)); - assertTrue(tns1.canBeSatisfiedBy(mans)); - } -} diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java deleted file mode 100644 index 3135062138ac..000000000000 --- a/tests/net/java/android/net/VpnManagerTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2019 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Intent; -import android.test.mock.MockContext; -import android.util.SparseArray; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.net.VpnProfile; -import com.android.internal.util.MessageUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link VpnManager}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class VpnManagerTest { - private static final String PKG_NAME = "fooPackage"; - - private static final String SESSION_NAME_STRING = "testSession"; - private static final String SERVER_ADDR_STRING = "1.2.3.4"; - private static final String IDENTITY_STRING = "Identity"; - private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); - - private IVpnManager mMockService; - private VpnManager mVpnManager; - private final MockContext mMockContext = - new MockContext() { - @Override - public String getOpPackageName() { - return PKG_NAME; - } - }; - - @Before - public void setUp() throws Exception { - mMockService = mock(IVpnManager.class); - mVpnManager = new VpnManager(mMockContext, mMockService); - } - - @Test - public void testProvisionVpnProfilePreconsented() throws Exception { - final PlatformVpnProfile profile = getPlatformVpnProfile(); - when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))) - .thenReturn(true); - - // Expect there to be no intent returned, as consent has already been granted. - assertNull(mVpnManager.provisionVpnProfile(profile)); - verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); - } - - @Test - public void testProvisionVpnProfileNeedsConsent() throws Exception { - final PlatformVpnProfile profile = getPlatformVpnProfile(); - when(mMockService.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))) - .thenReturn(false); - - // Expect intent to be returned, as consent has not already been granted. - final Intent intent = mVpnManager.provisionVpnProfile(profile); - assertNotNull(intent); - - final ComponentName expectedComponentName = - ComponentName.unflattenFromString( - "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog"); - assertEquals(expectedComponentName, intent.getComponent()); - verify(mMockService).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME)); - } - - @Test - public void testDeleteProvisionedVpnProfile() throws Exception { - mVpnManager.deleteProvisionedVpnProfile(); - verify(mMockService).deleteVpnProfile(eq(PKG_NAME)); - } - - @Test - public void testStartProvisionedVpnProfile() throws Exception { - mVpnManager.startProvisionedVpnProfile(); - verify(mMockService).startVpnProfile(eq(PKG_NAME)); - } - - @Test - public void testStopProvisionedVpnProfile() throws Exception { - mVpnManager.stopProvisionedVpnProfile(); - verify(mMockService).stopVpnProfile(eq(PKG_NAME)); - } - - private Ikev2VpnProfile getPlatformVpnProfile() throws Exception { - return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING) - .setBypassable(true) - .setMaxMtu(1300) - .setMetered(true) - .setAuthPsk(PSK_BYTES) - .build(); - } - - @Test - public void testVpnTypesEqual() throws Exception { - SparseArray vmVpnTypes = MessageUtils.findMessageNames( - new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" }); - SparseArray nativeVpnType = MessageUtils.findMessageNames( - new Class[] { NativeVpnType.class }, new String[]{ "" }); - - // TYPE_VPN_NONE = -1 is only defined in VpnManager. - assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size()); - for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) { - assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i)); - } - } -} diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java deleted file mode 100644 index ccaa5cf7e9f7..000000000000 --- a/tests/net/java/android/net/VpnTransportInfoTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2021 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; - -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; -import static android.net.NetworkCapabilities.REDACT_NONE; - -import static com.android.testutils.ParcelUtils.assertParcelSane; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class VpnTransportInfoTest { - - @Test - public void testParceling() { - VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345"); - assertParcelSane(v, 2 /* fieldCount */); - } - - @Test - public void testEqualsAndHashCode() { - String session1 = "12345"; - String session2 = "6789"; - VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); - VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1); - VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1); - VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1); - VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1); - VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2); - - VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS); - VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS); - - assertNotEquals(v11, v12); - assertNotEquals(v13, v14); - assertNotEquals(v14, v15); - assertNotEquals(v14, v21); - - assertEquals(v11, v13); - assertEquals(v31, v32); - assertEquals(v11.hashCode(), v13.hashCode()); - assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions()); - assertEquals(session1, v15.makeCopy(REDACT_NONE).getSessionId()); - } -} diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java deleted file mode 100644 index 603c87519532..000000000000 --- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2018 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.ipmemorystore; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; -import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirkParcelable; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.lang.reflect.Modifier; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.Arrays; -import java.util.Collections; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ParcelableTests { - @Test - public void testNetworkAttributesParceling() throws Exception { - final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); - NetworkAttributes in = builder.build(); - assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); - - builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - // lease will expire in two hours - builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); - // cluster stays null this time around - builder.setDnsAddresses(Collections.emptyList()); - builder.setMtu(18); - in = builder.build(); - assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); - - builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9")); - builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000); - builder.setCluster("groupHint"); - builder.setDnsAddresses(Arrays.asList( - InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"), - InetAddress.getByName("6.7.8.9"))); - builder.setMtu(1_000_000); - in = builder.build(); - assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); - - builder.setMtu(null); - in = builder.build(); - assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable()))); - - // Verify that this test does not miss any new field added later. - // If any field is added to NetworkAttributes it must be tested here for parceling - // roundtrip. - assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); - } - - @Test - public void testPrivateDataParceling() throws Exception { - final Blob in = new Blob(); - in.data = new byte[] {89, 111, 108, 111}; - final Blob out = parcelingRoundTrip(in); - // Object.equals on byte[] tests the references - assertEquals(in.data.length, out.data.length); - assertTrue(Arrays.equals(in.data, out.data)); - } - - @Test - public void testSameL3NetworkResponseParceling() throws Exception { - final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable(); - parcelable.l2Key1 = "key 1"; - parcelable.l2Key2 = "key 2"; - parcelable.confidence = 0.43f; - - final SameL3NetworkResponse in = new SameL3NetworkResponse(parcelable); - assertEquals("key 1", in.l2Key1); - assertEquals("key 2", in.l2Key2); - assertEquals(0.43f, in.confidence, 0.01f /* delta */); - - final SameL3NetworkResponse out = - new SameL3NetworkResponse(parcelingRoundTrip(in.toParcelable())); - - assertEquals(in, out); - assertEquals(in.l2Key1, out.l2Key1); - assertEquals(in.l2Key2, out.l2Key2); - assertEquals(in.confidence, out.confidence, 0.01f /* delta */); - } - - @Test - public void testIPv6ProvisioningLossQuirkParceling() throws Exception { - final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); - final IPv6ProvisioningLossQuirkParcelable parcelable = - new IPv6ProvisioningLossQuirkParcelable(); - final long expiry = System.currentTimeMillis() + 7_200_000; - - parcelable.detectionCount = 3; - parcelable.quirkExpiry = expiry; // quirk info will expire in two hours - builder.setIpv6ProvLossQuirk(IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable)); - final NetworkAttributes in = builder.build(); - - final NetworkAttributes out = new NetworkAttributes(parcelingRoundTrip(in.toParcelable())); - assertEquals(out.ipv6ProvisioningLossQuirk, in.ipv6ProvisioningLossQuirk); - } - - private T parcelingRoundTrip(final T in) throws Exception { - final Parcel p = Parcel.obtain(); - in.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - final byte[] marshalledData = p.marshall(); - p.recycle(); - - final Parcel q = Parcel.obtain(); - q.unmarshall(marshalledData, 0, marshalledData.length); - q.setDataPosition(0); - - final Parcelable.Creator creator = (Parcelable.Creator) - in.getClass().getField("CREATOR").get(null); // static object, so null receiver - final T unmarshalled = (T) creator.createFromParcel(q); - q.recycle(); - return unmarshalled; - } -} diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java deleted file mode 100644 index b0a9b8a55322..000000000000 --- a/tests/net/java/android/net/nsd/NsdManagerTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2017 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.nsd; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.AsyncChannel; -import com.android.testutils.HandlerUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NsdManagerTest { - - static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; - - @Mock Context mContext; - @Mock INsdManager mService; - MockServiceHandler mServiceHandler; - - NsdManager mManager; - - long mTimeoutMs = 200; // non-final so that tests can adjust the value. - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mServiceHandler = spy(MockServiceHandler.create(mContext)); - when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler)); - - mManager = makeManager(); - } - - @After - public void tearDown() throws Exception { - HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); - mServiceHandler.chan.disconnect(); - mServiceHandler.stop(); - if (mManager != null) { - mManager.disconnect(); - } - } - - @Test - public void testResolveService() { - NsdManager manager = mManager; - - NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); - NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); - NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); - - manager.resolveService(request, listener); - int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); - int err = 33; - sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null); - verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); - - manager.resolveService(request, listener); - int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); - sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); - verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); - } - - @Test - public void testParallelResolveService() { - NsdManager manager = mManager; - - NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); - NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); - - NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class); - NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); - - manager.resolveService(request, listener1); - int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); - - manager.resolveService(request, listener2); - int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); - - sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); - sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply); - - verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); - verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); - } - - @Test - public void testRegisterService() { - NsdManager manager = mManager; - - NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); - NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); - request1.setPort(2201); - request2.setPort(2202); - NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); - NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class); - - // Register two services - manager.registerService(request1, PROTOCOL, listener1); - int key1 = verifyRequest(NsdManager.REGISTER_SERVICE); - - manager.registerService(request2, PROTOCOL, listener2); - int key2 = verifyRequest(NsdManager.REGISTER_SERVICE); - - // First reques fails, second request succeeds - sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2); - verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); - - int err = 1; - sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1); - verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); - - // Client retries first request, it succeeds - manager.registerService(request1, PROTOCOL, listener1); - int key3 = verifyRequest(NsdManager.REGISTER_SERVICE); - - sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1); - verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); - - // First request is unregistered, it succeeds - manager.unregisterService(listener1); - int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE); - assertEquals(key3, key3again); - - sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null); - verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); - - // Second request is unregistered, it fails - manager.unregisterService(listener2); - int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE); - assertEquals(key2, key2again); - - sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null); - verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); - - // TODO: do not unregister listener until service is unregistered - // Client retries unregistration of second request, it succeeds - //manager.unregisterService(listener2); - //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE); - //assertEquals(key2, key2yetAgain); - - //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null); - //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); - } - - @Test - public void testDiscoverService() { - NsdManager manager = mManager; - - NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); - NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); - NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type"); - - NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class); - - // Client registers for discovery, request fails - manager.discoverServices("a_type", PROTOCOL, listener); - int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES); - - int err = 1; - sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null); - verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); - - // Client retries, request succeeds - manager.discoverServices("a_type", PROTOCOL, listener); - int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES); - - sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1); - verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); - - - // mdns notifies about services - sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1); - verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); - - sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2); - verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); - - sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2); - verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); - - - // Client unregisters its listener - manager.stopServiceDiscovery(listener); - int key2again = verifyRequest(NsdManager.STOP_DISCOVERY); - assertEquals(key2, key2again); - - // TODO: unregister listener immediately and stop notifying it about services - // Notifications are still passed to the client's listener - sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1); - verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); - - // Client is notified of complete unregistration - sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type"); - verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); - - // Notifications are not passed to the client anymore - sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3); - verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); - - - // Client registers for service discovery - reset(listener); - manager.discoverServices("a_type", PROTOCOL, listener); - int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES); - - sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1); - verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); - - // Client unregisters immediately, it fails - manager.stopServiceDiscovery(listener); - int key3again = verifyRequest(NsdManager.STOP_DISCOVERY); - assertEquals(key3, key3again); - - err = 2; - sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type"); - verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); - - // New notifications are not passed to the client anymore - sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1); - verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); - } - - @Test - public void testInvalidCalls() { - NsdManager manager = mManager; - - NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); - NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); - NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); - - NsdServiceInfo invalidService = new NsdServiceInfo(null, null); - NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type"); - validService.setPort(2222); - - // Service registration - // - invalid arguments - mustFail(() -> { manager.unregisterService(null); }); - mustFail(() -> { manager.registerService(null, -1, null); }); - mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); }); - mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); }); - mustFail(() -> { manager.registerService(validService, -1, listener1); }); - mustFail(() -> { manager.registerService(validService, PROTOCOL, null); }); - manager.registerService(validService, PROTOCOL, listener1); - // - listener already registered - mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); }); - manager.unregisterService(listener1); - // TODO: make listener immediately reusable - //mustFail(() -> { manager.unregisterService(listener1); }); - //manager.registerService(validService, PROTOCOL, listener1); - - // Discover service - // - invalid arguments - mustFail(() -> { manager.stopServiceDiscovery(null); }); - mustFail(() -> { manager.discoverServices(null, -1, null); }); - mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); }); - mustFail(() -> { manager.discoverServices("a_service", -1, listener2); }); - mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); }); - manager.discoverServices("a_service", PROTOCOL, listener2); - // - listener already registered - mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); }); - manager.stopServiceDiscovery(listener2); - // TODO: make listener immediately reusable - //mustFail(() -> { manager.stopServiceDiscovery(listener2); }); - //manager.discoverServices("another_service", PROTOCOL, listener2); - - // Resolver service - // - invalid arguments - mustFail(() -> { manager.resolveService(null, null); }); - mustFail(() -> { manager.resolveService(null, listener3); }); - mustFail(() -> { manager.resolveService(invalidService, listener3); }); - mustFail(() -> { manager.resolveService(validService, null); }); - manager.resolveService(validService, listener3); - // - listener already registered:w - mustFail(() -> { manager.resolveService(validService, listener3); }); - } - - public void mustFail(Runnable fn) { - try { - fn.run(); - fail(); - } catch (Exception expected) { - } - } - - NsdManager makeManager() { - NsdManager manager = new NsdManager(mContext, mService); - // Acknowledge first two messages connecting the AsyncChannel. - verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any()); - reset(mServiceHandler); - assertNotNull(mServiceHandler.chan); - return manager; - } - - int verifyRequest(int expectedMessageType) { - HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); - verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); - reset(mServiceHandler); - Message received = mServiceHandler.getLastMessage(); - assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what)); - return received.arg2; - } - - void sendResponse(int replyType, int arg, int key, Object obj) { - mServiceHandler.chan.sendMessage(replyType, arg, key, obj); - } - - // Implements the server side of AsyncChannel connection protocol - public static class MockServiceHandler extends Handler { - public final Context context; - public AsyncChannel chan; - public Message lastMessage; - - MockServiceHandler(Looper l, Context c) { - super(l); - context = c; - } - - synchronized Message getLastMessage() { - return lastMessage; - } - - synchronized void setLastMessage(Message msg) { - lastMessage = obtainMessage(); - lastMessage.copyFrom(msg); - } - - @Override - public void handleMessage(Message msg) { - setLastMessage(msg); - if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) { - chan = new AsyncChannel(); - chan.connect(context, this, msg.replyTo); - chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); - } - } - - void stop() { - getLooper().quitSafely(); - } - - static MockServiceHandler create(Context context) { - HandlerThread t = new HandlerThread("mock-service-handler"); - t.start(); - return new MockServiceHandler(t.getLooper(), context); - } - } -} diff --git a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java b/tests/net/java/android/net/nsd/NsdServiceInfoTest.java deleted file mode 100644 index 94dfc7515c67..000000000000 --- a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2014 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.nsd; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Bundle; -import android.os.Parcel; -import android.os.StrictMode; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.Map; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NsdServiceInfoTest { - - public final static InetAddress LOCALHOST; - static { - // Because test. - StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); - StrictMode.setThreadPolicy(policy); - - InetAddress _host = null; - try { - _host = InetAddress.getLocalHost(); - } catch (UnknownHostException e) { } - LOCALHOST = _host; - } - - @Test - public void testLimits() throws Exception { - NsdServiceInfo info = new NsdServiceInfo(); - - // Non-ASCII keys. - boolean exceptionThrown = false; - try { - info.setAttribute("猫", "meow"); - } catch (IllegalArgumentException e) { - exceptionThrown = true; - } - assertTrue(exceptionThrown); - assertEmptyServiceInfo(info); - - // ASCII keys with '=' character. - exceptionThrown = false; - try { - info.setAttribute("kitten=", "meow"); - } catch (IllegalArgumentException e) { - exceptionThrown = true; - } - assertTrue(exceptionThrown); - assertEmptyServiceInfo(info); - - // Single key + value length too long. - exceptionThrown = false; - try { - String longValue = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + - "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + - "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" + - "ooooooooooooooooooooooooooooong"; // 248 characters. - info.setAttribute("longcat", longValue); // Key + value == 255 characters. - } catch (IllegalArgumentException e) { - exceptionThrown = true; - } - assertTrue(exceptionThrown); - assertEmptyServiceInfo(info); - - // Total TXT record length too long. - exceptionThrown = false; - int recordsAdded = 0; - try { - for (int i = 100; i < 300; ++i) { - // 6 char key + 5 char value + 2 bytes overhead = 13 byte record length. - String key = String.format("key%d", i); - info.setAttribute(key, "12345"); - recordsAdded++; - } - } catch (IllegalArgumentException e) { - exceptionThrown = true; - } - assertTrue(exceptionThrown); - assertTrue(100 == recordsAdded); - assertTrue(info.getTxtRecord().length == 1300); - } - - @Test - public void testParcel() throws Exception { - NsdServiceInfo emptyInfo = new NsdServiceInfo(); - checkParcelable(emptyInfo); - - NsdServiceInfo fullInfo = new NsdServiceInfo(); - fullInfo.setServiceName("kitten"); - fullInfo.setServiceType("_kitten._tcp"); - fullInfo.setPort(4242); - fullInfo.setHost(LOCALHOST); - checkParcelable(fullInfo); - - NsdServiceInfo noHostInfo = new NsdServiceInfo(); - noHostInfo.setServiceName("kitten"); - noHostInfo.setServiceType("_kitten._tcp"); - noHostInfo.setPort(4242); - checkParcelable(noHostInfo); - - NsdServiceInfo attributedInfo = new NsdServiceInfo(); - attributedInfo.setServiceName("kitten"); - attributedInfo.setServiceType("_kitten._tcp"); - attributedInfo.setPort(4242); - attributedInfo.setHost(LOCALHOST); - attributedInfo.setAttribute("color", "pink"); - attributedInfo.setAttribute("sound", (new String("にゃあ")).getBytes("UTF-8")); - attributedInfo.setAttribute("adorable", (String) null); - attributedInfo.setAttribute("sticky", "yes"); - attributedInfo.setAttribute("siblings", new byte[] {}); - attributedInfo.setAttribute("edge cases", new byte[] {0, -1, 127, -128}); - attributedInfo.removeAttribute("sticky"); - checkParcelable(attributedInfo); - - // Sanity check that we actually wrote attributes to attributedInfo. - assertTrue(attributedInfo.getAttributes().keySet().contains("adorable")); - String sound = new String(attributedInfo.getAttributes().get("sound"), "UTF-8"); - assertTrue(sound.equals("にゃあ")); - byte[] edgeCases = attributedInfo.getAttributes().get("edge cases"); - assertTrue(Arrays.equals(edgeCases, new byte[] {0, -1, 127, -128})); - assertFalse(attributedInfo.getAttributes().keySet().contains("sticky")); - } - - public void checkParcelable(NsdServiceInfo original) { - // Write to parcel. - Parcel p = Parcel.obtain(); - Bundle writer = new Bundle(); - writer.putParcelable("test_info", original); - writer.writeToParcel(p, 0); - - // Extract from parcel. - p.setDataPosition(0); - Bundle reader = p.readBundle(); - reader.setClassLoader(NsdServiceInfo.class.getClassLoader()); - NsdServiceInfo result = reader.getParcelable("test_info"); - - // Assert equality of base fields. - assertEquals(original.getServiceName(), result.getServiceName()); - assertEquals(original.getServiceType(), result.getServiceType()); - assertEquals(original.getHost(), result.getHost()); - assertTrue(original.getPort() == result.getPort()); - - // Assert equality of attribute map. - Map originalMap = original.getAttributes(); - Map resultMap = result.getAttributes(); - assertEquals(originalMap.keySet(), resultMap.keySet()); - for (String key : originalMap.keySet()) { - assertTrue(Arrays.equals(originalMap.get(key), resultMap.get(key))); - } - } - - public void assertEmptyServiceInfo(NsdServiceInfo shouldBeEmpty) { - byte[] txtRecord = shouldBeEmpty.getTxtRecord(); - if (txtRecord == null || txtRecord.length == 0) { - return; - } - fail("NsdServiceInfo.getTxtRecord did not return null but " + Arrays.toString(txtRecord)); - } -} diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/tests/net/java/android/net/util/DnsUtilsTest.java deleted file mode 100644 index b626db8d89e4..000000000000 --- a/tests/net/java/android/net/util/DnsUtilsTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2019 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.util; - -import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_GLOBAL; -import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_LINKLOCAL; -import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_SITELOCAL; - -import static org.junit.Assert.assertEquals; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.InetAddresses; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.InetAddress; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DnsUtilsTest { - private InetAddress stringToAddress(@NonNull String addr) { - return InetAddresses.parseNumericAddress(addr); - } - - private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr) { - return makeSortableAddress(addr, null); - } - - private DnsUtils.SortableAddress makeSortableAddress(@NonNull String addr, - @Nullable String srcAddr) { - return new DnsUtils.SortableAddress(stringToAddress(addr), - srcAddr != null ? stringToAddress(srcAddr) : null); - } - - @Test - public void testRfc6724Comparator() { - final List test = Arrays.asList( - // Ipv4 - makeSortableAddress("216.58.200.36", "192.168.1.1"), - // global with different scope src - makeSortableAddress("2404:6800:4008:801::2004", "fe80::1111:2222"), - // global without src addr - makeSortableAddress("2404:6800:cafe:801::1"), - // loop back - makeSortableAddress("::1", "::1"), - // link local - makeSortableAddress("fe80::c46f:1cff:fe04:39b4", "fe80::1"), - // teredo tunneling - makeSortableAddress("2001::47c1", "2001::2"), - // 6bone without src addr - makeSortableAddress("3ffe::1234:5678"), - // IPv4-compatible - makeSortableAddress("::216.58.200.36", "::216.58.200.9"), - // 6bone - makeSortableAddress("3ffe::1234:5678", "3ffe::1234:1"), - // IPv4-mapped IPv6 - makeSortableAddress("::ffff:192.168.95.7", "::ffff:192.168.95.1")); - - final List expected = Arrays.asList( - stringToAddress("::1"), // loop back - stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local - stringToAddress("216.58.200.36"), // Ipv4 - stringToAddress("::ffff:192.168.95.7"), // IPv4-mapped IPv6 - stringToAddress("2001::47c1"), // teredo tunneling - stringToAddress("::216.58.200.36"), // IPv4-compatible - stringToAddress("3ffe::1234:5678"), // 6bone - stringToAddress("2404:6800:4008:801::2004"), // global with different scope src - stringToAddress("2404:6800:cafe:801::1"), // global without src addr - stringToAddress("3ffe::1234:5678")); // 6bone without src addr - - Collections.sort(test, new DnsUtils.Rfc6724Comparator()); - - for (int i = 0; i < test.size(); ++i) { - assertEquals(test.get(i).address, expected.get(i)); - } - - // TODO: add more combinations - } - - @Test - public void testV4SortableAddress() { - // Test V4 address - DnsUtils.SortableAddress test = makeSortableAddress("216.58.200.36"); - assertEquals(test.hasSrcAddr, 0); - assertEquals(test.prefixMatchLen, 0); - assertEquals(test.address, stringToAddress("216.58.200.36")); - assertEquals(test.labelMatch, 0); - assertEquals(test.scopeMatch, 0); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 4); - assertEquals(test.precedence, 35); - - // Test V4 loopback address with the same source address - test = makeSortableAddress("127.1.2.3", "127.1.2.3"); - assertEquals(test.hasSrcAddr, 1); - assertEquals(test.prefixMatchLen, 0); - assertEquals(test.address, stringToAddress("127.1.2.3")); - assertEquals(test.labelMatch, 1); - assertEquals(test.scopeMatch, 1); - assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); - assertEquals(test.label, 4); - assertEquals(test.precedence, 35); - } - - @Test - public void testV6SortableAddress() { - // Test global address - DnsUtils.SortableAddress test = makeSortableAddress("2404:6800:4008:801::2004"); - assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 1); - assertEquals(test.precedence, 40); - - // Test global address with global source address - test = makeSortableAddress("2404:6800:4008:801::2004", - "2401:fa00:fc:fd00:6d6c:7199:b8e7:41d6"); - assertEquals(test.address, stringToAddress("2404:6800:4008:801::2004")); - assertEquals(test.hasSrcAddr, 1); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.labelMatch, 1); - assertEquals(test.scopeMatch, 1); - assertEquals(test.label, 1); - assertEquals(test.precedence, 40); - assertEquals(test.prefixMatchLen, 13); - - // Test global address with linklocal source address - test = makeSortableAddress("2404:6800:4008:801::2004", "fe80::c46f:1cff:fe04:39b4"); - assertEquals(test.hasSrcAddr, 1); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.labelMatch, 1); - assertEquals(test.scopeMatch, 0); - assertEquals(test.label, 1); - assertEquals(test.precedence, 40); - assertEquals(test.prefixMatchLen, 0); - - // Test loopback address with the same source address - test = makeSortableAddress("::1", "::1"); - assertEquals(test.hasSrcAddr, 1); - assertEquals(test.prefixMatchLen, 16 * 8); - assertEquals(test.labelMatch, 1); - assertEquals(test.scopeMatch, 1); - assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); - assertEquals(test.label, 0); - assertEquals(test.precedence, 50); - - // Test linklocal address - test = makeSortableAddress("fe80::c46f:1cff:fe04:39b4"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); - assertEquals(test.label, 1); - assertEquals(test.precedence, 40); - - // Test linklocal address - test = makeSortableAddress("fe80::"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_LINKLOCAL); - assertEquals(test.label, 1); - assertEquals(test.precedence, 40); - - // Test 6to4 address - test = makeSortableAddress("2002:c000:0204::"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 2); - assertEquals(test.precedence, 30); - - // Test unique local address - test = makeSortableAddress("fc00::c000:13ab"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 13); - assertEquals(test.precedence, 3); - - // Test teredo tunneling address - test = makeSortableAddress("2001::47c1"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 5); - assertEquals(test.precedence, 5); - - // Test IPv4-compatible addresses - test = makeSortableAddress("::216.58.200.36"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 3); - assertEquals(test.precedence, 1); - - // Test site-local address - test = makeSortableAddress("fec0::cafe:3ab2"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_SITELOCAL); - assertEquals(test.label, 11); - assertEquals(test.precedence, 1); - - // Test 6bone address - test = makeSortableAddress("3ffe::1234:5678"); - assertEquals(test.scope, IPV6_ADDR_SCOPE_GLOBAL); - assertEquals(test.label, 12); - assertEquals(test.precedence, 1); - } -} diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt deleted file mode 100644 index 5006d5345f5f..000000000000 --- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2019 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.util - -import android.content.Context -import android.content.res.Resources -import android.net.ConnectivityResources -import android.net.NetworkCapabilities -import android.net.NetworkCapabilities.MAX_TRANSPORT -import android.net.NetworkCapabilities.TRANSPORT_CELLULAR -import android.net.NetworkCapabilities.TRANSPORT_ETHERNET -import android.net.NetworkCapabilities.TRANSPORT_VPN -import android.net.NetworkCapabilities.TRANSPORT_WIFI -import androidx.test.filters.SmallTest -import com.android.internal.R -import org.junit.After -import org.junit.Assert.assertArrayEquals -import org.junit.Assert.assertEquals -import org.junit.Assert.fail -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito.any -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock - -/** - * Tests for [KeepaliveUtils]. - * - * Build, install and run with: - * atest android.net.util.KeepaliveUtilsTest - */ -@RunWith(JUnit4::class) -@SmallTest -class KeepaliveUtilsTest { - - // Prepare mocked context with given resource strings. - private fun getMockedContextWithStringArrayRes( - id: Int, - name: String, - res: Array? - ): Context { - val mockRes = mock(Resources::class.java) - doReturn(res).`when`(mockRes).getStringArray(eq(id)) - doReturn(id).`when`(mockRes).getIdentifier(eq(name), any(), any()) - - return mock(Context::class.java).apply { - doReturn(mockRes).`when`(this).getResources() - ConnectivityResources.setResourcesContextForTest(this) - } - } - - @After - fun tearDown() { - ConnectivityResources.setResourcesContextForTest(null) - } - - @Test - fun testGetSupportedKeepalives() { - fun assertRunWithException(res: Array?) { - try { - val mockContext = getMockedContextWithStringArrayRes( - R.array.config_networkSupportedKeepaliveCount, - "config_networkSupportedKeepaliveCount", res) - KeepaliveUtils.getSupportedKeepalives(mockContext) - fail("Expected KeepaliveDeviceConfigurationException") - } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) { - } - } - - // Check resource with various invalid format. - assertRunWithException(null) - assertRunWithException(arrayOf(null)) - assertRunWithException(arrayOfNulls(10)) - assertRunWithException(arrayOf("")) - assertRunWithException(arrayOf("3,ABC")) - assertRunWithException(arrayOf("6,3,3")) - assertRunWithException(arrayOf("5")) - - // Check resource with invalid slots value. - assertRunWithException(arrayOf("3,-1")) - - // Check resource with invalid transport type. - assertRunWithException(arrayOf("-1,3")) - assertRunWithException(arrayOf("10,3")) - - // Check valid customization generates expected array. - val validRes = arrayOf("0,3", "1,0", "4,4") - val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0, 0) - - val mockContext = getMockedContextWithStringArrayRes( - R.array.config_networkSupportedKeepaliveCount, - "config_networkSupportedKeepaliveCount", validRes) - val actual = KeepaliveUtils.getSupportedKeepalives(mockContext) - assertArrayEquals(expectedValidRes, actual) - } - - @Test - fun testGetSupportedKeepalivesForNetworkCapabilities() { - // Mock customized supported keepalives for each transport type, and assuming: - // 3 for cellular, - // 6 for wifi, - // 0 for others. - val cust = IntArray(MAX_TRANSPORT + 1).apply { - this[TRANSPORT_CELLULAR] = 3 - this[TRANSPORT_WIFI] = 6 - } - - val nc = NetworkCapabilities() - // Check supported keepalives with single transport type. - nc.transportTypes = intArrayOf(TRANSPORT_CELLULAR) - assertEquals(3, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) - - // Check supported keepalives with multiple transport types. - nc.transportTypes = intArrayOf(TRANSPORT_WIFI, TRANSPORT_VPN) - assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) - - // Check supported keepalives with non-customized transport type. - nc.transportTypes = intArrayOf(TRANSPORT_ETHERNET) - assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)) - - // Check supported keepalives with undefined transport type. - nc.transportTypes = intArrayOf(MAX_TRANSPORT + 1) - try { - KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc) - fail("Expected ArrayIndexOutOfBoundsException") - } catch (expected: ArrayIndexOutOfBoundsException) { - } - } -} diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt deleted file mode 100644 index 25aa6266577e..000000000000 --- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2021 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.util - -import android.content.Context -import android.content.res.Resources -import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER -import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE -import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY -import android.net.ConnectivityResources -import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI -import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE -import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener -import android.provider.Settings -import android.telephony.SubscriptionInfo -import android.telephony.SubscriptionManager -import android.telephony.TelephonyManager -import android.test.mock.MockContentResolver -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.connectivity.resources.R -import com.android.internal.util.test.FakeSettingsProvider -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.argThat -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito.any -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify - -/** - * Tests for [MultinetworkPolicyTracker]. - * - * Build, install and run with: - * atest android.net.util.MultinetworkPolicyTrackerTest - */ -@RunWith(AndroidJUnit4::class) -@SmallTest -class MultinetworkPolicyTrackerTest { - private val resources = mock(Resources::class.java).also { - doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier( - eq("config_networkAvoidBadWifi"), eq("integer"), any()) - doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi) - } - private val telephonyManager = mock(TelephonyManager::class.java) - private val subscriptionManager = mock(SubscriptionManager::class.java).also { - doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt()) - } - private val resolver = MockContentResolver().apply { - addProvider(Settings.AUTHORITY, FakeSettingsProvider()) } - private val context = mock(Context::class.java).also { - doReturn(Context.TELEPHONY_SERVICE).`when`(it) - .getSystemServiceName(TelephonyManager::class.java) - doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE) - doReturn(subscriptionManager).`when`(it) - .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) - doReturn(resolver).`when`(it).contentResolver - doReturn(resources).`when`(it).resources - doReturn(it).`when`(it).createConfigurationContext(any()) - Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1") - ConnectivityResources.setResourcesContextForTest(it) - } - private val tracker = MultinetworkPolicyTracker(context, null /* handler */) - - private fun assertMultipathPreference(preference: Int) { - Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, - preference.toString()) - tracker.updateMeteredMultipathPreference() - assertEquals(preference, tracker.meteredMultipathPreference) - } - - @After - fun tearDown() { - ConnectivityResources.setResourcesContextForTest(null) - } - - @Test - fun testUpdateMeteredMultipathPreference() { - assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER) - assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY) - assertMultipathPreference(MULTIPATH_PREFERENCE_PERFORMANCE) - } - - @Test - fun testUpdateAvoidBadWifi() { - Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") - assertTrue(tracker.updateAvoidBadWifi()) - assertFalse(tracker.avoidBadWifi) - - doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi) - assertTrue(tracker.updateAvoidBadWifi()) - assertTrue(tracker.avoidBadWifi) - } - - @Test - fun testOnActiveDataSubscriptionIdChanged() { - val testSubId = 1000 - val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */, - "TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */, - "123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */, - ""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */, - "1"/* cardString */) - doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId) - - // Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in - // MultinetworkPolicyTracker should be also updated after subId changed. - Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") - Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, - MULTIPATH_PREFERENCE_PERFORMANCE.toString()) - - val listenerCaptor = ArgumentCaptor.forClass( - ActiveDataSubscriptionIdListener::class.java) - verify(telephonyManager, times(1)) - .registerTelephonyCallback(any(), listenerCaptor.capture()) - val listener = listenerCaptor.value - listener.onActiveDataSubscriptionIdChanged(testSubId) - - // Check it get resource value with test sub id. - verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId) - verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 }) - - // Check if avoidBadWifi and meteredMultipathPreference values have been updated. - assertFalse(tracker.avoidBadWifi) - assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference) - } -} diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java deleted file mode 100644 index 3cfecd552967..000000000000 --- a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2015 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.internal.net; - -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.AF_UNIX; -import static android.system.OsConstants.EPERM; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_STREAM; - -import static junit.framework.Assert.assertEquals; - -import static org.junit.Assert.fail; - -import android.system.ErrnoException; -import android.system.Os; - -import androidx.test.runner.AndroidJUnit4; - -import libcore.io.IoUtils; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@androidx.test.filters.SmallTest -public class NetworkUtilsInternalTest { - - private static void expectSocketSuccess(String msg, int domain, int type) { - try { - IoUtils.closeQuietly(Os.socket(domain, type, 0)); - } catch (ErrnoException e) { - fail(msg + e.getMessage()); - } - } - - private static void expectSocketPemissionError(String msg, int domain, int type) { - try { - IoUtils.closeQuietly(Os.socket(domain, type, 0)); - fail(msg); - } catch (ErrnoException e) { - assertEquals(msg, e.errno, EPERM); - } - } - - private static void expectHasNetworking() { - expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", - AF_UNIX, SOCK_STREAM); - expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException", - AF_INET, SOCK_DGRAM); - expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException", - AF_INET6, SOCK_DGRAM); - } - - private static void expectNoNetworking() { - expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException", - AF_UNIX, SOCK_STREAM); - expectSocketPemissionError( - "Creating a AF_INET socket should have thrown ErrnoException(EPERM)", - AF_INET, SOCK_DGRAM); - expectSocketPemissionError( - "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)", - AF_INET6, SOCK_DGRAM); - } - - @Test - public void testSetAllowNetworkingForProcess() { - expectHasNetworking(); - NetworkUtilsInternal.setAllowNetworkingForProcess(false); - expectNoNetworking(); - NetworkUtilsInternal.setAllowNetworkingForProcess(true); - expectHasNetworking(); - } -} diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java deleted file mode 100644 index 46597d19ef1b..000000000000 --- a/tests/net/java/com/android/internal/net/VpnProfileTest.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.net; - -import static com.android.testutils.ParcelUtils.assertParcelSane; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.net.IpSecAlgorithm; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** Unit tests for {@link VpnProfile}. */ -@SmallTest -@RunWith(JUnit4.class) -public class VpnProfileTest { - private static final String DUMMY_PROFILE_KEY = "Test"; - - private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23; - private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24; - - @Test - public void testDefaults() throws Exception { - final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY); - - assertEquals(DUMMY_PROFILE_KEY, p.key); - assertEquals("", p.name); - assertEquals(VpnProfile.TYPE_PPTP, p.type); - assertEquals("", p.server); - assertEquals("", p.username); - assertEquals("", p.password); - assertEquals("", p.dnsServers); - assertEquals("", p.searchDomains); - assertEquals("", p.routes); - assertTrue(p.mppe); - assertEquals("", p.l2tpSecret); - assertEquals("", p.ipsecIdentifier); - assertEquals("", p.ipsecSecret); - assertEquals("", p.ipsecUserCert); - assertEquals("", p.ipsecCaCert); - assertEquals("", p.ipsecServerCert); - assertEquals(null, p.proxy); - assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty()); - assertFalse(p.isBypassable); - assertFalse(p.isMetered); - assertEquals(1360, p.maxMtu); - assertFalse(p.areAuthParamsInline); - assertFalse(p.isRestrictedToTestNetworks); - } - - private VpnProfile getSampleIkev2Profile(String key) { - final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */); - - p.name = "foo"; - p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; - p.server = "bar"; - p.username = "baz"; - p.password = "qux"; - p.dnsServers = "8.8.8.8"; - p.searchDomains = ""; - p.routes = "0.0.0.0/0"; - p.mppe = false; - p.l2tpSecret = ""; - p.ipsecIdentifier = "quux"; - p.ipsecSecret = "quuz"; - p.ipsecUserCert = "corge"; - p.ipsecCaCert = "grault"; - p.ipsecServerCert = "garply"; - p.proxy = null; - p.setAllowedAlgorithms( - Arrays.asList( - IpSecAlgorithm.AUTH_CRYPT_AES_GCM, - IpSecAlgorithm.AUTH_HMAC_SHA512, - IpSecAlgorithm.CRYPT_AES_CBC)); - p.isBypassable = true; - p.isMetered = true; - p.maxMtu = 1350; - p.areAuthParamsInline = true; - - // Not saved, but also not compared. - p.saveLogin = true; - - return p; - } - - @Test - public void testEquals() { - assertEquals( - getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY)); - - final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - modified.maxMtu--; - assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified); - } - - @Test - public void testParcelUnparcel() { - assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23); - } - - @Test - public void testSetInvalidAlgorithmValueDelimiter() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - - try { - profile.setAllowedAlgorithms( - Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test")); - fail("Expected failure due to value separator in algorithm name"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testSetInvalidAlgorithmListDelimiter() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - - try { - profile.setAllowedAlgorithms( - Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test")); - fail("Expected failure due to value separator in algorithm name"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testEncodeDecode() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); - assertEquals(profile, decoded); - } - - @Test - public void testEncodeDecodeTooManyValues() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - final byte[] tooManyValues = - (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes(); - - assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues)); - } - - private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) { - // Sort to ensure when we remove, we can do it from greatest first. - Arrays.sort(missingIndices); - - final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode()); - final List parts = - new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER))); - - // Remove from back first to ensure indexing is consistent. - for (int i = missingIndices.length - 1; i >= 0; i--) { - parts.remove(missingIndices[i]); - } - - return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0])); - } - - @Test - public void testEncodeDecodeInvalidNumberOfValues() { - final String tooFewValues = - getEncodedDecodedIkev2ProfileMissingValues( - ENCODED_INDEX_AUTH_PARAMS_INLINE, - ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); - - assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes())); - } - - @Test - public void testEncodeDecodeMissingIsRestrictedToTestNetworks() { - final String tooFewValues = - getEncodedDecodedIkev2ProfileMissingValues( - ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */); - - // Verify decoding without isRestrictedToTestNetworks defaults to false - final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()); - assertFalse(decoded.isRestrictedToTestNetworks); - } - - @Test - public void testEncodeDecodeLoginsNotSaved() { - final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); - profile.saveLogin = false; - - final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); - assertNotEquals(profile, decoded); - - // Add the username/password back, everything else must be equal. - decoded.username = profile.username; - decoded.password = profile.password; - assertEquals(profile, decoded); - } -} diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java deleted file mode 100644 index d2fbdce9771a..000000000000 --- a/tests/net/java/com/android/internal/util/BitUtilsTest.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 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.internal.util; - -import static com.android.internal.util.BitUtils.bytesToBEInt; -import static com.android.internal.util.BitUtils.bytesToLEInt; -import static com.android.internal.util.BitUtils.getUint16; -import static com.android.internal.util.BitUtils.getUint32; -import static com.android.internal.util.BitUtils.getUint8; -import static com.android.internal.util.BitUtils.packBits; -import static com.android.internal.util.BitUtils.uint16; -import static com.android.internal.util.BitUtils.uint32; -import static com.android.internal.util.BitUtils.uint8; -import static com.android.internal.util.BitUtils.unpackBits; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Random; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class BitUtilsTest { - - @Test - public void testUnsignedByteWideningConversions() { - byte b0 = 0; - byte b1 = 1; - byte bm1 = -1; - assertEquals(0, uint8(b0)); - assertEquals(1, uint8(b1)); - assertEquals(127, uint8(Byte.MAX_VALUE)); - assertEquals(128, uint8(Byte.MIN_VALUE)); - assertEquals(255, uint8(bm1)); - assertEquals(255, uint8((byte)255)); - } - - @Test - public void testUnsignedShortWideningConversions() { - short s0 = 0; - short s1 = 1; - short sm1 = -1; - assertEquals(0, uint16(s0)); - assertEquals(1, uint16(s1)); - assertEquals(32767, uint16(Short.MAX_VALUE)); - assertEquals(32768, uint16(Short.MIN_VALUE)); - assertEquals(65535, uint16(sm1)); - assertEquals(65535, uint16((short)65535)); - } - - @Test - public void testUnsignedShortComposition() { - byte b0 = 0; - byte b1 = 1; - byte b2 = 2; - byte b10 = 10; - byte b16 = 16; - byte b128 = -128; - byte b224 = -32; - byte b255 = -1; - assertEquals(0x0000, uint16(b0, b0)); - assertEquals(0xffff, uint16(b255, b255)); - assertEquals(0x0a01, uint16(b10, b1)); - assertEquals(0x8002, uint16(b128, b2)); - assertEquals(0x01ff, uint16(b1, b255)); - assertEquals(0x80ff, uint16(b128, b255)); - assertEquals(0xe010, uint16(b224, b16)); - } - - @Test - public void testUnsignedIntWideningConversions() { - assertEquals(0, uint32(0)); - assertEquals(1, uint32(1)); - assertEquals(2147483647L, uint32(Integer.MAX_VALUE)); - assertEquals(2147483648L, uint32(Integer.MIN_VALUE)); - assertEquals(4294967295L, uint32(-1)); - assertEquals(4294967295L, uint32((int)4294967295L)); - } - - @Test - public void testBytesToInt() { - assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0))); - assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255))); - assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1))); - assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2))); - assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255))); - assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1))); - - assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0))); - assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1))); - assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255))); - } - - @Test - public void testUnsignedGetters() { - ByteBuffer b = ByteBuffer.allocate(4); - b.putInt(0xffff); - - assertEquals(0x0, getUint8(b, 0)); - assertEquals(0x0, getUint8(b, 1)); - assertEquals(0xff, getUint8(b, 2)); - assertEquals(0xff, getUint8(b, 3)); - - assertEquals(0x0, getUint16(b, 0)); - assertEquals(0xffff, getUint16(b, 2)); - - b.rewind(); - b.putInt(0xffffffff); - assertEquals(0xffffffffL, getUint32(b, 0)); - } - - @Test - public void testBitsPacking() { - BitPackingTestCase[] testCases = { - new BitPackingTestCase(0, ints()), - new BitPackingTestCase(1, ints(0)), - new BitPackingTestCase(2, ints(1)), - new BitPackingTestCase(3, ints(0, 1)), - new BitPackingTestCase(4, ints(2)), - new BitPackingTestCase(6, ints(1, 2)), - new BitPackingTestCase(9, ints(0, 3)), - new BitPackingTestCase(~Long.MAX_VALUE, ints(63)), - new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)), - new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)), - }; - for (BitPackingTestCase tc : testCases) { - int[] got = unpackBits(tc.packedBits); - assertTrue( - "unpackBits(" - + tc.packedBits - + "): expected " - + Arrays.toString(tc.bits) - + " but got " - + Arrays.toString(got), - Arrays.equals(tc.bits, got)); - } - for (BitPackingTestCase tc : testCases) { - long got = packBits(tc.bits); - assertEquals( - "packBits(" - + Arrays.toString(tc.bits) - + "): expected " - + tc.packedBits - + " but got " - + got, - tc.packedBits, - got); - } - - long[] moreTestCases = { - 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(), - }; - for (long l : moreTestCases) { - assertEquals(l, packBits(unpackBits(l))); - } - } - - static byte[] bytes(int b1, int b2, int b3, int b4) { - return new byte[] {b(b1), b(b2), b(b3), b(b4)}; - } - - static byte b(int i) { - return (byte) i; - } - - static int[] ints(int... array) { - return array; - } - - static class BitPackingTestCase { - final int[] bits; - final long packedBits; - - BitPackingTestCase(long packedBits, int[] bits) { - this.bits = bits; - this.packedBits = packedBits; - } - } -} diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java deleted file mode 100644 index d06095a690cf..000000000000 --- a/tests/net/java/com/android/internal/util/RingBufferTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2017 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.internal.util; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RingBufferTest { - - @Test - public void testEmptyRingBuffer() { - RingBuffer buffer = new RingBuffer<>(String.class, 100); - - assertArrayEquals(new String[0], buffer.toArray()); - } - - @Test - public void testIncorrectConstructorArguments() { - try { - RingBuffer buffer = new RingBuffer<>(String.class, -10); - fail("Should not be able to create a negative capacity RingBuffer"); - } catch (IllegalArgumentException expected) { - } - - try { - RingBuffer buffer = new RingBuffer<>(String.class, 0); - fail("Should not be able to create a 0 capacity RingBuffer"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testRingBufferWithNoWrapping() { - RingBuffer buffer = new RingBuffer<>(String.class, 100); - - buffer.append("a"); - buffer.append("b"); - buffer.append("c"); - buffer.append("d"); - buffer.append("e"); - - String[] expected = {"a", "b", "c", "d", "e"}; - assertArrayEquals(expected, buffer.toArray()); - } - - @Test - public void testRingBufferWithCapacity1() { - RingBuffer buffer = new RingBuffer<>(String.class, 1); - - buffer.append("a"); - assertArrayEquals(new String[]{"a"}, buffer.toArray()); - - buffer.append("b"); - assertArrayEquals(new String[]{"b"}, buffer.toArray()); - - buffer.append("c"); - assertArrayEquals(new String[]{"c"}, buffer.toArray()); - - buffer.append("d"); - assertArrayEquals(new String[]{"d"}, buffer.toArray()); - - buffer.append("e"); - assertArrayEquals(new String[]{"e"}, buffer.toArray()); - } - - @Test - public void testRingBufferWithWrapping() { - int capacity = 100; - RingBuffer buffer = new RingBuffer<>(String.class, capacity); - - buffer.append("a"); - buffer.append("b"); - buffer.append("c"); - buffer.append("d"); - buffer.append("e"); - - String[] expected1 = {"a", "b", "c", "d", "e"}; - assertArrayEquals(expected1, buffer.toArray()); - - String[] expected2 = new String[capacity]; - int firstIndex = 0; - int lastIndex = capacity - 1; - - expected2[firstIndex] = "e"; - for (int i = 1; i < capacity; i++) { - buffer.append("x"); - expected2[i] = "x"; - } - assertArrayEquals(expected2, buffer.toArray()); - - buffer.append("x"); - expected2[firstIndex] = "x"; - assertArrayEquals(expected2, buffer.toArray()); - - for (int i = 0; i < 10; i++) { - for (String s : expected2) { - buffer.append(s); - } - } - assertArrayEquals(expected2, buffer.toArray()); - - buffer.append("a"); - expected2[lastIndex] = "a"; - assertArrayEquals(expected2, buffer.toArray()); - } - - @Test - public void testGetNextSlot() { - int capacity = 100; - RingBuffer buffer = new RingBuffer<>(DummyClass1.class, capacity); - - final DummyClass1[] actual = new DummyClass1[capacity]; - final DummyClass1[] expected = new DummyClass1[capacity]; - for (int i = 0; i < capacity; ++i) { - final DummyClass1 obj = buffer.getNextSlot(); - obj.x = capacity * i; - actual[i] = obj; - expected[i] = new DummyClass1(); - expected[i].x = capacity * i; - } - assertArrayEquals(expected, buffer.toArray()); - - for (int i = 0; i < capacity; ++i) { - if (actual[i] != buffer.getNextSlot()) { - fail("getNextSlot() should re-use objects if available"); - } - } - - RingBuffer buffer2 = new RingBuffer<>(DummyClass2.class, capacity); - assertNull("getNextSlot() should return null if the object can't be initiated " - + "(No nullary constructor)", buffer2.getNextSlot()); - - RingBuffer buffer3 = new RingBuffer<>(DummyClass3.class, capacity); - assertNull("getNextSlot() should return null if the object can't be initiated " - + "(Inaccessible class)", buffer3.getNextSlot()); - } - - public static final class DummyClass1 { - int x; - - public boolean equals(Object o) { - if (o instanceof DummyClass1) { - final DummyClass1 other = (DummyClass1) o; - return other.x == this.x; - } - return false; - } - } - - public static final class DummyClass2 { - public DummyClass2(int x) {} - } - - private static final class DummyClass3 {} -} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java deleted file mode 100644 index 63501d7662ed..000000000000 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ /dev/null @@ -1,12792 +0,0 @@ -/* - * Copyright (C) 2012 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 static android.Manifest.permission.CHANGE_NETWORK_STATE; -import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; -import static android.Manifest.permission.DUMP; -import static android.Manifest.permission.LOCAL_MAC_ADDRESS; -import static android.Manifest.permission.NETWORK_FACTORY; -import static android.Manifest.permission.NETWORK_SETTINGS; -import static android.app.PendingIntent.FLAG_IMMUTABLE; -import static android.content.Intent.ACTION_PACKAGE_ADDED; -import static android.content.Intent.ACTION_PACKAGE_REMOVED; -import static android.content.Intent.ACTION_PACKAGE_REPLACED; -import static android.content.Intent.ACTION_USER_ADDED; -import static android.content.Intent.ACTION_USER_REMOVED; -import static android.content.Intent.ACTION_USER_UNLOCKED; -import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; -import static android.content.pm.PackageManager.FEATURE_WIFI; -import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT; -import static android.content.pm.PackageManager.GET_PERMISSIONS; -import static android.content.pm.PackageManager.MATCH_ANY_USER; -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; -import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; -import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; -import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; -import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; -import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; -import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE; -import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT; -import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; -import static android.net.ConnectivityManager.TYPE_ETHERNET; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; -import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; -import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; -import static android.net.ConnectivityManager.TYPE_PROXY; -import static android.net.ConnectivityManager.TYPE_VPN; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; -import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; -import static android.net.NetworkCapabilities.NET_CAPABILITY_BIP; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; -import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; -import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; -import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; -import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; -import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; -import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; -import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; -import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; -import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; -import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VSIM; -import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; -import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; -import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; -import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; -import static android.net.NetworkCapabilities.REDACT_NONE; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; -import static android.net.NetworkScore.KEEP_CONNECTED_FOR_HANDOVER; -import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; -import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; -import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; -import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; -import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; -import static android.net.RouteInfo.RTN_UNREACHABLE; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_REMOVED; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; -import static android.os.Process.INVALID_UID; -import static android.system.OsConstants.IPPROTO_TCP; - -import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType; -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; -import static com.android.testutils.MiscAsserts.assertRunsInAtMost; -import static com.android.testutils.MiscAsserts.assertThrows; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.AdditionalMatchers.aryEq; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.startsWith; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.AppOpsManager; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.usage.NetworkStatsManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.location.LocationManager; -import android.net.CaptivePortalData; -import android.net.ConnectionInfo; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.ConnectivityManager.PacketKeepalive; -import android.net.ConnectivityManager.PacketKeepaliveCallback; -import android.net.ConnectivityManager.TooManyRequestsException; -import android.net.ConnectivityResources; -import android.net.ConnectivitySettingsManager; -import android.net.ConnectivityThread; -import android.net.DataStallReportParcelable; -import android.net.EthernetManager; -import android.net.IConnectivityDiagnosticsCallback; -import android.net.IDnsResolver; -import android.net.INetd; -import android.net.INetworkMonitor; -import android.net.INetworkMonitorCallbacks; -import android.net.IOnCompleteListener; -import android.net.IQosCallback; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.IpSecManager; -import android.net.IpSecManager.UdpEncapsulationSocket; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MatchAllNetworkSpecifier; -import android.net.NativeNetworkConfig; -import android.net.NativeNetworkType; -import android.net.Network; -import android.net.NetworkAgent; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkFactory; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; -import android.net.NetworkPolicyManager; -import android.net.NetworkPolicyManager.NetworkPolicyCallback; -import android.net.NetworkRequest; -import android.net.NetworkScore; -import android.net.NetworkSpecifier; -import android.net.NetworkStack; -import android.net.NetworkStateSnapshot; -import android.net.NetworkTestResultParcelable; -import android.net.OemNetworkPreferences; -import android.net.ProxyInfo; -import android.net.QosCallbackException; -import android.net.QosFilter; -import android.net.QosSession; -import android.net.ResolverParamsParcel; -import android.net.RouteInfo; -import android.net.RouteInfoParcel; -import android.net.SocketKeepalive; -import android.net.TransportInfo; -import android.net.UidRange; -import android.net.UidRangeParcel; -import android.net.UnderlyingNetworkInfo; -import android.net.Uri; -import android.net.VpnManager; -import android.net.VpnTransportInfo; -import android.net.metrics.IpConnectivityLog; -import android.net.networkstack.NetworkStackClientBase; -import android.net.resolv.aidl.Nat64PrefixEventParcel; -import android.net.resolv.aidl.PrivateDnsValidationEventParcel; -import android.net.shared.NetworkMonitorUtils; -import android.net.shared.PrivateDnsConfig; -import android.net.util.MultinetworkPolicyTracker; -import android.os.BadParcelableException; -import android.os.Binder; -import android.os.Build; -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; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; -import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.os.SystemClock; -import android.os.SystemConfigManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.security.Credentials; -import android.system.Os; -import android.telephony.TelephonyManager; -import android.telephony.data.EpsBearerQosSessionAttributes; -import android.telephony.data.NrQosSessionAttributes; -import android.test.mock.MockContentResolver; -import android.text.TextUtils; -import android.util.ArraySet; -import android.util.Log; -import android.util.Pair; -import android.util.Range; -import android.util.SparseArray; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.R; -import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnProfile; -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.net.module.util.ArrayTrackRecord; -import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; -import com.android.server.connectivity.MockableSystemProperties; -import com.android.server.connectivity.Nat464Xlat; -import com.android.server.connectivity.NetworkAgentInfo; -import com.android.server.connectivity.NetworkNotificationManager.NotificationType; -import com.android.server.connectivity.ProxyTracker; -import com.android.server.connectivity.QosCallbackTracker; -import com.android.server.connectivity.Vpn; -import com.android.server.connectivity.VpnProfileStore; -import com.android.server.net.NetworkPinner; -import com.android.testutils.ExceptionUtils; -import com.android.testutils.HandlerUtils; -import com.android.testutils.RecorderCallback.CallbackEntry; -import com.android.testutils.TestableNetworkCallback; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.AdditionalAnswers; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; -import org.mockito.stubbing.Answer; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.net.DatagramSocket; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import kotlin.reflect.KClass; - -/** - * Tests for {@link ConnectivityService}. - * - * Build, install and run with: - * runtest frameworks-net -c com.android.server.ConnectivityServiceTest - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ConnectivityServiceTest { - private static final String TAG = "ConnectivityServiceTest"; - - private static final int TIMEOUT_MS = 500; - // Broadcasts can take a long time to be delivered. The test will not wait for that long unless - // there is a failure, so use a long timeout. - private static final int BROADCAST_TIMEOUT_MS = 30_000; - private static final int TEST_LINGER_DELAY_MS = 400; - private static final int TEST_NASCENT_DELAY_MS = 300; - // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish - // between a LOST callback that arrives immediately and a LOST callback that arrives after - // the linger/nascent timeout. For this, our assertions should run fast enough to leave - // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are - // supposedly fired, and the time we call expectCallback. - private static final int TEST_CALLBACK_TIMEOUT_MS = 250; - // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to - // complete before callbacks are verified. - private static final int TEST_REQUEST_TIMEOUT_MS = 150; - - private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000; - - private static final long TIMESTAMP = 1234L; - - private static final int NET_ID = 110; - private static final int OEM_PREF_ANY_NET_ID = -1; - // Set a non-zero value to verify the flow to set tcp init rwnd value. - private static final int TEST_TCP_INIT_RWND = 60; - - // Used for testing the per-work-profile default network. - private static final int TEST_APP_ID = 103; - private static final int TEST_WORK_PROFILE_USER_ID = 2; - private static final int TEST_WORK_PROFILE_APP_UID = - UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID); - private static final String CLAT_PREFIX = "v4-"; - private static final String MOBILE_IFNAME = "test_rmnet_data0"; - private static final String WIFI_IFNAME = "test_wlan0"; - private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; - private static final String VPN_IFNAME = "tun10042"; - private static final String TEST_PACKAGE_NAME = "com.android.test.package"; - private static final int TEST_PACKAGE_UID = 123; - private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn"; - - private static final String INTERFACE_NAME = "interface"; - - private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/"; - private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/"; - private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT = - "https://android.com/terms/"; - private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER = - "https://example.com/terms/"; - private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; - private static final String TEST_USER_PORTAL_API_URL_CAPPORT = - "https://android.com/user/api/capport/"; - private static final String TEST_FRIENDLY_NAME = "Network friendly name"; - private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; - - private MockContext mServiceContext; - private HandlerThread mCsHandlerThread; - private HandlerThread mVMSHandlerThread; - private ConnectivityService.Dependencies mDeps; - private ConnectivityService mService; - private WrappedConnectivityManager mCm; - private TestNetworkAgentWrapper mWiFiNetworkAgent; - private TestNetworkAgentWrapper mCellNetworkAgent; - private TestNetworkAgentWrapper mEthernetNetworkAgent; - private MockVpn mMockVpn; - private Context mContext; - private NetworkPolicyCallback mPolicyCallback; - private WrappedMultinetworkPolicyTracker mPolicyTracker; - private HandlerThread mAlarmManagerThread; - private TestNetIdManager mNetIdManager; - private QosCallbackMockHelper mQosCallbackMockHelper; - private QosCallbackTracker mQosCallbackTracker; - private VpnManagerService mVpnManagerService; - private TestNetworkCallback mDefaultNetworkCallback; - private TestNetworkCallback mSystemDefaultNetworkCallback; - private TestNetworkCallback mProfileDefaultNetworkCallback; - - // State variables required to emulate NetworkPolicyManagerService behaviour. - private int mBlockedReasons = BLOCKED_REASON_NONE; - - @Mock DeviceIdleInternal mDeviceIdleInternal; - @Mock INetworkManagementService mNetworkManagementService; - @Mock NetworkStatsManager mStatsManager; - @Mock IDnsResolver mMockDnsResolver; - @Mock INetd mMockNetd; - @Mock NetworkStackClientBase mNetworkStack; - @Mock PackageManager mPackageManager; - @Mock UserManager mUserManager; - @Mock NotificationManager mNotificationManager; - @Mock AlarmManager mAlarmManager; - @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback; - @Mock IBinder mIBinder; - @Mock LocationManager mLocationManager; - @Mock AppOpsManager mAppOpsManager; - @Mock TelephonyManager mTelephonyManager; - @Mock MockableSystemProperties mSystemProperties; - @Mock EthernetManager mEthernetManager; - @Mock NetworkPolicyManager mNetworkPolicyManager; - @Mock VpnProfileStore mVpnProfileStore; - @Mock SystemConfigManager mSystemConfigManager; - @Mock Resources mResources; - - private ArgumentCaptor mResolverParamsParcelCaptor = - ArgumentCaptor.forClass(ResolverParamsParcel.class); - - // This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods - // do not go through ConnectivityService but talk to netd directly, so they don't automatically - // reflect the state of our test ConnectivityService. - private class WrappedConnectivityManager extends ConnectivityManager { - private Network mFakeBoundNetwork; - - public synchronized boolean bindProcessToNetwork(Network network) { - mFakeBoundNetwork = network; - return true; - } - - public synchronized Network getBoundNetworkForProcess() { - return mFakeBoundNetwork; - } - - public WrappedConnectivityManager(Context context, ConnectivityService service) { - super(context, service); - } - } - - private class MockContext extends BroadcastInterceptingContext { - private final MockContentResolver mContentResolver; - - @Spy private Resources mInternalResources; - private final LinkedBlockingQueue mStartedActivities = new LinkedBlockingQueue<>(); - - // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant - private final HashMap mMockedPermissions = new HashMap<>(); - - MockContext(Context base, ContentProvider settingsProvider) { - super(base); - - mInternalResources = spy(base.getResources()); - when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes)) - .thenReturn(new String[] { - "wifi,1,1,1,-1,true", - "mobile,0,0,0,-1,true", - "mobile_mms,2,0,2,60000,true", - "mobile_supl,3,0,2,60000,true", - }); - - mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); - } - - @Override - public void startActivityAsUser(Intent intent, UserHandle handle) { - mStartedActivities.offer(intent); - } - - public Intent expectStartActivityIntent(int timeoutMs) { - Intent intent = null; - try { - intent = mStartedActivities.poll(timeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) {} - assertNotNull("Did not receive sign-in intent after " + timeoutMs + "ms", intent); - return intent; - } - - public void expectNoStartActivityIntent(int timeoutMs) { - try { - assertNull("Received unexpected Intent to start activity", - mStartedActivities.poll(timeoutMs, TimeUnit.MILLISECONDS)); - } catch (InterruptedException e) {} - } - - @Override - public ComponentName startService(Intent service) { - final String action = service.getAction(); - if (!VpnConfig.SERVICE_INTERFACE.equals(action)) { - fail("Attempt to start unknown service, action=" + action); - } - return new ComponentName(service.getPackage(), "com.android.test.Service"); - } - - @Override - public Object getSystemService(String name) { - if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; - if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager; - if (Context.USER_SERVICE.equals(name)) return mUserManager; - if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager; - if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager; - if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager; - if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; - if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; - if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; - if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager; - if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; - return super.getSystemService(name); - } - - final HashMap mUserManagers = new HashMap<>(); - @Override - public Context createContextAsUser(UserHandle user, int flags) { - final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this)); - doReturn(user).when(asUser).getUser(); - doAnswer((inv) -> { - final UserManager um = mUserManagers.computeIfAbsent(user, - u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager))); - return um; - }).when(asUser).getSystemService(Context.USER_SERVICE); - return asUser; - } - - public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) { - // This relies on all contexts for a given user returning the same UM mock - final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */) - .getSystemService(UserManager.class); - doReturn(value).when(umMock).isManagedProfile(); - doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier())); - } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } - - @Override - public Resources getResources() { - return mInternalResources; - } - - @Override - public PackageManager getPackageManager() { - return mPackageManager; - } - - private int checkMockedPermission(String permission, Supplier ifAbsent) { - final Integer granted = mMockedPermissions.get(permission); - return granted != null ? granted : ifAbsent.get(); - } - - @Override - public int checkPermission(String permission, int pid, int uid) { - return checkMockedPermission( - permission, () -> super.checkPermission(permission, pid, uid)); - } - - @Override - public int checkCallingOrSelfPermission(String permission) { - return checkMockedPermission( - permission, () -> super.checkCallingOrSelfPermission(permission)); - } - - @Override - public void enforceCallingOrSelfPermission(String permission, String message) { - final Integer granted = mMockedPermissions.get(permission); - if (granted == null) { - super.enforceCallingOrSelfPermission(permission, message); - return; - } - - if (!granted.equals(PERMISSION_GRANTED)) { - throw new SecurityException("[Test] permission denied: " + permission); - } - } - - /** - * Mock checks for the specified permission, and have them behave as per {@code granted}. - * - *

Passing null reverts to default behavior, which does a real permission check on the - * test package. - * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or - * {@link PackageManager#PERMISSION_DENIED}. - */ - public void setPermission(String permission, Integer granted) { - mMockedPermissions.put(permission, granted); - } - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - waitForIdle(mCellNetworkAgent, TIMEOUT_MS); - waitForIdle(mWiFiNetworkAgent, TIMEOUT_MS); - waitForIdle(mEthernetNetworkAgent, TIMEOUT_MS); - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); - } - - private void waitForIdle(TestNetworkAgentWrapper agent, long timeoutMs) { - if (agent == null) { - return; - } - agent.waitForIdle(timeoutMs); - } - - @Test - public void testWaitForIdle() throws Exception { - final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. - - // Tests that waitForIdle returns immediately if the service is already idle. - for (int i = 0; i < attempts; i++) { - waitForIdle(); - } - - // Bring up a network that we can use to send messages to ConnectivityService. - ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - Network n = mWiFiNetworkAgent.getNetwork(); - assertNotNull(n); - - // Tests that calling waitForIdle waits for messages to be processed. - for (int i = 0; i < attempts; i++) { - mWiFiNetworkAgent.setSignalStrength(i); - waitForIdle(); - assertEquals(i, mCm.getNetworkCapabilities(n).getSignalStrength()); - } - } - - // This test has an inherent race condition in it, and cannot be enabled for continuous testing - // or presubmit tests. It is kept for manual runs and documentation purposes. - @Ignore - public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { - // Bring up a network that we can use to send messages to ConnectivityService. - ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - Network n = mWiFiNetworkAgent.getNetwork(); - assertNotNull(n); - - // Ensure that not calling waitForIdle causes a race condition. - final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. - for (int i = 0; i < attempts; i++) { - mWiFiNetworkAgent.setSignalStrength(i); - if (i != mCm.getNetworkCapabilities(n).getSignalStrength()) { - // We hit a race condition, as expected. Pass the test. - return; - } - } - - // No race? There is a bug in this test. - fail("expected race condition at least once in " + attempts + " attempts"); - } - - private class TestNetworkAgentWrapper extends NetworkAgentWrapper { - private static final int VALIDATION_RESULT_INVALID = 0; - - private static final long DATA_STALL_TIMESTAMP = 10L; - private static final int DATA_STALL_DETECTION_METHOD = 1; - - private INetworkMonitor mNetworkMonitor; - private INetworkMonitorCallbacks mNmCallbacks; - private int mNmValidationResult = VALIDATION_RESULT_INVALID; - private int mProbesCompleted; - private int mProbesSucceeded; - private String mNmValidationRedirectUrl = null; - private boolean mNmProvNotificationRequested = false; - private Runnable mCreatedCallback; - private Runnable mUnwantedCallback; - private Runnable mDisconnectedCallback; - - private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); - // Contains the redirectUrl from networkStatus(). Before reading, wait for - // mNetworkStatusReceived. - private String mRedirectUrl; - - TestNetworkAgentWrapper(int transport) throws Exception { - this(transport, new LinkProperties(), null); - } - - TestNetworkAgentWrapper(int transport, LinkProperties linkProperties) - throws Exception { - this(transport, linkProperties, null); - } - - private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties, - NetworkCapabilities ncTemplate) throws Exception { - super(transport, linkProperties, ncTemplate, mServiceContext); - - // Waits for the NetworkAgent to be registered, which includes the creation of the - // NetworkMonitor. - waitForIdle(TIMEOUT_MS); - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS); - } - - @Override - protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties, - NetworkAgentConfig nac) throws Exception { - mNetworkMonitor = mock(INetworkMonitor.class); - - final Answer validateAnswer = inv -> { - new Thread(ignoreExceptions(this::onValidationRequested)).start(); - return null; - }; - - doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any()); - doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt()); - - final ArgumentCaptor nmNetworkCaptor = ArgumentCaptor.forClass(Network.class); - final ArgumentCaptor nmCbCaptor = - ArgumentCaptor.forClass(INetworkMonitorCallbacks.class); - doNothing().when(mNetworkStack).makeNetworkMonitor( - nmNetworkCaptor.capture(), - any() /* name */, - nmCbCaptor.capture()); - - final InstrumentedNetworkAgent na = - new InstrumentedNetworkAgent(this, linkProperties, nac) { - @Override - public void networkStatus(int status, String redirectUrl) { - mRedirectUrl = redirectUrl; - mNetworkStatusReceived.open(); - } - - @Override - public void onNetworkCreated() { - super.onNetworkCreated(); - if (mCreatedCallback != null) mCreatedCallback.run(); - } - - @Override - public void onNetworkUnwanted() { - super.onNetworkUnwanted(); - if (mUnwantedCallback != null) mUnwantedCallback.run(); - } - - @Override - public void onNetworkDestroyed() { - super.onNetworkDestroyed(); - if (mDisconnectedCallback != null) mDisconnectedCallback.run(); - } - }; - - assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId); - mNmCallbacks = nmCbCaptor.getValue(); - - mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor); - - return na; - } - - private void onValidationRequested() throws Exception { - if (mNmProvNotificationRequested - && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) { - mNmCallbacks.hideProvisioningNotification(); - mNmProvNotificationRequested = false; - } - - mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded); - final NetworkTestResultParcelable p = new NetworkTestResultParcelable(); - p.result = mNmValidationResult; - p.probesAttempted = mProbesCompleted; - p.probesSucceeded = mProbesSucceeded; - p.redirectUrl = mNmValidationRedirectUrl; - p.timestampMillis = TIMESTAMP; - mNmCallbacks.notifyNetworkTestedWithExtras(p); - - if (mNmValidationRedirectUrl != null) { - mNmCallbacks.showProvisioningNotification( - "test_provisioning_notif_action", TEST_PACKAGE_NAME); - mNmProvNotificationRequested = true; - } - } - - /** - * Connect without adding any internet capability. - */ - public void connectWithoutInternet() { - super.connect(); - } - - /** - * Transition this NetworkAgent to CONNECTED state with NET_CAPABILITY_INTERNET. - * @param validated Indicate if network should pretend to be validated. - */ - public void connect(boolean validated) { - connect(validated, true, false /* isStrictMode */); - } - - /** - * Transition this NetworkAgent to CONNECTED state. - * @param validated Indicate if network should pretend to be validated. - * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET. - */ - public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { - assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET)); - - ConnectivityManager.NetworkCallback callback = null; - final ConditionVariable validatedCv = new ConditionVariable(); - if (validated) { - setNetworkValid(isStrictMode); - NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(getNetworkCapabilities().getTransportTypes()[0]) - .clearCapabilities() - .build(); - callback = new ConnectivityManager.NetworkCallback() { - public void onCapabilitiesChanged(Network network, - NetworkCapabilities networkCapabilities) { - if (network.equals(getNetwork()) && - networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { - validatedCv.open(); - } - } - }; - mCm.registerNetworkCallback(request, callback); - } - if (hasInternet) { - addCapability(NET_CAPABILITY_INTERNET); - } - - connectWithoutInternet(); - - if (validated) { - // Wait for network to validate. - waitFor(validatedCv); - setNetworkInvalid(isStrictMode); - } - - if (callback != null) mCm.unregisterNetworkCallback(callback); - } - - public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) { - setNetworkPortal(redirectUrl, isStrictMode); - connect(false, true /* hasInternet */, isStrictMode); - } - - public void connectWithPartialConnectivity() { - setNetworkPartial(); - connect(false); - } - - public void connectWithPartialValidConnectivity(boolean isStrictMode) { - setNetworkPartialValid(isStrictMode); - connect(false, true /* hasInternet */, isStrictMode); - } - - void setNetworkValid(boolean isStrictMode) { - mNmValidationResult = NETWORK_VALIDATION_RESULT_VALID; - mNmValidationRedirectUrl = null; - int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS; - if (isStrictMode) { - probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS; - } - // The probesCompleted equals to probesSucceeded for the case of valid network, so put - // the same value into two different parameter of the method. - setProbesStatus(probesSucceeded, probesSucceeded); - } - - void setNetworkInvalid(boolean isStrictMode) { - mNmValidationResult = VALIDATION_RESULT_INVALID; - mNmValidationRedirectUrl = null; - int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS - | NETWORK_VALIDATION_PROBE_HTTP; - int probesSucceeded = 0; - // If the isStrictMode is true, it means the network is invalid when NetworkMonitor - // tried to validate the private DNS but failed. - if (isStrictMode) { - probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP; - probesSucceeded = probesCompleted; - probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; - } - setProbesStatus(probesCompleted, probesSucceeded); - } - - void setNetworkPortal(String redirectUrl, boolean isStrictMode) { - setNetworkInvalid(isStrictMode); - mNmValidationRedirectUrl = redirectUrl; - // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP - // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet. - int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; - int probesSucceeded = VALIDATION_RESULT_INVALID; - if (isStrictMode) { - probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; - } - setProbesStatus(probesCompleted, probesSucceeded); - } - - void setNetworkPartial() { - mNmValidationResult = NETWORK_VALIDATION_RESULT_PARTIAL; - mNmValidationRedirectUrl = null; - int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS - | NETWORK_VALIDATION_PROBE_FALLBACK; - int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK; - setProbesStatus(probesCompleted, probesSucceeded); - } - - void setNetworkPartialValid(boolean isStrictMode) { - setNetworkPartial(); - mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID; - int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS - | NETWORK_VALIDATION_PROBE_HTTP; - int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP; - // Suppose the partial network cannot pass the private DNS validation as well, so only - // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded. - if (isStrictMode) { - probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS; - } - setProbesStatus(probesCompleted, probesSucceeded); - } - - void setProbesStatus(int probesCompleted, int probesSucceeded) { - mProbesCompleted = probesCompleted; - mProbesSucceeded = probesSucceeded; - } - - void notifyCapportApiDataChanged(CaptivePortalData data) { - try { - mNmCallbacks.notifyCaptivePortalDataChanged(data); - } catch (RemoteException e) { - throw new AssertionError("This cannot happen", e); - } - } - - public String waitForRedirectUrl() { - assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS)); - return mRedirectUrl; - } - - public void expectDisconnected() { - expectDisconnected(TIMEOUT_MS); - } - - public void expectPreventReconnectReceived() { - expectPreventReconnectReceived(TIMEOUT_MS); - } - - void notifyDataStallSuspected() throws Exception { - final DataStallReportParcelable p = new DataStallReportParcelable(); - p.detectionMethod = DATA_STALL_DETECTION_METHOD; - p.timestampMillis = DATA_STALL_TIMESTAMP; - mNmCallbacks.notifyDataStallSuspected(p); - } - - public void setCreatedCallback(Runnable r) { - mCreatedCallback = r; - } - - public void setUnwantedCallback(Runnable r) { - mUnwantedCallback = r; - } - - public void setDisconnectedCallback(Runnable r) { - mDisconnectedCallback = r; - } - } - - /** - * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove - * operations have been processed and test for them. - */ - private static class MockNetworkFactory extends NetworkFactory { - private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false); - - static class RequestEntry { - @NonNull - public final NetworkRequest request; - - RequestEntry(@NonNull final NetworkRequest request) { - this.request = request; - } - - static final class Add extends RequestEntry { - Add(@NonNull final NetworkRequest request) { - super(request); - } - } - - static final class Remove extends RequestEntry { - Remove(@NonNull final NetworkRequest request) { - super(request); - } - } - - @Override - public String toString() { - return "RequestEntry [ " + getClass().getName() + " : " + request + " ]"; - } - } - - // History of received requests adds and removes. - private final ArrayTrackRecord.ReadHead mRequestHistory = - new ArrayTrackRecord().newReadHead(); - - private static T failIfNull(@Nullable final T obj, @Nullable final String message) { - if (null == obj) fail(null != message ? message : "Must not be null"); - return obj; - } - - public RequestEntry.Add expectRequestAdd() { - return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS, - it -> it instanceof RequestEntry.Add), "Expected request add"); - } - - public void expectRequestAdds(final int count) { - for (int i = count; i > 0; --i) { - expectRequestAdd(); - } - } - - public RequestEntry.Remove expectRequestRemove() { - return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS, - it -> it instanceof RequestEntry.Remove), "Expected request remove"); - } - - public void expectRequestRemoves(final int count) { - for (int i = count; i > 0; --i) { - expectRequestRemove(); - } - } - - // Used to collect the networks requests managed by this factory. This is a duplicate of - // the internal information stored in the NetworkFactory (which is private). - private SparseArray mNetworkRequests = new SparseArray<>(); - private final HandlerThread mHandlerSendingRequests; - - public MockNetworkFactory(Looper looper, Context context, String logTag, - NetworkCapabilities filter, HandlerThread threadSendingRequests) { - super(looper, context, logTag, filter); - mHandlerSendingRequests = threadSendingRequests; - } - - public int getMyRequestCount() { - return getRequestCount(); - } - - protected void startNetwork() { - mNetworkStarted.set(true); - } - - protected void stopNetwork() { - mNetworkStarted.set(false); - } - - public boolean getMyStartRequested() { - return mNetworkStarted.get(); - } - - - @Override - protected void needNetworkFor(NetworkRequest request) { - mNetworkRequests.put(request.requestId, request); - super.needNetworkFor(request); - mRequestHistory.add(new RequestEntry.Add(request)); - } - - @Override - protected void releaseNetworkFor(NetworkRequest request) { - mNetworkRequests.remove(request.requestId); - super.releaseNetworkFor(request); - mRequestHistory.add(new RequestEntry.Remove(request)); - } - - public void assertRequestCountEquals(final int count) { - assertEquals(count, getMyRequestCount()); - } - - @Override - public void terminate() { - super.terminate(); - // Make sure there are no remaining requests unaccounted for. - HandlerUtils.waitForIdle(mHandlerSendingRequests, TIMEOUT_MS); - assertNull(mRequestHistory.poll(0, r -> true)); - } - - // Trigger releasing the request as unfulfillable - public void triggerUnfulfillable(NetworkRequest r) { - super.releaseRequestAsUnfulfillableByAnyFactory(r); - } - - public void assertNoRequestChanged() { - assertNull(mRequestHistory.poll(0, r -> true)); - } - } - - private Set uidRangesForUids(int... uids) { - final ArraySet ranges = new ArraySet<>(); - for (final int uid : uids) { - ranges.add(new UidRange(uid, uid)); - } - return ranges; - } - - private static Looper startHandlerThreadAndReturnLooper() { - final HandlerThread handlerThread = new HandlerThread("MockVpnThread"); - handlerThread.start(); - return handlerThread.getLooper(); - } - - private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork { - // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does - // not inherit from NetworkAgent. - private TestNetworkAgentWrapper mMockNetworkAgent; - private boolean mAgentRegistered = false; - - private int mVpnType = VpnManager.TYPE_VPN_SERVICE; - private UnderlyingNetworkInfo mUnderlyingNetworkInfo; - - // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started. - // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the - // test expects two starts in a row, or even if the production code calls start twice in a - // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into - // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has - // extensive access into the internals of Vpn. - private ConditionVariable mStartLegacyVpnCv = new ConditionVariable(); - private ConditionVariable mStopVpnRunnerCv = new ConditionVariable(); - - public MockVpn(int userId) { - super(startHandlerThreadAndReturnLooper(), mServiceContext, - new Dependencies() { - @Override - public boolean isCallerSystem() { - return true; - } - - @Override - public DeviceIdleInternal getDeviceIdleInternal() { - return mDeviceIdleInternal; - } - }, - mNetworkManagementService, mMockNetd, userId, mVpnProfileStore); - } - - public void setUids(Set uids) { - mNetworkCapabilities.setUids(UidRange.toIntRanges(uids)); - if (mAgentRegistered) { - mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true); - } - } - - public void setVpnType(int vpnType) { - mVpnType = vpnType; - } - - @Override - public Network getNetwork() { - return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork(); - } - - @Override - public int getActiveVpnType() { - return mVpnType; - } - - private LinkProperties makeLinkProperties() { - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(VPN_IFNAME); - return lp; - } - - private void registerAgent(boolean isAlwaysMetered, Set uids, LinkProperties lp) - throws Exception { - if (mAgentRegistered) throw new IllegalStateException("already registered"); - updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent"); - mConfig = new VpnConfig(); - mConfig.session = "MySession12345"; - setUids(uids); - if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); - mInterface = VPN_IFNAME; - mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), - mConfig.session)); - mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, - mNetworkCapabilities); - mMockNetworkAgent.waitForIdle(TIMEOUT_MS); - - verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), - eq(toUidRangeStableParcels(uids))); - verify(mMockNetd, never()) - .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any()); - mAgentRegistered = true; - verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId, - !mMockNetworkAgent.isBypassableVpn(), mVpnType)); - updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent"); - mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); - mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); - } - - private void registerAgent(Set uids) throws Exception { - registerAgent(false /* isAlwaysMetered */, uids, makeLinkProperties()); - } - - private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { - mMockNetworkAgent.connect(validated, hasInternet, isStrictMode); - } - - private void connect(boolean validated) { - mMockNetworkAgent.connect(validated); - } - - private TestNetworkAgentWrapper getAgent() { - return mMockNetworkAgent; - } - - public void establish(LinkProperties lp, int uid, Set ranges, boolean validated, - boolean hasInternet, boolean isStrictMode) throws Exception { - mNetworkCapabilities.setOwnerUid(uid); - mNetworkCapabilities.setAdministratorUids(new int[]{uid}); - registerAgent(false, ranges, lp); - connect(validated, hasInternet, isStrictMode); - waitForIdle(); - } - - public void establish(LinkProperties lp, int uid, Set ranges) throws Exception { - establish(lp, uid, ranges, true, true, false); - } - - public void establishForMyUid(LinkProperties lp) throws Exception { - final int uid = Process.myUid(); - establish(lp, uid, uidRangesForUids(uid), true, true, false); - } - - public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode) - throws Exception { - final int uid = Process.myUid(); - establish(makeLinkProperties(), uid, uidRangesForUids(uid), validated, hasInternet, - isStrictMode); - } - - public void establishForMyUid() throws Exception { - establishForMyUid(makeLinkProperties()); - } - - public void sendLinkProperties(LinkProperties lp) { - mMockNetworkAgent.sendLinkProperties(lp); - } - - public void disconnect() { - if (mMockNetworkAgent != null) { - mMockNetworkAgent.disconnect(); - updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect"); - } - mAgentRegistered = false; - setUids(null); - // Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on. - mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); - mInterface = null; - } - - @Override - public void startLegacyVpnRunner() { - mStartLegacyVpnCv.open(); - } - - public void expectStartLegacyVpnRunner() { - assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms", - mStartLegacyVpnCv.block(TIMEOUT_MS)); - - // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just - // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect - // that the VpnRunner is stopped and immediately restarted by calling - // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back. - mStopVpnRunnerCv = new ConditionVariable(); - } - - @Override - public void stopVpnRunnerPrivileged() { - if (mVpnRunner != null) { - super.stopVpnRunnerPrivileged(); - disconnect(); - mStartLegacyVpnCv = new ConditionVariable(); - } - mVpnRunner = null; - mStopVpnRunnerCv.open(); - } - - public void expectStopVpnRunnerPrivileged() { - assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms", - mStopVpnRunnerCv.block(TIMEOUT_MS)); - } - - @Override - public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { - if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo; - - return super.getUnderlyingNetworkInfo(); - } - - private synchronized void setUnderlyingNetworkInfo( - UnderlyingNetworkInfo underlyingNetworkInfo) { - mUnderlyingNetworkInfo = underlyingNetworkInfo; - } - } - - private UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set ranges) { - return ranges.stream().map( - r -> new UidRangeParcel(r.start, r.stop)).toArray(UidRangeParcel[]::new); - } - - private VpnManagerService makeVpnManagerService() { - final VpnManagerService.Dependencies deps = new VpnManagerService.Dependencies() { - public int getCallingUid() { - return mDeps.getCallingUid(); - } - - public HandlerThread makeHandlerThread() { - return mVMSHandlerThread; - } - - @Override - public VpnProfileStore getVpnProfileStore() { - return mVpnProfileStore; - } - - public INetd getNetd() { - return mMockNetd; - } - - public INetworkManagementService getINetworkManagementService() { - return mNetworkManagementService; - } - }; - return new VpnManagerService(mServiceContext, deps); - } - - private void assertVpnTransportInfo(NetworkCapabilities nc, int type) { - assertNotNull(nc); - final TransportInfo ti = nc.getTransportInfo(); - assertTrue("VPN TransportInfo is not a VpnTransportInfo: " + ti, - ti instanceof VpnTransportInfo); - assertEquals(type, ((VpnTransportInfo) ti).getType()); - - } - - private void processBroadcast(Intent intent) { - mServiceContext.sendBroadcast(intent); - HandlerUtils.waitForIdle(mVMSHandlerThread, TIMEOUT_MS); - waitForIdle(); - } - - private void mockVpn(int uid) { - synchronized (mVpnManagerService.mVpns) { - int userId = UserHandle.getUserId(uid); - mMockVpn = new MockVpn(userId); - // Every running user always has a Vpn in the mVpns array, even if no VPN is running. - mVpnManagerService.mVpns.put(userId, mMockVpn); - } - } - - private void mockUidNetworkingBlocked() { - doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1)) - ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); - } - - private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) { - final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK); - if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) { - return true; - } - if (meteredNetwork) { - return blockedReasons != BLOCKED_REASON_NONE; - } - return false; - } - - private void setBlockedReasonChanged(int blockedReasons) { - mBlockedReasons = blockedReasons; - mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons); - } - - private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) { - return mService.getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd; - } - - private static class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { - volatile boolean mConfigRestrictsAvoidBadWifi; - volatile int mConfigMeteredMultipathPreference; - - WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { - super(c, h, r); - } - - @Override - public boolean configRestrictsAvoidBadWifi() { - return mConfigRestrictsAvoidBadWifi; - } - - @Override - public int configMeteredMultipathPreference() { - return mConfigMeteredMultipathPreference; - } - } - - /** - * Wait up to TIMEOUT_MS for {@code conditionVariable} to open. - * Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens. - */ - static private void waitFor(ConditionVariable conditionVariable) { - if (conditionVariable.block(TIMEOUT_MS)) { - return; - } - fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms"); - } - - private T doAsUid(final int uid, @NonNull final Supplier what) { - when(mDeps.getCallingUid()).thenReturn(uid); - try { - return what.get(); - } finally { - returnRealCallingUid(); - } - } - - private void doAsUid(final int uid, @NonNull final Runnable what) { - doAsUid(uid, () -> { - what.run(); return Void.TYPE; - }); - } - - private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback, - int uid) { - doAsUid(uid, () -> { - mCm.registerNetworkCallback(request, callback); - }); - } - - private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback, - final int uid) { - doAsUid(uid, () -> { - mCm.registerDefaultNetworkCallback(callback); - waitForIdle(); - }); - } - - private interface ExceptionalRunnable { - void run() throws Exception; - } - - private void withPermission(String permission, ExceptionalRunnable r) throws Exception { - if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { - r.run(); - return; - } - try { - mServiceContext.setPermission(permission, PERMISSION_GRANTED); - r.run(); - } finally { - mServiceContext.setPermission(permission, PERMISSION_DENIED); - } - } - - private static final int PRIMARY_USER = 0; - private static final UidRange PRIMARY_UIDRANGE = - UidRange.createForUser(UserHandle.of(PRIMARY_USER)); - private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100); - private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101); - private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043); - private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "", - UserInfo.FLAG_PRIMARY); - private static final UserHandle PRIMARY_USER_HANDLE = new UserHandle(PRIMARY_USER); - - private static final int RESTRICTED_USER = 1; - private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "", - UserInfo.FLAG_RESTRICTED); - static { - RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER; - } - - @Before - public void setUp() throws Exception { - mNetIdManager = new TestNetIdManager(); - - mContext = InstrumentationRegistry.getContext(); - - MockitoAnnotations.initMocks(this); - - when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE)); - when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO); - // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context - // it was started from, i.e., PRIMARY_USER. - when(mUserManager.canHaveRestrictedProfile()).thenReturn(true); - when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO); - - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; - when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) - .thenReturn(applicationInfo); - when(mPackageManager.getTargetSdkVersion(anyString())) - .thenReturn(applicationInfo.targetSdkVersion); - when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); - - // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. - // http://b/25897652 . - if (Looper.myLooper() == null) { - Looper.prepare(); - } - mockDefaultPackages(); - mockHasSystemFeature(FEATURE_WIFI, true); - mockHasSystemFeature(FEATURE_WIFI_DIRECT, true); - doReturn(true).when(mTelephonyManager).isDataCapable(); - - FakeSettingsProvider.clearSettingsProvider(); - mServiceContext = new MockContext(InstrumentationRegistry.getContext(), - new FakeSettingsProvider()); - mServiceContext.setUseRegisteredHandlers(true); - - mAlarmManagerThread = new HandlerThread("TestAlarmManager"); - mAlarmManagerThread.start(); - initAlarmManager(mAlarmManager, mAlarmManagerThread.getThreadHandler()); - - mCsHandlerThread = new HandlerThread("TestConnectivityService"); - mVMSHandlerThread = new HandlerThread("TestVpnManagerService"); - mDeps = makeDependencies(); - returnRealCallingUid(); - mService = new ConnectivityService(mServiceContext, - mMockDnsResolver, - mock(IpConnectivityLog.class), - mMockNetd, - mDeps); - mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; - mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; - verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); - - final ArgumentCaptor policyCallbackCaptor = - ArgumentCaptor.forClass(NetworkPolicyCallback.class); - verify(mNetworkPolicyManager).registerNetworkPolicyCallback(any(), - policyCallbackCaptor.capture()); - mPolicyCallback = policyCallbackCaptor.getValue(); - - // Create local CM before sending system ready so that we can answer - // getSystemService() correctly. - mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); - mService.systemReadyInternal(); - mVpnManagerService = makeVpnManagerService(); - mVpnManagerService.systemReady(); - mockVpn(Process.myUid()); - mCm.bindProcessToNetwork(null); - mQosCallbackTracker = mock(QosCallbackTracker.class); - - // Ensure that the default setting for Captive Portals is used for most tests - setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT); - setAlwaysOnNetworks(false); - setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); - } - - private void returnRealCallingUid() { - doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid(); - } - - private ConnectivityService.Dependencies makeDependencies() { - doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false); - final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); - doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); - doReturn(mNetIdManager).when(deps).makeNetIdManager(); - doReturn(mNetworkStack).when(deps).getNetworkStack(); - doReturn(mSystemProperties).when(deps).getSystemProperties(); - doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); - doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any()); - doAnswer(inv -> { - mPolicyTracker = new WrappedMultinetworkPolicyTracker( - inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); - return mPolicyTracker; - }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any()); - doReturn(true).when(deps).getCellular464XlatEnabled(); - - doReturn(60000).when(mResources).getInteger(R.integer.config_networkTransitionTimeout); - doReturn("").when(mResources).getString(R.string.config_networkCaptivePortalServerUrl); - doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray( - R.array.config_wakeonlan_supported_interfaces); - doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray( - R.array.config_networkSupportedKeepaliveCount); - doReturn(new String[0]).when(mResources).getStringArray( - R.array.config_networkNotifySwitches); - doReturn(new int[]{10, 11, 12, 14, 15}).when(mResources).getIntArray( - R.array.config_protectedNetworks); - // We don't test the actual notification value strings, so just return an empty array. - // It doesn't matter what the values are as long as it's not null. - doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name); - - doReturn(R.array.config_networkSupportedKeepaliveCount).when(mResources) - .getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any()); - doReturn(R.array.network_switch_type_name).when(mResources) - .getIdentifier(eq("network_switch_type_name"), eq("array"), any()); - - - final ConnectivityResources connRes = mock(ConnectivityResources.class); - doReturn(mResources).when(connRes).get(); - doReturn(connRes).when(deps).getResources(any()); - - final Context mockResContext = mock(Context.class); - doReturn(mResources).when(mockResContext).getResources(); - ConnectivityResources.setResourcesContextForTest(mockResContext); - - return deps; - } - - private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) { - doAnswer(inv -> { - final long when = inv.getArgument(1); - final WakeupMessage wakeupMsg = inv.getArgument(3); - final Handler handler = inv.getArgument(4); - - long delayMs = when - SystemClock.elapsedRealtime(); - if (delayMs < 0) delayMs = 0; - if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) { - fail("Attempting to send msg more than " + UNREASONABLY_LONG_ALARM_WAIT_MS - + "ms into the future: " + delayMs); - } - alarmHandler.postDelayed(() -> handler.post(wakeupMsg::onAlarm), wakeupMsg /* token */, - delayMs); - - return null; - }).when(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(), - any(WakeupMessage.class), any()); - - doAnswer(inv -> { - final WakeupMessage wakeupMsg = inv.getArgument(0); - alarmHandler.removeCallbacksAndMessages(wakeupMsg /* token */); - return null; - }).when(am).cancel(any(WakeupMessage.class)); - } - - @After - public void tearDown() throws Exception { - unregisterDefaultNetworkCallbacks(); - maybeTearDownEnterpriseNetwork(); - setAlwaysOnNetworks(false); - if (mCellNetworkAgent != null) { - mCellNetworkAgent.disconnect(); - mCellNetworkAgent = null; - } - if (mWiFiNetworkAgent != null) { - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent = null; - } - if (mEthernetNetworkAgent != null) { - mEthernetNetworkAgent.disconnect(); - mEthernetNetworkAgent = null; - } - - if (mQosCallbackMockHelper != null) { - mQosCallbackMockHelper.tearDown(); - mQosCallbackMockHelper = null; - } - mMockVpn.disconnect(); - waitForIdle(); - - FakeSettingsProvider.clearSettingsProvider(); - ConnectivityResources.setResourcesContextForTest(null); - - mCsHandlerThread.quitSafely(); - mAlarmManagerThread.quitSafely(); - } - - private void mockDefaultPackages() throws Exception { - final String myPackageName = mContext.getPackageName(); - final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo( - myPackageName, PackageManager.GET_PERMISSIONS); - when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn( - new String[] {myPackageName}); - when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(), - eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo); - - when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - Arrays.asList(new PackageInfo[] { - buildPackageInfo(/* SYSTEM */ false, APP1_UID), - buildPackageInfo(/* SYSTEM */ false, APP2_UID), - buildPackageInfo(/* SYSTEM */ false, VPN_UID) - })); - - // Create a fake always-on VPN package. - final int userId = UserHandle.getCallingUserId(); - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; // Always-on supported in N+. - when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(), - eq(userId))).thenReturn(applicationInfo); - - // Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy. - ResolveInfo rInfo = new ResolveInfo(); - rInfo.serviceInfo = new ServiceInfo(); - rInfo.serviceInfo.metaData = new Bundle(); - final List services = Arrays.asList(new ResolveInfo[]{rInfo}); - when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA), - eq(userId))).thenReturn(services); - when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId)) - .thenReturn(Process.myUid()); - when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId)) - .thenReturn(VPN_UID); - } - - private void verifyActiveNetwork(int transport) { - // Test getActiveNetworkInfo() - assertNotNull(mCm.getActiveNetworkInfo()); - assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType()); - // Test getActiveNetwork() - assertNotNull(mCm.getActiveNetwork()); - assertEquals(mCm.getActiveNetwork(), mCm.getActiveNetworkForUid(Process.myUid())); - if (!NetworkCapabilities.isValidTransport(transport)) { - throw new IllegalStateException("Unknown transport " + transport); - } - switch (transport) { - case TRANSPORT_WIFI: - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - break; - case TRANSPORT_CELLULAR: - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - break; - case TRANSPORT_ETHERNET: - assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - break; - default: - break; - } - // Test getNetworkInfo(Network) - assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork())); - assertEquals(transportToLegacyType(transport), - mCm.getNetworkInfo(mCm.getActiveNetwork()).getType()); - assertNotNull(mCm.getActiveNetworkInfoForUid(Process.myUid())); - // Test getNetworkCapabilities(Network) - assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork())); - assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport)); - } - - private void verifyNoNetwork() { - waitForIdle(); - // Test getActiveNetworkInfo() - assertNull(mCm.getActiveNetworkInfo()); - // Test getActiveNetwork() - assertNull(mCm.getActiveNetwork()); - assertNull(mCm.getActiveNetworkForUid(Process.myUid())); - // Test getAllNetworks() - assertEmpty(mCm.getAllNetworks()); - assertEmpty(mCm.getAllNetworkStateSnapshots()); - } - - /** - * Class to simplify expecting broadcasts using BroadcastInterceptingContext. - * Ensures that the receiver is unregistered after the expected broadcast is received. This - * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs - * the receivers' receive method while iterating over the list of receivers, and unregistering - * the receiver during iteration throws ConcurrentModificationException. - */ - private class ExpectedBroadcast extends CompletableFuture { - private final BroadcastReceiver mReceiver; - - ExpectedBroadcast(BroadcastReceiver receiver) { - mReceiver = receiver; - } - - public Intent expectBroadcast(int timeoutMs) throws Exception { - try { - return get(timeoutMs, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - fail("Expected broadcast not received after " + timeoutMs + " ms"); - return null; - } finally { - mServiceContext.unregisterReceiver(mReceiver); - } - } - - public Intent expectBroadcast() throws Exception { - return expectBroadcast(BROADCAST_TIMEOUT_MS); - } - - public void expectNoBroadcast(int timeoutMs) throws Exception { - waitForIdle(); - try { - final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS); - fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras()); - } catch (TimeoutException expected) { - } finally { - mServiceContext.unregisterReceiver(mReceiver); - } - } - } - - /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */ - private ExpectedBroadcast registerConnectivityBroadcast(final int count) { - return registerConnectivityBroadcastThat(count, intent -> true); - } - - private ExpectedBroadcast registerConnectivityBroadcastThat(final int count, - @NonNull final Predicate filter) { - final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION); - // AtomicReference allows receiver to access expected even though it is constructed later. - final AtomicReference expectedRef = new AtomicReference<>(); - final BroadcastReceiver receiver = new BroadcastReceiver() { - private int mRemaining = count; - public void onReceive(Context context, Intent intent) { - final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); - final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); - Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni); - if (!filter.test(intent)) return; - if (--mRemaining == 0) { - expectedRef.get().complete(intent); - } - } - }; - final ExpectedBroadcast expected = new ExpectedBroadcast(receiver); - expectedRef.set(expected); - mServiceContext.registerReceiver(receiver, intentFilter); - return expected; - } - - private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) { - final DetailedState state = ni.getDetailedState(); - if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false; - // Expect a null extraInfo if the network is CONNECTING, because a CONNECTIVITY_ACTION - // broadcast with a state of CONNECTING only happens due to legacy VPN lockdown, which also - // nulls out extraInfo. - if (state == DetailedState.CONNECTING && ni.getExtraInfo() != null) return false; - // Can't make any assertions about DISCONNECTED broadcasts. When a network actually - // disconnects, disconnectAndDestroyNetwork sets its state to DISCONNECTED and its extraInfo - // to null. But if the DISCONNECTED broadcast is just simulated by LegacyTypeTracker due to - // a network switch, extraInfo will likely be populated. - // This is likely a bug in CS, but likely not one we can fix without impacting apps. - return true; - } - - private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) { - return registerConnectivityBroadcastThat(1, intent -> { - final int actualType = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); - final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); - return type == actualType - && state == ni.getDetailedState() - && extraInfoInBroadcastHasExpectedNullness(ni); - }); - } - - @Test - public void testNetworkTypes() { - // Ensure that our mocks for the networkAttributes config variable work as expected. If they - // don't, then tests that depend on CONNECTIVITY_ACTION broadcasts for these network types - // will fail. Failing here is much easier to debug. - assertTrue(mCm.isNetworkSupported(TYPE_WIFI)); - assertTrue(mCm.isNetworkSupported(TYPE_MOBILE)); - assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_MMS)); - assertTrue(mCm.isNetworkSupported(TYPE_MOBILE_FOTA)); - assertFalse(mCm.isNetworkSupported(TYPE_PROXY)); - - // Check that TYPE_ETHERNET is supported. Unlike the asserts above, which only validate our - // mocks, this assert exercises the ConnectivityService code path that ensures that - // TYPE_ETHERNET is supported if the ethernet service is running. - assertTrue(mCm.isNetworkSupported(TYPE_ETHERNET)); - } - - @Test - public void testNetworkFeature() throws Exception { - // Connect the cell agent and wait for the connected broadcast. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL); - ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - - // Build legacy request for SUPL. - final NetworkCapabilities legacyCaps = new NetworkCapabilities(); - legacyCaps.addTransportType(TRANSPORT_CELLULAR); - legacyCaps.addCapability(NET_CAPABILITY_SUPL); - final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL, - ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST); - - // File request, withdraw it and make sure no broadcast is sent - b = registerConnectivityBroadcast(1); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.requestNetwork(legacyRequest, callback); - callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); - mCm.unregisterNetworkCallback(callback); - b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent - - // Disconnect the network and expect mobile disconnected broadcast. - b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); - mCellNetworkAgent.disconnect(); - b.expectBroadcast(); - } - - @Test - public void testLingering() throws Exception { - verifyNoNetwork(); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - assertNull(mCm.getActiveNetworkInfo()); - assertNull(mCm.getActiveNetwork()); - // Test bringing up validated cellular. - ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - assertLength(2, mCm.getAllNetworks()); - assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || - mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork())); - assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) || - mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork())); - // Test bringing up validated WiFi. - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - assertLength(2, mCm.getAllNetworks()); - assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || - mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork())); - assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) || - mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork())); - // Test cellular linger timeout. - mCellNetworkAgent.expectDisconnected(); - waitForIdle(); - assertLength(1, mCm.getAllNetworks()); - verifyActiveNetwork(TRANSPORT_WIFI); - assertLength(1, mCm.getAllNetworks()); - assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); - // Test WiFi disconnect. - b = registerConnectivityBroadcast(1); - mWiFiNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyNoNetwork(); - } - - /** - * Verify a newly created network will be inactive instead of torn down even if no one is - * requesting. - */ - @Test - public void testNewNetworkInactive() throws Exception { - // Create a callback that monitoring the testing network. - final TestNetworkCallback listenCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback); - - // 1. Create a network that is not requested by anyone, and does not satisfy any of the - // default requests. Verify that the network will be inactive instead of torn down. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithoutInternet(); - listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - listenCallback.assertNoCallback(); - - // Verify that the network will be torn down after nascent expiry. A small period of time - // is added in case of flakiness. - final int nascentTimeoutMs = - mService.mNascentDelayMs + mService.mNascentDelayMs / 4; - listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs); - - // 2. Create a network that is satisfied by a request comes later. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithoutInternet(); - listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final TestNetworkCallback wifiCallback = new TestNetworkCallback(); - mCm.requestNetwork(wifiRequest, wifiCallback); - wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // Verify that the network will be kept since the request is still satisfied. And is able - // to get disconnected as usual if the request is released after the nascent timer expires. - listenCallback.assertNoCallback(nascentTimeoutMs); - mCm.unregisterNetworkCallback(wifiCallback); - listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // 3. Create a network that is satisfied by a request comes later. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithoutInternet(); - listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - mCm.requestNetwork(wifiRequest, wifiCallback); - wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // Verify that the network will still be torn down after the request gets removed. - mCm.unregisterNetworkCallback(wifiCallback); - listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // There is no need to ensure that LOSING is never sent in the common case that the - // network immediately satisfies a request that was already present, because it is already - // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called. - - mCm.unregisterNetworkCallback(listenCallback); - } - - /** - * Verify a newly created network will be inactive and switch to background if only background - * request is satisfied. - */ - @Test - public void testNewNetworkInactive_bgNetwork() throws Exception { - // Create a callback that monitoring the wifi network. - final TestNetworkCallback wifiListenCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback); - - // Create callbacks that can monitor background and foreground mobile networks. - // This is done by granting using background networks permission before registration. Thus, - // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default. - grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); - final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback(); - final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback); - mCm.registerNetworkCallback(new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback); - - // Connect wifi, which satisfies default request. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - - // Connect a cellular network, verify that satisfies only the background callback. - setAlwaysOnNetworks(true); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - fgMobileListenCallback.assertNoCallback(); - assertFalse(isForegroundNetwork(mCellNetworkAgent)); - - mCellNetworkAgent.disconnect(); - bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - fgMobileListenCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(wifiListenCallback); - mCm.unregisterNetworkCallback(bgMobileListenCallback); - mCm.unregisterNetworkCallback(fgMobileListenCallback); - } - - @Test - public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { - // Test bringing up unvalidated WiFi - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test bringing up unvalidated cellular - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false); - waitForIdle(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test cellular disconnect. - mCellNetworkAgent.disconnect(); - waitForIdle(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test bringing up validated cellular - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - b = registerConnectivityBroadcast(2); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test cellular disconnect. - b = registerConnectivityBroadcast(2); - mCellNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test WiFi disconnect. - b = registerConnectivityBroadcast(1); - mWiFiNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyNoNetwork(); - } - - @Test - public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { - // Test bringing up unvalidated cellular. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mCellNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test WiFi disconnect. - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test cellular disconnect. - b = registerConnectivityBroadcast(1); - mCellNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyNoNetwork(); - } - - @Test - public void testUnlingeringDoesNotValidate() throws Exception { - // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - // Test bringing up validated cellular. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - b = registerConnectivityBroadcast(2); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - // Test cellular disconnect. - b = registerConnectivityBroadcast(2); - mCellNetworkAgent.disconnect(); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Unlingering a network should not cause it to be marked as validated. - assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - } - - @Test - public void testCellularOutscoresWeakWifi() throws Exception { - // Test bringing up validated cellular. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test bringing up validated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test WiFi getting really weak. - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.adjustScore(-11); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test WiFi restoring signal strength. - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.adjustScore(11); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - } - - @Test - public void testReapingNetwork() throws Exception { - // Test bringing up WiFi without NET_CAPABILITY_INTERNET. - // Expect it to be torn down immediately because it satisfies no requests. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithoutInternet(); - mWiFiNetworkAgent.expectDisconnected(); - // Test bringing up cellular without NET_CAPABILITY_INTERNET. - // Expect it to be torn down immediately because it satisfies no requests. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mCellNetworkAgent.connectWithoutInternet(); - mCellNetworkAgent.expectDisconnected(); - // Test bringing up validated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test bringing up unvalidated cellular. - // Expect it to be torn down because it could never be the highest scoring network - // satisfying the default request even if it validated. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false); - mCellNetworkAgent.expectDisconnected(); - verifyActiveNetwork(TRANSPORT_WIFI); - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - } - - @Test - public void testCellularFallback() throws Exception { - // Test bringing up validated cellular. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Test bringing up validated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Reevaluate WiFi (it'll instantly fail DNS). - b = registerConnectivityBroadcast(2); - assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); - // Should quickly fall back to Cellular. - b.expectBroadcast(); - assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Reevaluate cellular (it'll instantly fail DNS). - b = registerConnectivityBroadcast(2); - assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); - // Should quickly fall back to WiFi. - b.expectBroadcast(); - assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - verifyActiveNetwork(TRANSPORT_WIFI); - } - - @Test - public void testWiFiFallback() throws Exception { - // Test bringing up unvalidated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mWiFiNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - // Test bringing up validated cellular. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - b = registerConnectivityBroadcast(2); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - // Reevaluate cellular (it'll instantly fail DNS). - b = registerConnectivityBroadcast(2); - assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); - // Should quickly fall back to WiFi. - b.expectBroadcast(); - assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( - NET_CAPABILITY_VALIDATED)); - verifyActiveNetwork(TRANSPORT_WIFI); - } - - @Test - public void testRequiresValidation() { - assertTrue(NetworkMonitorUtils.isValidationRequired( - mCm.getDefaultRequest().networkCapabilities)); - } - - /** - * Utility NetworkCallback for testing. The caller must explicitly test for all the callbacks - * this class receives, by calling expectCallback() exactly once each time a callback is - * received. assertNoCallback may be called at any time. - */ - private class TestNetworkCallback extends TestableNetworkCallback { - TestNetworkCallback() { - super(TEST_CALLBACK_TIMEOUT_MS); - } - - @Override - public void assertNoCallback() { - // TODO: better support this use case in TestableNetworkCallback - waitForIdle(); - assertNoCallback(0 /* timeout */); - } - - @Override - public T expectCallback(final KClass type, final HasNetwork n, - final long timeoutMs) { - final T callback = super.expectCallback(type, n, timeoutMs); - if (callback instanceof CallbackEntry.Losing) { - // TODO : move this to the specific test(s) needing this rather than here. - final CallbackEntry.Losing losing = (CallbackEntry.Losing) callback; - final int maxMsToLive = losing.getMaxMsToLive(); - String msg = String.format( - "Invalid linger time value %d, must be between %d and %d", - maxMsToLive, 0, mService.mLingerDelayMs); - assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= mService.mLingerDelayMs); - } - return callback; - } - } - - // Can't be part of TestNetworkCallback because "cannot be declared static; static methods can - // only be declared in a static or top level type". - static void assertNoCallbacks(TestNetworkCallback ... callbacks) { - for (TestNetworkCallback c : callbacks) { - c.assertNoCallback(); - } - } - - static void expectOnLost(TestNetworkAgentWrapper network, TestNetworkCallback ... callbacks) { - for (TestNetworkCallback c : callbacks) { - c.expectCallback(CallbackEntry.LOST, network); - } - } - - static void expectAvailableCallbacksUnvalidatedWithSpecifier(TestNetworkAgentWrapper network, - NetworkSpecifier specifier, TestNetworkCallback ... callbacks) { - for (TestNetworkCallback c : callbacks) { - c.expectCallback(CallbackEntry.AVAILABLE, network); - c.expectCapabilitiesThat(network, (nc) -> - !nc.hasCapability(NET_CAPABILITY_VALIDATED) - && Objects.equals(specifier, nc.getNetworkSpecifier())); - c.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, network); - c.expectCallback(CallbackEntry.BLOCKED_STATUS, network); - } - } - - @Test - public void testStateChangeNetworkCallbacks() throws Exception { - final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest genericRequest = new NetworkRequest.Builder() - .clearCapabilities().build(); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); - mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); - mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - - // Test unvalidated networks - ExpectedBroadcast b = registerConnectivityBroadcast(1); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false); - genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - b.expectBroadcast(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - // This should not trigger spurious onAvailable() callbacks, b/21762680. - mCellNetworkAgent.adjustScore(-1); - waitForIdle(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - b.expectBroadcast(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - b = registerConnectivityBroadcast(2); - mWiFiNetworkAgent.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - cellNetworkCallback.assertNoCallback(); - b.expectBroadcast(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - b = registerConnectivityBroadcast(1); - mCellNetworkAgent.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - b.expectBroadcast(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - // Test validated networks - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - genericNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - // This should not trigger spurious onAvailable() callbacks, b/21762680. - mCellNetworkAgent.adjustScore(-1); - waitForIdle(); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - genericNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - mWiFiNetworkAgent.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - - mCellNetworkAgent.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - } - - private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception { - final TestNetworkCallback callback = new TestNetworkCallback(); - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - mCm.registerNetworkCallback(wifiRequest, callback); - mCm.registerDefaultNetworkCallback(defaultCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - final LinkProperties newLp = new LinkProperties(); - final Uri capportUrl = Uri.parse("https://capport.example.com/api"); - final CaptivePortalData capportData = new CaptivePortalData.Builder() - .setCaptive(true).build(); - - final Uri expectedCapportUrl = sanitized ? null : capportUrl; - newLp.setCaptivePortalApiUrl(capportUrl); - mWiFiNetworkAgent.sendLinkProperties(newLp); - callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> - Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); - defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> - Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); - - final CaptivePortalData expectedCapportData = sanitized ? null : capportData; - mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData); - callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> - Objects.equals(expectedCapportData, lp.getCaptivePortalData())); - defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> - Objects.equals(expectedCapportData, lp.getCaptivePortalData())); - - final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork()); - assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl()); - assertEquals(expectedCapportData, lp.getCaptivePortalData()); - } - - @Test - public void networkCallbacksSanitizationTest_Sanitize() throws Exception { - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_DENIED); - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); - doNetworkCallbacksSanitizationTest(true /* sanitized */); - } - - @Test - public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception { - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_GRANTED); - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); - doNetworkCallbacksSanitizationTest(false /* sanitized */); - } - - @Test - public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception { - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_DENIED); - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - doNetworkCallbacksSanitizationTest(false /* sanitized */); - } - - @Test - public void testOwnerUidCannotChange() throws Exception { - final NetworkCapabilities ncTemplate = new NetworkCapabilities(); - final int originalOwnerUid = Process.myUid(); - ncTemplate.setOwnerUid(originalOwnerUid); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), - ncTemplate); - mWiFiNetworkAgent.connect(false); - waitForIdle(); - - // Send ConnectivityService an update to the mWiFiNetworkAgent's capabilities that changes - // the owner UID and an unrelated capability. - NetworkCapabilities agentCapabilities = mWiFiNetworkAgent.getNetworkCapabilities(); - assertEquals(originalOwnerUid, agentCapabilities.getOwnerUid()); - agentCapabilities.setOwnerUid(42); - assertFalse(agentCapabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - agentCapabilities.addCapability(NET_CAPABILITY_NOT_CONGESTED); - mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true); - waitForIdle(); - - // Owner UIDs are not visible without location permission. - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - // Check that the capability change has been applied but the owner UID is not modified. - NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); - assertEquals(originalOwnerUid, nc.getOwnerUid()); - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - } - - @Test - public void testMultipleLingering() throws Exception { - // This test would be flaky with the default 120ms timer: that is short enough that - // lingered networks are torn down before assertions can be run. We don't want to mock the - // lingering timer to keep the WakeupMessage logic realistic: this has already proven useful - // in detecting races. - mService.mLingerDelayMs = 300; - - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities().addCapability(NET_CAPABILITY_NOT_METERED) - .build(); - TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mWiFiNetworkAgent.connect(true); - // We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request. - // We then get LOSING when wifi validates and cell is outscored. - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - // TODO: Investigate sending validated before losing. - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mEthernetNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); - // TODO: Investigate sending validated before losing. - callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); - assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mEthernetNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - for (int i = 0; i < 4; i++) { - TestNetworkAgentWrapper oldNetwork, newNetwork; - if (i % 2 == 0) { - mWiFiNetworkAgent.adjustScore(-15); - oldNetwork = mWiFiNetworkAgent; - newNetwork = mCellNetworkAgent; - } else { - mWiFiNetworkAgent.adjustScore(15); - oldNetwork = mCellNetworkAgent; - newNetwork = mWiFiNetworkAgent; - - } - callback.expectCallback(CallbackEntry.LOSING, oldNetwork); - // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no - // longer lingering? - defaultCallback.expectAvailableCallbacksValidated(newNetwork); - assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork()); - } - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even - // if the network is still up. - mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - // We expect a notification about the capabilities change, and nothing else. - defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); - defaultCallback.assertNoCallback(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Wifi no longer satisfies our listen, which is for an unmetered network. - // But because its score is 55, it's still up (and the default network). - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Disconnect our test networks. - mWiFiNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - mCellNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitForIdle(); - assertEquals(null, mCm.getActiveNetwork()); - - mCm.unregisterNetworkCallback(callback); - waitForIdle(); - - // Check that a network is only lingered or torn down if it would not satisfy a request even - // if it validated. - request = new NetworkRequest.Builder().clearCapabilities().build(); - callback = new TestNetworkCallback(); - - mCm.registerNetworkCallback(request, callback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false); // Score: 10 - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring up wifi with a score of 20. - // Cell stays up because it would satisfy the default request if it validated. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); // Score: 20 - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but - // it's arguably correct to linger it, since it was the default network before it validated. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - // TODO: Investigate sending validated before losing. - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - mCellNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitForIdle(); - assertEquals(null, mCm.getActiveNetwork()); - - // If a network is lingering, and we add and remove a request from it, resume lingering. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - // TODO: Investigate sending validated before losing. - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - NetworkCallback noopCallback = new NetworkCallback(); - mCm.requestNetwork(cellRequest, noopCallback); - // TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer - // lingering? - mCm.unregisterNetworkCallback(noopCallback); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - - // Similar to the above: lingering can start even after the lingered request is removed. - // Disconnect wifi and switch to cell. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Cell is now the default network. Pin it with a cell-specific request. - noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525 - mCm.requestNetwork(cellRequest, noopCallback); - - // Now connect wifi, and expect it to become the default network. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - // The default request is lingering on cell, but nothing happens to cell, and we send no - // callbacks for it, because it's kept up by cellRequest. - callback.assertNoCallback(); - // Now unregister cellRequest and expect cell to start lingering. - mCm.unregisterNetworkCallback(noopCallback); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - - // Let linger run its course. - callback.assertNoCallback(); - final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4; - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs); - - // Register a TRACK_DEFAULT request and check that it does not affect lingering. - TestNetworkCallback trackDefaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(trackDefaultCallback); - trackDefaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); - trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Let linger run its course. - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs); - - // Clean up. - mEthernetNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - trackDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - - mCm.unregisterNetworkCallback(callback); - mCm.unregisterNetworkCallback(defaultCallback); - mCm.unregisterNetworkCallback(trackDefaultCallback); - } - - private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception { - grantUsingBackgroundNetworksPermissionForUid(uid, mContext.getPackageName()); - } - - private void grantUsingBackgroundNetworksPermissionForUid( - final int uid, final String packageName) throws Exception { - when(mPackageManager.getPackageInfo( - eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER))) - .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid)); - mService.mPermissionMonitor.onPackageAdded(packageName, uid); - } - - @Test - public void testNetworkGoesIntoBackgroundAfterLinger() throws Exception { - setAlwaysOnNetworks(true); - grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities() - .build(); - TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - - // Wifi comes up and cell lingers. - mWiFiNetworkAgent.connect(true); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - - // File a request for cellular, then release it. - NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - NetworkCallback noopCallback = new NetworkCallback(); - mCm.requestNetwork(cellRequest, noopCallback); - mCm.unregisterNetworkCallback(noopCallback); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - - // Let linger run its course. - callback.assertNoCallback(); - final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; - callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent, - lingerTimeoutMs); - - // Clean up. - mCm.unregisterNetworkCallback(defaultCallback); - mCm.unregisterNetworkCallback(callback); - } - - private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) { - return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission, - /*secure=*/ false, VpnManager.TYPE_VPN_NONE); - } - - private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) { - return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE, - secure, vpnType); - } - - @Test - public void testNetworkAgentCallbacks() throws Exception { - // Keeps track of the order of events that happen in this test. - final LinkedBlockingQueue eventOrder = new LinkedBlockingQueue<>(); - - final NetworkRequest request = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - final AtomicReference wifiNetwork = new AtomicReference<>(); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - - // Expectations for state when various callbacks fire. These expectations run on the handler - // thread and not on the test thread because they need to prevent the handler thread from - // advancing while they examine state. - - // 1. When onCreated fires, netd has been told to create the network. - mWiFiNetworkAgent.setCreatedCallback(() -> { - eventOrder.offer("onNetworkCreated"); - wifiNetwork.set(mWiFiNetworkAgent.getNetwork()); - assertNotNull(wifiNetwork.get()); - try { - verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE)); - } catch (RemoteException impossible) { - fail(); - } - }); - - // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just - // check that it is fired at some point after disconnect. - mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted")); - - // 3. While the teardown timer is running, connectivity APIs report the network is gone, but - // netd has not yet been told to destroy it. - final Runnable duringTeardown = () -> { - eventOrder.offer("timePasses"); - assertNull(mCm.getLinkProperties(wifiNetwork.get())); - try { - verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId()); - } catch (RemoteException impossible) { - fail(); - } - }; - - // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone, - // and netd has been told to destroy it. - mWiFiNetworkAgent.setDisconnectedCallback(() -> { - eventOrder.offer("onNetworkDisconnected"); - assertNull(mCm.getLinkProperties(wifiNetwork.get())); - try { - verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId()); - } catch (RemoteException impossible) { - fail(); - } - }); - - // Connect a network, and file a request for it after it has come up, to ensure the nascent - // timer is cleared and the test does not have to wait for it. Filing the request after the - // network has come up is necessary because ConnectivityService does not appear to clear the - // nascent timer if the first request satisfied by the network was filed before the network - // connected. - // TODO: fix this bug, file the request before connecting, and remove the waitForIdle. - mWiFiNetworkAgent.connectWithoutInternet(); - waitForIdle(); - mCm.requestNetwork(request, callback); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // Set teardown delay and make sure CS has processed it. - mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMillis(300); - waitForIdle(); - - // Post the duringTeardown lambda to the handler so it fires while teardown is in progress. - // The delay must be long enough it will run after the unregisterNetworkCallback has torn - // down the network and started the teardown timer, and short enough that the lambda is - // scheduled to run before the teardown timer. - final Handler h = new Handler(mCsHandlerThread.getLooper()); - h.postDelayed(duringTeardown, 150); - - // Disconnect the network and check that events happened in the right order. - mCm.unregisterNetworkCallback(callback); - assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - - mCm.unregisterNetworkCallback(callback); - } - - @Test - public void testExplicitlySelected() throws Exception { - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) - .build(); - TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - // Bring up validated cell. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - - // Bring up unvalidated wifi with explicitlySelected=true. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(true, false); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // Cell Remains the default. - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Lower wifi's score to below than cell, and check that it doesn't disconnect because - // it's explicitly selected. - mWiFiNetworkAgent.adjustScore(-40); - mWiFiNetworkAgent.adjustScore(40); - callback.assertNoCallback(); - - // If the user chooses yes on the "No Internet access, stay connected?" dialog, we switch to - // wifi even though it's unvalidated. - mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), true, false); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Disconnect wifi, and then reconnect, again with explicitlySelected=true. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(true, false); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the - // network to disconnect. - mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), false, false); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Reconnect, again with explicitlySelected=true, but this time validate. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(true, false); - mWiFiNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); - assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - callback.assertNoCallback(); - - // Disconnect wifi, and then reconnect as if the user had selected "yes, don't ask again" - // (i.e., with explicitlySelected=true and acceptUnvalidated=true). Expect to switch to - // wifi immediately. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(true, true); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mEthernetNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - mEthernetNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - - // Disconnect and reconnect with explicitlySelected=false and acceptUnvalidated=true. - // Check that the network is not scored specially and that the device prefers cell data. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(false, true); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Clean up. - mWiFiNetworkAgent.disconnect(); - mCellNetworkAgent.disconnect(); - - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - } - - private void tryNetworkFactoryRequests(int capability) throws Exception { - // Verify NOT_RESTRICTED is set appropriately - final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability) - .build().networkCapabilities; - if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN - || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA - || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS - || capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP - || capability == NET_CAPABILITY_VSIM || capability == NET_CAPABILITY_BIP - || capability == NET_CAPABILITY_ENTERPRISE) { - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - } else { - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - } - - NetworkCapabilities filter = new NetworkCapabilities(); - filter.addTransportType(TRANSPORT_CELLULAR); - filter.addCapability(capability); - // Add NOT_VCN_MANAGED capability into filter unconditionally since some requests will add - // NOT_VCN_MANAGED automatically but not for NetworkCapabilities, - // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details. - filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); - handlerThread.start(); - final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter, mCsHandlerThread); - testFactory.setScoreFilter(45); - testFactory.register(); - - final NetworkCallback networkCallback; - if (capability != NET_CAPABILITY_INTERNET) { - // If the capability passed in argument is part of the default request, then the - // factory will see the default request. Otherwise the filter will prevent the - // factory from seeing it. In that case, add a request so it can be tested. - assertFalse(testFactory.getMyStartRequested()); - NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build(); - networkCallback = new NetworkCallback(); - mCm.requestNetwork(request, networkCallback); - } else { - networkCallback = null; - } - testFactory.expectRequestAdd(); - testFactory.assertRequestCountEquals(1); - assertTrue(testFactory.getMyStartRequested()); - - // Now bring in a higher scored network. - TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - // When testAgent connects, because of its score (50 legacy int / cell transport) - // it will beat or equal the testFactory's offer, so the request will be removed. - // Note the agent as validated only if the capability is INTERNET, as it's the only case - // where it makes sense. - testAgent.connect(NET_CAPABILITY_INTERNET == capability /* validated */); - testAgent.addCapability(capability); - testFactory.expectRequestRemove(); - testFactory.assertRequestCountEquals(0); - assertFalse(testFactory.getMyStartRequested()); - - // Add a request and make sure it's not sent to the factory, because the agent - // is satisfying it better. - final NetworkCallback cb = new ConnectivityManager.NetworkCallback(); - mCm.requestNetwork(new NetworkRequest.Builder().addCapability(capability).build(), cb); - expectNoRequestChanged(testFactory); - testFactory.assertRequestCountEquals(0); - assertFalse(testFactory.getMyStartRequested()); - - // If using legacy scores, make the test agent weak enough to have the exact same score as - // the factory (50 for cell - 5 adjustment). Make sure the factory doesn't see the request. - // If not using legacy score, this is a no-op and the "same score removes request" behavior - // has already been tested above. - testAgent.adjustScore(-5); - expectNoRequestChanged(testFactory); - assertFalse(testFactory.getMyStartRequested()); - - // Make the test agent weak enough that the factory will see the two requests (the one that - // was just sent, and either the default one or the one sent at the top of this test if - // the default won't be seen). - testAgent.setScore(new NetworkScore.Builder().setLegacyInt(2).setExiting(true).build()); - testFactory.expectRequestAdds(2); - testFactory.assertRequestCountEquals(2); - assertTrue(testFactory.getMyStartRequested()); - - // Now unregister and make sure the request is removed. - mCm.unregisterNetworkCallback(cb); - testFactory.expectRequestRemove(); - - // Bring in a bunch of requests. - assertEquals(1, testFactory.getMyRequestCount()); - ConnectivityManager.NetworkCallback[] networkCallbacks = - new ConnectivityManager.NetworkCallback[10]; - for (int i = 0; i< networkCallbacks.length; i++) { - networkCallbacks[i] = new ConnectivityManager.NetworkCallback(); - NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addCapability(capability); - mCm.requestNetwork(builder.build(), networkCallbacks[i]); - } - testFactory.expectRequestAdds(10); - testFactory.assertRequestCountEquals(11); // +1 for the default/test specific request - assertTrue(testFactory.getMyStartRequested()); - - // Remove the requests. - for (int i = 0; i < networkCallbacks.length; i++) { - mCm.unregisterNetworkCallback(networkCallbacks[i]); - } - testFactory.expectRequestRemoves(10); - testFactory.assertRequestCountEquals(1); - assertTrue(testFactory.getMyStartRequested()); - - // Adjust the agent score up again. Expect the request to be withdrawn. - testAgent.setScore(new NetworkScore.Builder().setLegacyInt(50).build()); - testFactory.expectRequestRemove(); - testFactory.assertRequestCountEquals(0); - assertFalse(testFactory.getMyStartRequested()); - - // Drop the higher scored network. - testAgent.disconnect(); - testFactory.expectRequestAdd(); - testFactory.assertRequestCountEquals(1); - assertEquals(1, testFactory.getMyRequestCount()); - assertTrue(testFactory.getMyStartRequested()); - - testFactory.terminate(); - if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback); - handlerThread.quit(); - } - - @Test - public void testNetworkFactoryRequests() throws Exception { - tryNetworkFactoryRequests(NET_CAPABILITY_MMS); - tryNetworkFactoryRequests(NET_CAPABILITY_SUPL); - tryNetworkFactoryRequests(NET_CAPABILITY_DUN); - tryNetworkFactoryRequests(NET_CAPABILITY_FOTA); - tryNetworkFactoryRequests(NET_CAPABILITY_IMS); - tryNetworkFactoryRequests(NET_CAPABILITY_CBS); - tryNetworkFactoryRequests(NET_CAPABILITY_WIFI_P2P); - tryNetworkFactoryRequests(NET_CAPABILITY_IA); - tryNetworkFactoryRequests(NET_CAPABILITY_RCS); - tryNetworkFactoryRequests(NET_CAPABILITY_XCAP); - tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE); - tryNetworkFactoryRequests(NET_CAPABILITY_EIMS); - tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED); - tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET); - tryNetworkFactoryRequests(NET_CAPABILITY_TRUSTED); - tryNetworkFactoryRequests(NET_CAPABILITY_NOT_VPN); - tryNetworkFactoryRequests(NET_CAPABILITY_VSIM); - tryNetworkFactoryRequests(NET_CAPABILITY_BIP); - // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed. - } - - @Test - public void testRegisterIgnoringScore() throws Exception { - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(90).build()); - mWiFiNetworkAgent.connect(true /* validated */); - - // Make sure the factory sees the default network - final NetworkCapabilities filter = new NetworkCapabilities(); - filter.addTransportType(TRANSPORT_CELLULAR); - filter.addCapability(NET_CAPABILITY_INTERNET); - filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); - handlerThread.start(); - final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter, mCsHandlerThread); - testFactory.register(); - - final MockNetworkFactory testFactoryAll = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactoryAll", filter, mCsHandlerThread); - testFactoryAll.registerIgnoringScore(); - - // The regular test factory should not see the request, because WiFi is stronger than cell. - expectNoRequestChanged(testFactory); - // With ignoringScore though the request is seen. - testFactoryAll.expectRequestAdd(); - - // The legacy int will be ignored anyway, set the only other knob to true - mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(110) - .setTransportPrimary(true).build()); - - expectNoRequestChanged(testFactory); // still not seeing the request - expectNoRequestChanged(testFactoryAll); // still seeing the request - - mWiFiNetworkAgent.disconnect(); - } - - @Test - public void testNetworkFactoryUnregister() throws Exception { - // Make sure the factory sees the default network - final NetworkCapabilities filter = new NetworkCapabilities(); - filter.addCapability(NET_CAPABILITY_INTERNET); - filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - - final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); - handlerThread.start(); - - // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it - // does not crash. - for (int i = 0; i < 100; i++) { - final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter, mCsHandlerThread); - // Register the factory and don't be surprised when the default request arrives. - testFactory.register(); - testFactory.expectRequestAdd(); - - testFactory.setScoreFilter(42); - testFactory.terminate(); - - if (i % 2 == 0) { - try { - testFactory.register(); - fail("Re-registering terminated NetworkFactory should throw"); - } catch (IllegalStateException expected) { - } - } - } - handlerThread.quit(); - } - - @Test - public void testNoMutableNetworkRequests() throws Exception { - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); - NetworkRequest request1 = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_VALIDATED) - .build(); - NetworkRequest request2 = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL) - .build(); - - Class expected = IllegalArgumentException.class; - assertThrows(expected, () -> mCm.requestNetwork(request1, new NetworkCallback())); - assertThrows(expected, () -> mCm.requestNetwork(request1, pendingIntent)); - assertThrows(expected, () -> mCm.requestNetwork(request2, new NetworkCallback())); - assertThrows(expected, () -> mCm.requestNetwork(request2, pendingIntent)); - } - - @Test - public void testMMSonWiFi() throws Exception { - // Test bringing up cellular without MMS NetworkRequest gets reaped - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); - mCellNetworkAgent.connectWithoutInternet(); - mCellNetworkAgent.expectDisconnected(); - waitForIdle(); - assertEmpty(mCm.getAllNetworks()); - verifyNoNetwork(); - - // Test bringing up validated WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - - // Register MMS NetworkRequest - NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(builder.build(), networkCallback); - - // Test bringing up unvalidated cellular with MMS - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS); - mCellNetworkAgent.connectWithoutInternet(); - networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - verifyActiveNetwork(TRANSPORT_WIFI); - - // Test releasing NetworkRequest disconnects cellular with MMS - mCm.unregisterNetworkCallback(networkCallback); - mCellNetworkAgent.expectDisconnected(); - verifyActiveNetwork(TRANSPORT_WIFI); - } - - @Test - public void testMMSonCell() throws Exception { - // Test bringing up cellular without MMS - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); - mCellNetworkAgent.connect(false); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - - // Register MMS NetworkRequest - NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(builder.build(), networkCallback); - - // Test bringing up MMS cellular network - TestNetworkAgentWrapper - mmsNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS); - mmsNetworkAgent.connectWithoutInternet(); - networkCallback.expectAvailableCallbacksUnvalidated(mmsNetworkAgent); - verifyActiveNetwork(TRANSPORT_CELLULAR); - - // Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent - mCm.unregisterNetworkCallback(networkCallback); - mmsNetworkAgent.expectDisconnected(); - verifyActiveNetwork(TRANSPORT_CELLULAR); - } - - @Test - public void testPartialConnectivity() throws Exception { - // Register network callback. - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) - .build(); - TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - // Bring up validated mobile data. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - - // Bring up wifi with partial connectivity. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithPartialConnectivity(); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); - - // Mobile data should be the default network. - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - callback.assertNoCallback(); - - // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http - // probe. - mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); - // If the user chooses yes to use this partial connectivity wifi, switch the default - // network to wifi and check if wifi becomes valid or not. - mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, - false /* always */); - // If user accepts partial connectivity network, - // NetworkMonitor#setAcceptPartialConnectivity() should be called too. - waitForIdle(); - verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); - - // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is - // validated. - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, - mWiFiNetworkAgent); - assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // Disconnect and reconnect wifi with partial connectivity again. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connectWithPartialConnectivity(); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); - - // Mobile data should be the default network. - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - - // If the user chooses no, disconnect wifi immediately. - mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */, - false /* always */); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // If user accepted partial connectivity before, and device reconnects to that network - // again, but now the network has full connectivity. The network shouldn't contain - // NET_CAPABILITY_PARTIAL_CONNECTIVITY. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - // acceptUnvalidated is also used as setting for accepting partial networks. - mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, - true /* acceptUnvalidated */); - mWiFiNetworkAgent.connect(true); - - // If user accepted partial connectivity network before, - // NetworkMonitor#setAcceptPartialConnectivity() will be called in - // ConnectivityService#updateNetworkInfo(). - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)); - - // Wifi should be the default network. - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // The user accepted partial connectivity and selected "don't ask again". Now the user - // reconnects to the partial connectivity network. Switch to wifi as soon as partial - // connectivity is detected. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(true /* explicitlySelected */, - true /* acceptUnvalidated */); - mWiFiNetworkAgent.connectWithPartialConnectivity(); - // If user accepted partial connectivity network before, - // NetworkMonitor#setAcceptPartialConnectivity() will be called in - // ConnectivityService#updateNetworkInfo(). - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent); - mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); - - // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is - // validated. - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // If the user accepted partial connectivity, and the device auto-reconnects to the partial - // connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.explicitlySelected(false /* explicitlySelected */, - true /* acceptUnvalidated */); - - // NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as - // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls - // notifyNetworkConnected. - mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity(); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith( - NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - } - - @Test - public void testCaptivePortalOnPartialConnectivity() throws Exception { - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - final TestNetworkCallback validatedCallback = new TestNetworkCallback(); - final NetworkRequest validatedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_VALIDATED).build(); - mCm.registerNetworkCallback(validatedRequest, validatedCallback); - - // Bring up a network with a captive portal. - // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - String redirectUrl = "http://android.com/path"; - mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl); - - // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. - mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork()); - verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) - .launchCaptivePortalApp(); - - // Report that the captive portal is dismissed with partial connectivity, and check that - // callbacks are fired. - mWiFiNetworkAgent.setNetworkPartial(); - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - waitForIdle(); - captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, - mWiFiNetworkAgent); - - // Report partial connectivity is accepted. - mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */); - mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, - false /* always */); - waitForIdle(); - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - NetworkCapabilities nc = - validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, - mWiFiNetworkAgent); - - mCm.unregisterNetworkCallback(captivePortalCallback); - mCm.unregisterNetworkCallback(validatedCallback); - } - - @Test - public void testCaptivePortal() throws Exception { - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - final TestNetworkCallback validatedCallback = new TestNetworkCallback(); - final NetworkRequest validatedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_VALIDATED).build(); - mCm.registerNetworkCallback(validatedRequest, validatedCallback); - - // Bring up a network with a captive portal. - // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - String firstRedirectUrl = "http://example.com/firstPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl); - - // Take down network. - // Expect onLost callback. - mWiFiNetworkAgent.disconnect(); - captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Bring up a network with a captive portal. - // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - String secondRedirectUrl = "http://example.com/secondPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl); - - // Make captive portal disappear then revalidate. - // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); - captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - - // Break network connectivity. - // Expect NET_CAPABILITY_VALIDATED onLost callback. - mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); - mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); - validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - } - - @Test - public void testCaptivePortalApp() throws Exception { - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - final TestNetworkCallback validatedCallback = new TestNetworkCallback(); - final NetworkRequest validatedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_VALIDATED).build(); - mCm.registerNetworkCallback(validatedRequest, validatedCallback); - - // Bring up wifi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); - - // Check that calling startCaptivePortalApp does nothing. - final int fastTimeoutMs = 100; - mCm.startCaptivePortalApp(wifiNetwork); - waitForIdle(); - verify(mWiFiNetworkAgent.mNetworkMonitor, never()).launchCaptivePortalApp(); - mServiceContext.expectNoStartActivityIntent(fastTimeoutMs); - - // Turn into a captive portal. - mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */); - mCm.reportNetworkConnectivity(wifiNetwork, false); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. - mCm.startCaptivePortalApp(wifiNetwork); - waitForIdle(); - verify(mWiFiNetworkAgent.mNetworkMonitor).launchCaptivePortalApp(); - - // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal) - final Bundle testBundle = new Bundle(); - final String testKey = "testkey"; - final String testValue = "testvalue"; - testBundle.putString(testKey, testValue); - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_GRANTED); - mCm.startCaptivePortalApp(wifiNetwork, testBundle); - final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); - assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); - assertEquals(testValue, signInIntent.getStringExtra(testKey)); - - // Report that the captive portal is dismissed, and check that callbacks are fired - mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */); - mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - mCm.unregisterNetworkCallback(validatedCallback); - mCm.unregisterNetworkCallback(captivePortalCallback); - } - - @Test - public void testAvoidOrIgnoreCaptivePortals() throws Exception { - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - final TestNetworkCallback validatedCallback = new TestNetworkCallback(); - final NetworkRequest validatedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_VALIDATED).build(); - mCm.registerNetworkCallback(validatedRequest, validatedCallback); - - setCaptivePortalMode(ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID); - // Bring up a network with a captive portal. - // Expect it to fail to connect and not result in any callbacks. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - String firstRedirectUrl = "http://example.com/firstPath"; - - mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */); - mWiFiNetworkAgent.expectDisconnected(); - mWiFiNetworkAgent.expectPreventReconnectReceived(); - - assertNoCallbacks(captivePortalCallback, validatedCallback); - } - - @Test - public void testCaptivePortalApi() throws Exception { - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final String redirectUrl = "http://example.com/firstPath"; - - mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - final CaptivePortalData testData = new CaptivePortalData.Builder() - .setUserPortalUrl(Uri.parse(redirectUrl)) - .setBytesRemaining(12345L) - .build(); - - mWiFiNetworkAgent.notifyCapportApiDataChanged(testData); - - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> testData.equals(lp.getCaptivePortalData())); - - final LinkProperties newLps = new LinkProperties(); - newLps.setMtu(1234); - mWiFiNetworkAgent.sendLinkProperties(newLps); - // CaptivePortalData is not lost and unchanged when LPs are received from the NetworkAgent - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234); - } - - private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception { - // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks - // with sensitive (captive portal) data - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); - final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); - mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - - mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */); - captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - return captivePortalCallback; - } - - private class CaptivePortalTestData { - CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData, - CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData, - CaptivePortalData expectedMergedOtherData) { - mNaPasspointData = naPasspointData; - mCapportData = capportData; - mNaOtherData = naOtherData; - mExpectedMergedPasspointData = expectedMergedPasspointData; - mExpectedMergedOtherData = expectedMergedOtherData; - } - - public final CaptivePortalData mNaPasspointData; - public final CaptivePortalData mCapportData; - public final CaptivePortalData mNaOtherData; - public final CaptivePortalData mExpectedMergedPasspointData; - public final CaptivePortalData mExpectedMergedOtherData; - - } - - private CaptivePortalTestData setupCaptivePortalData() { - final CaptivePortalData capportData = new CaptivePortalData.Builder() - .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) - .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) - .setExpiryTime(1000000L) - .setBytesRemaining(12345L) - .build(); - - final CaptivePortalData naPasspointData = new CaptivePortalData.Builder() - .setBytesRemaining(80802L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - - final CaptivePortalData naOtherData = new CaptivePortalData.Builder() - .setBytesRemaining(80802L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) - .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) - .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - - final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder() - .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) - .setBytesRemaining(12345L) - .setExpiryTime(1000000L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - - final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder() - .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) - .setBytesRemaining(12345L) - .setExpiryTime(1000000L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) - .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) - .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - return new CaptivePortalTestData(naPasspointData, capportData, naOtherData, - expectedMergedPasspointData, expectedMergedOtherData); - } - - @Test - public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception { - final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); - final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); - - // Baseline capport data - mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); - - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); - - // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm - // that API data gets precedence on the bytes remaining. - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); - mWiFiNetworkAgent.sendLinkProperties(linkProperties); - - // Make sure that the capport data is merged - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedPasspointData - .equals(lp.getCaptivePortalData())); - - // Now send this information from non-Passpoint source, confirm that Capport data takes - // precedence - linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); - mWiFiNetworkAgent.sendLinkProperties(linkProperties); - - // Make sure that the capport data is merged - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedOtherData - .equals(lp.getCaptivePortalData())); - - // Create a new LP with no Network agent capport data - final LinkProperties newLps = new LinkProperties(); - newLps.setMtu(1234); - mWiFiNetworkAgent.sendLinkProperties(newLps); - // CaptivePortalData is not lost and has the original values when LPs are received from the - // NetworkAgent - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()) - && lp.getMtu() == 1234); - - // Now send capport data only from the Network agent - mWiFiNetworkAgent.notifyCapportApiDataChanged(null); - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> lp.getCaptivePortalData() == null); - - newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData); - mWiFiNetworkAgent.sendLinkProperties(newLps); - - // Make sure that only the network agent capport data is available - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); - } - - @Test - public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception { - final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); - final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); - - // Venue URL and friendly name from Network agent, confirm that API data gets precedence - // on the bytes remaining. - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); - mWiFiNetworkAgent.sendLinkProperties(linkProperties); - - // Make sure that the data is saved correctly - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); - - // Expected merged data: Network agent data is preferred, and values that are not used by - // it are merged from capport data - mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); - - // Make sure that the Capport data is merged correctly - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedPasspointData.equals( - lp.getCaptivePortalData())); - - // Now set the naData to null - linkProperties.setCaptivePortalData(null); - mWiFiNetworkAgent.sendLinkProperties(linkProperties); - - // Make sure that the Capport data is retained correctly - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); - } - - @Test - public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport() - throws Exception { - final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); - final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); - - // Venue URL and friendly name from Network agent, confirm that API data gets precedence - // on the bytes remaining. - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); - mWiFiNetworkAgent.sendLinkProperties(linkProperties); - - // Make sure that the data is saved correctly - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData())); - - // Expected merged data: Network agent data is preferred, and values that are not used by - // it are merged from capport data - mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); - - // Make sure that the Capport data is merged correctly - captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedOtherData.equals( - lp.getCaptivePortalData())); - } - - private NetworkRequest.Builder newWifiRequestBuilder() { - return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); - } - - /** - * Verify request matching behavior with network specifiers. - * - * This test does not check updating the specifier on a live network because the specifier is - * immutable and this triggers a WTF in - * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)}. - */ - @Test - public void testNetworkSpecifier() throws Exception { - // A NetworkSpecifier subclass that matches all networks but must not be visible to apps. - class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements - Parcelable { - @Override - public boolean canBeSatisfiedBy(NetworkSpecifier other) { - return true; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) {} - - @Override - public NetworkSpecifier redact() { - return null; - } - } - - // A network specifier that matches either another LocalNetworkSpecifier with the same - // string or a ConfidentialMatchAllNetworkSpecifier, and can be passed to apps as is. - class LocalStringNetworkSpecifier extends NetworkSpecifier implements Parcelable { - private String mString; - - LocalStringNetworkSpecifier(String string) { - mString = string; - } - - @Override - public boolean canBeSatisfiedBy(NetworkSpecifier other) { - if (other instanceof LocalStringNetworkSpecifier) { - return TextUtils.equals(mString, - ((LocalStringNetworkSpecifier) other).mString); - } - if (other instanceof ConfidentialMatchAllNetworkSpecifier) return true; - return false; - } - - @Override - public int describeContents() { - return 0; - } - @Override - public void writeToParcel(Parcel dest, int flags) {} - } - - - NetworkRequest rEmpty1 = newWifiRequestBuilder().build(); - NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build(); - NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build(); - NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier( - (NetworkSpecifier) null).build(); - NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier( - new LocalStringNetworkSpecifier("foo")).build(); - NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier( - new LocalStringNetworkSpecifier("bar")).build(); - - TestNetworkCallback cEmpty1 = new TestNetworkCallback(); - TestNetworkCallback cEmpty2 = new TestNetworkCallback(); - TestNetworkCallback cEmpty3 = new TestNetworkCallback(); - TestNetworkCallback cEmpty4 = new TestNetworkCallback(); - TestNetworkCallback cFoo = new TestNetworkCallback(); - TestNetworkCallback cBar = new TestNetworkCallback(); - TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] { - cEmpty1, cEmpty2, cEmpty3, cEmpty4 }; - - mCm.registerNetworkCallback(rEmpty1, cEmpty1); - mCm.registerNetworkCallback(rEmpty2, cEmpty2); - mCm.registerNetworkCallback(rEmpty3, cEmpty3); - mCm.registerNetworkCallback(rEmpty4, cEmpty4); - mCm.registerNetworkCallback(rFoo, cFoo); - mCm.registerNetworkCallback(rBar, cBar); - - LocalStringNetworkSpecifier nsFoo = new LocalStringNetworkSpecifier("foo"); - LocalStringNetworkSpecifier nsBar = new LocalStringNetworkSpecifier("bar"); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, - cEmpty1, cEmpty2, cEmpty3, cEmpty4); - assertNoCallbacks(cFoo, cBar); - - mWiFiNetworkAgent.disconnect(); - expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.setNetworkSpecifier(nsFoo); - mWiFiNetworkAgent.connect(false); - expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsFoo, - cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); - cBar.assertNoCallback(); - assertEquals(nsFoo, - mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); - - mWiFiNetworkAgent.disconnect(); - expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.setNetworkSpecifier(nsBar); - mWiFiNetworkAgent.connect(false); - expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsBar, - cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); - cFoo.assertNoCallback(); - assertEquals(nsBar, - mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - - mWiFiNetworkAgent.disconnect(); - expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); - cFoo.assertNoCallback(); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier()); - mWiFiNetworkAgent.connect(false); - expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, - cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); - assertNull( - mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - - mWiFiNetworkAgent.disconnect(); - expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); - } - - /** - * @return the context's attribution tag - */ - private String getAttributionTag() { - return mContext.getAttributionTag(); - } - - @Test - public void testInvalidNetworkSpecifier() { - assertThrows(IllegalArgumentException.class, () -> { - NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.setNetworkSpecifier(new MatchAllNetworkSpecifier()); - }); - - assertThrows(IllegalArgumentException.class, () -> { - NetworkCapabilities networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addTransportType(TRANSPORT_WIFI) - .setNetworkSpecifier(new MatchAllNetworkSpecifier()); - mService.requestNetwork(Process.INVALID_UID, networkCapabilities, - NetworkRequest.Type.REQUEST.ordinal(), null, 0, null, - ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, - mContext.getPackageName(), getAttributionTag()); - }); - - class NonParcelableSpecifier extends NetworkSpecifier { - @Override - public boolean canBeSatisfiedBy(NetworkSpecifier other) { - return false; - } - }; - class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable { - @Override public int describeContents() { return 0; } - @Override public void writeToParcel(Parcel p, int flags) {} - } - - final NetworkRequest.Builder builder = - new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET); - assertThrows(ClassCastException.class, () -> { - builder.setNetworkSpecifier(new NonParcelableSpecifier()); - Parcel parcelW = Parcel.obtain(); - builder.build().writeToParcel(parcelW, 0); - }); - - final NetworkRequest nr = - new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET) - .setNetworkSpecifier(new ParcelableSpecifier()) - .build(); - assertNotNull(nr); - - assertThrows(BadParcelableException.class, () -> { - Parcel parcelW = Parcel.obtain(); - nr.writeToParcel(parcelW, 0); - byte[] bytes = parcelW.marshall(); - parcelW.recycle(); - - Parcel parcelR = Parcel.obtain(); - parcelR.unmarshall(bytes, 0, bytes.length); - parcelR.setDataPosition(0); - NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR); - }); - } - - @Test - public void testNetworkRequestUidSpoofSecurityException() throws Exception { - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - NetworkRequest networkRequest = newWifiRequestBuilder().build(); - TestNetworkCallback networkCallback = new TestNetworkCallback(); - doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString()); - assertThrows(SecurityException.class, () -> { - mCm.requestNetwork(networkRequest, networkCallback); - }); - } - - @Test - public void testInvalidSignalStrength() { - NetworkRequest r = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .addTransportType(TRANSPORT_WIFI) - .setSignalStrength(-75) - .build(); - // Registering a NetworkCallback with signal strength but w/o NETWORK_SIGNAL_STRENGTH_WAKEUP - // permission should get SecurityException. - assertThrows(SecurityException.class, () -> - mCm.registerNetworkCallback(r, new NetworkCallback())); - - assertThrows(SecurityException.class, () -> - mCm.registerNetworkCallback(r, PendingIntent.getService( - mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))); - - // Requesting a Network with signal strength should get IllegalArgumentException. - assertThrows(IllegalArgumentException.class, () -> - mCm.requestNetwork(r, new NetworkCallback())); - - assertThrows(IllegalArgumentException.class, () -> - mCm.requestNetwork(r, PendingIntent.getService( - mServiceContext, 0 /* requestCode */, new Intent(), FLAG_IMMUTABLE))); - } - - @Test - public void testRegisterDefaultNetworkCallback() throws Exception { - // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultNetworkCallback); - defaultNetworkCallback.assertNoCallback(); - - final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); - final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); - mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler); - systemDefaultCallback.assertNoCallback(); - - // Create a TRANSPORT_CELLULAR request to keep the mobile interface up - // whenever Wi-Fi is up. Without this, the mobile network agent is - // reaped before any other activity can take place. - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.requestNetwork(cellRequest, cellNetworkCallback); - cellNetworkCallback.assertNoCallback(); - - // Bring up cell and expect CALLBACK_AVAILABLE. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring up wifi and expect CALLBACK_AVAILABLE. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - cellNetworkCallback.assertNoCallback(); - defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring down cell. Expect no default network callback, since it wasn't the default. - mCellNetworkAgent.disconnect(); - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultNetworkCallback.assertNoCallback(); - systemDefaultCallback.assertNoCallback(); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring up cell. Expect no default network callback, since it won't be the default. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultNetworkCallback.assertNoCallback(); - systemDefaultCallback.assertNoCallback(); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - // Bring down wifi. Expect the default network callback to notified of LOST wifi - // followed by AVAILABLE cell. - mWiFiNetworkAgent.disconnect(); - cellNetworkCallback.assertNoCallback(); - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - mCellNetworkAgent.disconnect(); - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitForIdle(); - assertEquals(null, mCm.getActiveNetwork()); - - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - systemDefaultCallback.assertNoCallback(); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(null, systemDefaultCallback.getLastAvailableNetwork()); - - mMockVpn.disconnect(); - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - systemDefaultCallback.assertNoCallback(); - waitForIdle(); - assertEquals(null, mCm.getActiveNetwork()); - } - - @Test - public void testAdditionalStateCallbacks() throws Exception { - // File a network request for mobile. - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.requestNetwork(cellRequest, cellNetworkCallback); - - // Bring up the mobile network. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - // We should get onAvailable(), onCapabilitiesChanged(), and - // onLinkPropertiesChanged() in rapid succession. Additionally, we - // should get onCapabilitiesChanged() when the mobile network validates. - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - - // Update LinkProperties. - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("foonet_data0"); - mCellNetworkAgent.sendLinkProperties(lp); - // We should get onLinkPropertiesChanged(). - cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - - // Suspend the network. - mCellNetworkAgent.suspend(); - cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED, - mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState()); - - // Register a garden variety default network request. - TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(dfltNetworkCallback); - // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(), - // as well as onNetworkSuspended() in rapid succession. - dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true); - dfltNetworkCallback.assertNoCallback(); - mCm.unregisterNetworkCallback(dfltNetworkCallback); - - mCellNetworkAgent.resume(); - cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED, - mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState()); - - dfltNetworkCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(dfltNetworkCallback); - // This time onNetworkSuspended should not be called. - dfltNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - dfltNetworkCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(dfltNetworkCallback); - mCm.unregisterNetworkCallback(cellNetworkCallback); - } - - @Test - public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception { - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false /* validated */); - - final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); - final TestNetworkCallback callback = new TestNetworkCallback(); - assertThrows(SecurityException.class, - () -> mCm.registerSystemDefaultNetworkCallback(callback, handler)); - callback.assertNoCallback(); - assertThrows(SecurityException.class, - () -> mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler)); - callback.assertNoCallback(); - - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - mCm.registerSystemDefaultNetworkCallback(callback, handler); - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - mCm.unregisterNetworkCallback(callback); - - mCm.registerDefaultNetworkCallbackForUid(APP1_UID, callback, handler); - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - mCm.unregisterNetworkCallback(callback); - } - - private void setCaptivePortalMode(int mode) { - ContentResolver cr = mServiceContext.getContentResolver(); - Settings.Global.putInt(cr, ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE, mode); - } - - private void setAlwaysOnNetworks(boolean enable) { - ContentResolver cr = mServiceContext.getContentResolver(); - Settings.Global.putInt(cr, ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, - enable ? 1 : 0); - mService.updateAlwaysOnNetworks(); - waitForIdle(); - } - - private void setPrivateDnsSettings(int mode, String specifier) { - ConnectivitySettingsManager.setPrivateDnsMode(mServiceContext, mode); - ConnectivitySettingsManager.setPrivateDnsHostname(mServiceContext, specifier); - mService.updatePrivateDnsSettings(); - waitForIdle(); - } - - private boolean isForegroundNetwork(TestNetworkAgentWrapper network) { - NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork()); - assertNotNull(nc); - return nc.hasCapability(NET_CAPABILITY_FOREGROUND); - } - - @Test - public void testBackgroundNetworks() throws Exception { - // Create a cellular background request. - grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); - final TestNetworkCallback cellBgCallback = new TestNetworkCallback(); - mCm.requestBackgroundNetwork(new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(), - cellBgCallback, mCsHandlerThread.getThreadHandler()); - - // Make callbacks for monitoring. - final NetworkRequest request = new NetworkRequest.Builder().build(); - final NetworkRequest fgRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_FOREGROUND).build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - final TestNetworkCallback fgCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - mCm.registerNetworkCallback(fgRequest, fgCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - fgCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertTrue(isForegroundNetwork(mCellNetworkAgent)); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - - // When wifi connects, cell lingers. - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - fgCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - assertTrue(isForegroundNetwork(mCellNetworkAgent)); - assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); - - // When lingering is complete, cell is still there but is now in the background. - waitForIdle(); - int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; - fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs); - // Expect a network capabilities update sans FOREGROUND. - callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); - assertFalse(isForegroundNetwork(mCellNetworkAgent)); - assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); - - // File a cell request and check that cell comes into the foreground. - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - final TestNetworkCallback cellCallback = new TestNetworkCallback(); - mCm.requestNetwork(cellRequest, cellCallback); - cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - // Expect a network capabilities update with FOREGROUND, because the most recent - // request causes its state to change. - cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); - callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); - assertTrue(isForegroundNetwork(mCellNetworkAgent)); - assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); - - // Release the request. The network immediately goes into the background, since it was not - // lingering. - mCm.unregisterNetworkCallback(cellCallback); - fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - // Expect a network capabilities update sans FOREGROUND. - callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent); - assertFalse(isForegroundNetwork(mCellNetworkAgent)); - assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); - - // Disconnect wifi and check that cell is foreground again. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - fgCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertTrue(isForegroundNetwork(mCellNetworkAgent)); - - mCm.unregisterNetworkCallback(callback); - mCm.unregisterNetworkCallback(fgCallback); - mCm.unregisterNetworkCallback(cellBgCallback); - } - - @Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing. - public void benchmarkRequestRegistrationAndCallbackDispatch() throws Exception { - // TODO: turn this unit test into a real benchmarking test. - // Benchmarks connecting and switching performance in the presence of a large number of - // NetworkRequests. - // 1. File NUM_REQUESTS requests. - // 2. Have a network connect. Wait for NUM_REQUESTS onAvailable callbacks to fire. - // 3. Have a new network connect and outscore the previous. Wait for NUM_REQUESTS onLosing - // and NUM_REQUESTS onAvailable callbacks to fire. - // See how long it took. - final int NUM_REQUESTS = 90; - final int REGISTER_TIME_LIMIT_MS = 200; - final int CONNECT_TIME_LIMIT_MS = 60; - final int SWITCH_TIME_LIMIT_MS = 60; - final int UNREGISTER_TIME_LIMIT_MS = 20; - - final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); - final NetworkCallback[] callbacks = new NetworkCallback[NUM_REQUESTS]; - final CountDownLatch availableLatch = new CountDownLatch(NUM_REQUESTS); - final CountDownLatch losingLatch = new CountDownLatch(NUM_REQUESTS); - - for (int i = 0; i < NUM_REQUESTS; i++) { - callbacks[i] = new NetworkCallback() { - @Override public void onAvailable(Network n) { availableLatch.countDown(); } - @Override public void onLosing(Network n, int t) { losingLatch.countDown(); } - }; - } - - assertRunsInAtMost("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> { - for (NetworkCallback cb : callbacks) { - mCm.registerNetworkCallback(request, cb); - } - }); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - // Don't request that the network validate, because otherwise connect() will block until - // the network gets NET_CAPABILITY_VALIDATED, after all the callbacks below have fired, - // and we won't actually measure anything. - mCellNetworkAgent.connect(false); - - long onAvailableDispatchingDuration = durationOf(() -> { - await(availableLatch, 10 * CONNECT_TIME_LIMIT_MS); - }); - Log.d(TAG, String.format("Dispatched %d of %d onAvailable callbacks in %dms", - NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS, - onAvailableDispatchingDuration)); - assertTrue(String.format("Dispatching %d onAvailable callbacks in %dms, expected %dms", - NUM_REQUESTS, onAvailableDispatchingDuration, CONNECT_TIME_LIMIT_MS), - onAvailableDispatchingDuration <= CONNECT_TIME_LIMIT_MS); - - // Give wifi a high enough score that we'll linger cell when wifi comes up. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.adjustScore(40); - mWiFiNetworkAgent.connect(false); - - long onLostDispatchingDuration = durationOf(() -> { - await(losingLatch, 10 * SWITCH_TIME_LIMIT_MS); - }); - Log.d(TAG, String.format("Dispatched %d of %d onLosing callbacks in %dms", - NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, onLostDispatchingDuration)); - assertTrue(String.format("Dispatching %d onLosing callbacks in %dms, expected %dms", - NUM_REQUESTS, onLostDispatchingDuration, SWITCH_TIME_LIMIT_MS), - onLostDispatchingDuration <= SWITCH_TIME_LIMIT_MS); - - assertRunsInAtMost("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> { - for (NetworkCallback cb : callbacks) { - mCm.unregisterNetworkCallback(cb); - } - }); - } - - @Test - public void testMobileDataAlwaysOn() throws Exception { - grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - - final HandlerThread handlerThread = new HandlerThread("MobileDataAlwaysOnFactory"); - handlerThread.start(); - NetworkCapabilities filter = new NetworkCapabilities() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .addCapability(NET_CAPABILITY_INTERNET); - final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter, mCsHandlerThread); - testFactory.setScoreFilter(40); - - // Register the factory and expect it to start looking for a network. - testFactory.register(); - - try { - // Expect the factory to receive the default network request. - testFactory.expectRequestAdd(); - testFactory.assertRequestCountEquals(1); - assertTrue(testFactory.getMyStartRequested()); - - // Bring up wifi. The factory stops looking for a network. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - // Score 60 - 40 penalty for not validated yet, then 60 when it validates - mWiFiNetworkAgent.connect(true); - // The network connects with a low score, so the offer can still beat it and - // nothing happens. Then the network validates, and the offer with its filter score - // of 40 can no longer beat it and the request is removed. - testFactory.expectRequestRemove(); - testFactory.assertRequestCountEquals(0); - - assertFalse(testFactory.getMyStartRequested()); - - // Turn on mobile data always on. This request will not match the wifi request, so - // it will be sent to the test factory whose filters allow to see it. - setAlwaysOnNetworks(true); - testFactory.expectRequestAdd(); - testFactory.assertRequestCountEquals(1); - - assertTrue(testFactory.getMyStartRequested()); - - // Bring up cell data and check that the factory stops looking. - assertLength(1, mCm.getAllNetworks()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false); - cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent, false, false, false, - TEST_CALLBACK_TIMEOUT_MS); - // When cell connects, it will satisfy the "mobile always on request" right away - // by virtue of being the only network that can satisfy the request. However, its - // score is low (50 - 40 = 10) so the test factory can still hope to beat it. - expectNoRequestChanged(testFactory); - - // Next, cell validates. This gives it a score of 50 and the test factory can't - // hope to beat that according to its filters. It will see the message that its - // offer is now unnecessary. - mCellNetworkAgent.setNetworkValid(true); - // Need a trigger point to let NetworkMonitor tell ConnectivityService that network is - // validated – see testPartialConnectivity. - mCm.reportNetworkConnectivity(mCellNetworkAgent.getNetwork(), true); - cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent); - testFactory.expectRequestRemove(); - testFactory.assertRequestCountEquals(0); - // Accordingly, the factory shouldn't be started. - assertFalse(testFactory.getMyStartRequested()); - - // Check that cell data stays up. - waitForIdle(); - verifyActiveNetwork(TRANSPORT_WIFI); - assertLength(2, mCm.getAllNetworks()); - - // Cell disconnects. There is still the "mobile data always on" request outstanding, - // and the test factory should see it now that it isn't hopelessly outscored. - mCellNetworkAgent.disconnect(); - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - assertLength(1, mCm.getAllNetworks()); - testFactory.expectRequestAdd(); - testFactory.assertRequestCountEquals(1); - - // Reconnect cell validated, see the request disappear again. Then withdraw the - // mobile always on request. This will tear down cell, and there shouldn't be a - // blip where the test factory briefly sees the request or anything. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertLength(2, mCm.getAllNetworks()); - testFactory.expectRequestRemove(); - testFactory.assertRequestCountEquals(0); - setAlwaysOnNetworks(false); - expectNoRequestChanged(testFactory); - testFactory.assertRequestCountEquals(0); - assertFalse(testFactory.getMyStartRequested()); - // ... and cell data to be torn down immediately since it is no longer nascent. - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitForIdle(); - assertLength(1, mCm.getAllNetworks()); - } finally { - testFactory.terminate(); - mCm.unregisterNetworkCallback(cellNetworkCallback); - handlerThread.quit(); - } - } - - @Test - public void testAvoidBadWifiSetting() throws Exception { - final ContentResolver cr = mServiceContext.getContentResolver(); - final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI; - - mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; - String[] values = new String[] {null, "0", "1"}; - for (int i = 0; i < values.length; i++) { - Settings.Global.putInt(cr, settingName, 1); - mPolicyTracker.reevaluate(); - waitForIdle(); - String msg = String.format("config=false, setting=%s", values[i]); - assertTrue(mService.avoidBadWifi()); - assertFalse(msg, mPolicyTracker.shouldNotifyWifiUnvalidated()); - } - - mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; - - Settings.Global.putInt(cr, settingName, 0); - mPolicyTracker.reevaluate(); - waitForIdle(); - assertFalse(mService.avoidBadWifi()); - assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); - - Settings.Global.putInt(cr, settingName, 1); - mPolicyTracker.reevaluate(); - waitForIdle(); - assertTrue(mService.avoidBadWifi()); - assertFalse(mPolicyTracker.shouldNotifyWifiUnvalidated()); - - Settings.Global.putString(cr, settingName, null); - mPolicyTracker.reevaluate(); - waitForIdle(); - assertFalse(mService.avoidBadWifi()); - assertTrue(mPolicyTracker.shouldNotifyWifiUnvalidated()); - } - - @Ignore("Refactoring in progress b/178071397") - @Test - public void testAvoidBadWifi() throws Exception { - final ContentResolver cr = mServiceContext.getContentResolver(); - - // Pretend we're on a carrier that restricts switching away from bad wifi. - mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; - - // File a request for cell to ensure it doesn't go down. - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.requestNetwork(cellRequest, cellNetworkCallback); - - TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - NetworkRequest validatedWifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI) - .addCapability(NET_CAPABILITY_VALIDATED) - .build(); - TestNetworkCallback validatedWifiCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(validatedWifiRequest, validatedWifiCallback); - - Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 0); - mPolicyTracker.reevaluate(); - - // Bring up validated cell. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - Network cellNetwork = mCellNetworkAgent.getNetwork(); - - // Bring up validated wifi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); - - // Fail validation on wifi. - mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); - mCm.reportNetworkConnectivity(wifiNetwork, false); - defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Because avoid bad wifi is off, we don't switch to cellular. - defaultCallback.assertNoCallback(); - assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertEquals(mCm.getActiveNetwork(), wifiNetwork); - - // Simulate switching to a carrier that does not restrict avoiding bad wifi, and expect - // that we switch back to cell. - mPolicyTracker.mConfigRestrictsAvoidBadWifi = false; - mPolicyTracker.reevaluate(); - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertEquals(mCm.getActiveNetwork(), cellNetwork); - - // Switch back to a restrictive carrier. - mPolicyTracker.mConfigRestrictsAvoidBadWifi = true; - mPolicyTracker.reevaluate(); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mCm.getActiveNetwork(), wifiNetwork); - - // Simulate the user selecting "switch" on the dialog, and check that we switch to cell. - mCm.setAvoidUnvalidated(wifiNetwork); - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertEquals(mCm.getActiveNetwork(), cellNetwork); - - // Disconnect and reconnect wifi to clear the one-time switch above. - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - validatedWifiCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - wifiNetwork = mWiFiNetworkAgent.getNetwork(); - - // Fail validation on wifi and expect the dialog to appear. - mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */); - mCm.reportNetworkConnectivity(wifiNetwork, false); - defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Simulate the user selecting "switch" and checking the don't ask again checkbox. - Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); - mPolicyTracker.reevaluate(); - - // We now switch to cell. - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability( - NET_CAPABILITY_VALIDATED)); - assertEquals(mCm.getActiveNetwork(), cellNetwork); - - // Simulate the user turning the cellular fallback setting off and then on. - // We switch to wifi and then to cell. - Settings.Global.putString(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null); - mPolicyTracker.reevaluate(); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mCm.getActiveNetwork(), wifiNetwork); - Settings.Global.putInt(cr, ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, 1); - mPolicyTracker.reevaluate(); - defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertEquals(mCm.getActiveNetwork(), cellNetwork); - - // If cell goes down, we switch to wifi. - mCellNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - validatedWifiCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(cellNetworkCallback); - mCm.unregisterNetworkCallback(validatedWifiCallback); - mCm.unregisterNetworkCallback(defaultCallback); - } - - @Test - public void testMeteredMultipathPreferenceSetting() throws Exception { - final ContentResolver cr = mServiceContext.getContentResolver(); - final String settingName = ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE; - - for (int config : Arrays.asList(0, 3, 2)) { - for (String setting: Arrays.asList(null, "0", "2", "1")) { - mPolicyTracker.mConfigMeteredMultipathPreference = config; - Settings.Global.putString(cr, settingName, setting); - mPolicyTracker.reevaluate(); - waitForIdle(); - - final int expected = (setting != null) ? Integer.parseInt(setting) : config; - String msg = String.format("config=%d, setting=%s", config, setting); - assertEquals(msg, expected, mCm.getMultipathPreference(null)); - } - } - } - - /** - * Validate that a satisfied network request does not trigger onUnavailable() once the - * time-out period expires. - */ - @Test - public void testSatisfiedNetworkRequestDoesNotTriggerOnUnavailable() throws Exception { - NetworkRequest nr = new NetworkRequest.Builder().addTransportType( - NetworkCapabilities.TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, - TEST_CALLBACK_TIMEOUT_MS); - - // pass timeout and validate that UNAVAILABLE is not called - networkCallback.assertNoCallback(); - } - - /** - * Validate that a satisfied network request followed by a disconnected (lost) network does - * not trigger onUnavailable() once the time-out period expires. - */ - @Test - public void testSatisfiedThenLostNetworkRequestDoesNotTriggerOnUnavailable() throws Exception { - NetworkRequest nr = new NetworkRequest.Builder().addTransportType( - NetworkCapabilities.TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false, - TEST_CALLBACK_TIMEOUT_MS); - mWiFiNetworkAgent.disconnect(); - networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - // Validate that UNAVAILABLE is not called - networkCallback.assertNoCallback(); - } - - /** - * Validate that when a time-out is specified for a network request the onUnavailable() - * callback is called when time-out expires. Then validate that if network request is - * (somehow) satisfied - the callback isn't called later. - */ - @Test - public void testTimedoutNetworkRequest() throws Exception { - NetworkRequest nr = new NetworkRequest.Builder().addTransportType( - NetworkCapabilities.TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - final int timeoutMs = 10; - mCm.requestNetwork(nr, networkCallback, timeoutMs); - - // pass timeout and validate that UNAVAILABLE is called - networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null); - - // create a network satisfying request - validate that request not triggered - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - networkCallback.assertNoCallback(); - } - - /** - * Validate that when a network request is unregistered (cancelled), no posterior event can - * trigger the callback. - */ - @Test - public void testNoCallbackAfterUnregisteredNetworkRequest() throws Exception { - NetworkRequest nr = new NetworkRequest.Builder().addTransportType( - NetworkCapabilities.TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - final int timeoutMs = 10; - - mCm.requestNetwork(nr, networkCallback, timeoutMs); - mCm.unregisterNetworkCallback(networkCallback); - // Regardless of the timeout, unregistering the callback in ConnectivityManager ensures - // that this callback will not be called. - networkCallback.assertNoCallback(); - - // create a network satisfying request - validate that request not triggered - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - networkCallback.assertNoCallback(); - } - - @Test - public void testUnfulfillableNetworkRequest() throws Exception { - runUnfulfillableNetworkRequest(false); - } - - @Test - public void testUnfulfillableNetworkRequestAfterUnregister() throws Exception { - runUnfulfillableNetworkRequest(true); - } - - /** - * Validate the callback flow for a factory releasing a request as unfulfillable. - */ - private void runUnfulfillableNetworkRequest(boolean preUnregister) throws Exception { - NetworkRequest nr = new NetworkRequest.Builder().addTransportType( - NetworkCapabilities.TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - - final HandlerThread handlerThread = new HandlerThread("testUnfulfillableNetworkRequest"); - handlerThread.start(); - NetworkCapabilities filter = new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter, mCsHandlerThread); - testFactory.setScoreFilter(40); - - // Register the factory and expect it to receive the default request. - testFactory.register(); - testFactory.expectRequestAdd(); - - try { - // Now file the test request and expect it. - mCm.requestNetwork(nr, networkCallback); - final NetworkRequest newRequest = testFactory.expectRequestAdd().request; - - if (preUnregister) { - mCm.unregisterNetworkCallback(networkCallback); - - // The request has been released : the factory should see it removed - // immediately. - testFactory.expectRequestRemove(); - - // Simulate the factory releasing the request as unfulfillable: no-op since - // the callback has already been unregistered (but a test that no exceptions are - // thrown). - testFactory.triggerUnfulfillable(newRequest); - } else { - // Simulate the factory releasing the request as unfulfillable and expect - // onUnavailable! - testFactory.triggerUnfulfillable(newRequest); - - networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null); - - // Declaring a request unfulfillable releases it automatically. - testFactory.expectRequestRemove(); - - // unregister network callback - a no-op (since already freed by the - // on-unavailable), but should not fail or throw exceptions. - mCm.unregisterNetworkCallback(networkCallback); - - // The factory should not see any further removal, as this request has - // already been removed. - } - } finally { - testFactory.terminate(); - handlerThread.quit(); - } - } - - private static class TestKeepaliveCallback extends PacketKeepaliveCallback { - - public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR } - - private class CallbackValue { - public CallbackType callbackType; - public int error; - - public CallbackValue(CallbackType type) { - this.callbackType = type; - this.error = PacketKeepalive.SUCCESS; - assertTrue("onError callback must have error", type != CallbackType.ON_ERROR); - } - - public CallbackValue(CallbackType type, int error) { - this.callbackType = type; - this.error = error; - assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR); - } - - @Override - public boolean equals(Object o) { - return o instanceof CallbackValue && - this.callbackType == ((CallbackValue) o).callbackType && - this.error == ((CallbackValue) o).error; - } - - @Override - public String toString() { - return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error); - } - } - - private final LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>(); - - @Override - public void onStarted() { - mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED)); - } - - @Override - public void onStopped() { - mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED)); - } - - @Override - public void onError(int error) { - mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error)); - } - - private void expectCallback(CallbackValue callbackValue) throws InterruptedException { - assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - public void expectStarted() throws Exception { - expectCallback(new CallbackValue(CallbackType.ON_STARTED)); - } - - public void expectStopped() throws Exception { - expectCallback(new CallbackValue(CallbackType.ON_STOPPED)); - } - - public void expectError(int error) throws Exception { - expectCallback(new CallbackValue(CallbackType.ON_ERROR, error)); - } - } - - private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback { - - public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; - - private class CallbackValue { - public CallbackType callbackType; - public int error; - - CallbackValue(CallbackType type) { - this.callbackType = type; - this.error = SocketKeepalive.SUCCESS; - assertTrue("onError callback must have error", type != CallbackType.ON_ERROR); - } - - CallbackValue(CallbackType type, int error) { - this.callbackType = type; - this.error = error; - assertEquals("error can only be set for onError", type, CallbackType.ON_ERROR); - } - - @Override - public boolean equals(Object o) { - return o instanceof CallbackValue - && this.callbackType == ((CallbackValue) o).callbackType - && this.error == ((CallbackValue) o).error; - } - - @Override - public String toString() { - return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, - error); - } - } - - private LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>(); - private final Executor mExecutor; - - TestSocketKeepaliveCallback(@NonNull Executor executor) { - mExecutor = executor; - } - - @Override - public void onStarted() { - mCallbacks.add(new CallbackValue(CallbackType.ON_STARTED)); - } - - @Override - public void onStopped() { - mCallbacks.add(new CallbackValue(CallbackType.ON_STOPPED)); - } - - @Override - public void onError(int error) { - mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, error)); - } - - private void expectCallback(CallbackValue callbackValue) throws InterruptedException { - assertEquals(callbackValue, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - - } - - public void expectStarted() throws InterruptedException { - expectCallback(new CallbackValue(CallbackType.ON_STARTED)); - } - - public void expectStopped() throws InterruptedException { - expectCallback(new CallbackValue(CallbackType.ON_STOPPED)); - } - - public void expectError(int error) throws InterruptedException { - expectCallback(new CallbackValue(CallbackType.ON_ERROR, error)); - } - - public void assertNoCallback() { - waitForIdleSerialExecutor(mExecutor, TIMEOUT_MS); - CallbackValue cv = mCallbacks.peek(); - assertNull("Unexpected callback: " + cv, cv); - } - } - - private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception { - // Ensure the network is disconnected before anything else occurs - if (mWiFiNetworkAgent != null) { - assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork())); - } - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - mWiFiNetworkAgent.connect(true); - b.expectBroadcast(); - verifyActiveNetwork(TRANSPORT_WIFI); - mWiFiNetworkAgent.sendLinkProperties(lp); - waitForIdle(); - return mWiFiNetworkAgent.getNetwork(); - } - - @Test - public void testPacketKeepalives() throws Exception { - InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); - InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); - InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); - InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); - InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888"); - - final int validKaInterval = 15; - final int invalidKaInterval = 9; - - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("wlan12"); - lp.addLinkAddress(new LinkAddress(myIPv6, 64)); - lp.addLinkAddress(new LinkAddress(myIPv4, 25)); - lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); - lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); - - Network notMyNet = new Network(61234); - Network myNet = connectKeepaliveNetwork(lp); - - TestKeepaliveCallback callback = new TestKeepaliveCallback(); - PacketKeepalive ka; - - // Attempt to start keepalives with invalid parameters and check for errors. - ka = mCm.startNattKeepalive(notMyNet, validKaInterval, callback, myIPv4, 1234, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); - - ka = mCm.startNattKeepalive(myNet, invalidKaInterval, callback, myIPv4, 1234, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_INVALID_INTERVAL); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 1234, dstIPv6); - callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); - - // NAT-T is only supported for IPv4. - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6); - callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_INVALID_PORT); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); - - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectError(PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED); - - // Check that a started keepalive can be stopped. - mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectStarted(); - mWiFiNetworkAgent.setStopKeepaliveEvent(PacketKeepalive.SUCCESS); - ka.stop(); - callback.expectStopped(); - - // Check that deleting the IP address stops the keepalive. - LinkProperties bogusLp = new LinkProperties(lp); - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectStarted(); - bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25)); - bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25)); - mWiFiNetworkAgent.sendLinkProperties(bogusLp); - callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS); - mWiFiNetworkAgent.sendLinkProperties(lp); - - // Check that a started keepalive is stopped correctly when the network disconnects. - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectStarted(); - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); - - // ... and that stopping it after that has no adverse effects. - waitForIdle(); - final Network myNetAlias = myNet; - assertNull(mCm.getNetworkCapabilities(myNetAlias)); - ka.stop(); - - // Reconnect. - myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveEvent(PacketKeepalive.SUCCESS); - - // Check that keepalive slots start from 1 and increment. The first one gets slot 1. - mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); - ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 12345, dstIPv4); - callback.expectStarted(); - - // The second one gets slot 2. - mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); - TestKeepaliveCallback callback2 = new TestKeepaliveCallback(); - PacketKeepalive ka2 = mCm.startNattKeepalive( - myNet, validKaInterval, callback2, myIPv4, 6789, dstIPv4); - callback2.expectStarted(); - - // Now stop the first one and create a third. This also gets slot 1. - ka.stop(); - callback.expectStopped(); - - mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); - TestKeepaliveCallback callback3 = new TestKeepaliveCallback(); - PacketKeepalive ka3 = mCm.startNattKeepalive( - myNet, validKaInterval, callback3, myIPv4, 9876, dstIPv4); - callback3.expectStarted(); - - ka2.stop(); - callback2.expectStopped(); - - ka3.stop(); - callback3.expectStopped(); - } - - // Helper method to prepare the executor and run test - private void runTestWithSerialExecutors(ExceptionUtils.ThrowingConsumer functor) - throws Exception { - final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor(); - final Executor executorInline = (Runnable r) -> r.run(); - functor.accept(executorSingleThread); - executorSingleThread.shutdown(); - functor.accept(executorInline); - } - - @Test - public void testNattSocketKeepalives() throws Exception { - runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesWithExecutor(executor)); - runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesFdWithExecutor(executor)); - } - - private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception { - // TODO: 1. Move this outside of ConnectivityServiceTest. - // 2. Make test to verify that Nat-T keepalive socket is created by IpSecService. - // 3. Mock ipsec service. - final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); - final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35"); - final InetAddress myIPv6 = InetAddress.getByName("2001:db8::1"); - final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); - final InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888"); - - final int validKaInterval = 15; - final int invalidKaInterval = 9; - - final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); - final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(); - final int srcPort = testSocket.getPort(); - - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("wlan12"); - lp.addLinkAddress(new LinkAddress(myIPv6, 64)); - lp.addLinkAddress(new LinkAddress(myIPv4, 25)); - lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); - lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); - - Network notMyNet = new Network(61234); - Network myNet = connectKeepaliveNetwork(lp); - - TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); - - // Attempt to start keepalives with invalid parameters and check for errors. - // Invalid network. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - notMyNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); - } - - // Invalid interval. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(invalidKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_INTERVAL); - } - - // Invalid destination. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv6, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); - } - - // Invalid source; - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv6, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); - } - - // NAT-T is only supported for IPv4. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv6, dstIPv6, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); - } - - // Basic check before testing started keepalive. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_UNSUPPORTED); - } - - // Check that a started keepalive can be stopped. - mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); - ka.stop(); - callback.expectStopped(); - - // Check that keepalive could be restarted. - ka.start(validKaInterval); - callback.expectStarted(); - ka.stop(); - callback.expectStopped(); - - // Check that keepalive can be restarted without waiting for callback. - ka.start(validKaInterval); - callback.expectStarted(); - ka.stop(); - ka.start(validKaInterval); - callback.expectStopped(); - callback.expectStarted(); - ka.stop(); - callback.expectStopped(); - } - - // Check that deleting the IP address stops the keepalive. - LinkProperties bogusLp = new LinkProperties(lp); - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - bogusLp.removeLinkAddress(new LinkAddress(myIPv4, 25)); - bogusLp.addLinkAddress(new LinkAddress(notMyIPv4, 25)); - mWiFiNetworkAgent.sendLinkProperties(bogusLp); - callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS); - mWiFiNetworkAgent.sendLinkProperties(lp); - } - - // Check that a started keepalive is stopped correctly when the network disconnects. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); - - // ... and that stopping it after that has no adverse effects. - waitForIdle(); - final Network myNetAlias = myNet; - assertNull(mCm.getNetworkCapabilities(myNetAlias)); - ka.stop(); - callback.assertNoCallback(); - } - - // Reconnect. - myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); - - // Check that a stop followed by network disconnects does not result in crash. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - // Delay the response of keepalive events in networkAgent long enough to make sure - // the follow-up network disconnection will be processed first. - mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS); - ka.stop(); - - // Make sure the stop has been processed. Wait for executor idle is needed to prevent - // flaky since the actual stop call to the service is delegated to executor thread. - waitForIdleSerialExecutor(executor, TIMEOUT_MS); - waitForIdle(); - - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - callback.expectStopped(); - callback.assertNoCallback(); - } - - // Reconnect. - waitForIdle(); - myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); - - // Check that keepalive slots start from 1 and increment. The first one gets slot 1. - mWiFiNetworkAgent.setExpectedKeepaliveSlot(1); - int srcPort2 = 0; - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocket, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - - // The second one gets slot 2. - mWiFiNetworkAgent.setExpectedKeepaliveSlot(2); - final UdpEncapsulationSocket testSocket2 = mIpSec.openUdpEncapsulationSocket(); - srcPort2 = testSocket2.getPort(); - TestSocketKeepaliveCallback callback2 = new TestSocketKeepaliveCallback(executor); - try (SocketKeepalive ka2 = mCm.createSocketKeepalive( - myNet, testSocket2, myIPv4, dstIPv4, executor, callback2)) { - ka2.start(validKaInterval); - callback2.expectStarted(); - - ka.stop(); - callback.expectStopped(); - - ka2.stop(); - callback2.expectStopped(); - - testSocket.close(); - testSocket2.close(); - } - } - - // Check that there is no port leaked after all keepalives and sockets are closed. - // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7. - // assertFalse(isUdpPortInUse(srcPort)); - // assertFalse(isUdpPortInUse(srcPort2)); - - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - mWiFiNetworkAgent = null; - } - - @Test - public void testTcpSocketKeepalives() throws Exception { - runTestWithSerialExecutors(executor -> doTestTcpSocketKeepalivesWithExecutor(executor)); - } - - private void doTestTcpSocketKeepalivesWithExecutor(Executor executor) throws Exception { - final int srcPortV4 = 12345; - final int srcPortV6 = 23456; - final InetAddress myIPv4 = InetAddress.getByName("127.0.0.1"); - final InetAddress myIPv6 = InetAddress.getByName("::1"); - - final int validKaInterval = 15; - - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("wlan12"); - lp.addLinkAddress(new LinkAddress(myIPv6, 64)); - lp.addLinkAddress(new LinkAddress(myIPv4, 25)); - lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234"))); - lp.addRoute(new RouteInfo(InetAddress.getByName("127.0.0.254"))); - - final Network notMyNet = new Network(61234); - final Network myNet = connectKeepaliveNetwork(lp); - - final Socket testSocketV4 = new Socket(); - final Socket testSocketV6 = new Socket(); - - TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); - - // Attempt to start Tcp keepalives with invalid parameters and check for errors. - // Invalid network. - try (SocketKeepalive ka = mCm.createSocketKeepalive( - notMyNet, testSocketV4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_NETWORK); - } - - // Invalid Socket (socket is not bound with IPv4 address). - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocketV4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); - } - - // Invalid Socket (socket is not bound with IPv6 address). - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocketV6, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); - } - - // Bind the socket address - testSocketV4.bind(new InetSocketAddress(myIPv4, srcPortV4)); - testSocketV6.bind(new InetSocketAddress(myIPv6, srcPortV6)); - - // Invalid Socket (socket is bound with IPv4 address). - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocketV4, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); - } - - // Invalid Socket (socket is bound with IPv6 address). - try (SocketKeepalive ka = mCm.createSocketKeepalive( - myNet, testSocketV6, executor, callback)) { - ka.start(validKaInterval); - callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET); - } - - testSocketV4.close(); - testSocketV6.close(); - - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - mWiFiNetworkAgent = null; - } - - private void doTestNattSocketKeepalivesFdWithExecutor(Executor executor) throws Exception { - final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129"); - final InetAddress anyIPv4 = InetAddress.getByName("0.0.0.0"); - final InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8"); - final int validKaInterval = 15; - - // Prepare the target network. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("wlan12"); - lp.addLinkAddress(new LinkAddress(myIPv4, 25)); - lp.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254"))); - Network myNet = connectKeepaliveNetwork(lp); - mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS); - mWiFiNetworkAgent.setStopKeepaliveEvent(SocketKeepalive.SUCCESS); - - TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(executor); - - // Prepare the target file descriptor, keep only one instance. - final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); - final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(); - final int srcPort = testSocket.getPort(); - final ParcelFileDescriptor testPfd = - ParcelFileDescriptor.dup(testSocket.getFileDescriptor()); - testSocket.close(); - assertTrue(isUdpPortInUse(srcPort)); - - // Start keepalive and explicit make the variable goes out of scope with try-with-resources - // block. - try (SocketKeepalive ka = mCm.createNattKeepalive( - myNet, testPfd, myIPv4, dstIPv4, executor, callback)) { - ka.start(validKaInterval); - callback.expectStarted(); - ka.stop(); - callback.expectStopped(); - } - - // Check that the ParcelFileDescriptor is still valid after keepalive stopped, - // ErrnoException with EBADF will be thrown if the socket is closed when checking local - // address. - assertTrue(isUdpPortInUse(srcPort)); - final InetSocketAddress sa = - (InetSocketAddress) Os.getsockname(testPfd.getFileDescriptor()); - assertEquals(anyIPv4, sa.getAddress()); - - testPfd.close(); - // TODO: enable this check after ensuring a valid free port. See b/129512753#comment7. - // assertFalse(isUdpPortInUse(srcPort)); - - mWiFiNetworkAgent.disconnect(); - mWiFiNetworkAgent.expectDisconnected(); - mWiFiNetworkAgent = null; - } - - private static boolean isUdpPortInUse(int port) { - try (DatagramSocket ignored = new DatagramSocket(port)) { - return false; - } catch (IOException alreadyInUse) { - return true; - } - } - - @Test - public void testGetCaptivePortalServerUrl() throws Exception { - String url = mCm.getCaptivePortalServerUrl(); - assertEquals("http://connectivitycheck.gstatic.com/generate_204", url); - } - - private static class TestNetworkPinner extends NetworkPinner { - public static boolean awaitPin(int timeoutMs) throws InterruptedException { - synchronized(sLock) { - if (sNetwork == null) { - sLock.wait(timeoutMs); - } - return sNetwork != null; - } - } - - public static boolean awaitUnpin(int timeoutMs) throws InterruptedException { - synchronized(sLock) { - if (sNetwork != null) { - sLock.wait(timeoutMs); - } - return sNetwork == null; - } - } - } - - private void assertPinnedToWifiWithCellDefault() { - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getBoundNetworkForProcess()); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - } - - private void assertPinnedToWifiWithWifiDefault() { - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getBoundNetworkForProcess()); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - } - - private void assertNotPinnedToWifi() { - assertNull(mCm.getBoundNetworkForProcess()); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - } - - @Test - public void testNetworkPinner() throws Exception { - NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI) - .build(); - assertNull(mCm.getBoundNetworkForProcess()); - - TestNetworkPinner.pin(mServiceContext, wifiRequest); - assertNull(mCm.getBoundNetworkForProcess()); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - - // When wi-fi connects, expect to be pinned. - assertTrue(TestNetworkPinner.awaitPin(100)); - assertPinnedToWifiWithCellDefault(); - - // Disconnect and expect the pin to drop. - mWiFiNetworkAgent.disconnect(); - assertTrue(TestNetworkPinner.awaitUnpin(100)); - assertNotPinnedToWifi(); - - // Reconnecting does not cause the pin to come back. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - assertFalse(TestNetworkPinner.awaitPin(100)); - assertNotPinnedToWifi(); - - // Pinning while connected causes the pin to take effect immediately. - TestNetworkPinner.pin(mServiceContext, wifiRequest); - assertTrue(TestNetworkPinner.awaitPin(100)); - assertPinnedToWifiWithCellDefault(); - - // Explicitly unpin and expect to use the default network again. - TestNetworkPinner.unpin(); - assertNotPinnedToWifi(); - - // Disconnect cell and wifi. - ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. - mCellNetworkAgent.disconnect(); - mWiFiNetworkAgent.disconnect(); - b.expectBroadcast(); - - // Pinning takes effect even if the pinned network is the default when the pin is set... - TestNetworkPinner.pin(mServiceContext, wifiRequest); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - assertTrue(TestNetworkPinner.awaitPin(100)); - assertPinnedToWifiWithWifiDefault(); - - // ... and is maintained even when that network is no longer the default. - b = registerConnectivityBroadcast(1); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mCellNetworkAgent.connect(true); - b.expectBroadcast(); - assertPinnedToWifiWithCellDefault(); - } - - @Test - public void testNetworkCallbackMaximum() throws Exception { - final int MAX_REQUESTS = 100; - final int CALLBACKS = 89; - final int INTENTS = 11; - final int SYSTEM_ONLY_MAX_REQUESTS = 250; - assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS); - - NetworkRequest networkRequest = new NetworkRequest.Builder().build(); - ArrayList registered = new ArrayList<>(); - - int j = 0; - while (j++ < CALLBACKS / 2) { - NetworkCallback cb = new NetworkCallback(); - mCm.requestNetwork(networkRequest, cb); - registered.add(cb); - } - while (j++ < CALLBACKS) { - NetworkCallback cb = new NetworkCallback(); - mCm.registerNetworkCallback(networkRequest, cb); - registered.add(cb); - } - j = 0; - while (j++ < INTENTS / 2) { - final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, - new Intent("a" + j), FLAG_IMMUTABLE); - mCm.requestNetwork(networkRequest, pi); - registered.add(pi); - } - while (j++ < INTENTS) { - final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */, - new Intent("b" + j), FLAG_IMMUTABLE); - mCm.registerNetworkCallback(networkRequest, pi); - registered.add(pi); - } - - // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added. - assertThrows(TooManyRequestsException.class, () -> - mCm.requestNetwork(networkRequest, new NetworkCallback()) - ); - assertThrows(TooManyRequestsException.class, () -> - mCm.registerNetworkCallback(networkRequest, new NetworkCallback()) - ); - assertThrows(TooManyRequestsException.class, () -> - mCm.requestNetwork(networkRequest, - PendingIntent.getBroadcast(mContext, 0 /* requestCode */, - new Intent("c"), FLAG_IMMUTABLE)) - ); - assertThrows(TooManyRequestsException.class, () -> - mCm.registerNetworkCallback(networkRequest, - PendingIntent.getBroadcast(mContext, 0 /* requestCode */, - new Intent("d"), FLAG_IMMUTABLE)) - ); - - // The system gets another SYSTEM_ONLY_MAX_REQUESTS slots. - final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); - withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { - ArrayList systemRegistered = new ArrayList<>(); - for (int i = 0; i < SYSTEM_ONLY_MAX_REQUESTS - 1; i++) { - NetworkCallback cb = new NetworkCallback(); - if (i % 2 == 0) { - mCm.registerDefaultNetworkCallbackForUid(1000000 + i, cb, handler); - } else { - mCm.registerNetworkCallback(networkRequest, cb); - } - systemRegistered.add(cb); - } - waitForIdle(); - - assertThrows(TooManyRequestsException.class, () -> - mCm.registerDefaultNetworkCallbackForUid(1001042, new NetworkCallback(), - handler)); - assertThrows(TooManyRequestsException.class, () -> - mCm.registerNetworkCallback(networkRequest, new NetworkCallback())); - - for (NetworkCallback callback : systemRegistered) { - mCm.unregisterNetworkCallback(callback); - } - waitForIdle(); // Wait for requests to be unregistered before giving up the permission. - }); - - for (Object o : registered) { - if (o instanceof NetworkCallback) { - mCm.unregisterNetworkCallback((NetworkCallback)o); - } - if (o instanceof PendingIntent) { - mCm.unregisterNetworkCallback((PendingIntent)o); - } - } - waitForIdle(); - - // Test that the limit is not hit when MAX_REQUESTS requests are added and removed. - for (int i = 0; i < MAX_REQUESTS; i++) { - NetworkCallback networkCallback = new NetworkCallback(); - mCm.requestNetwork(networkRequest, networkCallback); - mCm.unregisterNetworkCallback(networkCallback); - } - waitForIdle(); - - for (int i = 0; i < MAX_REQUESTS; i++) { - NetworkCallback networkCallback = new NetworkCallback(); - mCm.registerNetworkCallback(networkRequest, networkCallback); - mCm.unregisterNetworkCallback(networkCallback); - } - waitForIdle(); - - for (int i = 0; i < MAX_REQUESTS; i++) { - NetworkCallback networkCallback = new NetworkCallback(); - mCm.registerDefaultNetworkCallback(networkCallback); - mCm.unregisterNetworkCallback(networkCallback); - } - waitForIdle(); - - for (int i = 0; i < MAX_REQUESTS; i++) { - NetworkCallback networkCallback = new NetworkCallback(); - mCm.registerDefaultNetworkCallback(networkCallback); - mCm.unregisterNetworkCallback(networkCallback); - } - waitForIdle(); - - withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { - for (int i = 0; i < MAX_REQUESTS; i++) { - NetworkCallback networkCallback = new NetworkCallback(); - mCm.registerDefaultNetworkCallbackForUid(1000000 + i, networkCallback, - new Handler(ConnectivityThread.getInstanceLooper())); - mCm.unregisterNetworkCallback(networkCallback); - } - }); - waitForIdle(); - - for (int i = 0; i < MAX_REQUESTS; i++) { - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE); - mCm.requestNetwork(networkRequest, pendingIntent); - mCm.unregisterNetworkCallback(pendingIntent); - } - waitForIdle(); - - for (int i = 0; i < MAX_REQUESTS; i++) { - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, 0 /* requestCode */, new Intent("f" + i), FLAG_IMMUTABLE); - mCm.registerNetworkCallback(networkRequest, pendingIntent); - mCm.unregisterNetworkCallback(pendingIntent); - } - } - - @Test - public void testNetworkInfoOfTypeNone() throws Exception { - ExpectedBroadcast b = registerConnectivityBroadcast(1); - - verifyNoNetwork(); - TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); - assertNull(mCm.getActiveNetworkInfo()); - - Network[] allNetworks = mCm.getAllNetworks(); - assertLength(1, allNetworks); - Network network = allNetworks[0]; - NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network); - assertTrue(capabilities.hasTransport(TRANSPORT_WIFI_AWARE)); - - final NetworkRequest request = - new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI_AWARE).build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - // Bring up wifi aware network. - wifiAware.connect(false, false, false /* isStrictMode */); - callback.expectAvailableCallbacksUnvalidated(wifiAware); - - assertNull(mCm.getActiveNetworkInfo()); - assertNull(mCm.getActiveNetwork()); - // TODO: getAllNetworkInfo is dirty and returns a non-empty array right from the start - // of this test. Fix it and uncomment the assert below. - //assertEmpty(mCm.getAllNetworkInfo()); - - // Disconnect wifi aware network. - wifiAware.disconnect(); - callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackEntry.Lost); - mCm.unregisterNetworkCallback(callback); - - verifyNoNetwork(); - b.expectNoBroadcast(10); - } - - @Test - public void testDeprecatedAndUnsupportedOperations() throws Exception { - final int TYPE_NONE = ConnectivityManager.TYPE_NONE; - assertNull(mCm.getNetworkInfo(TYPE_NONE)); - assertNull(mCm.getNetworkForType(TYPE_NONE)); - assertNull(mCm.getLinkProperties(TYPE_NONE)); - assertFalse(mCm.isNetworkSupported(TYPE_NONE)); - - assertThrows(IllegalArgumentException.class, - () -> mCm.networkCapabilitiesForType(TYPE_NONE)); - - Class unsupported = UnsupportedOperationException.class; - assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_WIFI, "")); - assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_WIFI, "")); - // TODO: let test context have configuration application target sdk version - // and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED - assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_NONE, "")); - assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_NONE, "")); - assertThrows(unsupported, () -> mCm.requestRouteToHostAddress(TYPE_NONE, null)); - } - - @Test - public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() throws Exception { - final NetworkRequest networkRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(networkRequest, networkCallback); - - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(WIFI_IFNAME); - LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24"); - RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null, - InetAddresses.parseNumericAddress("192.168.12.1"), lp.getInterfaceName()); - lp.addLinkAddress(myIpv4Address); - lp.addRoute(myIpv4DefaultRoute); - - // Verify direct routes are added when network agent is first registered in - // ConnectivityService. - TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); - networkAgent.connect(true); - networkCallback.expectCallback(CallbackEntry.AVAILABLE, networkAgent); - networkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent); - CallbackEntry.LinkPropertiesChanged cbi = - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - networkAgent); - networkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, networkAgent); - networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent); - networkCallback.assertNoCallback(); - checkDirectlyConnectedRoutes(cbi.getLp(), Arrays.asList(myIpv4Address), - Arrays.asList(myIpv4DefaultRoute)); - checkDirectlyConnectedRoutes(mCm.getLinkProperties(networkAgent.getNetwork()), - Arrays.asList(myIpv4Address), Arrays.asList(myIpv4DefaultRoute)); - - // Verify direct routes are added during subsequent link properties updates. - LinkProperties newLp = new LinkProperties(lp); - LinkAddress myIpv6Address1 = new LinkAddress("fe80::cafe/64"); - LinkAddress myIpv6Address2 = new LinkAddress("2001:db8::2/64"); - newLp.addLinkAddress(myIpv6Address1); - newLp.addLinkAddress(myIpv6Address2); - networkAgent.sendLinkProperties(newLp); - cbi = networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent); - networkCallback.assertNoCallback(); - checkDirectlyConnectedRoutes(cbi.getLp(), - Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2), - Arrays.asList(myIpv4DefaultRoute)); - mCm.unregisterNetworkCallback(networkCallback); - } - - private void assertSameElementsNoDuplicates(T[] expected, T[] actual) { - // Easier to implement than a proper "assertSameElements" method that also correctly deals - // with duplicates. - final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual); - assertEquals(msg, expected.length, actual.length); - Set expectedSet = new ArraySet<>(Arrays.asList(expected)); - assertEquals("expected contains duplicates", expectedSet.size(), expected.length); - // actual cannot have duplicates because it's the same length and has the same elements. - Set actualSet = new ArraySet<>(Arrays.asList(actual)); - assertEquals(expectedSet, actualSet); - } - - private void expectNetworkStatus(Network[] networks, String defaultIface, - Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { - ArgumentCaptor> networksCaptor = ArgumentCaptor.forClass(List.class); - ArgumentCaptor> vpnInfosCaptor = - ArgumentCaptor.forClass(List.class); - - verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(), - any(List.class), eq(defaultIface), vpnInfosCaptor.capture()); - - assertSameElementsNoDuplicates(networksCaptor.getValue().toArray(), networks); - - UnderlyingNetworkInfo[] infos = - vpnInfosCaptor.getValue().toArray(new UnderlyingNetworkInfo[0]); - if (vpnUid != null) { - assertEquals("Should have exactly one VPN:", 1, infos.length); - UnderlyingNetworkInfo info = infos[0]; - assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid()); - assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface()); - assertSameElementsNoDuplicates(underlyingIfaces, - info.getUnderlyingInterfaces().toArray(new String[0])); - } else { - assertEquals(0, infos.length); - return; - } - } - - private void expectNetworkStatus( - Network[] networks, String defaultIface) throws Exception { - expectNetworkStatus(networks, defaultIface, null, null, new String[0]); - } - - @Test - public void testStatsIfacesChanged() throws Exception { - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - - final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; - final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; - - LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - LinkProperties wifiLp = new LinkProperties(); - wifiLp.setInterfaceName(WIFI_IFNAME); - - // Simple connection should have updated ifaces - mCellNetworkAgent.connect(false); - mCellNetworkAgent.sendLinkProperties(cellLp); - waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); - reset(mStatsManager); - - // Default network switch should update ifaces. - mWiFiNetworkAgent.connect(false); - mWiFiNetworkAgent.sendLinkProperties(wifiLp); - waitForIdle(); - assertEquals(wifiLp, mService.getActiveLinkProperties()); - expectNetworkStatus(onlyWifi, WIFI_IFNAME); - reset(mStatsManager); - - // Disconnect should update ifaces. - mWiFiNetworkAgent.disconnect(); - waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); - reset(mStatsManager); - - // Metered change should update ifaces - mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); - reset(mStatsManager); - - mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); - reset(mStatsManager); - - // Temp metered change shouldn't update ifaces - mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); - waitForIdle(); - verify(mStatsManager, never()).notifyNetworkStatus(eq(Arrays.asList(onlyCell)), - any(List.class), eq(MOBILE_IFNAME), any(List.class)); - reset(mStatsManager); - - // Roaming change should update ifaces - mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - waitForIdle(); - expectNetworkStatus(onlyCell, MOBILE_IFNAME); - reset(mStatsManager); - - // Test VPNs. - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(VPN_IFNAME); - - mMockVpn.establishForMyUid(lp); - assertUidRangesUpdatedForMyUid(true); - - final Network[] cellAndVpn = new Network[] { - mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; - - // A VPN with default (null) underlying networks sets the underlying network's interfaces... - expectNetworkStatus(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME}); - - // ...and updates them as the default network switches. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - mWiFiNetworkAgent.sendLinkProperties(wifiLp); - final Network[] onlyNull = new Network[]{null}; - final Network[] wifiAndVpn = new Network[] { - mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; - final Network[] cellAndWifi = new Network[] { - mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; - final Network[] cellNullAndWifi = new Network[] { - mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()}; - - waitForIdle(); - assertEquals(wifiLp, mService.getActiveLinkProperties()); - expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{WIFI_IFNAME}); - reset(mStatsManager); - - // A VPN that sets its underlying networks passes the underlying interfaces, and influences - // the default interface sent to NetworkStatsService by virtue of applying to the system - // server UID (or, in this test, to the test's UID). This is the reason for sending - // MOBILE_IFNAME even though the default network is wifi. - // TODO: fix this to pass in the actual default network interface. Whether or not the VPN - // applies to the system server UID should not have any bearing on network stats. - mMockVpn.setUnderlyingNetworks(onlyCell); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME}); - reset(mStatsManager); - - mMockVpn.setUnderlyingNetworks(cellAndWifi); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); - reset(mStatsManager); - - // Null underlying networks are ignored. - mMockVpn.setUnderlyingNetworks(cellNullAndWifi); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); - reset(mStatsManager); - - // If an underlying network disconnects, that interface should no longer be underlying. - // This doesn't actually work because disconnectAndDestroyNetwork only notifies - // NetworkStatsService before the underlying network is actually removed. So the underlying - // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This - // could result in incorrect data usage measurements if the interface used by the - // disconnected network is reused by a system component that does not register an agent for - // it (e.g., tethering). - mCellNetworkAgent.disconnect(); - waitForIdle(); - assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork())); - expectNetworkStatus(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{MOBILE_IFNAME, WIFI_IFNAME}); - - // Confirm that we never tell NetworkStatsService that cell is no longer the underlying - // network for the VPN... - verify(mStatsManager, never()).notifyNetworkStatus(any(List.class), - any(List.class), any() /* anyString() doesn't match null */, - argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1 - && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0)))); - verifyNoMoreInteractions(mStatsManager); - reset(mStatsManager); - - // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be - // called again, it does. For example, connect Ethernet, but with a low score, such that it - // does not become the default network. - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.setScore( - new NetworkScore.Builder().setLegacyInt(30).setExiting(true).build()); - mEthernetNetworkAgent.connect(false); - waitForIdle(); - verify(mStatsManager).notifyNetworkStatus(any(List.class), - any(List.class), any() /* anyString() doesn't match null */, - argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1 - && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0)))); - mEthernetNetworkAgent.disconnect(); - waitForIdle(); - reset(mStatsManager); - - // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo - // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes - // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which - // is probably a performance improvement (though it's very unlikely that a VPN would declare - // no underlying networks). - // Also, for the same reason as above, the active interface passed in is null. - mMockVpn.setUnderlyingNetworks(new Network[0]); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); - reset(mStatsManager); - - // Specifying only a null underlying network is the same as no networks. - mMockVpn.setUnderlyingNetworks(onlyNull); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); - reset(mStatsManager); - - // Specifying networks that are all disconnected is the same as specifying no networks. - mMockVpn.setUnderlyingNetworks(onlyCell); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, null); - reset(mStatsManager); - - // Passing in null again means follow the default network again. - mMockVpn.setUnderlyingNetworks(null); - waitForIdle(); - expectNetworkStatus(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, - new String[]{WIFI_IFNAME}); - reset(mStatsManager); - } - - @Test - public void testBasicDnsConfigurationPushed() throws Exception { - setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - - // Clear any interactions that occur as a result of CS starting up. - reset(mMockDnsResolver); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - waitForIdle(); - verify(mMockDnsResolver, never()).setResolverConfiguration(any()); - verifyNoMoreInteractions(mMockDnsResolver); - - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does - // "is-reachable" testing in order to not program netd with unreachable - // nameservers that it might try repeated to validate. - cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24")); - cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), - MOBILE_IFNAME)); - cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); - cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), - MOBILE_IFNAME)); - mCellNetworkAgent.sendLinkProperties(cellLp); - mCellNetworkAgent.connect(false); - waitForIdle(); - - verify(mMockDnsResolver, times(1)).createNetworkCache( - eq(mCellNetworkAgent.getNetwork().netId)); - // CS tells dnsresolver about the empty DNS config for this network. - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); - reset(mMockDnsResolver); - - cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); - mCellNetworkAgent.sendLinkProperties(cellLp); - waitForIdle(); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(1, resolvrParams.servers.length); - assertTrue(ArrayUtils.contains(resolvrParams.servers, "2001:db8::1")); - // Opportunistic mode. - assertTrue(ArrayUtils.contains(resolvrParams.tlsServers, "2001:db8::1")); - reset(mMockDnsResolver); - - cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); - mCellNetworkAgent.sendLinkProperties(cellLp); - waitForIdle(); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(2, resolvrParams.servers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[]{"2001:db8::1", "192.0.2.1"})); - // Opportunistic mode. - assertEquals(2, resolvrParams.tlsServers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[]{"2001:db8::1", "192.0.2.1"})); - reset(mMockDnsResolver); - - final String TLS_SPECIFIER = "tls.example.com"; - final String TLS_SERVER6 = "2001:db8:53::53"; - final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) }; - final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 }; - mCellNetworkAgent.mNmCallbacks.notifyPrivateDnsConfigResolved( - new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel()); - - waitForIdle(); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(2, resolvrParams.servers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[]{"2001:db8::1", "192.0.2.1"})); - reset(mMockDnsResolver); - } - - @Test - public void testDnsConfigurationTransTypesPushed() throws Exception { - // Clear any interactions that occur as a result of CS starting up. - reset(mMockDnsResolver); - - final NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) - .build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - verify(mMockDnsResolver, times(1)).createNetworkCache( - eq(mWiFiNetworkAgent.getNetwork().netId)); - verify(mMockDnsResolver, times(2)).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - final ResolverParamsParcel resolverParams = mResolverParamsParcelCaptor.getValue(); - assertContainsExactly(resolverParams.transportTypes, TRANSPORT_WIFI); - reset(mMockDnsResolver); - } - - @Test - public void testPrivateDnsNotification() throws Exception { - NetworkRequest request = new NetworkRequest.Builder() - .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) - .build(); - TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - // Bring up wifi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - // Private DNS resolution failed, checking if the notification will be shown or not. - mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); - mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - waitForIdle(); - // If network validation failed, NetworkMonitor will re-evaluate the network. - // ConnectivityService should filter the redundant notification. This part is trying to - // simulate that situation and check if ConnectivityService could filter that case. - mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notify(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); - // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be - // shown. - mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */); - mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancel(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId)); - // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be - // shown again. - mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */); - mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - waitForIdle(); - verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notify(anyString(), - eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any()); - } - - @Test - public void testPrivateDnsSettingsChange() throws Exception { - // Clear any interactions that occur as a result of CS starting up. - reset(mMockDnsResolver); - - // The default on Android is opportunistic mode ("Automatic"). - setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.requestNetwork(cellRequest, cellNetworkCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - waitForIdle(); - // CS tells netd about the empty DNS config for this network. - verify(mMockDnsResolver, never()).setResolverConfiguration(any()); - verifyNoMoreInteractions(mMockDnsResolver); - - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does - // "is-reachable" testing in order to not program netd with unreachable - // nameservers that it might try repeated to validate. - cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24")); - cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), - MOBILE_IFNAME)); - cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); - cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), - MOBILE_IFNAME)); - cellLp.addDnsServer(InetAddress.getByName("2001:db8::1")); - cellLp.addDnsServer(InetAddress.getByName("192.0.2.1")); - - mCellNetworkAgent.sendLinkProperties(cellLp); - mCellNetworkAgent.connect(false); - waitForIdle(); - verify(mMockDnsResolver, times(1)).createNetworkCache( - eq(mCellNetworkAgent.getNetwork().netId)); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(2, resolvrParams.tlsServers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[] { "2001:db8::1", "192.0.2.1" })); - // Opportunistic mode. - assertEquals(2, resolvrParams.tlsServers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[] { "2001:db8::1", "192.0.2.1" })); - reset(mMockDnsResolver); - cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, - mCellNetworkAgent); - CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback( - CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertFalse(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - - setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); - verify(mMockDnsResolver, times(1)).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(2, resolvrParams.servers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[] { "2001:db8::1", "192.0.2.1" })); - reset(mMockDnsResolver); - cellNetworkCallback.assertNoCallback(); - - setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(2, resolvrParams.servers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[] { "2001:db8::1", "192.0.2.1" })); - assertEquals(2, resolvrParams.tlsServers.length); - assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[] { "2001:db8::1", "192.0.2.1" })); - reset(mMockDnsResolver); - cellNetworkCallback.assertNoCallback(); - - setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com"); - // Can't test dns configuration for strict mode without properly mocking - // out the DNS lookups, but can test that LinkProperties is updated. - cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertTrue(cbi.getLp().isPrivateDnsActive()); - assertEquals("strict.example.com", cbi.getLp().getPrivateDnsServerName()); - } - - private PrivateDnsValidationEventParcel makePrivateDnsValidationEvent( - final int netId, final String ipAddress, final String hostname, final int validation) { - final PrivateDnsValidationEventParcel event = new PrivateDnsValidationEventParcel(); - event.netId = netId; - event.ipAddress = ipAddress; - event.hostname = hostname; - event.validation = validation; - return event; - } - - @Test - public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception { - // The default on Android is opportunistic mode ("Automatic"). - setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com"); - - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.requestNetwork(cellRequest, cellNetworkCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - waitForIdle(); - LinkProperties lp = new LinkProperties(); - mCellNetworkAgent.sendLinkProperties(lp); - mCellNetworkAgent.connect(false); - waitForIdle(); - cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, - mCellNetworkAgent); - CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback( - CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertFalse(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - Set dnsServers = new HashSet<>(); - checkDnsServers(cbi.getLp(), dnsServers); - - // Send a validation event for a server that is not part of the current - // resolver config. The validation event should be ignored. - mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( - makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, "", - "145.100.185.18", VALIDATION_RESULT_SUCCESS)); - cellNetworkCallback.assertNoCallback(); - - // Add a dns server to the LinkProperties. - LinkProperties lp2 = new LinkProperties(lp); - lp2.addDnsServer(InetAddress.getByName("145.100.185.16")); - mCellNetworkAgent.sendLinkProperties(lp2); - cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertFalse(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - dnsServers.add(InetAddress.getByName("145.100.185.16")); - checkDnsServers(cbi.getLp(), dnsServers); - - // Send a validation event containing a hostname that is not part of - // the current resolver config. The validation event should be ignored. - mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( - makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, - "145.100.185.16", "hostname", VALIDATION_RESULT_SUCCESS)); - cellNetworkCallback.assertNoCallback(); - - // Send a validation event where validation failed. - mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( - makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, - "145.100.185.16", "", VALIDATION_RESULT_FAILURE)); - cellNetworkCallback.assertNoCallback(); - - // Send a validation event where validation succeeded for a server in - // the current resolver config. A LinkProperties callback with updated - // private dns fields should be sent. - mService.mResolverUnsolEventCallback.onPrivateDnsValidationEvent( - makePrivateDnsValidationEvent(mCellNetworkAgent.getNetwork().netId, - "145.100.185.16", "", VALIDATION_RESULT_SUCCESS)); - cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertTrue(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - checkDnsServers(cbi.getLp(), dnsServers); - - // The private dns fields in LinkProperties should be preserved when - // the network agent sends unrelated changes. - LinkProperties lp3 = new LinkProperties(lp2); - lp3.setMtu(1300); - mCellNetworkAgent.sendLinkProperties(lp3); - cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertTrue(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - checkDnsServers(cbi.getLp(), dnsServers); - assertEquals(1300, cbi.getLp().getMtu()); - - // Removing the only validated server should affect the private dns - // fields in LinkProperties. - LinkProperties lp4 = new LinkProperties(lp3); - lp4.removeDnsServer(InetAddress.getByName("145.100.185.16")); - mCellNetworkAgent.sendLinkProperties(lp4); - cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - cellNetworkCallback.assertNoCallback(); - assertFalse(cbi.getLp().isPrivateDnsActive()); - assertNull(cbi.getLp().getPrivateDnsServerName()); - dnsServers.remove(InetAddress.getByName("145.100.185.16")); - checkDnsServers(cbi.getLp(), dnsServers); - assertEquals(1300, cbi.getLp().getMtu()); - } - - private void checkDirectlyConnectedRoutes(Object callbackObj, - Collection linkAddresses, Collection otherRoutes) { - assertTrue(callbackObj instanceof LinkProperties); - LinkProperties lp = (LinkProperties) callbackObj; - - Set expectedRoutes = new ArraySet<>(); - expectedRoutes.addAll(otherRoutes); - for (LinkAddress address : linkAddresses) { - RouteInfo localRoute = new RouteInfo(address, null, lp.getInterfaceName()); - // Duplicates in linkAddresses are considered failures - assertTrue(expectedRoutes.add(localRoute)); - } - List observedRoutes = lp.getRoutes(); - assertEquals(expectedRoutes.size(), observedRoutes.size()); - assertTrue(observedRoutes.containsAll(expectedRoutes)); - } - - private static void checkDnsServers(Object callbackObj, Set dnsServers) { - assertTrue(callbackObj instanceof LinkProperties); - LinkProperties lp = (LinkProperties) callbackObj; - assertEquals(dnsServers.size(), lp.getDnsServers().size()); - assertTrue(lp.getDnsServers().containsAll(dnsServers)); - } - - @Test - public void testApplyUnderlyingCapabilities() throws Exception { - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mCellNetworkAgent.connect(false /* validated */); - mWiFiNetworkAgent.connect(false /* validated */); - - final NetworkCapabilities cellNc = new NetworkCapabilities() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .setLinkDownstreamBandwidthKbps(10); - final NetworkCapabilities wifiNc = new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_METERED) - .addCapability(NET_CAPABILITY_NOT_ROAMING) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NET_CAPABILITY_NOT_SUSPENDED) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .setLinkUpstreamBandwidthKbps(20); - mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); - mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); - waitForIdle(); - - final Network mobile = mCellNetworkAgent.getNetwork(); - final Network wifi = mWiFiNetworkAgent.getNetwork(); - - final NetworkCapabilities initialCaps = new NetworkCapabilities(); - initialCaps.addTransportType(TRANSPORT_VPN); - initialCaps.addCapability(NET_CAPABILITY_INTERNET); - initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN); - - final NetworkCapabilities withNoUnderlying = new NetworkCapabilities(); - withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET); - withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED); - withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING); - withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - withNoUnderlying.addTransportType(TRANSPORT_VPN); - withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN); - - final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying); - withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); - withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); - withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - withMobileUnderlying.setLinkDownstreamBandwidthKbps(10); - - final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying); - withWifiUnderlying.addTransportType(TRANSPORT_WIFI); - withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); - withWifiUnderlying.setLinkUpstreamBandwidthKbps(20); - - final NetworkCapabilities withWifiAndMobileUnderlying = - new NetworkCapabilities(withNoUnderlying); - withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); - withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI); - withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); - withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); - withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10); - withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20); - - final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps); - initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED); - - NetworkCapabilities caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps); - assertEquals(withNoUnderlying, caps); - - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps); - assertEquals(withNoUnderlying, caps); - - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps); - assertEquals(withMobileUnderlying, caps); - - mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps); - assertEquals(withWifiUnderlying, caps); - - withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps); - assertEquals(withWifiUnderlying, caps); - - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps); - assertEquals(withWifiAndMobileUnderlying, caps); - - withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, - initialCapsNotMetered, caps); - assertEquals(withWifiAndMobileUnderlying, caps); - - caps = new NetworkCapabilities(initialCaps); - mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, - initialCapsNotMetered, caps); - assertEquals(withWifiAndMobileUnderlying, caps); - - mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps); - assertEquals(withWifiUnderlying, caps); - } - - @Test - public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { - final TestNetworkCallback callback = new TestNetworkCallback(); - final NetworkRequest request = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN).build(); - - mCm.registerNetworkCallback(request, callback); - - // Bring up a VPN that specifies an underlying network that does not exist yet. - // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet, - // (and doing so is difficult without using reflection) but it's good to test that the code - // behaves approximately correctly. - mMockVpn.establishForMyUid(false, true, false); - assertUidRangesUpdatedForMyUid(true); - final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); - mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork}); - callback.expectAvailableCallbacksUnvalidated(mMockVpn); - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasTransport(TRANSPORT_VPN)); - assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasTransport(TRANSPORT_WIFI)); - - // Make that underlying network connect, and expect to see its capabilities immediately - // reflected in the VPN's capabilities. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork()); - mWiFiNetworkAgent.connect(false); - // TODO: the callback for the VPN happens before any callbacks are called for the wifi - // network that has just connected. There appear to be two issues here: - // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for - // it returns non-null (which happens very early, during handleRegisterNetworkAgent). - // This is not correct because that that point the network is not connected and cannot - // pass any traffic. - // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities - // before rematching networks. - // Given that this scenario can't really happen, this is probably fine for now. - callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasTransport(TRANSPORT_VPN)); - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasTransport(TRANSPORT_WIFI)); - - // Disconnect the network, and expect to see the VPN capabilities change accordingly. - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - callback.expectCapabilitiesThat(mMockVpn, (nc) -> - nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN)); - - mMockVpn.disconnect(); - mCm.unregisterNetworkCallback(callback); - } - - private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) { - // What Chromium used to do before https://chromium-review.googlesource.com/2605304 - assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())", - expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected()); - } - - @Test - public void testVpnUnderlyingNetworkSuspended() throws Exception { - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(callback); - - // Connect a VPN. - mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, - false /* isStrictMode */); - callback.expectAvailableCallbacksUnvalidated(mMockVpn); - - // Connect cellular data. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false /* validated */); - callback.expectCapabilitiesThat(mMockVpn, - nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - callback.assertNoCallback(); - - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - - // Suspend the cellular network and expect the VPN to be suspended. - mCellNetworkAgent.suspend(); - callback.expectCapabilitiesThat(mMockVpn, - nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); - callback.assertNoCallback(); - - assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); - // VPN's main underlying network is suspended, so no connectivity. - assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); - - // Switch to another network. The VPN should no longer be suspended. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false /* validated */); - callback.expectCapabilitiesThat(mMockVpn, - nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_WIFI)); - callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); - callback.assertNoCallback(); - - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - - // Unsuspend cellular and then switch back to it. The VPN remains not suspended. - mCellNetworkAgent.resume(); - callback.assertNoCallback(); - mWiFiNetworkAgent.disconnect(); - callback.expectCapabilitiesThat(mMockVpn, - nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - // Spurious double callback? - callback.expectCapabilitiesThat(mMockVpn, - nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - callback.assertNoCallback(); - - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - - // Suspend cellular and expect no connectivity. - mCellNetworkAgent.suspend(); - callback.expectCapabilitiesThat(mMockVpn, - nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); - callback.assertNoCallback(); - - assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); - assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); - - // Resume cellular and expect that connectivity comes back. - mCellNetworkAgent.resume(); - callback.expectCapabilitiesThat(mMockVpn, - nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) - && nc.hasTransport(TRANSPORT_CELLULAR)); - callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); - callback.assertNoCallback(); - - assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) - .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); - } - - @Test - public void testVpnNetworkActive() throws Exception { - // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final int uid = Process.myUid(); - - final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); - final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build(); - final NetworkRequest genericRequest = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN).build(); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .addTransportType(TRANSPORT_VPN).build(); - mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); - mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback); - mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); - mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); - mCm.registerDefaultNetworkCallback(defaultCallback); - mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, - new Handler(ConnectivityThread.getInstanceLooper())); - defaultCallback.assertNoCallback(); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false); - - genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - vpnNetworkCallback.assertNoCallback(); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - final Set ranges = uidRangesForUids(uid); - mMockVpn.registerAgent(ranges); - mMockVpn.setUnderlyingNetworks(new Network[0]); - - // VPN networks do not satisfy the default request and are automatically validated - // by NetworkMonitor - assertFalse(NetworkMonitorUtils.isValidationRequired( - mMockVpn.getAgent().getNetworkCapabilities())); - mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); - - mMockVpn.connect(false); - - genericNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - genericNotVpnNetworkCallback.assertNoCallback(); - wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - systemDefaultCallback.assertNoCallback(); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - assertEquals(mWiFiNetworkAgent.getNetwork(), - systemDefaultCallback.getLastAvailableNetwork()); - - ranges.clear(); - mMockVpn.setUids(ranges); - - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - genericNotVpnNetworkCallback.assertNoCallback(); - wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - - // TODO : The default network callback should actually get a LOST call here (also see the - // comment below for AVAILABLE). This is because ConnectivityService does not look at UID - // ranges at all when determining whether a network should be rematched. In practice, VPNs - // can't currently update their UIDs without disconnecting, so this does not matter too - // much, but that is the reason the test here has to check for an update to the - // capabilities instead of the expected LOST then AVAILABLE. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); - systemDefaultCallback.assertNoCallback(); - - ranges.add(new UidRange(uid, uid)); - mMockVpn.setUids(ranges); - - genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); - genericNotVpnNetworkCallback.assertNoCallback(); - wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); - // TODO : Here like above, AVAILABLE would be correct, but because this can't actually - // happen outside of the test, ConnectivityService does not rematch callbacks. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); - systemDefaultCallback.assertNoCallback(); - - mWiFiNetworkAgent.disconnect(); - - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - genericNotVpnNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - vpnNetworkCallback.assertNoCallback(); - defaultCallback.assertNoCallback(); - systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - - mMockVpn.disconnect(); - - genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - genericNotVpnNetworkCallback.assertNoCallback(); - wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - systemDefaultCallback.assertNoCallback(); - assertEquals(null, mCm.getActiveNetwork()); - - mCm.unregisterNetworkCallback(genericNetworkCallback); - mCm.unregisterNetworkCallback(wifiNetworkCallback); - mCm.unregisterNetworkCallback(vpnNetworkCallback); - mCm.unregisterNetworkCallback(defaultCallback); - mCm.unregisterNetworkCallback(systemDefaultCallback); - } - - @Test - public void testVpnWithoutInternet() throws Exception { - final int uid = Process.myUid(); - - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - - defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - defaultCallback.assertNoCallback(); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mMockVpn.disconnect(); - defaultCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(defaultCallback); - } - - @Test - public void testVpnWithInternet() throws Exception { - final int uid = Process.myUid(); - - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - - defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - mMockVpn.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - - mCm.unregisterNetworkCallback(defaultCallback); - } - - @Test - public void testVpnUnvalidated() throws Exception { - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(callback); - - // Bring up Ethernet. - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); - callback.assertNoCallback(); - - // Bring up a VPN that has the INTERNET capability, initially unvalidated. - mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - // Even though the VPN is unvalidated, it becomes the default network for our app. - callback.expectAvailableCallbacksUnvalidated(mMockVpn); - callback.assertNoCallback(); - - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - - NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)); - assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET)); - - assertFalse(NetworkMonitorUtils.isValidationRequired( - mMockVpn.getAgent().getNetworkCapabilities())); - assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired( - mMockVpn.getAgent().getNetworkCapabilities())); - - // Pretend that the VPN network validates. - mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); - mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid()); - // Expect to see the validated capability, but no other changes, because the VPN is already - // the default network for the app. - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn); - callback.assertNoCallback(); - - mMockVpn.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mMockVpn); - callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent); - } - - @Test - public void testVpnStartsWithUnderlyingCaps() throws Exception { - final int uid = Process.myUid(); - - final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); - final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .addTransportType(TRANSPORT_VPN) - .build(); - mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); - vpnNetworkCallback.assertNoCallback(); - - // Connect cell. It will become the default network, and in the absence of setting - // underlying networks explicitly it will become the sole underlying network for the vpn. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - mCellNetworkAgent.connect(true); - - mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), - false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS, - nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)); - - final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertTrue(nc.hasTransport(TRANSPORT_VPN)); - assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(nc.hasTransport(TRANSPORT_WIFI)); - assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED)); - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); - } - - private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) { - final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser( - userId, "com.android.calling.package", "com.test"); - final String defaultCapsString = Arrays.toString(defaultCaps); - assertEquals(defaultCapsString, defaultCaps.length, networks.length); - final Set defaultCapsSet = new ArraySet<>(defaultCaps); - for (NetworkAgentWrapper network : networks) { - final NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork()); - final String msg = "Did not find " + nc + " in " + Arrays.toString(defaultCaps); - assertTrue(msg, defaultCapsSet.contains(nc)); - } - } - - @Test - public void testVpnSetUnderlyingNetworks() throws Exception { - final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); - final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .addTransportType(TRANSPORT_VPN) - .build(); - NetworkCapabilities nc; - mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); - vpnNetworkCallback.assertNoCallback(); - - mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertTrue(nc.hasTransport(TRANSPORT_VPN)); - assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(nc.hasTransport(TRANSPORT_WIFI)); - // For safety reasons a VPN without underlying networks is considered metered. - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); - // A VPN without underlying networks is not suspended. - assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); - - final int userId = UserHandle.getUserId(Process.myUid()); - assertDefaultNetworkCapabilities(userId /* no networks */); - - // Connect cell and use it as an underlying network. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - mCellNetworkAgent.connect(true); - - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - mWiFiNetworkAgent.connect(true); - - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); - - // Don't disconnect, but note the VPN is not using wifi any more. - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - // The return value of getDefaultNetworkCapabilitiesForUser always includes the default - // network (wifi) as well as the underlying networks (cell). - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); - - // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended. - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); - - // Add NOT_SUSPENDED again and observe VPN is no longer suspended. - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); - - // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. - mMockVpn.setUnderlyingNetworks( - new Network[] { mWiFiNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); - - // Use both again. - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); - - // Cell is suspended again. As WiFi is not, this should not cause a callback. - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.assertNoCallback(); - - // Stop using WiFi. The VPN is suspended again. - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); - - // Use both again. - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) - && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); - - // Disconnect cell. Receive update without even removing the dead network from the - // underlying networks – it's dead anyway. Not metered any more. - mCellNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); - - // Disconnect wifi too. No underlying networks means this is now metered. - mWiFiNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - // When a network disconnects, the callbacks are fired before all state is updated, so for a - // short time, synchronous calls will behave as if the network is still connected. Wait for - // things to settle. - waitForIdle(); - assertDefaultNetworkCapabilities(userId /* no networks */); - - mMockVpn.disconnect(); - } - - @Test - public void testNullUnderlyingNetworks() throws Exception { - final int uid = Process.myUid(); - - final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); - final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .addTransportType(TRANSPORT_VPN) - .build(); - NetworkCapabilities nc; - mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); - vpnNetworkCallback.assertNoCallback(); - - mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, - false /* isStrictMode */); - assertUidRangesUpdatedForMyUid(true); - - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertTrue(nc.hasTransport(TRANSPORT_VPN)); - assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(nc.hasTransport(TRANSPORT_WIFI)); - // By default, VPN is set to track default network (i.e. its underlying networks is null). - // In case of no default network, VPN is considered metered. - assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); - - // Connect to Cell; Cell is the default network. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - - // Connect to WiFi; WiFi is the new default. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) - && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - - // Disconnect Cell. The default network did not change, so there shouldn't be any changes in - // the capabilities. - mCellNetworkAgent.disconnect(); - - // Disconnect wifi too. Now we have no default network. - mWiFiNetworkAgent.disconnect(); - - vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, - (caps) -> caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) - && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - - mMockVpn.disconnect(); - } - - @Test - public void testRestrictedProfileAffectsVpnUidRanges() throws Exception { - // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final NetworkRequest request = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - // Bring up a VPN - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - callback.expectAvailableThenValidatedCallbacks(mMockVpn); - callback.assertNoCallback(); - - final int uid = Process.myUid(); - NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertNotNull("nc=" + nc, nc.getUids()); - assertEquals(nc.getUids(), UidRange.toIntRanges(uidRangesForUids(uid))); - assertVpnTransportInfo(nc, VpnManager.TYPE_VPN_SERVICE); - - // Set an underlying network and expect to see the VPN transports change. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCapabilitiesThat(mMockVpn, (caps) - -> caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_WIFI)); - callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps) - -> caps.hasCapability(NET_CAPABILITY_VALIDATED)); - - when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) - .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); - - final Intent addedIntent = new Intent(ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - - // Send a USER_ADDED broadcast for it. - processBroadcast(addedIntent); - - // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added - // restricted user. - final UidRange rRange = UidRange.createForUser(UserHandle.of(RESTRICTED_USER)); - final Range restrictUidRange = new Range(rRange.start, rRange.stop); - final Range singleUidRange = new Range(uid, uid); - callback.expectCapabilitiesThat(mMockVpn, (caps) - -> caps.getUids().size() == 2 - && caps.getUids().contains(singleUidRange) - && caps.getUids().contains(restrictUidRange) - && caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_WIFI)); - - // Change the VPN's capabilities somehow (specifically, disconnect wifi). - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - callback.expectCapabilitiesThat(mMockVpn, (caps) - -> caps.getUids().size() == 2 - && caps.getUids().contains(singleUidRange) - && caps.getUids().contains(restrictUidRange) - && caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_WIFI)); - - // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. - final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); - removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcast(removedIntent); - - // Expect that the VPN gains the UID range for the restricted user, and that the capability - // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. - callback.expectCapabilitiesThat(mMockVpn, (caps) - -> caps.getUids().size() == 1 - && caps.getUids().contains(singleUidRange) - && caps.hasTransport(TRANSPORT_VPN) - && !caps.hasTransport(TRANSPORT_WIFI)); - } - - @Test - public void testLockdownVpnWithRestrictedProfiles() throws Exception { - // For ConnectivityService#setAlwaysOnVpnPackage. - mServiceContext.setPermission( - Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); - // For call Vpn#setAlwaysOnPackage. - mServiceContext.setPermission( - Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); - // Necessary to see the UID ranges in NetworkCapabilities. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final NetworkRequest request = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - final int uid = Process.myUid(); - - // Connect wifi and check that UIDs in the main and restricted profiles have network access. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true /* validated */); - final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */); - assertNotNull(mCm.getActiveNetworkForUid(uid)); - assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); - - // Enable always-on VPN lockdown. The main user loses network access because no VPN is up. - final ArrayList allowList = new ArrayList<>(); - mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, - true /* lockdown */, allowList); - waitForIdle(); - assertNull(mCm.getActiveNetworkForUid(uid)); - // This is arguably overspecified: a UID that is not running doesn't have an active network. - // But it's useful to check that non-default users do not lose network access, and to prove - // that the loss of connectivity below is indeed due to the restricted profile coming up. - assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); - - // Start the restricted profile, and check that the UID within it loses network access. - when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) - .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); - when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO, - RESTRICTED_USER_INFO)); - // TODO: check that VPN app within restricted profile still has access, etc. - final Intent addedIntent = new Intent(ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcast(addedIntent); - assertNull(mCm.getActiveNetworkForUid(uid)); - assertNull(mCm.getActiveNetworkForUid(restrictedUid)); - - // Stop the restricted profile, and check that the UID within it has network access again. - when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); - - // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. - final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); - removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - processBroadcast(removedIntent); - assertNull(mCm.getActiveNetworkForUid(uid)); - assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); - - mVpnManagerService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, - allowList); - waitForIdle(); - } - - @Test - public void testIsActiveNetworkMeteredOverWifi() throws Exception { - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - - assertFalse(mCm.isActiveNetworkMetered()); - } - - @Test - public void testIsActiveNetworkMeteredOverCell() throws Exception { - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - mCellNetworkAgent.connect(true); - waitForIdle(); - - assertTrue(mCm.isActiveNetworkMetered()); - } - - @Test - public void testIsActiveNetworkMeteredOverVpnTrackingPlatformDefault() throws Exception { - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - mCellNetworkAgent.connect(true); - waitForIdle(); - assertTrue(mCm.isActiveNetworkMetered()); - - // Connect VPN network. By default it is using current default network (Cell). - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - - // Ensure VPN is now the active network. - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - - // Expect VPN to be metered. - assertTrue(mCm.isActiveNetworkMetered()); - - // Connect WiFi. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - // VPN should still be the active network. - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - - // Expect VPN to be unmetered as it should now be using WiFi (new default). - assertFalse(mCm.isActiveNetworkMetered()); - - // Disconnecting Cell should not affect VPN's meteredness. - mCellNetworkAgent.disconnect(); - waitForIdle(); - - assertFalse(mCm.isActiveNetworkMetered()); - - // Disconnect WiFi; Now there is no platform default network. - mWiFiNetworkAgent.disconnect(); - waitForIdle(); - - // VPN without any underlying networks is treated as metered. - assertTrue(mCm.isActiveNetworkMetered()); - - mMockVpn.disconnect(); - } - - @Test - public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception { - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - mCellNetworkAgent.connect(true); - waitForIdle(); - assertTrue(mCm.isActiveNetworkMetered()); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - assertFalse(mCm.isActiveNetworkMetered()); - - // Connect VPN network. - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - - // Ensure VPN is now the active network. - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - // VPN is using Cell - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork() }); - waitForIdle(); - - // Expect VPN to be metered. - assertTrue(mCm.isActiveNetworkMetered()); - - // VPN is now using WiFi - mMockVpn.setUnderlyingNetworks( - new Network[] { mWiFiNetworkAgent.getNetwork() }); - waitForIdle(); - - // Expect VPN to be unmetered - assertFalse(mCm.isActiveNetworkMetered()); - - // VPN is using Cell | WiFi. - mMockVpn.setUnderlyingNetworks( - new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - waitForIdle(); - - // Expect VPN to be metered. - assertTrue(mCm.isActiveNetworkMetered()); - - // VPN is using WiFi | Cell. - mMockVpn.setUnderlyingNetworks( - new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() }); - waitForIdle(); - - // Order should not matter and VPN should still be metered. - assertTrue(mCm.isActiveNetworkMetered()); - - // VPN is not using any underlying networks. - mMockVpn.setUnderlyingNetworks(new Network[0]); - waitForIdle(); - - // VPN without underlying networks is treated as metered. - assertTrue(mCm.isActiveNetworkMetered()); - - mMockVpn.disconnect(); - } - - @Test - public void testIsActiveNetworkMeteredOverAlwaysMeteredVpn() throws Exception { - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - assertFalse(mCm.isActiveNetworkMetered()); - - // Connect VPN network. - mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUids(Process.myUid()), - new LinkProperties()); - mMockVpn.connect(true); - waitForIdle(); - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - - // VPN is tracking current platform default (WiFi). - mMockVpn.setUnderlyingNetworks(null); - waitForIdle(); - - // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered. - assertTrue(mCm.isActiveNetworkMetered()); - - - // VPN explicitly declares WiFi as its underlying network. - mMockVpn.setUnderlyingNetworks( - new Network[] { mWiFiNetworkAgent.getNetwork() }); - waitForIdle(); - - // Doesn't really matter whether VPN declares its underlying networks explicitly. - assertTrue(mCm.isActiveNetworkMetered()); - - // With WiFi lost, VPN is basically without any underlying networks. And in that case it is - // anyways suppose to be metered. - mWiFiNetworkAgent.disconnect(); - waitForIdle(); - - assertTrue(mCm.isActiveNetworkMetered()); - - mMockVpn.disconnect(); - } - - private class DetailedBlockedStatusCallback extends TestNetworkCallback { - public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) { - super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS); - } - public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) { - // This doesn't work: - // super.expectBlockedStatusCallback(blockedStatus, n.getNetwork()); - super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS); - } - public void onBlockedStatusChanged(Network network, int blockedReasons) { - getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons)); - } - } - - @Test - public void testNetworkBlockedStatus() throws Exception { - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR) - .build(); - mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback(); - mCm.registerNetworkCallback(cellRequest, detailedCallback); - - mockUidNetworkingBlocked(); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent, - BLOCKED_REASON_NONE); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); - cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, - BLOCKED_REASON_BATTERY_SAVER); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mCellNetworkAgent); - - // If blocked state does not change but blocked reason does, the boolean callback is called. - // TODO: investigate de-duplicating. - setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED); - cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, - BLOCKED_METERED_REASON_USER_RESTRICTED); - - setBlockedReasonChanged(BLOCKED_REASON_NONE); - cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); - cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, - BLOCKED_METERED_REASON_DATA_SAVER); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mCellNetworkAgent); - - // Restrict the network based on UID rule and NOT_METERED capability change. - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); - cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, - mCellNetworkAgent); - cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, - mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, - BLOCKED_METERED_REASON_DATA_SAVER); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mCellNetworkAgent); - - setBlockedReasonChanged(BLOCKED_REASON_NONE); - cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - setBlockedReasonChanged(BLOCKED_REASON_NONE); - cellNetworkCallback.assertNoCallback(); - detailedCallback.assertNoCallback(); - - // Restrict background data. Networking is not blocked because the network is unmetered. - setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); - cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, - BLOCKED_METERED_REASON_DATA_SAVER); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mCellNetworkAgent); - setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); - cellNetworkCallback.assertNoCallback(); - - setBlockedReasonChanged(BLOCKED_REASON_NONE); - cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - setBlockedReasonChanged(BLOCKED_REASON_NONE); - cellNetworkCallback.assertNoCallback(); - detailedCallback.assertNoCallback(); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - - mCm.unregisterNetworkCallback(cellNetworkCallback); - } - - @Test - public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception { - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - mockUidNetworkingBlocked(); - - // No Networkcallbacks invoked before any network is active. - setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); - setBlockedReasonChanged(BLOCKED_REASON_NONE); - setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); - defaultCallback.assertNoCallback(); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent); - - // Allow to use the network after switching to NOT_METERED network. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - - // Switch to METERED network. Restrict the use of the network. - mWiFiNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent); - - // Network becomes NOT_METERED. - mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); - defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); - - // Verify there's no Networkcallbacks invoked after data saver on/off. - setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); - setBlockedReasonChanged(BLOCKED_REASON_NONE); - defaultCallback.assertNoCallback(); - - mCellNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(defaultCallback); - } - - private void expectNetworkRejectNonSecureVpn(InOrder inOrder, boolean add, - UidRangeParcel... expected) throws Exception { - inOrder.verify(mMockNetd).networkRejectNonSecureVpn(eq(add), aryEq(expected)); - } - - private void checkNetworkInfo(NetworkInfo ni, int type, DetailedState state) { - assertNotNull(ni); - assertEquals(type, ni.getType()); - assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState()); - if (state == DetailedState.CONNECTED || state == DetailedState.SUSPENDED) { - assertNotNull(ni.getExtraInfo()); - } else { - // Technically speaking, a network that's in CONNECTING state will generally have a - // non-null extraInfo. This doesn't actually happen in this test because it never calls - // a legacy API while a network is connecting. When a network is in CONNECTING state - // because of legacy lockdown VPN, its extraInfo is always null. - assertNull(ni.getExtraInfo()); - } - } - - private void assertActiveNetworkInfo(int type, DetailedState state) { - checkNetworkInfo(mCm.getActiveNetworkInfo(), type, state); - } - private void assertNetworkInfo(int type, DetailedState state) { - checkNetworkInfo(mCm.getNetworkInfo(type), type, state); - } - - private void assertExtraInfoFromCm(TestNetworkAgentWrapper network, boolean present) { - final NetworkInfo niForNetwork = mCm.getNetworkInfo(network.getNetwork()); - final NetworkInfo niForType = mCm.getNetworkInfo(network.getLegacyType()); - if (present) { - assertEquals(network.getExtraInfo(), niForNetwork.getExtraInfo()); - assertEquals(network.getExtraInfo(), niForType.getExtraInfo()); - } else { - assertNull(niForNetwork.getExtraInfo()); - assertNull(niForType.getExtraInfo()); - } - } - - private void assertExtraInfoFromCmBlocked(TestNetworkAgentWrapper network) { - assertExtraInfoFromCm(network, false); - } - - private void assertExtraInfoFromCmPresent(TestNetworkAgentWrapper network) { - assertExtraInfoFromCm(network, true); - } - - // Checks that each of the |agents| receive a blocked status change callback with the specified - // |blocked| value, in any order. This is needed because when an event affects multiple - // networks, ConnectivityService does not guarantee the order in which callbacks are fired. - private void assertBlockedCallbackInAnyOrder(TestNetworkCallback callback, boolean blocked, - TestNetworkAgentWrapper... agents) { - final List expectedNetworks = Arrays.asList(agents).stream() - .map((agent) -> agent.getNetwork()) - .collect(Collectors.toList()); - - // Expect exactly one blocked callback for each agent. - for (int i = 0; i < agents.length; i++) { - CallbackEntry e = callback.expectCallbackThat(TIMEOUT_MS, (c) -> - c instanceof CallbackEntry.BlockedStatus - && ((CallbackEntry.BlockedStatus) c).getBlocked() == blocked); - Network network = e.getNetwork(); - assertTrue("Received unexpected blocked callback for network " + network, - expectedNetworks.remove(network)); - } - } - - @Test - public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception { - mServiceContext.setPermission( - Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); - mServiceContext.setPermission( - Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final TestNetworkCallback callback = new TestNetworkCallback(); - final NetworkRequest request = new NetworkRequest.Builder() - .removeCapability(NET_CAPABILITY_NOT_VPN) - .build(); - mCm.registerNetworkCallback(request, callback); - - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - final TestNetworkCallback vpnUidCallback = new TestNetworkCallback(); - final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build(); - registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID); - - final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback(); - registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID); - - final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallbackForUid(VPN_UID, vpnDefaultCallbackAsUid, - new Handler(ConnectivityThread.getInstanceLooper())); - - final int uid = Process.myUid(); - final int userId = UserHandle.getUserId(uid); - final ArrayList allowList = new ArrayList<>(); - mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, - allowList); - waitForIdle(); - - UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1); - UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999); - InOrder inOrder = inOrder(mMockNetd); - expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); - - // Connect a network when lockdown is active, expect to see it blocked. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(false /* validated */); - callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); - vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - vpnDefaultCallbackAsUid.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - // Mobile is BLOCKED even though it's not actually connected. - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - - // Disable lockdown, expect to see the network unblocked. - mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); - defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - // Add our UID to the allowlist and re-enable lockdown, expect network is not blocked. - allowList.add(TEST_PACKAGE_NAME); - mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, - allowList); - callback.assertNoCallback(); - defaultCallback.assertNoCallback(); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - - // The following requires that the UID of this test package is greater than VPN_UID. This - // is always true in practice because a plain AOSP build with no apps installed has almost - // 200 packages installed. - final UidRangeParcel piece1 = new UidRangeParcel(1, VPN_UID - 1); - final UidRangeParcel piece2 = new UidRangeParcel(VPN_UID + 1, uid - 1); - final UidRangeParcel piece3 = new UidRangeParcel(uid + 1, 99999); - expectNetworkRejectNonSecureVpn(inOrder, true, piece1, piece2, piece3); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - // Connect a new network, expect it to be unblocked. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(false /* validated */); - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - defaultCallback.assertNoCallback(); - vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - // Cellular is DISCONNECTED because it's not the default and there are no requests for it. - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown. - // Everything should now be blocked. - mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - waitForIdle(); - expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3); - allowList.clear(); - mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, - allowList); - waitForIdle(); - expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); - defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); - assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - - // Disable lockdown. Everything is unblocked. - mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); - assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - // Enable and disable an always-on VPN package without lockdown. Expect no changes. - reset(mMockNetd); - mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, false /* lockdown */, - allowList); - inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); - callback.assertNoCallback(); - defaultCallback.assertNoCallback(); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - inOrder.verify(mMockNetd, never()).networkRejectNonSecureVpn(anyBoolean(), any()); - callback.assertNoCallback(); - defaultCallback.assertNoCallback(); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - // Enable lockdown and connect a VPN. The VPN is not blocked. - mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, - allowList); - defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); - assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertNull(mCm.getActiveNetwork()); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. - vpnUidDefaultCallback.assertNoCallback(); // VPN does not apply to VPN_UID - vpnDefaultCallbackAsUid.assertNoCallback(); - assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - - mMockVpn.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); - vpnUidCallback.assertNoCallback(); - vpnUidDefaultCallback.assertNoCallback(); - vpnDefaultCallbackAsUid.assertNoCallback(); - assertNull(mCm.getActiveNetwork()); - - mCm.unregisterNetworkCallback(callback); - mCm.unregisterNetworkCallback(defaultCallback); - mCm.unregisterNetworkCallback(vpnUidCallback); - mCm.unregisterNetworkCallback(vpnUidDefaultCallback); - mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid); - } - - private void setupLegacyLockdownVpn() { - final String profileName = "testVpnProfile"; - final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8); - when(mVpnProfileStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag); - - final VpnProfile profile = new VpnProfile(profileName); - profile.name = "My VPN"; - profile.server = "192.0.2.1"; - profile.dnsServers = "8.8.8.8"; - profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK; - final byte[] encodedProfile = profile.encode(); - when(mVpnProfileStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); - } - - private void establishLegacyLockdownVpn(Network underlying) throws Exception { - // The legacy lockdown VPN only supports userId 0, and must have an underlying network. - assertNotNull(underlying); - mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); - // The legacy lockdown VPN only supports userId 0. - final Set ranges = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.registerAgent(ranges); - mMockVpn.setUnderlyingNetworks(new Network[]{underlying}); - mMockVpn.connect(true); - } - - @Test - public void testLegacyLockdownVpn() throws Exception { - mServiceContext.setPermission( - Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); - // For LockdownVpnTracker to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(defaultCallback); - - final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); - mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, - new Handler(ConnectivityThread.getInstanceLooper())); - - // Pretend lockdown VPN was configured. - setupLegacyLockdownVpn(); - - // LockdownVpnTracker disables the Vpn teardown code and enables lockdown. - // Check the VPN's state before it does so. - assertTrue(mMockVpn.getEnableTeardown()); - assertFalse(mMockVpn.getLockdown()); - - // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker. - final int userId = UserHandle.getUserId(Process.myUid()); - final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); - addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - processBroadcast(addedIntent); - - // Lockdown VPN disables teardown and enables lockdown. - assertFalse(mMockVpn.getEnableTeardown()); - assertTrue(mMockVpn.getLockdown()); - - // Bring up a network. - // Expect nothing to happen because the network does not have an IPv4 default route: legacy - // VPN only supports IPv4. - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName("rmnet0"); - cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64")); - cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0")); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); - mCellNetworkAgent.connect(false /* validated */); - callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - waitForIdle(); - assertNull(mMockVpn.getAgent()); - - // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls - // LockdownVpnTracker#handleStateChangedLocked. This is a bug. - // TODO: consider fixing this. - cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25")); - cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0")); - mCellNetworkAgent.sendLinkProperties(cellLp); - callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - systemDefaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, - mCellNetworkAgent); - waitForIdle(); - assertNull(mMockVpn.getAgent()); - - // Disconnect, then try again with a network that supports IPv4 at connection time. - // Expect lockdown VPN to come up. - ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); - mCellNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - b1.expectBroadcast(); - - // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten - // with the state of the VPN network. So expect a CONNECTING broadcast. - b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); - mCellNetworkAgent.connect(false /* validated */); - callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - systemDefaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); - b1.expectBroadcast(); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mCellNetworkAgent); - - // TODO: it would be nice if we could simply rely on the production code here, and have - // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with - // ConnectivityService, etc. That would require duplicating a fair bit of code from the - // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not - // work for at least two reasons: - // 1. In this test, calling registerNetworkAgent does not actually result in an agent being - // registered. This is because nothing calls onNetworkMonitorCreated, which is what - // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test - // that wants to register an agent must use TestNetworkAgentWrapper. - // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call - // the TestNetworkAgentWrapper code, this would deadlock because the - // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls - // waitForIdle(). - mMockVpn.expectStartLegacyVpnRunner(); - b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); - ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); - establishLegacyLockdownVpn(mCellNetworkAgent.getNetwork()); - callback.expectAvailableThenValidatedCallbacks(mMockVpn); - defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - systemDefaultCallback.assertNoCallback(); - NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - b1.expectBroadcast(); - b2.expectBroadcast(); - assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mCellNetworkAgent); - assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); - assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI)); - assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertVpnTransportInfo(vpnNc, VpnManager.TYPE_VPN_LEGACY); - - // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. - final LinkProperties wifiLp = new LinkProperties(); - wifiLp.setInterfaceName("wlan0"); - wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25")); - wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0")); - final NetworkCapabilities wifiNc = new NetworkCapabilities(); - wifiNc.addTransportType(TRANSPORT_WIFI); - wifiNc.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc); - - b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); - // Wifi is CONNECTING because the VPN isn't up yet. - b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING); - ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); - mWiFiNetworkAgent.connect(false /* validated */); - b1.expectBroadcast(); - b2.expectBroadcast(); - b3.expectBroadcast(); - mMockVpn.expectStopVpnRunnerPrivileged(); - mMockVpn.expectStartLegacyVpnRunner(); - - // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still - // connected, so the network is not considered blocked by the lockdown UID ranges? But the - // fact that a VPN is connected should only result in the VPN itself being unblocked, not - // any other network. Bug in isUidBlockedByVpn? - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - callback.expectCallback(CallbackEntry.LOST, mMockVpn); - defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); - defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); - systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // While the VPN is reconnecting on the new network, everything is blocked. - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); - assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); - assertExtraInfoFromCmBlocked(mWiFiNetworkAgent); - - // The VPN comes up again on wifi. - b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); - b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); - establishLegacyLockdownVpn(mWiFiNetworkAgent.getNetwork()); - callback.expectAvailableThenValidatedCallbacks(mMockVpn); - defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); - systemDefaultCallback.assertNoCallback(); - b1.expectBroadcast(); - b2.expectBroadcast(); - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mWiFiNetworkAgent); - vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); - assertTrue(vpnNc.hasTransport(TRANSPORT_VPN)); - assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI)); - assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED)); - - // Disconnect cell. Nothing much happens since it's not the default network. - mCellNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - defaultCallback.assertNoCallback(); - systemDefaultCallback.assertNoCallback(); - - assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); - assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); - assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); - assertExtraInfoFromCmPresent(mWiFiNetworkAgent); - - b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); - b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - b1.expectBroadcast(); - callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI)); - mMockVpn.expectStopVpnRunnerPrivileged(); - callback.expectCallback(CallbackEntry.LOST, mMockVpn); - b2.expectBroadcast(); - } - - /** - * Test mutable and requestable network capabilities such as - * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and - * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the - * {@code ConnectivityService} re-assign the networks accordingly. - */ - @Test - public final void testLoseMutableAndRequestableCaps() throws Exception { - final int[] testCaps = new int [] { - NET_CAPABILITY_TRUSTED, - NET_CAPABILITY_NOT_VCN_MANAGED - }; - for (final int testCap : testCaps) { - // Create requests with and without the testing capability. - final TestNetworkCallback callbackWithCap = new TestNetworkCallback(); - final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback(); - mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(), - callbackWithCap); - mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(), - callbackWithoutCap); - - // Setup networks with testing capability and verify the default network changes. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.addCapability(testCap); - mCellNetworkAgent.connect(true); - callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(testCap); - mWiFiNetworkAgent.connect(true); - callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - - // Remove the testing capability on wifi, verify the callback and default network - // changes back to cellular. - mWiFiNetworkAgent.removeCapability(testCap); - callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); - callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - - mCellNetworkAgent.removeCapability(testCap); - callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - callbackWithoutCap.assertNoCallback(); - verify(mMockNetd).networkClearDefault(); - - mCm.unregisterNetworkCallback(callbackWithCap); - mCm.unregisterNetworkCallback(callbackWithoutCap); - } - } - - @Test - public final void testBatteryStatsNetworkType() throws Exception { - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName("cell0"); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); - mCellNetworkAgent.connect(true); - waitForIdle(); - verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, - cellLp.getInterfaceName(), - new int[] { TRANSPORT_CELLULAR }); - - final LinkProperties wifiLp = new LinkProperties(); - wifiLp.setInterfaceName("wifi0"); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, - wifiLp.getInterfaceName(), - new int[] { TRANSPORT_WIFI }); - - mCellNetworkAgent.disconnect(); - mWiFiNetworkAgent.disconnect(); - - cellLp.setInterfaceName("wifi0"); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); - mCellNetworkAgent.connect(true); - waitForIdle(); - verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, - cellLp.getInterfaceName(), - new int[] { TRANSPORT_CELLULAR }); - mCellNetworkAgent.disconnect(); - } - - /** - * Make simulated InterfaceConfigParcel for Nat464Xlat to query clat lower layer info. - */ - private InterfaceConfigurationParcel getClatInterfaceConfigParcel(LinkAddress la) { - final InterfaceConfigurationParcel cfg = new InterfaceConfigurationParcel(); - cfg.hwAddr = "11:22:33:44:55:66"; - cfg.ipv4Addr = la.getAddress().getHostAddress(); - cfg.prefixLength = la.getPrefixLength(); - return cfg; - } - - /** - * Make expected stack link properties, copied from Nat464Xlat. - */ - private LinkProperties makeClatLinkProperties(LinkAddress la) { - LinkAddress clatAddress = la; - LinkProperties stacked = new LinkProperties(); - stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME); - RouteInfo ipv4Default = new RouteInfo( - new LinkAddress(Inet4Address.ANY, 0), - clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME); - stacked.addRoute(ipv4Default); - stacked.addLinkAddress(clatAddress); - return stacked; - } - - private Nat64PrefixEventParcel makeNat64PrefixEvent(final int netId, final int prefixOperation, - final String prefixAddress, final int prefixLength) { - final Nat64PrefixEventParcel event = new Nat64PrefixEventParcel(); - event.netId = netId; - event.prefixOperation = prefixOperation; - event.prefixAddress = prefixAddress; - event.prefixLength = prefixLength; - return event; - } - - @Test - public void testStackedLinkProperties() throws Exception { - final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24"); - final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64"); - final String kNat64PrefixString = "2001:db8:64:64:64:64::"; - final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96); - final String kOtherNat64PrefixString = "64:ff9b::"; - final IpPrefix kOtherNat64Prefix = new IpPrefix( - InetAddress.getByName(kOtherNat64PrefixString), 96); - final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(), - MOBILE_IFNAME); - final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME); - final RouteInfo ipv4Subnet = new RouteInfo(myIpv4, null, MOBILE_IFNAME); - final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(), - CLAT_PREFIX + MOBILE_IFNAME); - - final NetworkRequest networkRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(networkRequest, networkCallback); - - // Prepare ipv6 only link properties. - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - cellLp.addLinkAddress(myIpv6); - cellLp.addRoute(defaultRoute); - cellLp.addRoute(ipv6Subnet); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); - reset(mMockDnsResolver); - reset(mMockNetd); - - // Connect with ipv6 link properties. Expect prefix discovery to be started. - mCellNetworkAgent.connect(true); - final int cellNetId = mCellNetworkAgent.getNetwork().netId; - waitForIdle(); - - verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId, - INetd.PERMISSION_NONE)); - assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); - verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); - verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); - verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext, - cellLp.getInterfaceName(), - new int[] { TRANSPORT_CELLULAR }); - - networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); - - // Switching default network updates TCP buffer sizes. - verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); - // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that - // the NAT64 prefix was removed because one was never discovered. - cellLp.addLinkAddress(myIpv4); - mCellNetworkAgent.sendLinkProperties(cellLp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - assertRoutesAdded(cellNetId, ipv4Subnet); - verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); - verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any()); - - // Make sure BatteryStats was not told about any v4- interfaces, as none should have - // come online yet. - waitForIdle(); - verify(mDeps, never()) - .reportNetworkInterfaceForTransports(eq(mServiceContext), startsWith("v4-"), any()); - - verifyNoMoreInteractions(mMockNetd); - verifyNoMoreInteractions(mMockDnsResolver); - reset(mMockNetd); - reset(mMockDnsResolver); - when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) - .thenReturn(getClatInterfaceConfigParcel(myIpv4)); - - // Remove IPv4 address. Expect prefix discovery to be started again. - cellLp.removeLinkAddress(myIpv4); - mCellNetworkAgent.sendLinkProperties(cellLp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); - assertRoutesRemoved(cellNetId, ipv4Subnet); - - // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started. - Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent); - assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix()); - mService.mResolverUnsolEventCallback.onNat64PrefixEvent( - makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96)); - LinkProperties lpBeforeClat = networkCallback.expectCallback( - CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp(); - assertEquals(0, lpBeforeClat.getStackedLinks().size()); - assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix()); - verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); - - // Clat iface comes up. Expect stacked link to be added. - clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - List stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()) - .getStackedLinks(); - assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0)); - assertRoutesAdded(cellNetId, stackedDefault); - verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - // Change trivial linkproperties and see if stacked link is preserved. - cellLp.addDnsServer(InetAddress.getByName("8.8.8.8")); - mCellNetworkAgent.sendLinkProperties(cellLp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - - List stackedLpsAfterChange = - mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks(); - assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST); - assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0)); - - verify(mMockDnsResolver, times(1)).setResolverConfiguration( - mResolverParamsParcelCaptor.capture()); - ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); - assertEquals(1, resolvrParams.servers.length); - assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); - - for (final LinkProperties stackedLp : stackedLpsAfterChange) { - verify(mDeps).reportNetworkInterfaceForTransports( - mServiceContext, stackedLp.getInterfaceName(), - new int[] { TRANSPORT_CELLULAR }); - } - reset(mMockNetd); - when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) - .thenReturn(getClatInterfaceConfigParcel(myIpv4)); - // Change the NAT64 prefix without first removing it. - // Expect clatd to be stopped and started with the new prefix. - mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( - cellNetId, PREFIX_OPERATION_ADDED, kOtherNat64PrefixString, 96)); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getStackedLinks().size() == 0); - verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); - assertRoutesRemoved(cellNetId, stackedDefault); - verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - - verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString()); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix)); - clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getStackedLinks().size() == 1); - assertRoutesAdded(cellNetId, stackedDefault); - verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - reset(mMockNetd); - - // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked - // linkproperties are cleaned up. - cellLp.addLinkAddress(myIpv4); - cellLp.addRoute(ipv4Subnet); - mCellNetworkAgent.sendLinkProperties(cellLp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - assertRoutesAdded(cellNetId, ipv4Subnet); - verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); - verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId); - - // As soon as stop is called, the linkproperties lose the stacked interface. - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()); - LinkProperties expected = new LinkProperties(cellLp); - expected.setNat64Prefix(kOtherNat64Prefix); - assertEquals(expected, actualLpAfterIpv4); - assertEquals(0, actualLpAfterIpv4.getStackedLinks().size()); - assertRoutesRemoved(cellNetId, stackedDefault); - - // The interface removed callback happens but has no effect after stop is called. - clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); - networkCallback.assertNoCallback(); - verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - verifyNoMoreInteractions(mMockNetd); - verifyNoMoreInteractions(mMockDnsResolver); - reset(mMockNetd); - reset(mMockDnsResolver); - when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) - .thenReturn(getClatInterfaceConfigParcel(myIpv4)); - - // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone. - mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( - cellNetId, PREFIX_OPERATION_REMOVED, kOtherNat64PrefixString, 96)); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getNat64Prefix() == null); - - // Remove IPv4 address and expect prefix discovery and clatd to be started again. - cellLp.removeLinkAddress(myIpv4); - cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME)); - cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8")); - mCellNetworkAgent.sendLinkProperties(cellLp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - assertRoutesRemoved(cellNetId, ipv4Subnet); // Directly-connected routes auto-added. - verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); - mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( - cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96)); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); - - // Clat iface comes up. Expect stacked link to be added. - clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null); - assertRoutesAdded(cellNetId, stackedDefault); - verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - - // NAT64 prefix is removed. Expect that clat is stopped. - mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent( - cellNetId, PREFIX_OPERATION_REMOVED, kNat64PrefixString, 96)); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null); - assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault); - - // Stop has no effect because clat is already stopped. - verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - (lp) -> lp.getStackedLinks().size() == 0); - verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_PREFIX + MOBILE_IFNAME); - verify(mMockNetd, times(1)).interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME); - verifyNoMoreInteractions(mMockNetd); - // Clean up. - mCellNetworkAgent.disconnect(); - networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - networkCallback.assertNoCallback(); - mCm.unregisterNetworkCallback(networkCallback); - } - - private void expectNat64PrefixChange(TestableNetworkCallback callback, - TestNetworkAgentWrapper agent, IpPrefix prefix) { - callback.expectLinkPropertiesThat(agent, x -> Objects.equals(x.getNat64Prefix(), prefix)); - } - - @Test - public void testNat64PrefixMultipleSources() throws Exception { - final String iface = "wlan0"; - final String pref64FromRaStr = "64:ff9b::"; - final String pref64FromDnsStr = "2001:db8:64::"; - final IpPrefix pref64FromRa = new IpPrefix(InetAddress.getByName(pref64FromRaStr), 96); - final IpPrefix pref64FromDns = new IpPrefix(InetAddress.getByName(pref64FromDnsStr), 96); - final IpPrefix newPref64FromRa = new IpPrefix("2001:db8:64:64:64:64::/96"); - - final NetworkRequest request = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .build(); - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, callback); - - final LinkProperties baseLp = new LinkProperties(); - baseLp.setInterfaceName(iface); - baseLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); - baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464")); - - reset(mMockNetd, mMockDnsResolver); - InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver); - - // If a network already has a NAT64 prefix on connect, clatd is started immediately and - // prefix discovery is never started. - LinkProperties lp = new LinkProperties(baseLp); - lp.setNat64Prefix(pref64FromRa); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); - mWiFiNetworkAgent.connect(false); - final Network network = mWiFiNetworkAgent.getNetwork(); - int netId = network.getNetId(); - callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - callback.assertNoCallback(); - assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); - - // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started. - lp.setNat64Prefix(null); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); - inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); - - // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and - // clatd is started with the prefix from the RA. - lp.setNat64Prefix(pref64FromRa); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); - inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); - inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); - - // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS - // discovery has succeeded. - lp.setNat64Prefix(null); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); - inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); - - mService.mResolverUnsolEventCallback.onNat64PrefixEvent( - makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96)); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); - inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); - - // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix - // discovery is not stopped, and there are no callbacks. - lp.setNat64Prefix(pref64FromDns); - mWiFiNetworkAgent.sendLinkProperties(lp); - callback.assertNoCallback(); - inOrder.verify(mMockNetd, never()).clatdStop(iface); - inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); - inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); - - // If the RA is later withdrawn, nothing happens again. - lp.setNat64Prefix(null); - mWiFiNetworkAgent.sendLinkProperties(lp); - callback.assertNoCallback(); - inOrder.verify(mMockNetd, never()).clatdStop(iface); - inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); - inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); - - // If the RA prefix changes, clatd is restarted and prefix discovery is stopped. - lp.setNat64Prefix(pref64FromRa); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); - - // Stopping prefix discovery results in a prefix removed notification. - mService.mResolverUnsolEventCallback.onNat64PrefixEvent( - makeNat64PrefixEvent(netId, PREFIX_OPERATION_REMOVED, pref64FromDnsStr, 96)); - - inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - - // If the RA prefix changes, clatd is restarted and prefix discovery is not started. - lp.setNat64Prefix(newPref64FromRa); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa); - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); - inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString()); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString()); - inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - - // If the RA prefix changes to the same value, nothing happens. - lp.setNat64Prefix(newPref64FromRa); - mWiFiNetworkAgent.sendLinkProperties(lp); - callback.assertNoCallback(); - assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); - inOrder.verify(mMockNetd, never()).clatdStop(iface); - inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); - inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); - - // The transition between no prefix and DNS prefix is tested in testStackedLinkProperties. - - // If the same prefix is learned first by DNS and then by RA, and clat is later stopped, - // (e.g., because the network disconnects) setPrefix64(netid, "") is never called. - lp.setNat64Prefix(null); - mWiFiNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); - inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); - mService.mResolverUnsolEventCallback.onNat64PrefixEvent( - makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96)); - expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); - inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any()); - - lp.setNat64Prefix(pref64FromDns); - mWiFiNetworkAgent.sendLinkProperties(lp); - callback.assertNoCallback(); - inOrder.verify(mMockNetd, never()).clatdStop(iface); - inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); - inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); - - // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but - // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that - // clat has been stopped, or the test will be flaky. - ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); - mWiFiNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - b.expectBroadcast(); - - inOrder.verify(mMockNetd).clatdStop(iface); - inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); - inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString()); - - mCm.unregisterNetworkCallback(callback); - } - - @Test - public void testWith464XlatDisable() throws Exception { - doReturn(false).when(mDeps).getCellular464XlatEnabled(); - - final TestNetworkCallback callback = new TestNetworkCallback(); - final TestNetworkCallback defaultCallback = new TestNetworkCallback(); - final NetworkRequest networkRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .build(); - mCm.registerNetworkCallback(networkRequest, callback); - mCm.registerDefaultNetworkCallback(defaultCallback); - - // Bring up validated cell. - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); - cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, MOBILE_IFNAME)); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - - mCellNetworkAgent.sendLinkProperties(cellLp); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - final int cellNetId = mCellNetworkAgent.getNetwork().netId; - waitForIdle(); - - verify(mMockDnsResolver, never()).startPrefix64Discovery(cellNetId); - Nat464Xlat clat = getNat464Xlat(mCellNetworkAgent); - assertTrue("Nat464Xlat was not IDLE", !clat.isStarted()); - - // This cannot happen because prefix discovery cannot succeed if it is never started. - mService.mResolverUnsolEventCallback.onNat64PrefixEvent( - makeNat64PrefixEvent(cellNetId, PREFIX_OPERATION_ADDED, "64:ff9b::", 96)); - - // ... but still, check that even if it did, clatd would not be started. - verify(mMockNetd, never()).clatdStart(anyString(), anyString()); - assertTrue("Nat464Xlat was not IDLE", !clat.isStarted()); - } - - @Test - public void testDataActivityTracking() throws Exception { - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - final NetworkRequest networkRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_INTERNET) - .build(); - mCm.registerNetworkCallback(networkRequest, networkCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - final LinkProperties cellLp = new LinkProperties(); - cellLp.setInterfaceName(MOBILE_IFNAME); - mCellNetworkAgent.sendLinkProperties(cellLp); - mCellNetworkAgent.connect(true); - networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_CELLULAR))); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final LinkProperties wifiLp = new LinkProperties(); - wifiLp.setInterfaceName(WIFI_IFNAME); - mWiFiNetworkAgent.sendLinkProperties(wifiLp); - - // Network switch - mWiFiNetworkAgent.connect(true); - networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_WIFI))); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_CELLULAR))); - - // Disconnect wifi and switch back to cell - reset(mMockNetd); - mWiFiNetworkAgent.disconnect(); - networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - assertNoCallbacks(networkCallback); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_WIFI))); - verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_CELLULAR))); - - // reconnect wifi - reset(mMockNetd); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - wifiLp.setInterfaceName(WIFI_IFNAME); - mWiFiNetworkAgent.sendLinkProperties(wifiLp); - mWiFiNetworkAgent.connect(true); - networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_WIFI))); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_CELLULAR))); - - // Disconnect cell - reset(mMockNetd); - mCellNetworkAgent.disconnect(); - networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - // LOST callback is triggered earlier than removing idle timer. Broadcast should also be - // sent as network being switched. Ensure rule removal for cell will not be triggered - // unexpectedly before network being removed. - waitForIdle(); - verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_CELLULAR))); - verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId)); - verify(mMockDnsResolver, times(1)) - .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); - - // Disconnect wifi - ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); - mWiFiNetworkAgent.disconnect(); - b.expectBroadcast(); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), - eq(Integer.toString(TRANSPORT_WIFI))); - - // Clean up - mCm.unregisterNetworkCallback(networkCallback); - } - - private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception { - String[] values = tcpBufferSizes.split(","); - String rmemValues = String.join(" ", values[0], values[1], values[2]); - String wmemValues = String.join(" ", values[3], values[4], values[5]); - verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues); - reset(mMockNetd); - } - - @Test - public void testTcpBufferReset() throws Exception { - final String testTcpBufferSizes = "1,2,3,4,5,6"; - final NetworkRequest networkRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(networkRequest, networkCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - reset(mMockNetd); - // Switching default network updates TCP buffer sizes. - mCellNetworkAgent.connect(false); - networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); - // Change link Properties should have updated tcp buffer size. - LinkProperties lp = new LinkProperties(); - lp.setTcpBufferSizes(testTcpBufferSizes); - mCellNetworkAgent.sendLinkProperties(lp); - networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); - verifyTcpBufferSizeChange(testTcpBufferSizes); - // Clean up. - mCellNetworkAgent.disconnect(); - networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - networkCallback.assertNoCallback(); - mCm.unregisterNetworkCallback(networkCallback); - } - - @Test - public void testGetGlobalProxyForNetwork() throws Exception { - final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); - when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo); - assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork)); - } - - @Test - public void testGetProxyForActiveNetwork() throws Exception { - final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - assertNull(mService.getProxyForNetwork(null)); - - final LinkProperties testLinkProperties = new LinkProperties(); - testLinkProperties.setHttpProxy(testProxyInfo); - - mWiFiNetworkAgent.sendLinkProperties(testLinkProperties); - waitForIdle(); - - assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); - } - - @Test - public void testGetProxyForVPN() throws Exception { - final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888); - - // Set up a WiFi network with no proxy - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - waitForIdle(); - assertNull(mService.getProxyForNetwork(null)); - - // Connect a VPN network with a proxy. - LinkProperties testLinkProperties = new LinkProperties(); - testLinkProperties.setHttpProxy(testProxyInfo); - mMockVpn.establishForMyUid(testLinkProperties); - assertUidRangesUpdatedForMyUid(true); - - // Test that the VPN network returns a proxy, and the WiFi does not. - assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); - assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); - assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); - - // Test that the VPN network returns no proxy when it is set to null. - testLinkProperties.setHttpProxy(null); - mMockVpn.sendLinkProperties(testLinkProperties); - waitForIdle(); - assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork())); - assertNull(mService.getProxyForNetwork(null)); - - // Set WiFi proxy and check that the vpn proxy is still null. - testLinkProperties.setHttpProxy(testProxyInfo); - mWiFiNetworkAgent.sendLinkProperties(testLinkProperties); - waitForIdle(); - assertNull(mService.getProxyForNetwork(null)); - - // Disconnect from VPN and check that the active network, which is now the WiFi, has the - // correct proxy setting. - mMockVpn.disconnect(); - waitForIdle(); - assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); - assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); - } - - @Test - public void testFullyRoutedVpnResultsInInterfaceFilteringRules() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); - // The uid range needs to cover the test app so the network is visible to it. - final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.establish(lp, VPN_UID, vpnRange); - assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); - - // A connected VPN should have interface rules set up. There are two expected invocations, - // one during the VPN initial connection, one during the VPN LinkProperties update. - ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); - verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); - assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); - assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); - assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange)); - - mMockVpn.disconnect(); - waitForIdle(); - - // Disconnected VPN should have interface rules removed - verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - assertNull(mService.mPermissionMonitor.getVpnUidRanges("tun0")); - } - - @Test - public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); - // The uid range needs to cover the test app so the network is visible to it. - final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); - assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); - - // Legacy VPN should not have interface rules set up - verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); - } - - @Test - public void testLocalIpv4OnlyVpnDoesNotResultInInterfaceFilteringRule() - throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); - // The uid range needs to cover the test app so the network is visible to it. - final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); - assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); - - // IPv6 unreachable route should not be misinterpreted as a default route - verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); - } - - @Test - public void testVpnHandoverChangesInterfaceFilteringRule() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - // The uid range needs to cover the test app so the network is visible to it. - final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.establish(lp, VPN_UID, vpnRange); - assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); - - // Connected VPN should have interface rules set up. There are two expected invocations, - // one during VPN uid update, one during VPN LinkProperties update - ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); - verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); - assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); - assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); - - reset(mMockNetd); - InOrder inOrder = inOrder(mMockNetd); - lp.setInterfaceName("tun1"); - mMockVpn.sendLinkProperties(lp); - waitForIdle(); - // VPN handover (switch to a new interface) should result in rules being updated (old rules - // removed first, then new rules added) - inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - - reset(mMockNetd); - lp = new LinkProperties(); - lp.setInterfaceName("tun1"); - lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1")); - mMockVpn.sendLinkProperties(lp); - waitForIdle(); - // VPN not routing everything should no longer have interface filtering rules - verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - - reset(mMockNetd); - lp = new LinkProperties(); - lp.setInterfaceName("tun1"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - mMockVpn.sendLinkProperties(lp); - waitForIdle(); - // Back to routing all IPv6 traffic should have filtering rules - verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - } - - @Test - public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - // The uid range needs to cover the test app so the network is visible to it. - final UidRange vpnRange = PRIMARY_UIDRANGE; - final Set vpnRanges = Collections.singleton(vpnRange); - mMockVpn.establish(lp, VPN_UID, vpnRanges); - assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); - - reset(mMockNetd); - InOrder inOrder = inOrder(mMockNetd); - - // Update to new range which is old range minus APP1, i.e. only APP2 - final Set newRanges = new HashSet<>(Arrays.asList( - new UidRange(vpnRange.start, APP1_UID - 1), - new UidRange(APP1_UID + 1, vpnRange.stop))); - mMockVpn.setUids(newRanges); - waitForIdle(); - - ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class); - // Verify old rules are removed before new rules are added - inOrder.verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID); - inOrder.verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); - assertContainsExactly(uidCaptor.getValue(), APP2_UID); - } - - @Test - public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception { - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - - LinkProperties wifiLp = new LinkProperties(); - wifiLp.setInterfaceName(WIFI_WOL_IFNAME); - wifiLp.setWakeOnLanSupported(false); - - // Default network switch should update ifaces. - mWiFiNetworkAgent.connect(false); - mWiFiNetworkAgent.sendLinkProperties(wifiLp); - waitForIdle(); - - // ConnectivityService should have changed the WakeOnLanSupported to true - wifiLp.setWakeOnLanSupported(true); - assertEquals(wifiLp, mService.getActiveLinkProperties()); - } - - @Test - public void testLegacyExtraInfoSentToNetworkMonitor() throws Exception { - class TestNetworkAgent extends NetworkAgent { - TestNetworkAgent(Context context, Looper looper, NetworkAgentConfig config) { - super(context, looper, "MockAgent", new NetworkCapabilities(), - new LinkProperties(), 40 , config, null /* provider */); - } - } - final NetworkAgent naNoExtraInfo = new TestNetworkAgent( - mServiceContext, mCsHandlerThread.getLooper(), new NetworkAgentConfig()); - naNoExtraInfo.register(); - verify(mNetworkStack).makeNetworkMonitor(any(), isNull(String.class), any()); - naNoExtraInfo.unregister(); - - reset(mNetworkStack); - final NetworkAgentConfig config = - new NetworkAgentConfig.Builder().setLegacyExtraInfo("legacyinfo").build(); - final NetworkAgent naExtraInfo = new TestNetworkAgent( - mServiceContext, mCsHandlerThread.getLooper(), config); - naExtraInfo.register(); - verify(mNetworkStack).makeNetworkMonitor(any(), eq("legacyinfo"), any()); - naExtraInfo.unregister(); - } - - // To avoid granting location permission bypass. - private void denyAllLocationPrivilegedPermissions() { - mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - PERMISSION_DENIED); - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); - mServiceContext.setPermission(Manifest.permission.NETWORK_STACK, - PERMISSION_DENIED); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD, - PERMISSION_DENIED); - } - - private void setupLocationPermissions( - int targetSdk, boolean locationToggle, String op, String perm) throws Exception { - denyAllLocationPrivilegedPermissions(); - - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = targetSdk; - when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) - .thenReturn(applicationInfo); - when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk); - - when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); - - if (op != null) { - when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), - eq(mContext.getPackageName()), eq(getAttributionTag()), anyString())) - .thenReturn(AppOpsManager.MODE_ALLOWED); - } - - if (perm != null) { - mServiceContext.setPermission(perm, PERMISSION_GRANTED); - } - } - - private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid, - boolean includeLocationSensitiveInfo) { - final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); - - return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, includeLocationSensitiveInfo, Process.myUid(), callerUid, - mContext.getPackageName(), getAttributionTag()) - .getOwnerUid(); - } - - private void verifyTransportInfoCopyNetCapsPermission( - int callerUid, boolean includeLocationSensitiveInfo, - boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { - final TransportInfo transportInfo = mock(TransportInfo.class); - when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION); - final NetworkCapabilities netCap = - new NetworkCapabilities().setTransportInfo(transportInfo); - - mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, includeLocationSensitiveInfo, Process.myPid(), callerUid, - mContext.getPackageName(), getAttributionTag()); - if (shouldMakeCopyWithLocationSensitiveFieldsParcelable) { - verify(transportInfo).makeCopy(REDACT_NONE); - } else { - verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); - } - } - - private void verifyOwnerUidAndTransportInfoNetCapsPermission( - boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag, - boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag, - boolean shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag, - boolean shouldInclLocationSensitiveTransportInfoWithIncludeFlag) { - final int myUid = Process.myUid(); - - final int expectedOwnerUidWithoutIncludeFlag = - shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag - ? myUid : INVALID_UID; - assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission( - myUid, myUid, false /* includeLocationSensitiveInfo */)); - - final int expectedOwnerUidWithIncludeFlag = - shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID; - assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission( - myUid, myUid, true /* includeLocationSensitiveInfo */)); - - verifyTransportInfoCopyNetCapsPermission(myUid, - false, /* includeLocationSensitiveInfo */ - shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag); - - verifyTransportInfoCopyNetCapsPermission(myUid, - true, /* includeLocationSensitiveInfo */ - shouldInclLocationSensitiveTransportInfoWithIncludeFlag); - - } - - private void verifyOwnerUidAndTransportInfoNetCapsPermissionPreS() { - verifyOwnerUidAndTransportInfoNetCapsPermission( - // Ensure that owner uid is included even if the request asks to remove it (which is - // the default) since the app has necessary permissions and targetSdk < S. - true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ - true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ - // Ensure that location info is removed if the request asks to remove it even if the - // app has necessary permissions. - false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ - true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ - ); - } - - @Test - public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQPreS() - throws Exception { - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); - } - - @Test - public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag() - throws Exception { - setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); - } - - @Test - public void - testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag() - throws Exception { - setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsPermission( - // Ensure that the owner UID is removed if the request asks us to remove it even - // if the app has necessary permissions since targetSdk >= S. - false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ - true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ - // Ensure that location info is removed if the request asks to remove it even if the - // app has necessary permissions. - false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ - true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ - ); - } - - @Test - public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ() - throws Exception { - setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsPermissionPreS(); - } - - private void verifyOwnerUidAndTransportInfoNetCapsNotIncluded() { - verifyOwnerUidAndTransportInfoNetCapsPermission( - false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ - false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ - false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */ - false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */ - ); - } - - @Test - public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception { - // Test that even with fine location permission, and UIDs matching, the UID is sanitized. - setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); - } - - @Test - public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception { - // Test that even with fine location permission, not being the owner leads to sanitization. - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - final int myUid = Process.myUid(); - assertEquals(Process.INVALID_UID, - getOwnerUidNetCapsPermission(myUid + 1, myUid, - true /* includeLocationSensitiveInfo */)); - } - - @Test - public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ() - throws Exception { - // Test that not having fine location permission leads to sanitization. - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); - } - - @Test - public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterS() - throws Exception { - // Test that not having fine location permission leads to sanitization. - setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_COARSE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION); - - verifyOwnerUidAndTransportInfoNetCapsNotIncluded(); - } - - @Test - public void testCreateForCallerWithLocalMacAddressSanitizedWithLocalMacAddressPermission() - throws Exception { - mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED); - - final TransportInfo transportInfo = mock(TransportInfo.class); - when(transportInfo.getApplicableRedactions()) - .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS); - final NetworkCapabilities netCap = - new NetworkCapabilities().setTransportInfo(transportInfo); - - mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, false /* includeLocationSensitiveInfoInTransportInfo */, - Process.myPid(), Process.myUid(), - mContext.getPackageName(), getAttributionTag()); - // don't redact MAC_ADDRESS fields, only location sensitive fields. - verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); - } - - @Test - public void testCreateForCallerWithLocalMacAddressSanitizedWithoutLocalMacAddressPermission() - throws Exception { - mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED); - - final TransportInfo transportInfo = mock(TransportInfo.class); - when(transportInfo.getApplicableRedactions()) - .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS); - final NetworkCapabilities netCap = - new NetworkCapabilities().setTransportInfo(transportInfo); - - mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, false /* includeLocationSensitiveInfoInTransportInfo */, - Process.myPid(), Process.myUid(), - mContext.getPackageName(), getAttributionTag()); - // redact both MAC_ADDRESS & location sensitive fields. - verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION - | REDACT_FOR_LOCAL_MAC_ADDRESS); - } - - @Test - public void testCreateForCallerWithLocalMacAddressSanitizedWithSettingsPermission() - throws Exception { - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - - final TransportInfo transportInfo = mock(TransportInfo.class); - when(transportInfo.getApplicableRedactions()) - .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); - final NetworkCapabilities netCap = - new NetworkCapabilities().setTransportInfo(transportInfo); - - mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, false /* includeLocationSensitiveInfoInTransportInfo */, - Process.myPid(), Process.myUid(), - mContext.getPackageName(), getAttributionTag()); - // don't redact NETWORK_SETTINGS fields, only location sensitive fields. - verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION); - } - - @Test - public void testCreateForCallerWithLocalMacAddressSanitizedWithoutSettingsPermission() - throws Exception { - mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED); - - final TransportInfo transportInfo = mock(TransportInfo.class); - when(transportInfo.getApplicableRedactions()) - .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); - final NetworkCapabilities netCap = - new NetworkCapabilities().setTransportInfo(transportInfo); - - mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, false /* includeLocationSensitiveInfoInTransportInfo */, - Process.myPid(), Process.myUid(), - mContext.getPackageName(), getAttributionTag()); - // redact both NETWORK_SETTINGS & location sensitive fields. - verify(transportInfo).makeCopy( - REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS); - } - - /** - * Test TransportInfo to verify redaction mechanism. - */ - private static class TestTransportInfo implements TransportInfo { - public final boolean locationRedacted; - public final boolean localMacAddressRedacted; - public final boolean settingsRedacted; - - TestTransportInfo() { - locationRedacted = false; - localMacAddressRedacted = false; - settingsRedacted = false; - } - - TestTransportInfo(boolean locationRedacted, boolean localMacAddressRedacted, - boolean settingsRedacted) { - this.locationRedacted = locationRedacted; - this.localMacAddressRedacted = - localMacAddressRedacted; - this.settingsRedacted = settingsRedacted; - } - - @Override - public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { - return new TestTransportInfo( - locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, - localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, - settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 - ); - } - - @Override - public @NetworkCapabilities.RedactionType long getApplicableRedactions() { - return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS - | REDACT_FOR_NETWORK_SETTINGS; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof TestTransportInfo)) return false; - TestTransportInfo that = (TestTransportInfo) other; - return that.locationRedacted == this.locationRedacted - && that.localMacAddressRedacted == this.localMacAddressRedacted - && that.settingsRedacted == this.settingsRedacted; - } - - @Override - public int hashCode() { - return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted); - } - - @Override - public String toString() { - return String.format( - "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}", - locationRedacted, localMacAddressRedacted, settingsRedacted); - } - } - - private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) { - return (TestTransportInfo) nc.getTransportInfo(); - } - - private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) { - final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork()); - assertNotNull(nc); - return getTestTransportInfo(nc); - } - - - private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( - @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid, - @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid, - @NonNull TransportInfo expectedTransportInfo) throws Exception { - when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S); - final NetworkCapabilities ncTemplate = - new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .setOwnerUid(actualOwnerUid); - - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), - ncTemplate); - mWiFiNetworkAgent.connect(false); - - wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - - // Send network capabilities update with TransportInfo to trigger capabilities changed - // callback. - mWiFiNetworkAgent.setNetworkCapabilities( - ncTemplate.setTransportInfo(actualTransportInfo), true); - - wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent, - nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid()) - && Objects.equals(expectedTransportInfo, nc.getTransportInfo())); - } - - @Test - public void testVerifyLocationDataIsNotIncludedWhenInclFlagNotSet() throws Exception { - final TestNetworkCallback wifiNetworkCallack = new TestNetworkCallback(); - final int ownerUid = Process.myUid(); - final TransportInfo transportInfo = new TestTransportInfo(); - // Even though the test uid holds privileged permissions, mask location fields since - // the callback did not explicitly opt-in to get location data. - final TransportInfo sanitizedTransportInfo = new TestTransportInfo( - true, /* locationRedacted */ - true, /* localMacAddressRedacted */ - true /* settingsRedacted */ - ); - // Should not expect location data since the callback does not set the flag for including - // location data. - verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( - wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo); - } - - @Test - public void testTransportInfoRedactionInSynchronousCalls() throws Exception { - final NetworkCapabilities ncTemplate = new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .setTransportInfo(new TestTransportInfo()); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), - ncTemplate); - mWiFiNetworkAgent.connect(true /* validated; waits for callback */); - - // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); - withPermission(NETWORK_SETTINGS, () -> { - assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); - }); - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); - - // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); - withPermission(LOCAL_MAC_ADDRESS, () -> { - assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); - }); - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); - - // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive - // information. - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); - setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); - denyAllLocationPrivilegedPermissions(); - assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); - } - - private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) - throws Exception { - final Set vpnRange = Collections.singleton(PRIMARY_UIDRANGE); - mMockVpn.setVpnType(vpnType); - mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); - assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); - - final UnderlyingNetworkInfo underlyingNetworkInfo = - new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList()); - mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo); - when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42); - } - - private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType) - throws Exception { - setupConnectionOwnerUid(vpnOwnerUid, vpnType); - - // Test as VPN app - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - mServiceContext.setPermission( - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED); - } - - private ConnectionInfo getTestConnectionInfo() throws Exception { - return new ConnectionInfo( - IPPROTO_TCP, - new InetSocketAddress(InetAddresses.parseNumericAddress("1.2.3.4"), 1234), - new InetSocketAddress(InetAddresses.parseNumericAddress("2.3.4.5"), 2345)); - } - - @Test - public void testGetConnectionOwnerUidPlatformVpn() throws Exception { - final int myUid = Process.myUid(); - setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM); - - assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); - } - - @Test - public void testGetConnectionOwnerUidVpnServiceWrongUser() throws Exception { - final int myUid = Process.myUid(); - setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE); - - assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); - } - - @Test - public void testGetConnectionOwnerUidVpnServiceDoesNotThrow() throws Exception { - final int myUid = Process.myUid(); - setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE); - - assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); - } - - @Test - public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception { - final int myUid = Process.myUid(); - setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE); - mServiceContext.setPermission( - android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); - - assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); - } - - @Test - public void testGetConnectionOwnerUidVpnServiceMainlineNetworkStackDoesNotThrow() - throws Exception { - final int myUid = Process.myUid(); - setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE); - mServiceContext.setPermission( - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED); - - assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); - } - - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { - final PackageInfo packageInfo = new PackageInfo(); - if (hasSystemPermission) { - packageInfo.requestedPermissions = new String[] { - CHANGE_NETWORK_STATE, CONNECTIVITY_USE_RESTRICTED_NETWORKS }; - packageInfo.requestedPermissionsFlags = new int[] { - REQUESTED_PERMISSION_GRANTED, REQUESTED_PERMISSION_GRANTED }; - } else { - packageInfo.requestedPermissions = new String[0]; - } - packageInfo.applicationInfo = new ApplicationInfo(); - packageInfo.applicationInfo.privateFlags = 0; - packageInfo.applicationInfo.uid = UserHandle.getUid(UserHandle.USER_SYSTEM, - 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, mContext.getPackageName()); - fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest"); - } catch (IllegalArgumentException expected) { - } - } - - private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) { - assertEquals(route.getDestination().toString(), parcel.destination); - assertEquals(route.getInterface(), parcel.ifName); - assertEquals(route.getMtu(), parcel.mtu); - - switch (route.getType()) { - case RouteInfo.RTN_UNICAST: - if (route.hasGateway()) { - assertEquals(route.getGateway().getHostAddress(), parcel.nextHop); - } else { - assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop); - } - break; - case RouteInfo.RTN_UNREACHABLE: - assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop); - break; - case RouteInfo.RTN_THROW: - assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop); - break; - default: - assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop); - break; - } - } - - private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception { - ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); - verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture()); - for (int i = 0; i < routes.length; i++) { - assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i)); - } - } - - private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception { - ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); - verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId), - captor.capture()); - for (int i = 0; i < routes.length; i++) { - assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i)); - } - } - - @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, mContext.getPackageName()); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); - verify(mConnectivityDiagnosticsCallback).asBinder(); - assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); - - mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback); - verify(mIBinder, timeout(TIMEOUT_MS)) - .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); - assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); - 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, mContext.getPackageName()); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); - verify(mConnectivityDiagnosticsCallback).asBinder(); - assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); - - // Register the same callback again - mService.registerConnectivityDiagnosticsCallback( - mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); - } - - public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) { - final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, - ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), - TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); - return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), - nc, new NetworkScore.Builder().setLegacyInt(0).build(), - mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, - INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker, - new ConnectivityService.Dependencies()); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); - - mServiceContext.setPermission( - android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); - assertTrue( - "NetworkStack permission not applied", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithoutUid, - mContext.getOpPackageName())); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); - - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - - assertFalse( - "Mismatched uid/package name should not pass the location permission check", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid, - mContext.getOpPackageName())); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); - - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - - assertFalse( - "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithoutUid, - mContext.getOpPackageName())); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); - - mMockVpn.establishForMyUid(); - assertUidRangesUpdatedForMyUid(true); - - // Wait for networks to connect and broadcasts to be sent before removing permissions. - waitForIdle(); - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - - assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network})); - waitForIdle(); - assertTrue( - "Active VPN permission not applied", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithoutUid, - mContext.getOpPackageName())); - - assertTrue(mMockVpn.setUnderlyingNetworks(null)); - waitForIdle(); - assertFalse( - "VPN shouldn't receive callback on non-underlying network", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithoutUid, - mContext.getOpPackageName())); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception { - final NetworkCapabilities nc = new NetworkCapabilities(); - nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); - - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - - assertTrue( - "NetworkCapabilities administrator uid permission not applied", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName())); - } - - @Test - public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception { - final NetworkCapabilities nc = new NetworkCapabilities(); - nc.setOwnerUid(Process.myUid()); - nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); - - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - - // Use wrong pid and uid - assertFalse( - "Permissions allowed when they shouldn't be granted", - mService.checkConnectivityDiagnosticsPermissions( - Process.myPid() + 1, Process.myUid() + 1, naiWithUid, - mContext.getOpPackageName())); - } - - @Test - public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport() - throws Exception { - // Set up the Network, which leads to a ConnectivityReport being cached for the network. - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(callback); - final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setInterfaceName(INTERFACE_NAME); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - callback.assertNoCallback(); - - final NetworkRequest request = new NetworkRequest.Builder().build(); - when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); - - mServiceContext.setPermission( - android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); - - mService.registerConnectivityDiagnosticsCallback( - mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - verify(mConnectivityDiagnosticsCallback) - .onConnectivityReportAvailable(argThat(report -> { - return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName()) - && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR); - })); - } - - private void setUpConnectivityDiagnosticsCallback() throws Exception { - final NetworkRequest request = new NetworkRequest.Builder().build(); - when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); - - mServiceContext.setPermission( - android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); - - mService.registerConnectivityDiagnosticsCallback( - mConnectivityDiagnosticsCallback, request, mContext.getPackageName()); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - // Connect the cell agent verify that it notifies TestNetworkCallback that it is available - final TestNetworkCallback callback = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(callback); - - final NetworkCapabilities ncTemplate = new NetworkCapabilities() - .addTransportType(TRANSPORT_CELLULAR) - .setTransportInfo(new TestTransportInfo()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), - ncTemplate); - mCellNetworkAgent.connect(true); - callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - callback.assertNoCallback(); - } - - private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) { - TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo(); - return nc.getUids() == null - && nc.getAdministratorUids().length == 0 - && nc.getOwnerUid() == Process.INVALID_UID - && getTestTransportInfo(nc).locationRedacted - && getTestTransportInfo(nc).localMacAddressRedacted - && getTestTransportInfo(nc).settingsRedacted; - } - - @Test - public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() - throws Exception { - setUpConnectivityDiagnosticsCallback(); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - // Verify onConnectivityReport fired - verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable( - argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); - } - - @Test - public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception { - setUpConnectivityDiagnosticsCallback(); - - // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the - // cellular network agent - mCellNetworkAgent.notifyDataStallSuspected(); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - // Verify onDataStallSuspected fired - verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( - argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); - } - - @Test - public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception { - setUpConnectivityDiagnosticsCallback(); - - final Network n = mCellNetworkAgent.getNetwork(); - final boolean hasConnectivity = true; - mService.reportNetworkConnectivity(n, hasConnectivity); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - // Verify onNetworkConnectivityReported fired - verify(mConnectivityDiagnosticsCallback) - .onNetworkConnectivityReported(eq(n), eq(hasConnectivity)); - - final boolean noConnectivity = false; - mService.reportNetworkConnectivity(n, noConnectivity); - - // Block until all other events are done processing. - HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS); - - // Wait for onNetworkConnectivityReported to fire - verify(mConnectivityDiagnosticsCallback) - .onNetworkConnectivityReported(eq(n), eq(noConnectivity)); - } - - @Test - public void testRouteAddDeleteUpdate() throws Exception { - final NetworkRequest request = new NetworkRequest.Builder().build(); - final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.registerNetworkCallback(request, networkCallback); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - reset(mMockNetd); - mCellNetworkAgent.connect(false); - networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - final int netId = mCellNetworkAgent.getNetwork().netId; - - final String iface = "rmnet_data0"; - final InetAddress gateway = InetAddress.getByName("fe80::5678"); - RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface); - RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface); - RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface); - RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface); - RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST, - 1280 /* mtu */); - - // Send LinkProperties and check that we ask netd to add routes. - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(iface); - lp.addRoute(direct); - lp.addRoute(rio1); - lp.addRoute(defaultRoute); - mCellNetworkAgent.sendLinkProperties(lp); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3); - - assertRoutesAdded(netId, direct, rio1, defaultRoute); - reset(mMockNetd); - - // Send updated LinkProperties and check that we ask netd to add, remove, update routes. - assertTrue(lp.getRoutes().contains(defaultRoute)); - lp.removeRoute(rio1); - lp.addRoute(rio2); - lp.addRoute(defaultWithMtu); - // Ensure adding the same route with a different MTU replaces the previous route. - assertFalse(lp.getRoutes().contains(defaultRoute)); - assertTrue(lp.getRoutes().contains(defaultWithMtu)); - - mCellNetworkAgent.sendLinkProperties(lp); - networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, - x -> x.getRoutes().contains(rio2)); - - assertRoutesRemoved(netId, rio1); - assertRoutesAdded(netId, rio2); - - ArgumentCaptor captor = ArgumentCaptor.forClass(RouteInfoParcel.class); - verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture()); - assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue()); - - - mCm.unregisterNetworkCallback(networkCallback); - } - - @Test - public void testDumpDoesNotCrash() { - mServiceContext.setPermission(DUMP, PERMISSION_GRANTED); - // Filing a couple requests prior to testing the dump. - final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); - final NetworkRequest genericRequest = new NetworkRequest.Builder() - .clearCapabilities().build(); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); - mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); - final StringWriter stringWriter = new StringWriter(); - - mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); - - assertFalse(stringWriter.toString().isEmpty()); - } - - @Test - public void testRequestsSortedByIdSortsCorrectly() { - final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); - final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); - final NetworkRequest genericRequest = new NetworkRequest.Builder() - .clearCapabilities().build(); - final NetworkRequest wifiRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_WIFI).build(); - final NetworkRequest cellRequest = new NetworkRequest.Builder() - .addTransportType(TRANSPORT_CELLULAR).build(); - mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); - mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); - mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - waitForIdle(); - - final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); - - assertTrue(nriOutput.length > 1); - for (int i = 0; i < nriOutput.length - 1; i++) { - final boolean isRequestIdInOrder = - nriOutput[i].mRequests.get(0).requestId - < nriOutput[i + 1].mRequests.get(0).requestId; - assertTrue(isRequestIdInOrder); - } - } - - private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception { - final int uid = Process.myUid(); - assertVpnUidRangesUpdated(add, uidRangesForUids(uid), uid); - } - - private void assertVpnUidRangesUpdated(boolean add, Set vpnRanges, int exemptUid) - throws Exception { - InOrder inOrder = inOrder(mMockNetd); - ArgumentCaptor exemptUidCaptor = ArgumentCaptor.forClass(int[].class); - - inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), - exemptUidCaptor.capture()); - assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); - - if (add) { - inOrder.verify(mMockNetd, times(1)) - .networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), - eq(toUidRangeStableParcels(vpnRanges))); - } else { - inOrder.verify(mMockNetd, times(1)) - .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), - eq(toUidRangeStableParcels(vpnRanges))); - } - - inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), - exemptUidCaptor.capture()); - assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); - } - - @Test - public void testVpnUidRangesUpdate() throws Exception { - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName("tun0"); - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - final UidRange vpnRange = PRIMARY_UIDRANGE; - Set vpnRanges = Collections.singleton(vpnRange); - mMockVpn.establish(lp, VPN_UID, vpnRanges); - assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); - - reset(mMockNetd); - // Update to new range which is old range minus APP1, i.e. only APP2 - final Set newRanges = new HashSet<>(Arrays.asList( - new UidRange(vpnRange.start, APP1_UID - 1), - new UidRange(APP1_UID + 1, vpnRange.stop))); - mMockVpn.setUids(newRanges); - waitForIdle(); - - assertVpnUidRangesUpdated(true, newRanges, VPN_UID); - assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID); - } - - @Test - public void testInvalidRequestTypes() { - final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(), - NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length}; - final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); - - for (int reqTypeInt : invalidReqTypeInts) { - assertThrows("Expect throws for invalid request type " + reqTypeInt, - IllegalArgumentException.class, - () -> mService.requestNetwork(Process.INVALID_UID, nc, reqTypeInt, null, 0, - null, ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, - mContext.getPackageName(), getAttributionTag()) - ); - } - } - - @Test - public void testKeepConnected() throws Exception { - setAlwaysOnNetworks(false); - registerDefaultNetworkCallbacks(); - final TestNetworkCallback allNetworksCb = new TestNetworkCallback(); - final NetworkRequest allNetworksRequest = new NetworkRequest.Builder().clearCapabilities() - .build(); - mCm.registerNetworkCallback(allNetworksRequest, allNetworksCb); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true /* validated */); - - mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true /* validated */); - - mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - // While the default callback doesn't see the network before it's validated, the listen - // sees the network come up and validate later - allNetworksCb.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); - allNetworksCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, - TEST_LINGER_DELAY_MS * 2); - - // The cell network has disconnected (see LOST above) because it was outscored and - // had no requests (see setAlwaysOnNetworks(false) above) - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - final NetworkScore score = new NetworkScore.Builder().setLegacyInt(30).build(); - mCellNetworkAgent.setScore(score); - mCellNetworkAgent.connect(false /* validated */); - - // The cell network gets torn down right away. - allNetworksCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); - allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, - TEST_NASCENT_DELAY_MS * 2); - allNetworksCb.assertNoCallback(); - - // Now create a cell network with KEEP_CONNECTED_FOR_HANDOVER and make sure it's - // not disconnected immediately when outscored. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - final NetworkScore scoreKeepup = new NetworkScore.Builder().setLegacyInt(30) - .setKeepConnectedReason(KEEP_CONNECTED_FOR_HANDOVER).build(); - mCellNetworkAgent.setScore(scoreKeepup); - mCellNetworkAgent.connect(true /* validated */); - - allNetworksCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mDefaultNetworkCallback.assertNoCallback(); - - mWiFiNetworkAgent.disconnect(); - - allNetworksCb.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - - // Reconnect a WiFi network and make sure the cell network is still not torn down. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true /* validated */); - - allNetworksCb.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); - mDefaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - - // Now remove the reason to keep connected and make sure the network lingers and is - // torn down. - mCellNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).build()); - allNetworksCb.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent, - TEST_NASCENT_DELAY_MS * 2); - allNetworksCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, - TEST_LINGER_DELAY_MS * 2); - mDefaultNetworkCallback.assertNoCallback(); - - mCm.unregisterNetworkCallback(allNetworksCb); - // mDefaultNetworkCallback will be unregistered by tearDown() - } - - private class QosCallbackMockHelper { - @NonNull public final QosFilter mFilter; - @NonNull public final IQosCallback mCallback; - @NonNull public final TestNetworkAgentWrapper mAgentWrapper; - @NonNull private final List mCallbacks = new ArrayList(); - - QosCallbackMockHelper() throws Exception { - Log.d(TAG, "QosCallbackMockHelper: "); - mFilter = mock(QosFilter.class); - - // Ensure the network is disconnected before anything else occurs - assertNull(mCellNetworkAgent); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - verifyActiveNetwork(TRANSPORT_CELLULAR); - waitForIdle(); - final Network network = mCellNetworkAgent.getNetwork(); - - final Pair pair = createQosCallback(); - mCallback = pair.first; - - when(mFilter.getNetwork()).thenReturn(network); - when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); - mAgentWrapper = mCellNetworkAgent; - } - - void registerQosCallback(@NonNull final QosFilter filter, - @NonNull final IQosCallback callback) { - mCallbacks.add(callback); - final NetworkAgentInfo nai = - mService.getNetworkAgentInfoForNetwork(filter.getNetwork()); - mService.registerQosCallbackInternal(filter, callback, nai); - } - - void tearDown() { - for (int i = 0; i < mCallbacks.size(); i++) { - mService.unregisterQosCallback(mCallbacks.get(i)); - } - } - } - - private Pair createQosCallback() { - final IQosCallback callback = mock(IQosCallback.class); - final IBinder binder = mock(Binder.class); - when(callback.asBinder()).thenReturn(binder); - when(binder.isBinderAlive()).thenReturn(true); - return new Pair<>(callback, binder); - } - - - @Test - public void testQosCallbackRegistration() throws Exception { - mQosCallbackMockHelper = new QosCallbackMockHelper(); - final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper; - - when(mQosCallbackMockHelper.mFilter.validate()) - .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); - mQosCallbackMockHelper.registerQosCallback( - mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); - - final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 = - (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister) - wrapper.getCallbackHistory().poll(1000, x -> true); - assertNotNull(cbRegister1); - - final int registerCallbackId = cbRegister1.mQosCallbackId; - mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback); - final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister; - cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister) - wrapper.getCallbackHistory().poll(1000, x -> true); - assertNotNull(cbUnregister); - assertEquals(registerCallbackId, cbUnregister.mQosCallbackId); - assertNull(wrapper.getCallbackHistory().poll(200, x -> true)); - } - - @Test - public void testQosCallbackNoRegistrationOnValidationError() throws Exception { - mQosCallbackMockHelper = new QosCallbackMockHelper(); - - when(mQosCallbackMockHelper.mFilter.validate()) - .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); - mQosCallbackMockHelper.registerQosCallback( - mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); - waitForIdle(); - verify(mQosCallbackMockHelper.mCallback) - .onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED)); - } - - @Test - public void testQosCallbackAvailableAndLost() throws Exception { - mQosCallbackMockHelper = new QosCallbackMockHelper(); - final int sessionId = 10; - final int qosCallbackId = 1; - - when(mQosCallbackMockHelper.mFilter.validate()) - .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); - mQosCallbackMockHelper.registerQosCallback( - mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); - waitForIdle(); - - final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes( - 1, 2, 3, 4, 5, new ArrayList<>()); - mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() - .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); - waitForIdle(); - - verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session -> - session.getSessionId() == sessionId - && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes)); - - mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() - .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER); - waitForIdle(); - verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> - session.getSessionId() == sessionId - && session.getSessionType() == QosSession.TYPE_EPS_BEARER)); - } - - @Test - public void testNrQosCallbackAvailableAndLost() throws Exception { - mQosCallbackMockHelper = new QosCallbackMockHelper(); - final int sessionId = 10; - final int qosCallbackId = 1; - - when(mQosCallbackMockHelper.mFilter.validate()) - .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); - mQosCallbackMockHelper.registerQosCallback( - mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); - waitForIdle(); - - final NrQosSessionAttributes attributes = new NrQosSessionAttributes( - 1, 2, 3, 4, 5, 6, 7, new ArrayList<>()); - mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() - .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); - waitForIdle(); - - verify(mQosCallbackMockHelper.mCallback).onNrQosSessionAvailable(argThat(session -> - session.getSessionId() == sessionId - && session.getSessionType() == QosSession.TYPE_NR_BEARER), eq(attributes)); - - mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() - .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_NR_BEARER); - waitForIdle(); - verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> - session.getSessionId() == sessionId - && session.getSessionType() == QosSession.TYPE_NR_BEARER)); - } - - @Test - public void testQosCallbackTooManyRequests() throws Exception { - mQosCallbackMockHelper = new QosCallbackMockHelper(); - - when(mQosCallbackMockHelper.mFilter.validate()) - .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); - for (int i = 0; i < 100; i++) { - final Pair pair = createQosCallback(); - - try { - mQosCallbackMockHelper.registerQosCallback( - mQosCallbackMockHelper.mFilter, pair.first); - } catch (ServiceSpecificException e) { - assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS); - if (i < 50) { - fail("TOO_MANY_REQUESTS thrown too early, the count is " + i); - } - - // As long as there is at least 50 requests, it is safe to assume it works. - // Note: The count isn't being tested precisely against 100 because the counter - // is shared with request network. - return; - } - } - fail("TOO_MANY_REQUESTS never thrown"); - } - - private UidRange createUidRange(int userId) { - return UidRange.createForUser(UserHandle.of(userId)); - } - - private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid) { - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.uid = uid; - try { - when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())) - .thenReturn(applicationInfo); - } catch (Exception e) { - fail(e.getMessage()); - } - } - - private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName) - throws Exception { - when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())) - .thenThrow(new PackageManager.NameNotFoundException(packageName)); - } - - private void mockHasSystemFeature(@NonNull final String featureName, - @NonNull final boolean hasFeature) { - when(mPackageManager.hasSystemFeature(eq(featureName))) - .thenReturn(hasFeature); - } - - private Range getNriFirstUidRange( - @NonNull final ConnectivityService.NetworkRequestInfo nri) { - return nri.mRequests.get(0).networkCapabilities.getUids().iterator().next(); - } - - private OemNetworkPreferences createDefaultOemNetworkPreferences( - @OemNetworkPreferences.OemNetworkPreference final int preference) { - // Arrange PackageManager mocks - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - - // Build OemNetworkPreferences object - return new OemNetworkPreferences.Builder() - .addNetworkPreference(TEST_PACKAGE_NAME, preference) - .build(); - } - - @Test - public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError() - throws PackageManager.NameNotFoundException { - @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OEM_NETWORK_PREFERENCE_UNINITIALIZED; - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - assertThrows(IllegalArgumentException.class, - () -> mService.new OemNetworkRequestFactory() - .createNrisFromOemNetworkPreferences( - createDefaultOemNetworkPreferences(prefToTest))); - } - - @Test - public void testOemNetworkRequestFactoryPreferenceOemPaid() - throws Exception { - // Expectations - final int expectedNumOfNris = 1; - final int expectedNumOfRequests = 3; - - @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OEM_NETWORK_PREFERENCE_OEM_PAID; - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory() - .createNrisFromOemNetworkPreferences( - createDefaultOemNetworkPreferences(prefToTest)); - - final List mRequests = nris.iterator().next().mRequests; - assertEquals(expectedNumOfNris, nris.size()); - assertEquals(expectedNumOfRequests, mRequests.size()); - assertTrue(mRequests.get(0).isListen()); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED)); - assertTrue(mRequests.get(1).isRequest()); - assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID)); - assertEquals(NetworkRequest.Type.TRACK_DEFAULT, mRequests.get(2).type); - assertTrue(mService.getDefaultRequest().networkCapabilities.equalsNetCapabilities( - mRequests.get(2).networkCapabilities)); - } - - @Test - public void testOemNetworkRequestFactoryPreferenceOemPaidNoFallback() - throws Exception { - // Expectations - final int expectedNumOfNris = 1; - final int expectedNumOfRequests = 2; - - @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory() - .createNrisFromOemNetworkPreferences( - createDefaultOemNetworkPreferences(prefToTest)); - - final List mRequests = nris.iterator().next().mRequests; - assertEquals(expectedNumOfNris, nris.size()); - assertEquals(expectedNumOfRequests, mRequests.size()); - assertTrue(mRequests.get(0).isListen()); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_VALIDATED)); - assertTrue(mRequests.get(1).isRequest()); - assertTrue(mRequests.get(1).hasCapability(NET_CAPABILITY_OEM_PAID)); - } - - @Test - public void testOemNetworkRequestFactoryPreferenceOemPaidOnly() - throws Exception { - // Expectations - final int expectedNumOfNris = 1; - final int expectedNumOfRequests = 1; - - @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory() - .createNrisFromOemNetworkPreferences( - createDefaultOemNetworkPreferences(prefToTest)); - - final List mRequests = nris.iterator().next().mRequests; - assertEquals(expectedNumOfNris, nris.size()); - assertEquals(expectedNumOfRequests, mRequests.size()); - assertTrue(mRequests.get(0).isRequest()); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID)); - } - - @Test - public void testOemNetworkRequestFactoryPreferenceOemPrivateOnly() - throws Exception { - // Expectations - final int expectedNumOfNris = 1; - final int expectedNumOfRequests = 1; - - @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory() - .createNrisFromOemNetworkPreferences( - createDefaultOemNetworkPreferences(prefToTest)); - - final List mRequests = nris.iterator().next().mRequests; - assertEquals(expectedNumOfNris, nris.size()); - assertEquals(expectedNumOfRequests, mRequests.size()); - assertTrue(mRequests.get(0).isRequest()); - assertTrue(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PRIVATE)); - assertFalse(mRequests.get(0).hasCapability(NET_CAPABILITY_OEM_PAID)); - } - - @Test - public void testOemNetworkRequestFactoryCreatesCorrectNumOfNris() - throws Exception { - // Expectations - final int expectedNumOfNris = 2; - - // Arrange PackageManager mocks - final String testPackageName2 = "com.google.apps.dialer"; - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - mockGetApplicationInfo(testPackageName2, TEST_PACKAGE_UID); - - // Build OemNetworkPreferences object - final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; - final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; - final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() - .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) - .addNetworkPreference(testPackageName2, testOemPref2) - .build(); - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref); - - assertNotNull(nris); - assertEquals(expectedNumOfNris, nris.size()); - } - - @Test - public void testOemNetworkRequestFactoryMultiplePrefsCorrectlySetsUids() - throws Exception { - // Arrange PackageManager mocks - final String testPackageName2 = "com.google.apps.dialer"; - final int testPackageNameUid2 = 456; - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - mockGetApplicationInfo(testPackageName2, testPackageNameUid2); - - // Build OemNetworkPreferences object - final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; - final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; - final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() - .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) - .addNetworkPreference(testPackageName2, testOemPref2) - .build(); - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final List nris = - new ArrayList<>( - mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences( - pref)); - - // Sort by uid to access nris by index - nris.sort(Comparator.comparingInt(nri -> getNriFirstUidRange(nri).getLower())); - assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getLower()); - assertEquals(TEST_PACKAGE_UID, (int) getNriFirstUidRange(nris.get(0)).getUpper()); - assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getLower()); - assertEquals(testPackageNameUid2, (int) getNriFirstUidRange(nris.get(1)).getUpper()); - } - - @Test - public void testOemNetworkRequestFactoryMultipleUsersCorrectlySetsUids() - throws Exception { - // Arrange users - final int secondUser = 10; - final UserHandle secondUserHandle = new UserHandle(secondUser); - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); - - // Arrange PackageManager mocks - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - - // Build OemNetworkPreferences object - final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; - final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() - .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) - .build(); - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final List nris = - new ArrayList<>( - mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences( - pref)); - - // UIDs for all users and all managed packages should be present. - // Two users each with two packages. - final int expectedUidSize = 2; - final List> uids = - new ArrayList<>(nris.get(0).mRequests.get(0).networkCapabilities.getUids()); - assertEquals(expectedUidSize, uids.size()); - - // Sort by uid to access nris by index - uids.sort(Comparator.comparingInt(uid -> uid.getLower())); - final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); - assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getLower()); - assertEquals(TEST_PACKAGE_UID, (int) uids.get(0).getUpper()); - assertEquals(secondUserTestPackageUid, (int) uids.get(1).getLower()); - assertEquals(secondUserTestPackageUid, (int) uids.get(1).getUpper()); - } - - @Test - public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference() - throws Exception { - // Expectations - final int expectedNumOfNris = 1; - final int expectedNumOfAppUids = 2; - - // Arrange PackageManager mocks - final String testPackageName2 = "com.google.apps.dialer"; - final int testPackageNameUid2 = 456; - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - mockGetApplicationInfo(testPackageName2, testPackageNameUid2); - - // Build OemNetworkPreferences object - final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; - final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() - .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) - .addNetworkPreference(testPackageName2, testOemPref) - .build(); - - // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() - final ArraySet nris = - mService.new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(pref); - - assertEquals(expectedNumOfNris, nris.size()); - assertEquals(expectedNumOfAppUids, - nris.iterator().next().mRequests.get(0).networkCapabilities.getUids().size()); - } - - @Test - public void testSetOemNetworkPreferenceNullListenerAndPrefParamThrowsNpe() { - mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); - - // Act on ConnectivityService.setOemNetworkPreference() - assertThrows(NullPointerException.class, - () -> mService.setOemNetworkPreference( - null, - null)); - } - - @Test - public void testSetOemNetworkPreferenceFailsForNonAutomotive() - throws Exception { - mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false); - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - - // Act on ConnectivityService.setOemNetworkPreference() - assertThrows(UnsupportedOperationException.class, - () -> mService.setOemNetworkPreference( - createDefaultOemNetworkPreferences(networkPref), - new TestOemListenerCallback())); - } - - private void setOemNetworkPreferenceAgentConnected(final int transportType, - final boolean connectAgent) throws Exception { - switch(transportType) { - // Corresponds to a metered cellular network. Will be used for the default network. - case TRANSPORT_CELLULAR: - if (!connectAgent) { - mCellNetworkAgent.disconnect(); - break; - } - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); - mCellNetworkAgent.connect(true); - break; - // Corresponds to a restricted ethernet network with OEM_PAID/OEM_PRIVATE. - case TRANSPORT_ETHERNET: - if (!connectAgent) { - stopOemManagedNetwork(); - break; - } - startOemManagedNetwork(true); - break; - // Corresponds to unmetered Wi-Fi. - case TRANSPORT_WIFI: - if (!connectAgent) { - mWiFiNetworkAgent.disconnect(); - break; - } - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.connect(true); - break; - default: - throw new AssertionError("Unsupported transport type passed in."); - - } - waitForIdle(); - } - - private void startOemManagedNetwork(final boolean isOemPaid) throws Exception { - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.addCapability( - isOemPaid ? NET_CAPABILITY_OEM_PAID : NET_CAPABILITY_OEM_PRIVATE); - mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - mEthernetNetworkAgent.connect(true); - } - - private void stopOemManagedNetwork() { - mEthernetNetworkAgent.disconnect(); - waitForIdle(); - } - - private void verifyMultipleDefaultNetworksTracksCorrectly( - final int expectedOemRequestsSize, - @NonNull final Network expectedDefaultNetwork, - @NonNull final Network expectedPerAppNetwork) { - // The current test setup assumes two tracked default network requests; one for the default - // network and the other for the OEM network preference being tested. This will be validated - // each time to confirm it doesn't change under test. - final int expectedDefaultNetworkRequestsSize = 2; - assertEquals(expectedDefaultNetworkRequestsSize, mService.mDefaultNetworkRequests.size()); - for (final ConnectivityService.NetworkRequestInfo defaultRequest - : mService.mDefaultNetworkRequests) { - final Network defaultNetwork = defaultRequest.getSatisfier() == null - ? null : defaultRequest.getSatisfier().network(); - // If this is the default request. - if (defaultRequest == mService.mDefaultRequest) { - assertEquals( - expectedDefaultNetwork, - defaultNetwork); - // Make sure this value doesn't change. - assertEquals(1, defaultRequest.mRequests.size()); - continue; - } - assertEquals(expectedPerAppNetwork, defaultNetwork); - assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size()); - } - verifyMultipleDefaultCallbacks(expectedDefaultNetwork, expectedPerAppNetwork); - } - - /** - * Verify default callbacks for 'available' fire as expected. This will only run if - * registerDefaultNetworkCallbacks() was executed prior and will only be different if the - * setOemNetworkPreference() per-app API was used for the current process. - * @param expectedSystemDefault the expected network for the system default. - * @param expectedPerAppDefault the expected network for the current process's default. - */ - private void verifyMultipleDefaultCallbacks( - @NonNull final Network expectedSystemDefault, - @NonNull final Network expectedPerAppDefault) { - if (null != mSystemDefaultNetworkCallback && null != expectedSystemDefault - && mService.mNoServiceNetwork.network() != expectedSystemDefault) { - // getLastAvailableNetwork() is used as this method can be called successively with - // the same network to validate therefore expectAvailableThenValidatedCallbacks - // can't be used. - assertEquals(mSystemDefaultNetworkCallback.getLastAvailableNetwork(), - expectedSystemDefault); - } - if (null != mDefaultNetworkCallback && null != expectedPerAppDefault - && mService.mNoServiceNetwork.network() != expectedPerAppDefault) { - assertEquals(mDefaultNetworkCallback.getLastAvailableNetwork(), - expectedPerAppDefault); - } - } - - private void registerDefaultNetworkCallbacks() { - // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback() - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); - mSystemDefaultNetworkCallback = new TestNetworkCallback(); - mDefaultNetworkCallback = new TestNetworkCallback(); - mProfileDefaultNetworkCallback = new TestNetworkCallback(); - mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback, - new Handler(ConnectivityThread.getInstanceLooper())); - mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback); - registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback, - TEST_WORK_PROFILE_APP_UID); - // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well. - mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); - } - - private void unregisterDefaultNetworkCallbacks() { - if (null != mDefaultNetworkCallback) { - mCm.unregisterNetworkCallback(mDefaultNetworkCallback); - } - if (null != mSystemDefaultNetworkCallback) { - mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback); - } - if (null != mProfileDefaultNetworkCallback) { - mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback); - } - } - - private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( - @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) - throws Exception { - final int testPackageNameUid = TEST_PACKAGE_UID; - final String testPackageName = "per.app.defaults.package"; - setupMultipleDefaultNetworksForOemNetworkPreferenceTest( - networkPrefToSetup, testPackageNameUid, testPackageName); - } - - private void setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest( - @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) - throws Exception { - final int testPackageNameUid = Process.myUid(); - final String testPackageName = "per.app.defaults.package"; - setupMultipleDefaultNetworksForOemNetworkPreferenceTest( - networkPrefToSetup, testPackageNameUid, testPackageName); - } - - private void setupMultipleDefaultNetworksForOemNetworkPreferenceTest( - @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, - final int testPackageUid, @NonNull final String testPackageName) throws Exception { - // Only the default request should be included at start. - assertEquals(1, mService.mDefaultNetworkRequests.size()); - - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(testPackageUid)); - setupSetOemNetworkPreferenceForPreferenceTest( - networkPrefToSetup, uidRanges, testPackageName); - } - - private void setupSetOemNetworkPreferenceForPreferenceTest( - @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, - @NonNull final UidRangeParcel[] uidRanges, - @NonNull final String testPackageName) - throws Exception { - // These tests work off a single UID therefore using 'start' is valid. - mockGetApplicationInfo(testPackageName, uidRanges[0].start); - - setOemNetworkPreference(networkPrefToSetup, testPackageName); - } - - private void setOemNetworkPreference(final int networkPrefToSetup, - @NonNull final String... testPackageNames) - throws Exception { - mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); - - // Build OemNetworkPreferences object - final OemNetworkPreferences.Builder builder = new OemNetworkPreferences.Builder(); - for (final String packageName : testPackageNames) { - builder.addNetworkPreference(packageName, networkPrefToSetup); - } - final OemNetworkPreferences pref = builder.build(); - - // Act on ConnectivityService.setOemNetworkPreference() - final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback(); - mService.setOemNetworkPreference(pref, oemPrefListener); - - // Verify call returned successfully - oemPrefListener.expectOnComplete(); - } - - private static class TestOemListenerCallback implements IOnCompleteListener { - final CompletableFuture mDone = new CompletableFuture<>(); - - @Override - public void onComplete() { - mDone.complete(new Object()); - } - - void expectOnComplete() { - try { - mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (TimeoutException e) { - fail("Expected onComplete() not received after " + TIMEOUT_MS + " ms"); - } catch (Exception e) { - fail(e.getMessage()); - } - } - - @Override - public IBinder asBinder() { - return null; - } - } - - @Test - public void testMultiDefaultGetActiveNetworkIsCorrect() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - final int expectedOemPrefRequestSize = 1; - registerDefaultNetworkCallbacks(); - - // Setup the test process to use networkPref for their default network. - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - // The active network for the default should be null at this point as this is a retricted - // network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // Verify that the active network is correct - verifyActiveNetwork(TRANSPORT_ETHERNET); - // default NCs will be unregistered in tearDown - } - - @Test - public void testMultiDefaultIsActiveNetworkMeteredIsCorrect() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - final int expectedOemPrefRequestSize = 1; - registerDefaultNetworkCallbacks(); - - // Setup the test process to use networkPref for their default network. - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - - // Returns true by default when no network is available. - assertTrue(mCm.isActiveNetworkMetered()); - - // Connect to an unmetered restricted network that will only be available to the OEM pref. - mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); - mEthernetNetworkAgent.addCapability(NET_CAPABILITY_OEM_PAID); - mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - mEthernetNetworkAgent.connect(true); - waitForIdle(); - - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - assertFalse(mCm.isActiveNetworkMetered()); - // default NCs will be unregistered in tearDown - } - - @Test - public void testPerAppDefaultRegisterDefaultNetworkCallback() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - final int expectedOemPrefRequestSize = 1; - final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); - - // Register the default network callback before the pref is already set. This means that - // the policy will be applied to the callback on setOemNetworkPreference(). - mCm.registerDefaultNetworkCallback(defaultNetworkCallback); - defaultNetworkCallback.assertNoCallback(); - - final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); - withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, - new Handler(ConnectivityThread.getInstanceLooper()))); - - // Setup the test process to use networkPref for their default network. - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - // The active nai for the default is null at this point as this is a restricted network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // At this point with a restricted network used, the available callback should trigger. - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), - mEthernetNetworkAgent.getNetwork()); - otherUidDefaultCallback.assertNoCallback(); - - // Now bring down the default network which should trigger a LOST callback. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - - // At this point, with no network is available, the lost callback should trigger - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - otherUidDefaultCallback.assertNoCallback(); - - // Confirm we can unregister without issues. - mCm.unregisterNetworkCallback(defaultNetworkCallback); - mCm.unregisterNetworkCallback(otherUidDefaultCallback); - } - - @Test - public void testPerAppDefaultRegisterDefaultNetworkCallbackAfterPrefSet() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - final int expectedOemPrefRequestSize = 1; - final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); - - // Setup the test process to use networkPref for their default network. - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - - // Register the default network callback after the pref is already set. This means that - // the policy will be applied to the callback on requestNetwork(). - mCm.registerDefaultNetworkCallback(defaultNetworkCallback); - defaultNetworkCallback.assertNoCallback(); - - final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); - withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, - new Handler(ConnectivityThread.getInstanceLooper()))); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - // The active nai for the default is null at this point as this is a restricted network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // At this point with a restricted network used, the available callback should trigger - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), - mEthernetNetworkAgent.getNetwork()); - otherUidDefaultCallback.assertNoCallback(); - - // Now bring down the default network which should trigger a LOST callback. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - otherUidDefaultCallback.assertNoCallback(); - - // At this point, with no network is available, the lost callback should trigger - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - otherUidDefaultCallback.assertNoCallback(); - - // Confirm we can unregister without issues. - mCm.unregisterNetworkCallback(defaultNetworkCallback); - mCm.unregisterNetworkCallback(otherUidDefaultCallback); - } - - @Test - public void testPerAppDefaultRegisterDefaultNetworkCallbackDoesNotFire() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - final int expectedOemPrefRequestSize = 1; - final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); - final int userId = UserHandle.getUserId(Process.myUid()); - - mCm.registerDefaultNetworkCallback(defaultNetworkCallback); - defaultNetworkCallback.assertNoCallback(); - - final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); - withPermission(NETWORK_SETTINGS, () -> - mCm.registerDefaultNetworkCallbackForUid(TEST_PACKAGE_UID, otherUidDefaultCallback, - new Handler(ConnectivityThread.getInstanceLooper()))); - - // Setup a process different than the test process to use the default network. This means - // that the defaultNetworkCallback won't be tracked by the per-app policy. - setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - // The active nai for the default is null at this point as this is a restricted network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // As this callback does not have access to the OEM_PAID network, it will not fire. - defaultNetworkCallback.assertNoCallback(); - assertDefaultNetworkCapabilities(userId /* no networks */); - - // The other UID does have access, and gets a callback. - otherUidDefaultCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); - - // Bring up unrestricted cellular. This should now satisfy the default network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // At this point with an unrestricted network used, the available callback should trigger - // The other UID is unaffected and remains on the paid network. - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), - mCellNetworkAgent.getNetwork()); - assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); - otherUidDefaultCallback.assertNoCallback(); - - // Now bring down the per-app network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - - // Since the callback didn't use the per-app network, only the other UID gets a callback. - // Because the preference specifies no fallback, it does not switch to cellular. - defaultNetworkCallback.assertNoCallback(); - otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); - - // Now bring down the default network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - - // As this callback was tracking the default, this should now trigger. - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - otherUidDefaultCallback.assertNoCallback(); - - // Confirm we can unregister without issues. - mCm.unregisterNetworkCallback(defaultNetworkCallback); - mCm.unregisterNetworkCallback(otherUidDefaultCallback); - } - - /** - * This method assumes that the same uidRanges input will be used to verify that dependencies - * are called as expected. - */ - private void verifySetOemNetworkPreferenceForPreference( - @NonNull final UidRangeParcel[] uidRanges, - final int addUidRangesNetId, - final int addUidRangesTimes, - final int removeUidRangesNetId, - final int removeUidRangesTimes, - final boolean shouldDestroyNetwork) throws RemoteException { - verifySetOemNetworkPreferenceForPreference(uidRanges, uidRanges, - addUidRangesNetId, addUidRangesTimes, removeUidRangesNetId, removeUidRangesTimes, - shouldDestroyNetwork); - } - - private void verifySetOemNetworkPreferenceForPreference( - @NonNull final UidRangeParcel[] addedUidRanges, - @NonNull final UidRangeParcel[] removedUidRanges, - final int addUidRangesNetId, - final int addUidRangesTimes, - final int removeUidRangesNetId, - final int removeUidRangesTimes, - final boolean shouldDestroyNetwork) throws RemoteException { - final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId; - final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId; - - // Validate netd. - verify(mMockNetd, times(addUidRangesTimes)) - .networkAddUidRanges( - (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(addedUidRanges)); - verify(mMockNetd, times(removeUidRangesTimes)) - .networkRemoveUidRanges( - (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), - eq(removedUidRanges)); - if (shouldDestroyNetwork) { - verify(mMockNetd, times(1)) - .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId))); - } - reset(mMockNetd); - } - - /** - * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference(). - */ - @Test - public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception { - @OemNetworkPreferences.OemNetworkPreference int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - final int testPackageUid = 123; - final String testPackageName = "com.google.apps.contacts"; - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(testPackageUid)); - - // Validate the starting requests only includes the fallback request. - assertEquals(1, mService.mDefaultNetworkRequests.size()); - - // Add an OEM default network request to track. - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); - - // Two requests should exist, one for the fallback and one for the pref. - assertEquals(2, mService.mDefaultNetworkRequests.size()); - - networkPref = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); - - // Two requests should still exist validating the previous per-app request was replaced. - assertEquals(2, mService.mDefaultNetworkRequests.size()); - } - - /** - * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: - * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback - */ - @Test - public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - - // Arrange PackageManager mocks - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - - // Verify the starting state. No networks should be connected. - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Disconnecting OEM_PAID should have no effect as it is lower in priority then unmetered. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - // netd should not be called as default networks haven't changed. - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Disconnecting unmetered should put PANS on lowest priority fallback request. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - - // Disconnecting the fallback network should result in no connectivity. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - mCellNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: - * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID - */ - @Test - public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; - - // Arrange PackageManager mocks - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - - // Verify the starting state. This preference doesn't support using the fallback network - // therefore should be on the disconnected network as it has no networks to connect to. - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network. - // This preference should not use this network as it doesn't support fallback usage. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Disconnecting unmetered should put PANS on OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - - // Disconnecting OEM_PAID should result in no connectivity. - // OEM_PAID_NO_FALLBACK not supporting a fallback now uses the disconnected network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: - * NET_CAPABILITY_OEM_PAID - * This preference should only apply to OEM_PAID networks. - */ - @Test - public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - - // Arrange PackageManager mocks - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - - // Verify the starting state. This preference doesn't support using the fallback network - // therefore should be on the disconnected network as it has no networks to connect to. - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up metered cellular. This should not apply to this preference. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up unmetered Wi-Fi. This should not apply to this preference. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - false /* shouldDestroyNetwork */); - - // Disconnecting OEM_PAID should result in no connectivity. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: - * NET_CAPABILITY_OEM_PRIVATE - * This preference should only apply to OEM_PRIVATE networks. - */ - @Test - public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - - // Arrange PackageManager mocks - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - - // Verify the starting state. This preference doesn't support using the fallback network - // therefore should be on the disconnected network as it has no networks to connect to. - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up metered cellular. This should not apply to this preference. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up unmetered Wi-Fi. This should not apply to this preference. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. - startOemManagedNetwork(false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - false /* shouldDestroyNetwork */); - - // Disconnecting OEM_PRIVATE should result in no connectivity. - stopOemManagedNetwork(); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, - mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - } - - @Test - public void testMultilayerForMultipleUsersEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - - // Arrange users - final int secondUser = 10; - final UserHandle secondUserHandle = new UserHandle(secondUser); - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); - - // Arrange PackageManager mocks - final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels( - uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - - // Verify the starting state. No networks should be connected. - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test that we correctly add the expected values for multiple users. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRanges, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test that we correctly remove the expected values for multiple users. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifySetOemNetworkPreferenceForPreference(uidRanges, - OEM_PREF_ANY_NET_ID, 0 /* times */, - mCellNetworkAgent.getNetwork().netId, 0 /* times */, - true /* shouldDestroyNetwork */); - } - - @Test - public void testMultilayerForBroadcastedUsersEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - - // Arrange users - final int secondUser = 10; - final UserHandle secondUserHandle = new UserHandle(secondUser); - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE)); - - // Arrange PackageManager mocks - final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID); - final UidRangeParcel[] uidRangesSingleUser = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - final UidRangeParcel[] uidRangesBothUsers = - toUidRangeStableParcels( - uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid)); - setupSetOemNetworkPreferenceForPreferenceTest( - networkPref, uidRangesSingleUser, TEST_PACKAGE_NAME); - - // Verify the starting state. No networks should be connected. - verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test that we correctly add the expected values for multiple users. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Send a broadcast indicating a user was added. - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE, secondUserHandle)); - final Intent addedIntent = new Intent(ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); - processBroadcast(addedIntent); - - // Test that we correctly add values for all users and remove for the single user. - verifySetOemNetworkPreferenceForPreference(uidRangesBothUsers, uidRangesSingleUser, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Send a broadcast indicating a user was removed. - when(mUserManager.getUserHandles(anyBoolean())).thenReturn( - Arrays.asList(PRIMARY_USER_HANDLE)); - final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser)); - processBroadcast(removedIntent); - - // Test that we correctly add values for the single user and remove for the all users. - verifySetOemNetworkPreferenceForPreference(uidRangesSingleUser, uidRangesBothUsers, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - } - - @Test - public void testMultilayerForPackageChangesEvaluatesCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - final String packageScheme = "package:"; - - // Arrange PackageManager mocks - final String packageToInstall = "package.to.install"; - final int packageToInstallUid = 81387; - final UidRangeParcel[] uidRangesSinglePackage = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); - mockGetApplicationInfoThrowsNameNotFound(packageToInstall); - setOemNetworkPreference(networkPref, TEST_PACKAGE_NAME, packageToInstall); - grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid(), packageToInstall); - - // Verify the starting state. No networks should be connected. - verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, - OEM_PREF_ANY_NET_ID, 0 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Test that we correctly add the expected values for installed packages. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - OEM_PREF_ANY_NET_ID, 0 /* times */, - false /* shouldDestroyNetwork */); - - // Set the system to recognize the package to be installed - mockGetApplicationInfo(packageToInstall, packageToInstallUid); - final UidRangeParcel[] uidRangesAllPackages = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID, packageToInstallUid)); - - // Send a broadcast indicating a package was installed. - final Intent addedIntent = new Intent(ACTION_PACKAGE_ADDED); - addedIntent.setData(Uri.parse(packageScheme + packageToInstall)); - processBroadcast(addedIntent); - - // Test the single package is removed and the combined packages are added. - verifySetOemNetworkPreferenceForPreference(uidRangesAllPackages, uidRangesSinglePackage, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Set the system to no longer recognize the package to be installed - mockGetApplicationInfoThrowsNameNotFound(packageToInstall); - - // Send a broadcast indicating a package was removed. - final Intent removedIntent = new Intent(ACTION_PACKAGE_REMOVED); - removedIntent.setData(Uri.parse(packageScheme + packageToInstall)); - processBroadcast(removedIntent); - - // Test the combined packages are removed and the single package is added. - verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, uidRangesAllPackages, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - - // Set the system to change the installed package's uid - final int replacedTestPackageUid = TEST_PACKAGE_UID + 1; - mockGetApplicationInfo(TEST_PACKAGE_NAME, replacedTestPackageUid); - final UidRangeParcel[] uidRangesReplacedPackage = - toUidRangeStableParcels(uidRangesForUids(replacedTestPackageUid)); - - // Send a broadcast indicating a package was replaced. - final Intent replacedIntent = new Intent(ACTION_PACKAGE_REPLACED); - replacedIntent.setData(Uri.parse(packageScheme + TEST_PACKAGE_NAME)); - processBroadcast(replacedIntent); - - // Test the original uid is removed and is replaced with the new uid. - verifySetOemNetworkPreferenceForPreference(uidRangesReplacedPackage, uidRangesSinglePackage, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - mCellNetworkAgent.getNetwork().netId, 1 /* times */, - false /* shouldDestroyNetwork */); - } - - /** - * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order: - * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback - */ - @Test - public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - final int expectedDefaultRequestSize = 2; - final int expectedOemPrefRequestSize = 3; - registerDefaultNetworkCallbacks(); - - // The fallback as well as the OEM preference should now be tracked. - assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mCellNetworkAgent.getNetwork()); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mWiFiNetworkAgent.getNetwork(), - mWiFiNetworkAgent.getNetwork()); - - // Disconnecting unmetered Wi-Fi will put the pref on OEM_PAID and fallback on cellular. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting OEM_PAID will put both on null as it is the last network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - null); - - // default callbacks will be unregistered in tearDown - } - - @Test - public void testNetworkFactoryRequestsWithMultilayerRequest() - throws Exception { - // First use OEM_PAID preference to create a multi-layer request : 1. listen for - // unmetered, 2. request network with cap OEM_PAID, 3, request the default network for - // fallback. - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - - final HandlerThread handlerThread = new HandlerThread("MockFactory"); - handlerThread.start(); - NetworkCapabilities internetFilter = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "internetFactory", internetFilter, mCsHandlerThread); - internetFactory.setScoreFilter(40); - internetFactory.register(); - // Default internet request only. The unmetered request is never sent to factories (it's a - // LISTEN, not requestable). The 3rd (fallback) request in OEM_PAID NRI is TRACK_DEFAULT - // which is also not sent to factories. Finally, the OEM_PAID request doesn't match the - // internetFactory filter. - internetFactory.expectRequestAdds(1); - internetFactory.assertRequestCountEquals(1); - - NetworkCapabilities oemPaidFilter = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_OEM_PAID) - .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) - .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - final MockNetworkFactory oemPaidFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "oemPaidFactory", oemPaidFilter, mCsHandlerThread); - oemPaidFactory.setScoreFilter(40); - oemPaidFactory.register(); - oemPaidFactory.expectRequestAdd(); // Because nobody satisfies the request - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - // A network connected that satisfies the default internet request. For the OEM_PAID - // preference, this is not as good as an OEM_PAID network, so even if the score of - // the network is better than the factory announced, it still should try to bring up - // the network. - expectNoRequestChanged(oemPaidFactory); - oemPaidFactory.assertRequestCountEquals(1); - // The internet factory however is outscored, and should lose its requests. - internetFactory.expectRequestRemove(); - internetFactory.assertRequestCountEquals(0); - - final NetworkCapabilities oemPaidNc = new NetworkCapabilities(); - oemPaidNc.addCapability(NET_CAPABILITY_OEM_PAID); - oemPaidNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - final TestNetworkAgentWrapper oemPaidAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, - new LinkProperties(), oemPaidNc); - oemPaidAgent.connect(true); - - // The oemPaidAgent has score 50/cell transport, so it beats what the oemPaidFactory can - // provide, therefore it loses the request. - oemPaidFactory.expectRequestRemove(); - oemPaidFactory.assertRequestCountEquals(0); - expectNoRequestChanged(internetFactory); - internetFactory.assertRequestCountEquals(0); - - oemPaidAgent.setScore(new NetworkScore.Builder().setLegacyInt(20).setExiting(true).build()); - // Now the that the agent is weak, the oemPaidFactory can beat the existing network for the - // OEM_PAID request. The internet factory however can't beat a network that has OEM_PAID - // for the preference request, so it doesn't see the request. - oemPaidFactory.expectRequestAdd(); - oemPaidFactory.assertRequestCountEquals(1); - expectNoRequestChanged(internetFactory); - internetFactory.assertRequestCountEquals(0); - - mCellNetworkAgent.disconnect(); - // The network satisfying the default internet request has disconnected, so the - // internetFactory sees the default request again. However there is a network with OEM_PAID - // connected, so the 2nd OEM_PAID req is already satisfied, so the oemPaidFactory doesn't - // care about networks that don't have OEM_PAID. - expectNoRequestChanged(oemPaidFactory); - oemPaidFactory.assertRequestCountEquals(1); - internetFactory.expectRequestAdd(); - internetFactory.assertRequestCountEquals(1); - - // Cell connects again, still with score 50. Back to the previous state. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - expectNoRequestChanged(oemPaidFactory); - oemPaidFactory.assertRequestCountEquals(1); - internetFactory.expectRequestRemove(); - internetFactory.assertRequestCountEquals(0); - - // Create a request that holds the upcoming wifi network. - final TestNetworkCallback wifiCallback = new TestNetworkCallback(); - mCm.requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(), - wifiCallback); - - // Now WiFi connects and it's unmetered, but it's weaker than cell. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); - mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).setExiting(true) - .build()); // Not the best Internet network, but unmetered - mWiFiNetworkAgent.connect(true); - - // The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so - // the oemPaidFactory can't beat wifi no matter how high its score. - oemPaidFactory.expectRequestRemove(); - expectNoRequestChanged(internetFactory); - - mCellNetworkAgent.disconnect(); - // Now that the best internet network (cell, with its 50 score compared to 30 for WiFi - // at this point), the default internet request is satisfied by a network worse than - // the internetFactory announced, so it gets the request. However, there is still an - // unmetered network, so the oemPaidNetworkFactory still can't beat this. - expectNoRequestChanged(oemPaidFactory); - internetFactory.expectRequestAdd(); - mCm.unregisterNetworkCallback(wifiCallback); - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK in the following order: - * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID - */ - @Test - public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidNoFallbackCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - final int expectedDefaultRequestSize = 2; - final int expectedOemPrefRequestSize = 2; - registerDefaultNetworkCallbacks(); - - // The fallback as well as the OEM preference should now be tracked. - assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network but not the pref. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mService.mNoServiceNetwork.network()); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mWiFiNetworkAgent.getNetwork(), - mWiFiNetworkAgent.getNetwork()); - - // Disconnecting unmetered Wi-Fi will put the OEM pref on OEM_PAID and fallback on cellular. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting cellular should keep OEM network on OEM_PAID and fallback will be null. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting OEM_PAID puts the fallback on null and the pref on the disconnected net. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mService.mNoServiceNetwork.network()); - - // default callbacks will be unregistered in tearDown - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY in the following order: - * NET_CAPABILITY_OEM_PAID - * This preference should only apply to OEM_PAID networks. - */ - @Test - public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPaidOnlyCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - final int expectedDefaultRequestSize = 2; - final int expectedOemPrefRequestSize = 1; - registerDefaultNetworkCallbacks(); - - // The fallback as well as the OEM preference should now be tracked. - assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mService.mNoServiceNetwork.network()); - - // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mWiFiNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting OEM_PAID will keep the fallback on cellular and nothing for OEM_PAID. - // OEM_PAID_ONLY not supporting a fallback now uses the disconnected network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mService.mNoServiceNetwork.network()); - - // Disconnecting cellular will put the fallback on null and the pref on disconnected. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mService.mNoServiceNetwork.network()); - - // default callbacks will be unregistered in tearDown - } - - /** - * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY in the following order: - * NET_CAPABILITY_OEM_PRIVATE - * This preference should only apply to OEM_PRIVATE networks. - */ - @Test - public void testMultipleDefaultNetworksTracksOemNetworkPreferenceOemPrivateOnlyCorrectly() - throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); - final int expectedDefaultRequestSize = 2; - final int expectedOemPrefRequestSize = 1; - registerDefaultNetworkCallbacks(); - - // The fallback as well as the OEM preference should now be tracked. - assertEquals(expectedDefaultRequestSize, mService.mDefaultNetworkRequests.size()); - - // Test lowest to highest priority requests. - // Bring up metered cellular. This will satisfy the fallback network. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mService.mNoServiceNetwork.network()); - - // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. - startOemManagedNetwork(false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Bring up unmetered Wi-Fi. The OEM network shouldn't change, the fallback will take Wi-Fi. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mWiFiNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting unmetered Wi-Fi shouldn't change the OEM network with fallback on cellular. - setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mEthernetNetworkAgent.getNetwork()); - - // Disconnecting OEM_PRIVATE will keep the fallback on cellular. - // OEM_PRIVATE_ONLY not supporting a fallback now uses to the disconnected network. - stopOemManagedNetwork(); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - mCellNetworkAgent.getNetwork(), - mService.mNoServiceNetwork.network()); - - // Disconnecting cellular will put the fallback on null and pref on disconnected. - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); - verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, - null, - mService.mNoServiceNetwork.network()); - - // default callbacks will be unregistered in tearDown - } - - @Test - public void testCapabilityWithOemNetworkPreference() throws Exception { - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); - registerDefaultNetworkCallbacks(); - - setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); - - mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - - mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); - mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> - nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> - nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - - // default callbacks will be unregistered in tearDown - } - - @Test - public void testSetOemNetworkPreferenceLogsRequest() throws Exception { - mServiceContext.setPermission(DUMP, PERMISSION_GRANTED); - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PAID; - final StringWriter stringWriter = new StringWriter(); - final String logIdentifier = "UPDATE INITIATED: OemNetworkPreferences"; - final Pattern pattern = Pattern.compile(logIdentifier); - - final int expectedNumLogs = 2; - final UidRangeParcel[] uidRanges = - toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID)); - - // Call twice to generate two logs. - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); - mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); - - final String dumpOutput = stringWriter.toString(); - final Matcher matcher = pattern.matcher(dumpOutput); - int count = 0; - while (matcher.find()) { - count++; - } - assertEquals(expectedNumLogs, count); - } - - @Test - public void testGetAllNetworkStateSnapshots() 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 snapshots = mCm.getAllNetworkStateSnapshots(); - 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.getAllNetworkStateSnapshots(); - 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.getAllNetworkStateSnapshots(); - assertLength(1, snapshots); - assertEquals(wifiSnapshot, snapshots.get(0)); - - // Disconnect wifi, verify the snapshots contain nothing. - mWiFiNetworkAgent.disconnect(); - waitForIdle(); - snapshots = mCm.getAllNetworkStateSnapshots(); - assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - assertLength(0, snapshots); - - mCellNetworkAgent.resume(); - waitForIdle(); - snapshots = mCm.getAllNetworkStateSnapshots(); - assertLength(1, snapshots); - assertEquals(cellSnapshot, snapshots.get(0)); - - mCellNetworkAgent.disconnect(); - waitForIdle(); - verifyNoNetwork(); - 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_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. It doesn't see the default request because its filter does - // not include INTERNET. - testFactory.register(); - expectNoRequestChanged(testFactory); - testFactory.assertRequestCountEquals(0); - // 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(0); - 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(1); - assertTrue(testFactory.getMyStartRequested()); - - // Unregister best matching callback, verify factory see no change. - mCm.unregisterNetworkCallback(bestMatchingCb); - expectNoRequestChanged(testFactory); - testFactory.assertRequestCountEquals(1); - 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); - } - - private UidRangeParcel[] uidRangeFor(final UserHandle handle) { - UidRange range = UidRange.createForUser(handle); - return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) }; - } - - private static class TestOnCompleteListener implements Runnable { - final class OnComplete {} - final ArrayTrackRecord.ReadHead mHistory = - new ArrayTrackRecord().newReadHead(); - - @Override - public void run() { - mHistory.add(new OnComplete()); - } - - public void expectOnComplete() { - assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true)); - } - } - - private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception { - final NetworkCapabilities workNc = new NetworkCapabilities(); - workNc.addCapability(NET_CAPABILITY_ENTERPRISE); - workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc); - } - - private TestNetworkCallback mEnterpriseCallback; - private UserHandle setupEnterpriseNetwork() { - final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); - mServiceContext.setWorkProfile(userHandle, true); - - // File a request to avoid the enterprise network being disconnected as soon as the default - // request goes away – it would make impossible to test that networkRemoveUidRanges - // is called, as the network would disconnect first for lack of a request. - mEnterpriseCallback = new TestNetworkCallback(); - final NetworkRequest keepUpRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_ENTERPRISE) - .build(); - mCm.requestNetwork(keepUpRequest, mEnterpriseCallback); - return userHandle; - } - - private void maybeTearDownEnterpriseNetwork() { - if (null != mEnterpriseCallback) { - mCm.unregisterNetworkCallback(mEnterpriseCallback); - } - } - - /** - * Make sure per-profile networking preference behaves as expected when the enterprise network - * goes up and down while the preference is active. Make sure they behave as expected whether - * there is a general default network or not. - */ - @Test - public void testPreferenceForUserNetworkUpDown() throws Exception { - final InOrder inOrder = inOrder(mMockNetd); - final UserHandle testHandle = setupEnterpriseNetwork(); - registerDefaultNetworkCallbacks(); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); - - - final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - - // Setting a network preference for this user will create a new set of routing rules for - // the UID range that corresponds to this user, so as to define the default network - // for these apps separately. This is true because the multi-layer request relevant to - // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific - // rules to the correct network – in this case the system default network. The case where - // the default network for the profile happens to be the same as the system default - // is not handled specially, the rules are always active as long as a preference is set. - inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, - uidRangeFor(testHandle)); - - // The enterprise network is not ready yet. - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - mProfileDefaultNetworkCallback); - - final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); - workAgent.connect(false); - - mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent); - mSystemDefaultNetworkCallback.assertNoCallback(); - mDefaultNetworkCallback.assertNoCallback(); - inOrder.verify(mMockNetd).networkCreate( - nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); - inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle)); - inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId, - uidRangeFor(testHandle)); - - // Make sure changes to the work agent send callbacks to the app in the work profile, but - // not to the other apps. - workAgent.setNetworkValid(true /* isStrictMode */); - workAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); - mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, - nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED) - && nc.hasCapability(NET_CAPABILITY_ENTERPRISE)); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - - workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); - mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc -> - nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - - // Conversely, change a capability on the system-wide default network and make sure - // that only the apps outside of the work profile receive the callbacks. - mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); - mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> - nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc -> - nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); - mProfileDefaultNetworkCallback.assertNoCallback(); - - // Disconnect and reconnect the system-wide default network and make sure that the - // apps on this network see the appropriate callbacks, and the app on the work profile - // doesn't because it continues to use the enterprise network. - mCellNetworkAgent.disconnect(); - mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - mProfileDefaultNetworkCallback.assertNoCallback(); - inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mProfileDefaultNetworkCallback.assertNoCallback(); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); - - // When the agent disconnects, test that the app on the work profile falls back to the - // default network. - workAgent.disconnect(); - mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent); - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, - uidRangeFor(testHandle)); - inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId); - - mCellNetworkAgent.disconnect(); - mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - - // Waiting for the handler to be idle before checking for networkDestroy is necessary - // here because ConnectivityService calls onLost before the network is fully torn down. - waitForIdle(); - inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId); - - // If the control comes here, callbacks seem to behave correctly in the presence of - // a default network when the enterprise network goes up and down. Now, make sure they - // also behave correctly in the absence of a system-wide default network. - final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(); - workAgent2.connect(false); - - mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM)); - inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId, - uidRangeFor(testHandle)); - - workAgent2.setNetworkValid(true /* isStrictMode */); - workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid()); - mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2, - nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE) - && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any()); - - // When the agent disconnects, test that the app on the work profile falls back to the - // default network. - workAgent2.disconnect(); - mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId); - - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - mProfileDefaultNetworkCallback); - - // Callbacks will be unregistered by tearDown() - } - - /** - * Test that, in a given networking context, calling setPreferenceForUser to set per-profile - * defaults on then off works as expected. - */ - @Test - public void testSetPreferenceForUserOnOff() throws Exception { - final InOrder inOrder = inOrder(mMockNetd); - final UserHandle testHandle = setupEnterpriseNetwork(); - - // Connect both a regular cell agent and an enterprise network first. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); - workAgent.connect(true); - - final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); - inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle)); - - registerDefaultNetworkCallbacks(); - - mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent); - - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT, - r -> r.run(), listener); - listener.expectOnComplete(); - - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle)); - - workAgent.disconnect(); - mCellNetworkAgent.disconnect(); - - // Callbacks will be unregistered by tearDown() - } - - /** - * Test per-profile default networks for two different profiles concurrently. - */ - @Test - public void testSetPreferenceForTwoProfiles() throws Exception { - final InOrder inOrder = inOrder(mMockNetd); - final UserHandle testHandle2 = setupEnterpriseNetwork(); - final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2); - mServiceContext.setWorkProfile(testHandle4, true); - registerDefaultNetworkCallbacks(); - - final TestNetworkCallback app4Cb = new TestNetworkCallback(); - final int testWorkProfileAppUid4 = - UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID); - registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4); - - // Connect both a regular cell agent and an enterprise network first. - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); - workAgent.connect(true); - - mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); - - final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle2)); - - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - app4Cb); - - mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle4)); - - app4Cb.expectAvailableCallbacksValidated(workAgent); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - mProfileDefaultNetworkCallback); - - mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT, - r -> r.run(), listener); - listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId, - uidRangeFor(testHandle2)); - - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - app4Cb); - - workAgent.disconnect(); - mCellNetworkAgent.disconnect(); - - mCm.unregisterNetworkCallback(app4Cb); - // Other callbacks will be unregistered by tearDown() - } - - @Test - public void testProfilePreferenceRemovedUponUserRemoved() throws Exception { - final InOrder inOrder = inOrder(mMockNetd); - final UserHandle testHandle = setupEnterpriseNetwork(); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - - final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( - mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); - inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, - uidRangeFor(testHandle)); - - final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER, testHandle); - processBroadcast(removedIntent); - - inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId, - uidRangeFor(testHandle)); - } - - /** - * Make sure that OEM preference and per-profile preference can't be used at the same - * time and throw ISE if tried - */ - @Test - public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception { - final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); - mServiceContext.setWorkProfile(testHandle, true); - final TestOnCompleteListener listener = new TestOnCompleteListener(); - - setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( - OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY); - assertThrows("Should not be able to set per-profile pref while OEM prefs present", - IllegalStateException.class, () -> - mCm.setProfileNetworkPreference(testHandle, - PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener)); - - // Empty the OEM prefs - final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback(); - final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build(); - mService.setOemNetworkPreference(emptyOemPref, oemPrefListener); - oemPrefListener.expectOnComplete(); - - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - r -> r.run(), listener); - listener.expectOnComplete(); - assertThrows("Should not be able to set OEM prefs while per-profile pref is on", - IllegalStateException.class , () -> - mService.setOemNetworkPreference(emptyOemPref, oemPrefListener)); - } - - /** - * Make sure wrong preferences for per-profile default networking are rejected. - */ - @Test - public void testProfileNetworkPrefWrongPreference() throws Exception { - final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); - mServiceContext.setWorkProfile(testHandle, true); - assertThrows("Should not be able to set an illegal preference", - IllegalArgumentException.class, - () -> mCm.setProfileNetworkPreference(testHandle, - PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null)); - } - - /** - * Make sure requests for per-profile default networking for a non-work profile are - * rejected - */ - @Test - public void testProfileNetworkPrefWrongProfile() throws Exception { - final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID); - mServiceContext.setWorkProfile(testHandle, false); - assertThrows("Should not be able to set a user pref for a non-work profile", - IllegalArgumentException.class , () -> - mCm.setProfileNetworkPreference(testHandle, - PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null)); - } - - @Test - public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception { - mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED); - final NetworkCapabilities nc = new NetworkCapabilities(); - nc.setSubscriptionIds(Collections.singleton(Process.myUid())); - - final NetworkCapabilities result = - mService.networkCapabilitiesRestrictedForCallerPermissions( - nc, Process.myPid(), Process.myUid()); - assertTrue(result.getSubscriptionIds().isEmpty()); - } - - @Test - public void testSubIdsExistWithNetworkFactoryPermission() throws Exception { - mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED); - - final Set subIds = Collections.singleton(Process.myUid()); - final NetworkCapabilities nc = new NetworkCapabilities(); - nc.setSubscriptionIds(subIds); - - final NetworkCapabilities result = - mService.networkCapabilitiesRestrictedForCallerPermissions( - nc, Process.myPid(), Process.myUid()); - assertEquals(subIds, result.getSubscriptionIds()); - } - - private NetworkRequest getRequestWithSubIds() { - return new NetworkRequest.Builder() - .setSubscriptionIds(Collections.singleton(Process.myUid())) - .build(); - } - - @Test - public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception { - mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED); - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); - final NetworkCallback networkCallback1 = new NetworkCallback(); - final NetworkCallback networkCallback2 = new NetworkCallback(); - - mCm.requestNetwork(getRequestWithSubIds(), networkCallback1); - mCm.requestNetwork(getRequestWithSubIds(), pendingIntent); - mCm.registerNetworkCallback(getRequestWithSubIds(), networkCallback2); - - mCm.unregisterNetworkCallback(networkCallback1); - mCm.releaseNetworkRequest(pendingIntent); - mCm.unregisterNetworkCallback(networkCallback2); - } - - @Test - public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception { - mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED); - final PendingIntent pendingIntent = PendingIntent.getBroadcast( - mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE); - - final Class expected = SecurityException.class; - assertThrows( - expected, () -> mCm.requestNetwork(getRequestWithSubIds(), new NetworkCallback())); - assertThrows(expected, () -> mCm.requestNetwork(getRequestWithSubIds(), pendingIntent)); - assertThrows( - expected, - () -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback())); - } - - /** - * Validate request counts are counted accurately on setProfileNetworkPreference on set/replace. - */ - @Test - public void testProfileNetworkPrefCountsRequestsCorrectlyOnSet() throws Exception { - final UserHandle testHandle = setupEnterpriseNetwork(); - testRequestCountLimits(() -> { - // Set initially to test the limit prior to having existing requests. - final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - Runnable::run, listener); - listener.expectOnComplete(); - - // re-set so as to test the limit as part of replacing existing requests. - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, - Runnable::run, listener); - listener.expectOnComplete(); - }); - } - - /** - * Validate request counts are counted accurately on setOemNetworkPreference on set/replace. - */ - @Test - public void testSetOemNetworkPreferenceCountsRequestsCorrectlyOnSet() throws Exception { - mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; - testRequestCountLimits(() -> { - // Set initially to test the limit prior to having existing requests. - final TestOemListenerCallback listener = new TestOemListenerCallback(); - mService.setOemNetworkPreference( - createDefaultOemNetworkPreferences(networkPref), listener); - listener.expectOnComplete(); - - // re-set so as to test the limit as part of replacing existing requests. - mService.setOemNetworkPreference( - createDefaultOemNetworkPreferences(networkPref), listener); - listener.expectOnComplete(); - }); - } - - private void testRequestCountLimits(@NonNull final Runnable r) throws Exception { - final ArraySet callbacks = new ArraySet<>(); - try { - final int requestCount = mService.mSystemNetworkRequestCounter - .mUidToNetworkRequestCount.get(Process.myUid()); - // The limit is hit when total requests <= limit. - final int maxCount = - ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount; - // Need permission so registerDefaultNetworkCallback uses mSystemNetworkRequestCounter - withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { - for (int i = 1; i < maxCount - 1; i++) { - final TestNetworkCallback cb = new TestNetworkCallback(); - mCm.registerDefaultNetworkCallback(cb); - callbacks.add(cb); - } - - // Code to run to check if it triggers a max request count limit error. - r.run(); - }); - } finally { - for (final TestNetworkCallback cb : callbacks) { - mCm.unregisterNetworkCallback(cb); - } - } - } -} diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java deleted file mode 100644 index cf2c9c783ac7..000000000000 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Copyright (C) 2017 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 static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.INetd.IF_STATE_DOWN; -import static android.net.INetd.IF_STATE_UP; -import static android.net.IpSecManager.DIRECTION_FWD; -import static android.net.IpSecManager.DIRECTION_IN; -import static android.net.IpSecManager.DIRECTION_OUT; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.AppOpsManager; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.INetd; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpSecAlgorithm; -import android.net.IpSecConfig; -import android.net.IpSecManager; -import android.net.IpSecSpiResponse; -import android.net.IpSecTransform; -import android.net.IpSecTransformResponse; -import android.net.IpSecTunnelInterfaceResponse; -import android.net.IpSecUdpEncapResponse; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.os.Binder; -import android.os.ParcelFileDescriptor; -import android.system.Os; -import android.test.mock.MockContext; -import android.util.ArraySet; - -import androidx.test.filters.SmallTest; - -import com.android.server.IpSecService.TunnelInterfaceRecord; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.net.Inet4Address; -import java.net.Socket; -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; - -/** Unit tests for {@link IpSecService}. */ -@SmallTest -@RunWith(Parameterized.class) -public class IpSecServiceParameterizedTest { - - private static final int TEST_SPI = 0xD1201D; - - private final String mSourceAddr; - private final String mDestinationAddr; - private final LinkAddress mLocalInnerAddress; - private final int mFamily; - - private static final int[] ADDRESS_FAMILIES = - new int[] {AF_INET, AF_INET6}; - - @Parameterized.Parameters - public static Collection ipSecConfigs() { - return Arrays.asList( - new Object[][] { - {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET}, - {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6} - }); - } - - private static final byte[] AEAD_KEY = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x73, 0x61, 0x6C, 0x74 - }; - private static final byte[] CRYPT_KEY = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - }; - private static final byte[] AUTH_KEY = { - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F - }; - - AppOpsManager mMockAppOps = mock(AppOpsManager.class); - ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class); - - TestContext mTestContext = new TestContext(); - - private class TestContext extends MockContext { - private Set mAllowedPermissions = new ArraySet<>(Arrays.asList( - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, - android.Manifest.permission.NETWORK_STACK, - PERMISSION_MAINLINE_NETWORK_STACK)); - - private void setAllowedPermissions(String... permissions) { - mAllowedPermissions = new ArraySet<>(permissions); - } - - @Override - public Object getSystemService(String name) { - switch(name) { - case Context.APP_OPS_SERVICE: - return mMockAppOps; - case Context.CONNECTIVITY_SERVICE: - return mMockConnectivityMgr; - default: - return null; - } - } - - @Override - public String getSystemServiceName(Class serviceClass) { - if (ConnectivityManager.class == serviceClass) { - return Context.CONNECTIVITY_SERVICE; - } - return null; - } - - @Override - public PackageManager getPackageManager() { - return mMockPkgMgr; - } - - @Override - public void enforceCallingOrSelfPermission(String permission, String message) { - if (mAllowedPermissions.contains(permission)) { - return; - } else { - throw new SecurityException("Unavailable permission requested"); - } - } - - @Override - public int checkCallingOrSelfPermission(String permission) { - if (mAllowedPermissions.contains(permission)) { - return PERMISSION_GRANTED; - } else { - return PERMISSION_DENIED; - } - } - } - - INetd mMockNetd; - PackageManager mMockPkgMgr; - IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; - IpSecService mIpSecService; - Network fakeNetwork = new Network(0xAB); - int mUid = Os.getuid(); - - private static final IpSecAlgorithm AUTH_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); - private static final IpSecAlgorithm CRYPT_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); - private static final IpSecAlgorithm AEAD_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); - private static final int REMOTE_ENCAP_PORT = 4500; - - private static final String BLESSED_PACKAGE = "blessedPackage"; - private static final String SYSTEM_PACKAGE = "systemPackage"; - private static final String BAD_PACKAGE = "badPackage"; - - public IpSecServiceParameterizedTest( - String sourceAddr, String destAddr, String localInnerAddr, int family) { - mSourceAddr = sourceAddr; - mDestinationAddr = destAddr; - mLocalInnerAddress = new LinkAddress(localInnerAddr); - mFamily = family; - } - - @Before - public void setUp() throws Exception { - mMockNetd = mock(INetd.class); - mMockPkgMgr = mock(PackageManager.class); - mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig); - - // Injecting mock netd - when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); - - // PackageManager should always return true (feature flag tests in IpSecServiceTest) - when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true); - - // A package granted the AppOp for MANAGE_IPSEC_TUNNELS will be MODE_ALLOWED. - when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BLESSED_PACKAGE))) - .thenReturn(AppOpsManager.MODE_ALLOWED); - // A system package will not be granted the app op, so this should fall back to - // a permissions check, which should pass. - when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(SYSTEM_PACKAGE))) - .thenReturn(AppOpsManager.MODE_DEFAULT); - // A mismatch between the package name and the UID will return MODE_IGNORED. - when(mMockAppOps.noteOp(anyInt(), anyInt(), eq(BAD_PACKAGE))) - .thenReturn(AppOpsManager.MODE_IGNORED); - } - - //TODO: Add a test to verify SPI. - - @Test - public void testIpSecServiceReserveSpi() throws Exception { - when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) - .thenReturn(TEST_SPI); - - IpSecSpiResponse spiResp = - mIpSecService.allocateSecurityParameterIndex( - mDestinationAddr, TEST_SPI, new Binder()); - assertEquals(IpSecManager.Status.OK, spiResp.status); - assertEquals(TEST_SPI, spiResp.spi); - } - - @Test - public void testReleaseSecurityParameterIndex() throws Exception { - when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) - .thenReturn(TEST_SPI); - - IpSecSpiResponse spiResp = - mIpSecService.allocateSecurityParameterIndex( - mDestinationAddr, TEST_SPI, new Binder()); - - mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); - - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(mUid), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), - anyInt(), - anyInt()); - - // Verify quota and RefcountedResource objects cleaned up - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); - try { - userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testSecurityParameterIndexBinderDeath() throws Exception { - when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) - .thenReturn(TEST_SPI); - - IpSecSpiResponse spiResp = - mIpSecService.allocateSecurityParameterIndex( - mDestinationAddr, TEST_SPI, new Binder()); - - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - IpSecService.RefcountedResource refcountedRecord = - userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); - - refcountedRecord.binderDied(); - - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(mUid), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), - anyInt(), - anyInt()); - - // Verify quota and RefcountedResource objects cleaned up - assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); - try { - userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - private int getNewSpiResourceId(String remoteAddress, int returnSpi) throws Exception { - when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), anyString(), anyInt())) - .thenReturn(returnSpi); - - IpSecSpiResponse spi = - mIpSecService.allocateSecurityParameterIndex( - InetAddresses.parseNumericAddress(remoteAddress).getHostAddress(), - IpSecManager.INVALID_SECURITY_PARAMETER_INDEX, - new Binder()); - return spi.resourceId; - } - - private void addDefaultSpisAndRemoteAddrToIpSecConfig(IpSecConfig config) throws Exception { - config.setSpiResourceId(getNewSpiResourceId(mDestinationAddr, TEST_SPI)); - config.setSourceAddress(mSourceAddr); - config.setDestinationAddress(mDestinationAddr); - } - - private void addAuthAndCryptToIpSecConfig(IpSecConfig config) throws Exception { - config.setEncryption(CRYPT_ALGO); - config.setAuthentication(AUTH_ALGO); - } - - private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception { - config.setEncapType(IpSecTransform.ENCAP_ESPINUDP); - config.setEncapSocketResourceId(resourceId); - config.setEncapRemotePort(REMOTE_ENCAP_PORT); - } - - private void verifyTransformNetdCalledForCreatingSA( - IpSecConfig config, IpSecTransformResponse resp) throws Exception { - verifyTransformNetdCalledForCreatingSA(config, resp, 0); - } - - private void verifyTransformNetdCalledForCreatingSA( - IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception { - IpSecAlgorithm auth = config.getAuthentication(); - IpSecAlgorithm crypt = config.getEncryption(); - IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption(); - - verify(mMockNetd, times(1)) - .ipSecAddSecurityAssociation( - eq(mUid), - eq(config.getMode()), - eq(config.getSourceAddress()), - eq(config.getDestinationAddress()), - eq((config.getNetwork() != null) ? config.getNetwork().netId : 0), - eq(TEST_SPI), - eq(0), - eq(0), - eq((auth != null) ? auth.getName() : ""), - eq((auth != null) ? auth.getKey() : new byte[] {}), - eq((auth != null) ? auth.getTruncationLengthBits() : 0), - eq((crypt != null) ? crypt.getName() : ""), - eq((crypt != null) ? crypt.getKey() : new byte[] {}), - eq((crypt != null) ? crypt.getTruncationLengthBits() : 0), - eq((authCrypt != null) ? authCrypt.getName() : ""), - eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}), - eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0), - eq(config.getEncapType()), - eq(encapSocketPort), - eq(config.getEncapRemotePort()), - eq(config.getXfrmInterfaceId())); - } - - @Test - public void testCreateTransform() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); - } - - @Test - public void testCreateTransformAead() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - - ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); - } - - @Test - public void testCreateTransportModeTransformWithEncap() throws Exception { - IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - - IpSecConfig ipSecConfig = new IpSecConfig(); - ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); - - if (mFamily == AF_INET) { - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); - } else { - try { - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); - } catch (IllegalArgumentException expected) { - } - } - } - - @Test - public void testCreateTunnelModeTransformWithEncap() throws Exception { - IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - - IpSecConfig ipSecConfig = new IpSecConfig(); - ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); - - if (mFamily == AF_INET) { - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); - } else { - try { - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); - } catch (IllegalArgumentException expected) { - } - } - } - - @Test - public void testCreateTwoTransformsWithSameSpis() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - // Attempting to create transform a second time with the same SPIs should throw an error... - try { - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - fail("IpSecService should have thrown an error for reuse of SPI"); - } catch (IllegalStateException expected) { - } - - // ... even if the transform is deleted - mIpSecService.deleteTransform(createTransformResp.resourceId); - try { - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - fail("IpSecService should have thrown an error for reuse of SPI"); - } catch (IllegalStateException expected) { - } - } - - @Test - public void testReleaseOwnedSpi() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - verify(mMockNetd, times(0)) - .ipSecDeleteSecurityAssociation( - eq(mUid), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), - anyInt(), - anyInt()); - // quota is not released until the SPI is released by the Transform - assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); - } - - @Test - public void testDeleteTransform() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - mIpSecService.deleteTransform(createTransformResp.resourceId); - - verify(mMockNetd, times(1)) - .ipSecDeleteSecurityAssociation( - eq(mUid), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), - anyInt(), - anyInt()); - - // Verify quota and RefcountedResource objects cleaned up - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent); - assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent); - - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - // Verify that ipSecDeleteSa was not called when the SPI was released because the - // ownedByTransform property should prevent it; (note, the called count is cumulative). - verify(mMockNetd, times(1)) - .ipSecDeleteSecurityAssociation( - anyInt(), - anyString(), - anyString(), - anyInt(), - anyInt(), - anyInt(), - anyInt()); - assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent); - - try { - userRecord.mTransformRecords.getRefcountedResourceOrThrow( - createTransformResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testTransportModeTransformBinderDeath() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - IpSecService.RefcountedResource refcountedRecord = - userRecord.mTransformRecords.getRefcountedResourceOrThrow( - createTransformResp.resourceId); - - refcountedRecord.binderDied(); - - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(mUid), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), - anyInt(), - anyInt()); - - // Verify quota and RefcountedResource objects cleaned up - assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent); - try { - userRecord.mTransformRecords.getRefcountedResourceOrThrow( - createTransformResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testApplyTransportModeTransform() throws Exception { - verifyApplyTransportModeTransformCommon(false); - } - - @Test - public void testApplyTransportModeTransformReleasedSpi() throws Exception { - verifyApplyTransportModeTransformCommon(true); - } - - public void verifyApplyTransportModeTransformCommon( - boolean closeSpiBeforeApply) throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - - if (closeSpiBeforeApply) { - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - } - - Socket socket = new Socket(); - socket.bind(null); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); - - int resourceId = createTransformResp.resourceId; - mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId); - - verify(mMockNetd) - .ipSecApplyTransportModeTransform( - eq(pfd), - eq(mUid), - eq(IpSecManager.DIRECTION_OUT), - anyString(), - anyString(), - eq(TEST_SPI)); - } - - @Test - public void testApplyTransportModeTransformWithClosedSpi() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - - // Close SPI record - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - - Socket socket = new Socket(); - socket.bind(null); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); - - int resourceId = createTransformResp.resourceId; - mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId); - - verify(mMockNetd) - .ipSecApplyTransportModeTransform( - eq(pfd), - eq(mUid), - eq(IpSecManager.DIRECTION_OUT), - anyString(), - anyString(), - eq(TEST_SPI)); - } - - @Test - public void testRemoveTransportModeTransform() throws Exception { - Socket socket = new Socket(); - socket.bind(null); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); - mIpSecService.removeTransportModeTransforms(pfd); - - verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); - } - - private IpSecTunnelInterfaceResponse createAndValidateTunnel( - String localAddr, String remoteAddr, String pkgName) throws Exception { - final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); - config.flags = new String[] {IF_STATE_DOWN}; - when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config); - IpSecTunnelInterfaceResponse createTunnelResp = - mIpSecService.createTunnelInterface( - mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName); - - assertNotNull(createTunnelResp); - assertEquals(IpSecManager.Status.OK, createTunnelResp.status); - for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT, DIRECTION_FWD}) { - for (int selAddrFamily : ADDRESS_FAMILIES) { - verify(mMockNetd).ipSecAddSecurityPolicy( - eq(mUid), - eq(selAddrFamily), - eq(direction), - anyString(), - anyString(), - eq(0), - anyInt(), // iKey/oKey - anyInt(), // mask - eq(createTunnelResp.resourceId)); - } - } - - return createTunnelResp; - } - - @Test - public void testCreateTunnelInterface() throws Exception { - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - - // Check that we have stored the tracking object, and retrieve it - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - IpSecService.RefcountedResource refcountedRecord = - userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( - createTunnelResp.resourceId); - - assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); - verify(mMockNetd) - .ipSecAddTunnelInterface( - eq(createTunnelResp.interfaceName), - eq(mSourceAddr), - eq(mDestinationAddr), - anyInt(), - anyInt(), - anyInt()); - verify(mMockNetd).interfaceSetCfg(argThat( - config -> Arrays.asList(config.flags).contains(IF_STATE_UP))); - } - - @Test - public void testDeleteTunnelInterface() throws Exception { - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - - mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, BLESSED_PACKAGE); - - // Verify quota and RefcountedResource objects cleaned up - assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); - verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); - try { - userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( - createTunnelResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - } - } - - private Network createFakeUnderlyingNetwork(String interfaceName) { - final Network fakeNetwork = new Network(1000); - final LinkProperties fakeLp = new LinkProperties(); - fakeLp.setInterfaceName(interfaceName); - when(mMockConnectivityMgr.getLinkProperties(eq(fakeNetwork))).thenReturn(fakeLp); - return fakeNetwork; - } - - @Test - public void testSetNetworkForTunnelInterface() throws Exception { - final IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - final Network newFakeNetwork = createFakeUnderlyingNetwork("newFakeNetworkInterface"); - final int tunnelIfaceResourceId = createTunnelResp.resourceId; - mIpSecService.setNetworkForTunnelInterface( - tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE); - - final IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(mUid); - assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent); - - final TunnelInterfaceRecord tunnelInterfaceInfo = - userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId); - assertEquals(newFakeNetwork, tunnelInterfaceInfo.getUnderlyingNetwork()); - } - - @Test - public void testSetNetworkForTunnelInterfaceFailsForInvalidResourceId() throws Exception { - final IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - final Network newFakeNetwork = new Network(1000); - - try { - mIpSecService.setNetworkForTunnelInterface( - IpSecManager.INVALID_RESOURCE_ID, newFakeNetwork, BLESSED_PACKAGE); - fail("Expected an IllegalArgumentException for invalid resource ID."); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testSetNetworkForTunnelInterfaceFailsWhenSettingTunnelNetwork() throws Exception { - final IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - final int tunnelIfaceResourceId = createTunnelResp.resourceId; - final IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(mUid); - final TunnelInterfaceRecord tunnelInterfaceInfo = - userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelIfaceResourceId); - - final Network newFakeNetwork = - createFakeUnderlyingNetwork(tunnelInterfaceInfo.getInterfaceName()); - - try { - mIpSecService.setNetworkForTunnelInterface( - tunnelIfaceResourceId, newFakeNetwork, BLESSED_PACKAGE); - fail( - "Expected an IllegalArgumentException because the underlying network is the" - + " network being exposed by this tunnel."); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testTunnelInterfaceBinderDeath() throws Exception { - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - - IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid); - IpSecService.RefcountedResource refcountedRecord = - userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( - createTunnelResp.resourceId); - - refcountedRecord.binderDied(); - - // Verify quota and RefcountedResource objects cleaned up - assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent); - verify(mMockNetd).ipSecRemoveTunnelInterface(eq(createTunnelResp.interfaceName)); - try { - userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow( - createTunnelResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testApplyTunnelModeTransformOutbound() throws Exception { - verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); - } - - @Test - public void testApplyTunnelModeTransformOutboundNonNetworkStack() throws Exception { - mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); - verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); - } - - @Test - public void testApplyTunnelModeTransformOutboundReleasedSpi() throws Exception { - verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_OUT); - } - - @Test - public void testApplyTunnelModeTransformInbound() throws Exception { - verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); - } - - @Test - public void testApplyTunnelModeTransformInboundNonNetworkStack() throws Exception { - mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); - verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); - } - - @Test - public void testApplyTunnelModeTransformForward() throws Exception { - verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); - } - - @Test - public void testApplyTunnelModeTransformForwardNonNetworkStack() throws Exception { - mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); - - try { - verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); - fail("Expected security exception due to use of forward policies without NETWORK_STACK" - + " or MAINLINE_NETWORK_STACK permission"); - } catch (SecurityException expected) { - } - } - - public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply, int direction) - throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - - if (closeSpiBeforeApply) { - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - } - - int transformResourceId = createTransformResp.resourceId; - int tunnelResourceId = createTunnelResp.resourceId; - mIpSecService.applyTunnelModeTransform( - tunnelResourceId, direction, transformResourceId, BLESSED_PACKAGE); - - for (int selAddrFamily : ADDRESS_FAMILIES) { - verify(mMockNetd) - .ipSecUpdateSecurityPolicy( - eq(mUid), - eq(selAddrFamily), - eq(direction), - anyString(), - anyString(), - eq(direction == DIRECTION_OUT ? TEST_SPI : 0), - anyInt(), // iKey/oKey - anyInt(), // mask - eq(tunnelResourceId)); - } - - ipSecConfig.setXfrmInterfaceId(tunnelResourceId); - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); - } - - - @Test - public void testApplyTunnelModeTransformWithClosedSpi() throws Exception { - IpSecConfig ipSecConfig = new IpSecConfig(); - ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); - addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); - addAuthAndCryptToIpSecConfig(ipSecConfig); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransform(ipSecConfig, new Binder(), BLESSED_PACKAGE); - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BLESSED_PACKAGE); - - // Close SPI record - mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId()); - - int transformResourceId = createTransformResp.resourceId; - int tunnelResourceId = createTunnelResp.resourceId; - mIpSecService.applyTunnelModeTransform( - tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE); - - for (int selAddrFamily : ADDRESS_FAMILIES) { - verify(mMockNetd) - .ipSecUpdateSecurityPolicy( - eq(mUid), - eq(selAddrFamily), - eq(IpSecManager.DIRECTION_OUT), - anyString(), - anyString(), - eq(TEST_SPI), - anyInt(), // iKey/oKey - anyInt(), // mask - eq(tunnelResourceId)); - } - - ipSecConfig.setXfrmInterfaceId(tunnelResourceId); - verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); - } - - @Test - public void testAddRemoveAddressFromTunnelInterface() throws Exception { - for (String pkgName : new String[] {BLESSED_PACKAGE, SYSTEM_PACKAGE}) { - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, pkgName); - mIpSecService.addAddressToTunnelInterface( - createTunnelResp.resourceId, mLocalInnerAddress, pkgName); - verify(mMockNetd, times(1)) - .interfaceAddAddress( - eq(createTunnelResp.interfaceName), - eq(mLocalInnerAddress.getAddress().getHostAddress()), - eq(mLocalInnerAddress.getPrefixLength())); - mIpSecService.removeAddressFromTunnelInterface( - createTunnelResp.resourceId, mLocalInnerAddress, pkgName); - verify(mMockNetd, times(1)) - .interfaceDelAddress( - eq(createTunnelResp.interfaceName), - eq(mLocalInnerAddress.getAddress().getHostAddress()), - eq(mLocalInnerAddress.getPrefixLength())); - mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId, pkgName); - } - } - - @Ignore - @Test - public void testAddTunnelFailsForBadPackageName() throws Exception { - try { - IpSecTunnelInterfaceResponse createTunnelResp = - createAndValidateTunnel(mSourceAddr, mDestinationAddr, BAD_PACKAGE); - fail("Expected a SecurityException for badPackage."); - } catch (SecurityException expected) { - } - } - - @Test - public void testFeatureFlagVerification() throws Exception { - when(mMockPkgMgr.hasSystemFeature(eq(PackageManager.FEATURE_IPSEC_TUNNELS))) - .thenReturn(false); - - try { - String addr = Inet4Address.getLoopbackAddress().getHostAddress(); - mIpSecService.createTunnelInterface( - addr, addr, new Network(0), new Binder(), BLESSED_PACKAGE); - fail("Expected UnsupportedOperationException for disabled feature"); - } catch (UnsupportedOperationException expected) { - } - } -} diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java deleted file mode 100644 index 22a2c94fc194..000000000000 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2017 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.IpSecService.IResource; -import com.android.server.IpSecService.RefcountedResource; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; - -/** Unit tests for {@link IpSecService.RefcountedResource}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpSecServiceRefcountedResourceTest { - Context mMockContext; - IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; - IpSecService mIpSecService; - - @Before - public void setUp() throws Exception { - mMockContext = mock(Context.class); - mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); - } - - private void assertResourceState( - RefcountedResource resource, - int refCount, - int userReleaseCallCount, - int releaseReferenceCallCount, - int invalidateCallCount, - int freeUnderlyingResourcesCallCount) - throws RemoteException { - // Check refcount on RefcountedResource - assertEquals(refCount, resource.mRefCount); - - // Check call count of RefcountedResource - verify(resource, times(userReleaseCallCount)).userRelease(); - verify(resource, times(releaseReferenceCallCount)).releaseReference(); - - // Check call count of IResource - verify(resource.getResource(), times(invalidateCallCount)).invalidate(); - verify(resource.getResource(), times(freeUnderlyingResourcesCallCount)) - .freeUnderlyingResources(); - } - - /** Adds mockito instrumentation */ - private RefcountedResource getTestRefcountedResource( - RefcountedResource... children) { - return getTestRefcountedResource(new Binder(), children); - } - - /** Adds mockito instrumentation with provided binder */ - private RefcountedResource getTestRefcountedResource( - IBinder binder, RefcountedResource... children) { - return spy( - mIpSecService - .new RefcountedResource(mock(IResource.class), binder, children)); - } - - @Test - public void testConstructor() throws RemoteException { - IBinder binderMock = mock(IBinder.class); - RefcountedResource resource = getTestRefcountedResource(binderMock); - - // Verify resource's refcount starts at 1 (for user-reference) - assertResourceState(resource, 1, 0, 0, 0, 0); - - // Verify linking to binder death - verify(binderMock).linkToDeath(anyObject(), anyInt()); - } - - @Test - public void testConstructorWithChildren() throws RemoteException { - IBinder binderMockChild = mock(IBinder.class); - IBinder binderMockParent = mock(IBinder.class); - RefcountedResource childResource = getTestRefcountedResource(binderMockChild); - RefcountedResource parentResource = - getTestRefcountedResource(binderMockParent, childResource); - - // Verify parent's refcount starts at 1 (for user-reference) - assertResourceState(parentResource, 1, 0, 0, 0, 0); - - // Verify child's refcounts were incremented - assertResourceState(childResource, 2, 0, 0, 0, 0); - - // Verify linking to binder death - verify(binderMockChild).linkToDeath(anyObject(), anyInt()); - verify(binderMockParent).linkToDeath(anyObject(), anyInt()); - } - - @Test - public void testFailLinkToDeath() throws RemoteException { - IBinder binderMock = mock(IBinder.class); - doThrow(new RemoteException()).when(binderMock).linkToDeath(anyObject(), anyInt()); - - try { - getTestRefcountedResource(binderMock); - fail("Expected exception to propogate when binder fails to link to death"); - } catch (RuntimeException expected) { - } - } - - @Test - public void testCleanupAndRelease() throws RemoteException { - IBinder binderMock = mock(IBinder.class); - RefcountedResource refcountedResource = getTestRefcountedResource(binderMock); - - // Verify user-initiated cleanup path decrements refcount and calls full cleanup flow - refcountedResource.userRelease(); - assertResourceState(refcountedResource, -1, 1, 1, 1, 1); - - // Verify user-initated cleanup path unlinks from binder - verify(binderMock).unlinkToDeath(eq(refcountedResource), eq(0)); - assertNull(refcountedResource.mBinder); - } - - @Test - public void testMultipleCallsToCleanupAndRelease() throws RemoteException { - RefcountedResource refcountedResource = getTestRefcountedResource(); - - // Verify calling userRelease multiple times does not trigger any other cleanup - // methods - refcountedResource.userRelease(); - assertResourceState(refcountedResource, -1, 1, 1, 1, 1); - - refcountedResource.userRelease(); - refcountedResource.userRelease(); - assertResourceState(refcountedResource, -1, 3, 1, 1, 1); - } - - @Test - public void testBinderDeathAfterCleanupAndReleaseDoesNothing() throws RemoteException { - RefcountedResource refcountedResource = getTestRefcountedResource(); - - refcountedResource.userRelease(); - assertResourceState(refcountedResource, -1, 1, 1, 1, 1); - - // Verify binder death call does not trigger any other cleanup methods if called after - // userRelease() - refcountedResource.binderDied(); - assertResourceState(refcountedResource, -1, 2, 1, 1, 1); - } - - @Test - public void testBinderDeath() throws RemoteException { - RefcountedResource refcountedResource = getTestRefcountedResource(); - - // Verify binder death caused cleanup - refcountedResource.binderDied(); - verify(refcountedResource, times(1)).binderDied(); - assertResourceState(refcountedResource, -1, 1, 1, 1, 1); - assertNull(refcountedResource.mBinder); - } - - @Test - public void testCleanupParentDecrementsChildRefcount() throws RemoteException { - RefcountedResource childResource = getTestRefcountedResource(); - RefcountedResource parentResource = getTestRefcountedResource(childResource); - - parentResource.userRelease(); - - // Verify parent gets cleaned up properly, and triggers releaseReference on - // child - assertResourceState(childResource, 1, 0, 1, 0, 0); - assertResourceState(parentResource, -1, 1, 1, 1, 1); - } - - @Test - public void testCleanupReferencedChildDoesNotTriggerRelease() throws RemoteException { - RefcountedResource childResource = getTestRefcountedResource(); - RefcountedResource parentResource = getTestRefcountedResource(childResource); - - childResource.userRelease(); - - // Verify that child does not clean up kernel resources and quota. - assertResourceState(childResource, 1, 1, 1, 1, 0); - assertResourceState(parentResource, 1, 0, 0, 0, 0); - } - - @Test - public void testTwoParents() throws RemoteException { - RefcountedResource childResource = getTestRefcountedResource(); - RefcountedResource parentResource1 = getTestRefcountedResource(childResource); - RefcountedResource parentResource2 = getTestRefcountedResource(childResource); - - // Verify that child does not cleanup kernel resources and quota until all references - // have been released. Assumption: parents release correctly based on - // testCleanupParentDecrementsChildRefcount() - childResource.userRelease(); - assertResourceState(childResource, 2, 1, 1, 1, 0); - - parentResource1.userRelease(); - assertResourceState(childResource, 1, 1, 2, 1, 0); - - parentResource2.userRelease(); - assertResourceState(childResource, -1, 1, 3, 1, 1); - } - - @Test - public void testTwoChildren() throws RemoteException { - RefcountedResource childResource1 = getTestRefcountedResource(); - RefcountedResource childResource2 = getTestRefcountedResource(); - RefcountedResource parentResource = - getTestRefcountedResource(childResource1, childResource2); - - childResource1.userRelease(); - assertResourceState(childResource1, 1, 1, 1, 1, 0); - assertResourceState(childResource2, 2, 0, 0, 0, 0); - - parentResource.userRelease(); - assertResourceState(childResource1, -1, 1, 2, 1, 1); - assertResourceState(childResource2, 1, 0, 1, 0, 0); - - childResource2.userRelease(); - assertResourceState(childResource1, -1, 1, 2, 1, 1); - assertResourceState(childResource2, -1, 1, 2, 1, 1); - } - - @Test - public void testSampleUdpEncapTranform() throws RemoteException { - RefcountedResource spi1 = getTestRefcountedResource(); - RefcountedResource spi2 = getTestRefcountedResource(); - RefcountedResource udpEncapSocket = getTestRefcountedResource(); - RefcountedResource transform = - getTestRefcountedResource(spi1, spi2, udpEncapSocket); - - // Pretend one SPI goes out of reference (releaseManagedResource -> userRelease) - spi1.userRelease(); - - // User called releaseManagedResource on udpEncap socket - udpEncapSocket.userRelease(); - - // User dies, and binder kills the rest - spi2.binderDied(); - transform.binderDied(); - - // Check resource states - assertResourceState(spi1, -1, 1, 2, 1, 1); - assertResourceState(spi2, -1, 1, 2, 1, 1); - assertResourceState(udpEncapSocket, -1, 1, 2, 1, 1); - assertResourceState(transform, -1, 1, 1, 1, 1); - } - - @Test - public void testSampleDualTransformEncapSocket() throws RemoteException { - RefcountedResource spi1 = getTestRefcountedResource(); - RefcountedResource spi2 = getTestRefcountedResource(); - RefcountedResource spi3 = getTestRefcountedResource(); - RefcountedResource spi4 = getTestRefcountedResource(); - RefcountedResource udpEncapSocket = getTestRefcountedResource(); - RefcountedResource transform1 = - getTestRefcountedResource(spi1, spi2, udpEncapSocket); - RefcountedResource transform2 = - getTestRefcountedResource(spi3, spi4, udpEncapSocket); - - // Pretend one SPIs goes out of reference (releaseManagedResource -> userRelease) - spi1.userRelease(); - - // User called releaseManagedResource on udpEncap socket and spi4 - udpEncapSocket.userRelease(); - spi4.userRelease(); - - // User dies, and binder kills the rest - spi2.binderDied(); - spi3.binderDied(); - transform2.binderDied(); - transform1.binderDied(); - - // Check resource states - assertResourceState(spi1, -1, 1, 2, 1, 1); - assertResourceState(spi2, -1, 1, 2, 1, 1); - assertResourceState(spi3, -1, 1, 2, 1, 1); - assertResourceState(spi4, -1, 1, 2, 1, 1); - assertResourceState(udpEncapSocket, -1, 1, 3, 1, 1); - assertResourceState(transform1, -1, 1, 1, 1, 1); - assertResourceState(transform2, -1, 1, 1, 1, 1); - } - - @Test - public void fuzzTest() throws RemoteException { - List> resources = new ArrayList<>(); - - // Build a tree of resources - for (int i = 0; i < 100; i++) { - // Choose a random number of children from the existing list - int numChildren = ThreadLocalRandom.current().nextInt(0, resources.size() + 1); - - // Build a (random) list of children - Set> children = new HashSet<>(); - for (int j = 0; j < numChildren; j++) { - int childIndex = ThreadLocalRandom.current().nextInt(0, resources.size()); - children.add(resources.get(childIndex)); - } - - RefcountedResource newRefcountedResource = - getTestRefcountedResource( - children.toArray(new RefcountedResource[children.size()])); - resources.add(newRefcountedResource); - } - - // Cleanup all resources in a random order - List> clonedResources = - new ArrayList<>(resources); // shallow copy - while (!clonedResources.isEmpty()) { - int index = ThreadLocalRandom.current().nextInt(0, clonedResources.size()); - RefcountedResource refcountedResource = clonedResources.get(index); - refcountedResource.userRelease(); - clonedResources.remove(index); - } - - // Verify all resources were cleaned up properly - for (RefcountedResource refcountedResource : resources) { - assertEquals(-1, refcountedResource.mRefCount); - } - } -} diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java deleted file mode 100644 index 6232423b4f9e..000000000000 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ /dev/null @@ -1,670 +0,0 @@ -/* - * Copyright (C) 2017 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 static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.EADDRINUSE; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.INetd; -import android.net.IpSecAlgorithm; -import android.net.IpSecConfig; -import android.net.IpSecManager; -import android.net.IpSecSpiResponse; -import android.net.IpSecUdpEncapResponse; -import android.os.Binder; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructStat; -import android.util.Range; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import dalvik.system.SocketTagger; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; - -import java.io.FileDescriptor; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -/** Unit tests for {@link IpSecService}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpSecServiceTest { - - private static final int DROID_SPI = 0xD1201D; - private static final int MAX_NUM_ENCAP_SOCKETS = 100; - private static final int MAX_NUM_SPIS = 100; - private static final int TEST_UDP_ENCAP_INVALID_PORT = 100; - private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000; - - private static final InetAddress INADDR_ANY; - - private static final byte[] AEAD_KEY = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x73, 0x61, 0x6C, 0x74 - }; - private static final byte[] CRYPT_KEY = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - }; - private static final byte[] AUTH_KEY = { - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F - }; - - private static final IpSecAlgorithm AUTH_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4); - private static final IpSecAlgorithm CRYPT_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); - private static final IpSecAlgorithm AEAD_ALGO = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); - - static { - try { - INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); - } catch (UnknownHostException e) { - throw new RuntimeException(e); - } - } - - Context mMockContext; - INetd mMockNetd; - IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; - IpSecService mIpSecService; - - @Before - public void setUp() throws Exception { - mMockContext = mock(Context.class); - mMockNetd = mock(INetd.class); - mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); - - // Injecting mock netd - when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); - } - - @Test - public void testIpSecServiceCreate() throws InterruptedException { - IpSecService ipSecSrv = IpSecService.create(mMockContext); - assertNotNull(ipSecSrv); - } - - @Test - public void testReleaseInvalidSecurityParameterIndex() throws Exception { - try { - mIpSecService.releaseSecurityParameterIndex(1); - fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { - } - } - - /** This function finds an available port */ - int findUnusedPort() throws Exception { - // Get an available port. - ServerSocket s = new ServerSocket(0); - int port = s.getLocalPort(); - s.close(); - return port; - } - - @Test - public void testOpenAndCloseUdpEncapsulationSocket() throws Exception { - int localport = -1; - IpSecUdpEncapResponse udpEncapResp = null; - - for (int i = 0; i < IpSecService.MAX_PORT_BIND_ATTEMPTS; i++) { - localport = findUnusedPort(); - - udpEncapResp = mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); - assertNotNull(udpEncapResp); - if (udpEncapResp.status == IpSecManager.Status.OK) { - break; - } - - // Else retry to reduce possibility for port-bind failures. - } - - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - assertEquals(localport, udpEncapResp.port); - - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - udpEncapResp.fileDescriptor.close(); - - // Verify quota and RefcountedResource objects cleaned up - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); - assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); - try { - userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testUdpEncapsulationSocketBinderDeath() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - - IpSecService.UserRecord userRecord = - mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid()); - IpSecService.RefcountedResource refcountedRecord = - userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow( - udpEncapResp.resourceId); - - refcountedRecord.binderDied(); - - // Verify quota and RefcountedResource objects cleaned up - assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent); - try { - userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId); - fail("Expected IllegalArgumentException on attempt to access deleted resource"); - } catch (IllegalArgumentException expected) { - - } - } - - @Test - public void testOpenUdpEncapsulationSocketAfterClose() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - int localport = udpEncapResp.port; - - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - udpEncapResp.fileDescriptor.close(); - - /** Check if localport is available. */ - FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - Os.bind(newSocket, INADDR_ANY, localport); - Os.close(newSocket); - } - - /** - * This function checks if the IpSecService holds the reserved port. If - * closeUdpEncapsulationSocket is not called, the socket cleanup should not be complete. - */ - @Test - public void testUdpEncapPortNotReleased() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - int localport = udpEncapResp.port; - - udpEncapResp.fileDescriptor.close(); - - FileDescriptor newSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - try { - Os.bind(newSocket, INADDR_ANY, localport); - fail("ErrnoException not thrown"); - } catch (ErrnoException e) { - assertEquals(EADDRINUSE, e.errno); - } - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - } - - @Test - public void testOpenUdpEncapsulationSocketOnRandomPort() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - assertNotEquals(0, udpEncapResp.port); - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - udpEncapResp.fileDescriptor.close(); - } - - @Test - public void testOpenUdpEncapsulationSocketPortRange() throws Exception { - try { - mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_INVALID_PORT, new Binder()); - fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { - } - - try { - mIpSecService.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT_OUT_RANGE, new Binder()); - fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { - } - } - - @Test - public void testOpenUdpEncapsulationSocketTwice() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - int localport = udpEncapResp.port; - - IpSecUdpEncapResponse testUdpEncapResp = - mIpSecService.openUdpEncapsulationSocket(localport, new Binder()); - assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, testUdpEncapResp.status); - - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - udpEncapResp.fileDescriptor.close(); - } - - @Test - public void testCloseInvalidUdpEncapsulationSocket() throws Exception { - try { - mIpSecService.closeUdpEncapsulationSocket(1); - fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { - } - } - - @Test - public void testValidateAlgorithmsAuth() { - // Validate that correct algorithm type succeeds - IpSecConfig config = new IpSecConfig(); - config.setAuthentication(AUTH_ALGO); - mIpSecService.validateAlgorithms(config); - - // Validate that incorrect algorithm types fails - for (IpSecAlgorithm algo : new IpSecAlgorithm[] {CRYPT_ALGO, AEAD_ALGO}) { - try { - config = new IpSecConfig(); - config.setAuthentication(algo); - mIpSecService.validateAlgorithms(config); - fail("Did not throw exception on invalid algorithm type"); - } catch (IllegalArgumentException expected) { - } - } - } - - @Test - public void testValidateAlgorithmsCrypt() { - // Validate that correct algorithm type succeeds - IpSecConfig config = new IpSecConfig(); - config.setEncryption(CRYPT_ALGO); - mIpSecService.validateAlgorithms(config); - - // Validate that incorrect algorithm types fails - for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, AEAD_ALGO}) { - try { - config = new IpSecConfig(); - config.setEncryption(algo); - mIpSecService.validateAlgorithms(config); - fail("Did not throw exception on invalid algorithm type"); - } catch (IllegalArgumentException expected) { - } - } - } - - @Test - public void testValidateAlgorithmsAead() { - // Validate that correct algorithm type succeeds - IpSecConfig config = new IpSecConfig(); - config.setAuthenticatedEncryption(AEAD_ALGO); - mIpSecService.validateAlgorithms(config); - - // Validate that incorrect algorithm types fails - for (IpSecAlgorithm algo : new IpSecAlgorithm[] {AUTH_ALGO, CRYPT_ALGO}) { - try { - config = new IpSecConfig(); - config.setAuthenticatedEncryption(algo); - mIpSecService.validateAlgorithms(config); - fail("Did not throw exception on invalid algorithm type"); - } catch (IllegalArgumentException expected) { - } - } - } - - @Test - public void testValidateAlgorithmsAuthCrypt() { - // Validate that correct algorithm type succeeds - IpSecConfig config = new IpSecConfig(); - config.setAuthentication(AUTH_ALGO); - config.setEncryption(CRYPT_ALGO); - mIpSecService.validateAlgorithms(config); - } - - @Test - public void testValidateAlgorithmsNoAlgorithms() { - IpSecConfig config = new IpSecConfig(); - try { - mIpSecService.validateAlgorithms(config); - fail("Expected exception; no algorithms specified"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testValidateAlgorithmsAeadWithAuth() { - IpSecConfig config = new IpSecConfig(); - config.setAuthenticatedEncryption(AEAD_ALGO); - config.setAuthentication(AUTH_ALGO); - try { - mIpSecService.validateAlgorithms(config); - fail("Expected exception; both AEAD and auth algorithm specified"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testValidateAlgorithmsAeadWithCrypt() { - IpSecConfig config = new IpSecConfig(); - config.setAuthenticatedEncryption(AEAD_ALGO); - config.setEncryption(CRYPT_ALGO); - try { - mIpSecService.validateAlgorithms(config); - fail("Expected exception; both AEAD and crypt algorithm specified"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testValidateAlgorithmsAeadWithAuthAndCrypt() { - IpSecConfig config = new IpSecConfig(); - config.setAuthenticatedEncryption(AEAD_ALGO); - config.setAuthentication(AUTH_ALGO); - config.setEncryption(CRYPT_ALGO); - try { - mIpSecService.validateAlgorithms(config); - fail("Expected exception; AEAD, auth and crypt algorithm specified"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testDeleteInvalidTransform() throws Exception { - try { - mIpSecService.deleteTransform(1); - fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { - } - } - - @Test - public void testRemoveTransportModeTransform() throws Exception { - Socket socket = new Socket(); - socket.bind(null); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); - mIpSecService.removeTransportModeTransforms(pfd); - - verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd); - } - - @Test - public void testValidateIpAddresses() throws Exception { - String[] invalidAddresses = - new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""}; - for (String address : invalidAddresses) { - try { - IpSecSpiResponse spiResp = - mIpSecService.allocateSecurityParameterIndex( - address, DROID_SPI, new Binder()); - fail("Invalid address was passed through IpSecService validation: " + address); - } catch (IllegalArgumentException e) { - } catch (Exception e) { - fail( - "Invalid InetAddress was not caught in validation: " - + address - + ", Exception: " - + e); - } - } - } - - /** - * This function checks if the number of encap UDP socket that one UID can reserve has a - * reasonable limit. - */ - @Test - public void testSocketResourceTrackerLimitation() throws Exception { - List openUdpEncapSockets = new ArrayList(); - // Reserve sockets until it fails. - for (int i = 0; i < MAX_NUM_ENCAP_SOCKETS; i++) { - IpSecUdpEncapResponse newUdpEncapSocket = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(newUdpEncapSocket); - if (IpSecManager.Status.OK != newUdpEncapSocket.status) { - break; - } - openUdpEncapSockets.add(newUdpEncapSocket); - } - // Assert that the total sockets quota has a reasonable limit. - assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty()); - assertTrue( - "Number of open UDP encap sockets is out of bound", - openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS); - - // Try to reserve one more UDP encapsulation socket, and should fail. - IpSecUdpEncapResponse extraUdpEncapSocket = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(extraUdpEncapSocket); - assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status); - - // Close one of the open UDP encapsulation sockets. - mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId); - openUdpEncapSockets.get(0).fileDescriptor.close(); - openUdpEncapSockets.remove(0); - - // Try to reserve one more UDP encapsulation socket, and should be successful. - extraUdpEncapSocket = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(extraUdpEncapSocket); - assertEquals(IpSecManager.Status.OK, extraUdpEncapSocket.status); - openUdpEncapSockets.add(extraUdpEncapSocket); - - // Close open UDP sockets. - for (IpSecUdpEncapResponse openSocket : openUdpEncapSockets) { - mIpSecService.closeUdpEncapsulationSocket(openSocket.resourceId); - openSocket.fileDescriptor.close(); - } - } - - /** - * This function checks if the number of SPI that one UID can reserve has a reasonable limit. - * This test does not test for both address families or duplicate SPIs because resource tracking - * code does not depend on them. - */ - @Test - public void testSpiResourceTrackerLimitation() throws Exception { - List reservedSpis = new ArrayList(); - // Return the same SPI for all SPI allocation since IpSecService only - // tracks the resource ID. - when(mMockNetd.ipSecAllocateSpi( - anyInt(), - anyString(), - eq(InetAddress.getLoopbackAddress().getHostAddress()), - anyInt())) - .thenReturn(DROID_SPI); - // Reserve spis until it fails. - for (int i = 0; i < MAX_NUM_SPIS; i++) { - IpSecSpiResponse newSpi = - mIpSecService.allocateSecurityParameterIndex( - InetAddress.getLoopbackAddress().getHostAddress(), - DROID_SPI + i, - new Binder()); - assertNotNull(newSpi); - if (IpSecManager.Status.OK != newSpi.status) { - break; - } - reservedSpis.add(newSpi); - } - // Assert that the SPI quota has a reasonable limit. - assertTrue(reservedSpis.size() > 0 && reservedSpis.size() < MAX_NUM_SPIS); - - // Try to reserve one more SPI, and should fail. - IpSecSpiResponse extraSpi = - mIpSecService.allocateSecurityParameterIndex( - InetAddress.getLoopbackAddress().getHostAddress(), - DROID_SPI + MAX_NUM_SPIS, - new Binder()); - assertNotNull(extraSpi); - assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraSpi.status); - - // Release one reserved spi. - mIpSecService.releaseSecurityParameterIndex(reservedSpis.get(0).resourceId); - reservedSpis.remove(0); - - // Should successfully reserve one more spi. - extraSpi = - mIpSecService.allocateSecurityParameterIndex( - InetAddress.getLoopbackAddress().getHostAddress(), - DROID_SPI + MAX_NUM_SPIS, - new Binder()); - assertNotNull(extraSpi); - assertEquals(IpSecManager.Status.OK, extraSpi.status); - - // Release reserved SPIs. - for (IpSecSpiResponse spiResp : reservedSpis) { - mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); - } - } - - @Test - public void testUidFdtagger() throws Exception { - SocketTagger actualSocketTagger = SocketTagger.get(); - - try { - FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - // Has to be done after socket creation because BlockGuardOS calls tag on new sockets - SocketTagger mockSocketTagger = mock(SocketTagger.class); - SocketTagger.set(mockSocketTagger); - - mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID); - verify(mockSocketTagger).tag(eq(sockFd)); - } finally { - SocketTagger.set(actualSocketTagger); - } - } - - /** - * Checks if two file descriptors point to the same file. - * - *

According to stat.h documentation, the correct way to check for equivalent or duplicated - * file descriptors is to check their inode and device. These two entries uniquely identify any - * file. - */ - private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) { - try { - StructStat fd1Stat = Os.fstat(fd1); - StructStat fd2Stat = Os.fstat(fd2); - - return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev; - } catch (ErrnoException e) { - return false; - } - } - - @Test - public void testOpenUdpEncapSocketTagsSocket() throws Exception { - IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class); - IpSecService testIpSecService = new IpSecService( - mMockContext, mMockIpSecSrvConfig, mockTagger); - - IpSecUdpEncapResponse udpEncapResp = - testIpSecService.openUdpEncapsulationSocket(0, new Binder()); - assertNotNull(udpEncapResp); - assertEquals(IpSecManager.Status.OK, udpEncapResp.status); - - FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); - ArgumentMatcher fdMatcher = - (argFd) -> { - return fileDescriptorsEqual(sockFd, argFd); - }; - verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid())); - - testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - udpEncapResp.fileDescriptor.close(); - } - - @Test - public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception { - IpSecUdpEncapResponse udpEncapResp = - mIpSecService.openUdpEncapsulationSocket(0, new Binder()); - - FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); - ArgumentMatcher fdMatcher = (arg) -> { - try { - StructStat sockStat = Os.fstat(sockFd); - StructStat argStat = Os.fstat(arg.getFileDescriptor()); - - return sockStat.st_ino == argStat.st_ino - && sockStat.st_dev == argStat.st_dev; - } catch (ErrnoException e) { - return false; - } - }; - - verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid())); - mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); - } - - @Test - public void testReserveNetId() { - final Range netIdRange = ConnectivityManager.getIpSecNetIdRange(); - for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) { - assertEquals(netId, mIpSecService.reserveNetId()); - } - - // Check that resource exhaustion triggers an exception - try { - mIpSecService.reserveNetId(); - fail("Did not throw error for all netIds reserved"); - } catch (IllegalStateException expected) { - } - - // Now release one and try again - int releasedNetId = - netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2; - mIpSecService.releaseNetId(releasedNetId); - assertEquals(releasedNetId, mIpSecService.reserveNetId()); - } -} diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt deleted file mode 100644 index 5ec111954fcc..000000000000 --- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -// Don't warn about deprecated types anywhere in this test, because LegacyTypeTracker's very reason -// for existence is to power deprecated APIs. The annotation has to apply to the whole file because -// otherwise warnings will be generated by the imports of deprecated constants like TYPE_xxx. -@file:Suppress("DEPRECATION") - -package com.android.server - -import android.content.Context -import android.content.pm.PackageManager -import android.content.pm.PackageManager.FEATURE_WIFI -import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT -import android.net.ConnectivityManager.TYPE_ETHERNET -import android.net.ConnectivityManager.TYPE_MOBILE -import android.net.ConnectivityManager.TYPE_MOBILE_CBS -import android.net.ConnectivityManager.TYPE_MOBILE_DUN -import android.net.ConnectivityManager.TYPE_MOBILE_EMERGENCY -import android.net.ConnectivityManager.TYPE_MOBILE_FOTA -import android.net.ConnectivityManager.TYPE_MOBILE_HIPRI -import android.net.ConnectivityManager.TYPE_MOBILE_IA -import android.net.ConnectivityManager.TYPE_MOBILE_IMS -import android.net.ConnectivityManager.TYPE_MOBILE_MMS -import android.net.ConnectivityManager.TYPE_MOBILE_SUPL -import android.net.ConnectivityManager.TYPE_VPN -import android.net.ConnectivityManager.TYPE_WIFI -import android.net.ConnectivityManager.TYPE_WIFI_P2P -import android.net.ConnectivityManager.TYPE_WIMAX -import android.net.EthernetManager -import android.net.NetworkInfo.DetailedState.CONNECTED -import android.net.NetworkInfo.DetailedState.DISCONNECTED -import android.telephony.TelephonyManager -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.server.ConnectivityService.LegacyTypeTracker -import com.android.server.connectivity.NetworkAgentInfo -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNull -import org.junit.Assert.assertSame -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify - -const val UNSUPPORTED_TYPE = TYPE_WIMAX - -@RunWith(AndroidJUnit4::class) -@SmallTest -class LegacyTypeTrackerTest { - private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE, - TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI, - TYPE_MOBILE_FOTA, TYPE_MOBILE_IMS, TYPE_MOBILE_CBS, TYPE_MOBILE_IA, - TYPE_MOBILE_EMERGENCY, TYPE_VPN) - - private val mMockService = mock(ConnectivityService::class.java).apply { - doReturn(false).`when`(this).isDefaultNetwork(any()) - } - private val mPm = mock(PackageManager::class.java) - private val mContext = mock(Context::class.java).apply { - doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI) - doReturn(true).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) - doReturn(mPm).`when`(this).packageManager - doReturn(mock(EthernetManager::class.java)).`when`(this).getSystemService( - Context.ETHERNET_SERVICE) - } - private val mTm = mock(TelephonyManager::class.java).apply { - doReturn(true).`when`(this).isDataCapable - } - - private fun makeTracker() = LegacyTypeTracker(mMockService).apply { - loadSupportedTypes(mContext, mTm) - } - - @Test - fun testSupportedTypes() { - val tracker = makeTracker() - supportedTypes.forEach { - assertTrue(tracker.isTypeSupported(it)) - } - assertFalse(tracker.isTypeSupported(UNSUPPORTED_TYPE)) - } - - @Test - fun testSupportedTypes_NoEthernet() { - doReturn(null).`when`(mContext).getSystemService(Context.ETHERNET_SERVICE) - assertFalse(makeTracker().isTypeSupported(TYPE_ETHERNET)) - } - - @Test - fun testSupportedTypes_NoTelephony() { - doReturn(false).`when`(mTm).isDataCapable - val tracker = makeTracker() - val nonMobileTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_VPN) - nonMobileTypes.forEach { - assertTrue(tracker.isTypeSupported(it)) - } - supportedTypes.toSet().minus(nonMobileTypes).forEach { - assertFalse(tracker.isTypeSupported(it)) - } - } - - @Test - fun testSupportedTypes_NoWifiDirect() { - doReturn(false).`when`(mPm).hasSystemFeature(FEATURE_WIFI_DIRECT) - val tracker = makeTracker() - assertFalse(tracker.isTypeSupported(TYPE_WIFI_P2P)) - supportedTypes.toSet().minus(TYPE_WIFI_P2P).forEach { - assertTrue(tracker.isTypeSupported(it)) - } - } - - @Test - fun testSupl() { - val tracker = makeTracker() - val mobileNai = mock(NetworkAgentInfo::class.java) - tracker.add(TYPE_MOBILE, mobileNai) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE) - reset(mMockService) - tracker.add(TYPE_MOBILE_SUPL, mobileNai) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) - reset(mMockService) - tracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) - reset(mMockService) - tracker.add(TYPE_MOBILE_SUPL, mobileNai) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) - reset(mMockService) - tracker.remove(mobileNai, false) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE) - } - - @Test - fun testAddNetwork() { - val tracker = makeTracker() - val mobileNai = mock(NetworkAgentInfo::class.java) - val wifiNai = mock(NetworkAgentInfo::class.java) - tracker.add(TYPE_MOBILE, mobileNai) - tracker.add(TYPE_WIFI, wifiNai) - assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) - assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) - // Make sure adding a second NAI does not change the results. - val secondMobileNai = mock(NetworkAgentInfo::class.java) - tracker.add(TYPE_MOBILE, secondMobileNai) - assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) - assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) - // Make sure removing a network that wasn't added for this type is a no-op. - tracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */) - assertSame(tracker.getNetworkForType(TYPE_MOBILE), mobileNai) - assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) - // Remove the top network for mobile and make sure the second one becomes the network - // of record for this type. - tracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */) - assertSame(tracker.getNetworkForType(TYPE_MOBILE), secondMobileNai) - assertSame(tracker.getNetworkForType(TYPE_WIFI), wifiNai) - // Make sure adding a network for an unsupported type does not register it. - tracker.add(UNSUPPORTED_TYPE, mobileNai) - assertNull(tracker.getNetworkForType(UNSUPPORTED_TYPE)) - } - - @Test - fun testBroadcastOnDisconnect() { - val tracker = makeTracker() - val mobileNai1 = mock(NetworkAgentInfo::class.java) - val mobileNai2 = mock(NetworkAgentInfo::class.java) - doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai1) - tracker.add(TYPE_MOBILE, mobileNai1) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, CONNECTED, TYPE_MOBILE) - reset(mMockService) - doReturn(false).`when`(mMockService).isDefaultNetwork(mobileNai2) - tracker.add(TYPE_MOBILE, mobileNai2) - verify(mMockService, never()).sendLegacyNetworkBroadcast(any(), any(), anyInt()) - tracker.remove(TYPE_MOBILE, mobileNai1, false /* wasDefault */) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai1, DISCONNECTED, TYPE_MOBILE) - verify(mMockService).sendLegacyNetworkBroadcast(mobileNai2, CONNECTED, TYPE_MOBILE) - } -} diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/tests/net/java/com/android/server/NetIdManagerTest.kt deleted file mode 100644 index 6f5e740d344c..000000000000 --- a/tests/net/java/com/android/server/NetIdManagerTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 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 androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.server.NetIdManager.MIN_NET_ID -import com.android.testutils.assertThrows -import com.android.testutils.ExceptionUtils.ThrowingRunnable -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NetIdManagerTest { - @Test - fun testReserveReleaseNetId() { - val manager = NetIdManager(MIN_NET_ID + 4) - assertEquals(MIN_NET_ID, manager.reserveNetId()) - assertEquals(MIN_NET_ID + 1, manager.reserveNetId()) - assertEquals(MIN_NET_ID + 2, manager.reserveNetId()) - assertEquals(MIN_NET_ID + 3, manager.reserveNetId()) - - manager.releaseNetId(MIN_NET_ID + 1) - manager.releaseNetId(MIN_NET_ID + 3) - // IDs only loop once there is no higher ID available - assertEquals(MIN_NET_ID + 4, manager.reserveNetId()) - assertEquals(MIN_NET_ID + 1, manager.reserveNetId()) - assertEquals(MIN_NET_ID + 3, manager.reserveNetId()) - assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() }) - manager.releaseNetId(MIN_NET_ID + 5) - // Still no ID available: MIN_NET_ID + 5 was not reserved - assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() }) - manager.releaseNetId(MIN_NET_ID + 2) - // Throwing an exception still leaves the manager in a working state - assertEquals(MIN_NET_ID + 2, manager.reserveNetId()) - } -} \ No newline at end of file diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java deleted file mode 100644 index 13516d75a50d..000000000000 --- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2012 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 static android.util.DebugUtils.valueToString; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.INetd; -import android.net.INetdUnsolicitedEventListener; -import android.net.LinkAddress; -import android.net.NetworkPolicyManager; -import android.os.BatteryStats; -import android.os.Binder; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.ArrayMap; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.app.IBatteryStats; -import com.android.server.NetworkManagementService.Dependencies; -import com.android.server.net.BaseNetworkObserver; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.function.BiFunction; - -/** - * Tests for {@link NetworkManagementService}. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkManagementServiceTest { - private NetworkManagementService mNMService; - @Mock private Context mContext; - @Mock private IBatteryStats.Stub mBatteryStatsService; - @Mock private INetd.Stub mNetdService; - - private static final int TEST_UID = 111; - - @NonNull - @Captor - private ArgumentCaptor mUnsolListenerCaptor; - - private final MockDependencies mDeps = new MockDependencies(); - - private final class MockDependencies extends Dependencies { - @Override - public IBinder getService(String name) { - switch (name) { - case BatteryStats.SERVICE_NAME: - return mBatteryStatsService; - default: - throw new UnsupportedOperationException("Unknown service " + name); - } - } - - @Override - public void registerLocalService(NetworkManagementInternal nmi) { - } - - @Override - public INetd getNetd() { - return mNetdService; - } - - @Override - public int getCallingUid() { - return Process.SYSTEM_UID; - } - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - doNothing().when(mNetdService) - .registerUnsolicitedEventListener(mUnsolListenerCaptor.capture()); - // Start the service and wait until it connects to our socket. - mNMService = NetworkManagementService.create(mContext, mDeps); - } - - @After - public void tearDown() throws Exception { - mNMService.shutdown(); - } - - private static T expectSoon(T mock) { - return verify(mock, timeout(200)); - } - - /** - * Tests that network observers work properly. - */ - @Test - public void testNetworkObservers() throws Exception { - BaseNetworkObserver observer = mock(BaseNetworkObserver.class); - doReturn(new Binder()).when(observer).asBinder(); // Used by registerObserver. - mNMService.registerObserver(observer); - - // Forget everything that happened to the mock so far, so we can explicitly verify - // everything that happens and does not happen to it from now on. - - INetdUnsolicitedEventListener unsolListener = mUnsolListenerCaptor.getValue(); - reset(observer); - // Now call unsolListener methods and ensure that the observer methods are - // called. After every method we expect a callback soon after; to ensure that - // invalid messages don't cause any callbacks, we call verifyNoMoreInteractions at the end. - - /** - * Interface changes. - */ - unsolListener.onInterfaceAdded("rmnet12"); - expectSoon(observer).interfaceAdded("rmnet12"); - - unsolListener.onInterfaceRemoved("eth1"); - expectSoon(observer).interfaceRemoved("eth1"); - - unsolListener.onInterfaceChanged("clat4", true); - expectSoon(observer).interfaceStatusChanged("clat4", true); - - unsolListener.onInterfaceLinkStateChanged("rmnet0", false); - expectSoon(observer).interfaceLinkStateChanged("rmnet0", false); - - /** - * Bandwidth control events. - */ - unsolListener.onQuotaLimitReached("data", "rmnet_usb0"); - expectSoon(observer).limitReached("data", "rmnet_usb0"); - - /** - * Interface class activity. - */ - unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, TEST_UID); - expectSoon(observer).interfaceClassDataActivityChanged(1, true, 1234, TEST_UID); - - unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, TEST_UID); - expectSoon(observer).interfaceClassDataActivityChanged(9, false, 5678, TEST_UID); - - unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, TEST_UID); - expectSoon(observer).interfaceClassDataActivityChanged(9, false, 4321, TEST_UID); - - /** - * IP address changes. - */ - unsolListener.onInterfaceAddressUpdated("fe80::1/64", "wlan0", 128, 253); - expectSoon(observer).addressUpdated("wlan0", new LinkAddress("fe80::1/64", 128, 253)); - - unsolListener.onInterfaceAddressRemoved("fe80::1/64", "wlan0", 128, 253); - expectSoon(observer).addressRemoved("wlan0", new LinkAddress("fe80::1/64", 128, 253)); - - unsolListener.onInterfaceAddressRemoved("2001:db8::1/64", "wlan0", 1, 0); - expectSoon(observer).addressRemoved("wlan0", new LinkAddress("2001:db8::1/64", 1, 0)); - - /** - * DNS information broadcasts. - */ - unsolListener.onInterfaceDnsServerInfo("rmnet_usb0", 3600, new String[]{"2001:db8::1"}); - expectSoon(observer).interfaceDnsServerInfo("rmnet_usb0", 3600, - new String[]{"2001:db8::1"}); - - unsolListener.onInterfaceDnsServerInfo("wlan0", 14400, - new String[]{"2001:db8::1", "2001:db8::2"}); - expectSoon(observer).interfaceDnsServerInfo("wlan0", 14400, - new String[]{"2001:db8::1", "2001:db8::2"}); - - // We don't check for negative lifetimes, only for parse errors. - unsolListener.onInterfaceDnsServerInfo("wlan0", -3600, new String[]{"::1"}); - expectSoon(observer).interfaceDnsServerInfo("wlan0", -3600, - new String[]{"::1"}); - - // No syntax checking on the addresses. - unsolListener.onInterfaceDnsServerInfo("wlan0", 600, - new String[]{"", "::", "", "foo", "::1"}); - expectSoon(observer).interfaceDnsServerInfo("wlan0", 600, - new String[]{"", "::", "", "foo", "::1"}); - - // Make sure nothing else was called. - verifyNoMoreInteractions(observer); - } - - @Test - public void testFirewallEnabled() { - mNMService.setFirewallEnabled(true); - assertTrue(mNMService.isFirewallEnabled()); - - mNMService.setFirewallEnabled(false); - assertFalse(mNMService.isFirewallEnabled()); - } - - @Test - public void testNetworkRestrictedDefault() { - assertFalse(mNMService.isNetworkRestricted(TEST_UID)); - } - - @Test - public void testMeteredNetworkRestrictions() throws RemoteException { - // Make sure the mocked netd method returns true. - doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean()); - - // Restrict usage of mobile data in background - mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, true); - assertTrue("Should be true since mobile data usage is restricted", - mNMService.isNetworkRestricted(TEST_UID)); - - mNMService.setDataSaverModeEnabled(true); - verify(mNetdService).bandwidthEnableDataSaver(true); - - mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false); - assertTrue("Should be true since data saver is on and the uid is not allowlisted", - mNMService.isNetworkRestricted(TEST_UID)); - - mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, true); - assertFalse("Should be false since data saver is on and the uid is allowlisted", - mNMService.isNetworkRestricted(TEST_UID)); - - // remove uid from allowlist and turn datasaver off again - mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false); - mNMService.setDataSaverModeEnabled(false); - verify(mNetdService).bandwidthEnableDataSaver(false); - assertFalse("Network should not be restricted when data saver is off", - mNMService.isNetworkRestricted(TEST_UID)); - } - - @Test - public void testFirewallChains() { - final ArrayMap> expected = new ArrayMap<>(); - // Dozable chain - final ArrayMap isRestrictedForDozable = new ArrayMap<>(); - isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); - isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false); - isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true); - expected.put(INetd.FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable); - // Powersaver chain - final ArrayMap isRestrictedForPowerSave = new ArrayMap<>(); - isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); - isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false); - isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true); - expected.put(INetd.FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave); - // Standby chain - final ArrayMap isRestrictedForStandby = new ArrayMap<>(); - isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false); - isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false); - isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true); - expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby); - // Restricted mode chain - final ArrayMap isRestrictedForRestrictedMode = new ArrayMap<>(); - isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); - isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false); - isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true); - expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode); - - final int[] chains = { - INetd.FIREWALL_CHAIN_STANDBY, - INetd.FIREWALL_CHAIN_POWERSAVE, - INetd.FIREWALL_CHAIN_DOZABLE, - INetd.FIREWALL_CHAIN_RESTRICTED - }; - final int[] states = { - INetd.FIREWALL_RULE_ALLOW, - INetd.FIREWALL_RULE_DENY, - NetworkPolicyManager.FIREWALL_RULE_DEFAULT - }; - BiFunction errorMsg = (chain, state) -> { - return String.format("Unexpected value for chain: %s and state: %s", - valueToString(INetd.class, "FIREWALL_CHAIN_", chain), - valueToString(INetd.class, "FIREWALL_RULE_", state)); - }; - for (int chain : chains) { - final ArrayMap expectedValues = expected.get(chain); - mNMService.setFirewallChainEnabled(chain, true); - for (int state : states) { - mNMService.setFirewallUidRule(chain, TEST_UID, state); - assertEquals(errorMsg.apply(chain, state), - expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID)); - } - mNMService.setFirewallChainEnabled(chain, false); - } - } -} diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/tests/net/java/com/android/server/NsdServiceTest.java deleted file mode 100644 index a90fa6882c25..000000000000 --- a/tests/net/java/com/android/server/NsdServiceTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2017 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 static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ContentResolver; -import android.content.Context; -import android.net.nsd.NsdManager; -import android.net.nsd.NsdServiceInfo; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.NsdService.DaemonConnection; -import com.android.server.NsdService.DaemonConnectionSupplier; -import com.android.server.NsdService.NativeCallbackReceiver; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -// TODOs: -// - test client can send requests and receive replies -// - test NSD_ON ENABLE/DISABLED listening -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NsdServiceTest { - - static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; - - long mTimeoutMs = 100; // non-final so that tests can adjust the value. - - @Mock Context mContext; - @Mock ContentResolver mResolver; - @Mock NsdService.NsdSettings mSettings; - @Mock DaemonConnection mDaemon; - NativeCallbackReceiver mDaemonCallback; - HandlerThread mThread; - TestHandler mHandler; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mThread = new HandlerThread("mock-service-handler"); - mThread.start(); - mHandler = new TestHandler(mThread.getLooper()); - when(mContext.getContentResolver()).thenReturn(mResolver); - } - - @After - public void tearDown() throws Exception { - if (mThread != null) { - mThread.quit(); - mThread = null; - } - } - - @Test - public void testClientsCanConnectAndDisconnect() { - when(mSettings.isEnabled()).thenReturn(true); - - NsdService service = makeService(); - - NsdManager client1 = connectClient(service); - verify(mDaemon, timeout(100).times(1)).start(); - - NsdManager client2 = connectClient(service); - - client1.disconnect(); - client2.disconnect(); - - verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); - - client1.disconnect(); - client2.disconnect(); - } - - @Test - public void testClientRequestsAreGCedAtDisconnection() { - when(mSettings.isEnabled()).thenReturn(true); - when(mDaemon.execute(any())).thenReturn(true); - - NsdService service = makeService(); - NsdManager client = connectClient(service); - - verify(mDaemon, timeout(100).times(1)).start(); - - NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); - request.setPort(2201); - - // Client registration request - NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); - client.registerService(request, PROTOCOL, listener1); - verifyDaemonCommand("register 2 a_name a_type 2201"); - - // Client discovery request - NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); - client.discoverServices("a_type", PROTOCOL, listener2); - verifyDaemonCommand("discover 3 a_type"); - - // Client resolve request - NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); - client.resolveService(request, listener3); - verifyDaemonCommand("resolve 4 a_name a_type local."); - - // Client disconnects - client.disconnect(); - verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); - - // checks that request are cleaned - verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); - - client.disconnect(); - } - - NsdService makeService() { - DaemonConnectionSupplier supplier = (callback) -> { - mDaemonCallback = callback; - return mDaemon; - }; - NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); - verify(mDaemon, never()).execute(any(String.class)); - return service; - } - - NsdManager connectClient(NsdService service) { - return new NsdManager(mContext, service); - } - - void verifyDaemonCommands(String... wants) { - verifyDaemonCommand(String.join(" ", wants), wants.length); - } - - void verifyDaemonCommand(String want) { - verifyDaemonCommand(want, 1); - } - - void verifyDaemonCommand(String want, int n) { - ArgumentCaptor argumentsCaptor = ArgumentCaptor.forClass(Object.class); - verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); - String got = ""; - for (Object o : argumentsCaptor.getAllValues()) { - got += o + " "; - } - assertEquals(want, got.trim()); - // rearm deamon for next command verification - reset(mDaemon); - when(mDaemon.execute(any())).thenReturn(true); - } - - public static class TestHandler extends Handler { - public Message lastMessage; - - TestHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - lastMessage = obtainMessage(); - lastMessage.copyFrom(msg); - } - } -} diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java deleted file mode 100644 index 0ffeec98cf90..000000000000 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2018, 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.connectivity; - -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; -import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER; -import static android.net.NetworkCapabilities.MAX_TRANSPORT; -import static android.net.NetworkCapabilities.MIN_TRANSPORT; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; -import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; - -import static com.android.testutils.MiscAsserts.assertContainsExactly; -import static com.android.testutils.MiscAsserts.assertContainsStringsExactly; -import static com.android.testutils.MiscAsserts.assertFieldCountEquals; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.ConnectivitySettingsManager; -import android.net.IDnsResolver; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.ResolverOptionsParcel; -import android.net.ResolverParamsParcel; -import android.net.RouteInfo; -import android.net.shared.PrivateDnsConfig; -import android.provider.Settings; -import android.test.mock.MockContentResolver; -import android.util.SparseArray; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.MessageUtils; -import com.android.internal.util.test.FakeSettingsProvider; - -import libcore.net.InetAddressUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.Arrays; - -/** - * Tests for {@link DnsManager}. - * - * Build, install and run with: - * runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DnsManagerTest { - static final String TEST_IFACENAME = "test_wlan0"; - static final int TEST_NETID = 100; - static final int TEST_NETID_ALTERNATE = 101; - static final int TEST_NETID_UNTRACKED = 102; - static final int TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800; - static final int TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25; - static final int TEST_DEFAULT_MIN_SAMPLES = 8; - static final int TEST_DEFAULT_MAX_SAMPLES = 64; - static final int[] TEST_TRANSPORT_TYPES = {TRANSPORT_WIFI, TRANSPORT_VPN}; - - DnsManager mDnsManager; - MockContentResolver mContentResolver; - - @Mock Context mCtx; - @Mock IDnsResolver mMockDnsResolver; - - private void assertResolverOptionsEquals( - @NonNull ResolverOptionsParcel actual, - @NonNull ResolverOptionsParcel expected) { - assertEquals(actual.hosts, expected.hosts); - assertEquals(actual.tcMode, expected.tcMode); - assertEquals(actual.enforceDnsUid, expected.enforceDnsUid); - assertFieldCountEquals(3, ResolverOptionsParcel.class); - } - - private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual, - @NonNull ResolverParamsParcel expected) { - assertEquals(actual.netId, expected.netId); - assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds); - assertEquals(actual.successThreshold, expected.successThreshold); - assertEquals(actual.minSamples, expected.minSamples); - assertEquals(actual.maxSamples, expected.maxSamples); - assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec); - assertEquals(actual.retryCount, expected.retryCount); - assertContainsStringsExactly(actual.servers, expected.servers); - assertContainsStringsExactly(actual.domains, expected.domains); - assertEquals(actual.tlsName, expected.tlsName); - assertContainsStringsExactly(actual.tlsServers, expected.tlsServers); - assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints); - assertEquals(actual.caCertificate, expected.caCertificate); - assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs); - assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions); - assertContainsExactly(actual.transportTypes, expected.transportTypes); - assertFieldCountEquals(16, ResolverParamsParcel.class); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, - new FakeSettingsProvider()); - when(mCtx.getContentResolver()).thenReturn(mContentResolver); - mDnsManager = new DnsManager(mCtx, mMockDnsResolver); - - // Clear the private DNS settings - Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, ""); - Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, ""); - Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, ""); - } - - @Test - public void testTrackedValidationUpdates() throws Exception { - mDnsManager.updatePrivateDns(new Network(TEST_NETID), - mDnsManager.getPrivateDnsConfig()); - mDnsManager.updatePrivateDns(new Network(TEST_NETID_ALTERNATE), - mDnsManager.getPrivateDnsConfig()); - LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(TEST_IFACENAME); - lp.addDnsServer(InetAddress.getByName("3.3.3.3")); - lp.addDnsServer(InetAddress.getByName("4.4.4.4")); - - // Send a validation event that is tracked on the alternate netId - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp); - mDnsManager.flushVmDnsCache(); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE, - InetAddress.parseNumericAddress("4.4.4.4"), "", - VALIDATION_RESULT_SUCCESS)); - LinkProperties fixedLp = new LinkProperties(lp); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); - assertFalse(fixedLp.isPrivateDnsActive()); - assertNull(fixedLp.getPrivateDnsServerName()); - fixedLp = new LinkProperties(lp); - mDnsManager.updatePrivateDnsStatus(TEST_NETID_ALTERNATE, fixedLp); - assertTrue(fixedLp.isPrivateDnsActive()); - assertNull(fixedLp.getPrivateDnsServerName()); - assertEquals(Arrays.asList(InetAddress.getByName("4.4.4.4")), - fixedLp.getValidatedPrivateDnsServers()); - - // Set up addresses for strict mode and switch to it. - lp.addLinkAddress(new LinkAddress("192.0.2.4/24")); - lp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), - TEST_IFACENAME)); - lp.addLinkAddress(new LinkAddress("2001:db8:1::1/64")); - lp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), - TEST_IFACENAME)); - - ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); - ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com"); - mDnsManager.updatePrivateDns(new Network(TEST_NETID), - new PrivateDnsConfig("strictmode.com", new InetAddress[] { - InetAddress.parseNumericAddress("6.6.6.6"), - InetAddress.parseNumericAddress("2001:db8:66:66::1") - })); - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - fixedLp = new LinkProperties(lp); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); - assertTrue(fixedLp.isPrivateDnsActive()); - assertEquals("strictmode.com", fixedLp.getPrivateDnsServerName()); - // No validation events yet. - assertEquals(Arrays.asList(new InetAddress[0]), fixedLp.getValidatedPrivateDnsServers()); - // Validate one. - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("6.6.6.6"), "strictmode.com", - VALIDATION_RESULT_SUCCESS)); - fixedLp = new LinkProperties(lp); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); - assertEquals(Arrays.asList(InetAddress.parseNumericAddress("6.6.6.6")), - fixedLp.getValidatedPrivateDnsServers()); - // Validate the 2nd one. - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("2001:db8:66:66::1"), "strictmode.com", - VALIDATION_RESULT_SUCCESS)); - fixedLp = new LinkProperties(lp); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); - assertEquals(Arrays.asList( - InetAddress.parseNumericAddress("2001:db8:66:66::1"), - InetAddress.parseNumericAddress("6.6.6.6")), - fixedLp.getValidatedPrivateDnsServers()); - } - - @Test - public void testIgnoreUntrackedValidationUpdates() throws Exception { - // The PrivateDnsConfig map is empty, so no validation events will - // be tracked. - LinkProperties lp = new LinkProperties(); - lp.addDnsServer(InetAddress.getByName("3.3.3.3")); - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("3.3.3.3"), "", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Validation event has untracked netId - mDnsManager.updatePrivateDns(new Network(TEST_NETID), - mDnsManager.getPrivateDnsConfig()); - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED, - InetAddress.parseNumericAddress("3.3.3.3"), "", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Validation event has untracked ipAddress - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("4.4.4.4"), "", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Validation event has untracked hostname - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("3.3.3.3"), "hostname", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Validation event failed - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("3.3.3.3"), "", - VALIDATION_RESULT_FAILURE)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Network removed - mDnsManager.removeNetwork(new Network(TEST_NETID)); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("3.3.3.3"), "", VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - - // Turn private DNS mode off - ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_OFF); - mDnsManager.updatePrivateDns(new Network(TEST_NETID), - mDnsManager.getPrivateDnsConfig()); - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, - InetAddress.parseNumericAddress("3.3.3.3"), "", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - assertFalse(lp.isPrivateDnsActive()); - assertNull(lp.getPrivateDnsServerName()); - } - - @Test - public void testOverrideDefaultMode() throws Exception { - // Hard-coded default is opportunistic mode. - final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx); - assertTrue(cfgAuto.useTls); - assertEquals("", cfgAuto.hostname); - assertEquals(new InetAddress[0], cfgAuto.ips); - - // Pretend a gservices push sets the default to "off". - ConnectivitySettingsManager.setPrivateDnsDefaultMode(mCtx, PRIVATE_DNS_MODE_OFF); - final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx); - assertFalse(cfgOff.useTls); - assertEquals("", cfgOff.hostname); - assertEquals(new InetAddress[0], cfgOff.ips); - - // Strict mode still works. - ConnectivitySettingsManager.setPrivateDnsMode(mCtx, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); - ConnectivitySettingsManager.setPrivateDnsHostname(mCtx, "strictmode.com"); - final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx); - assertTrue(cfgStrict.useTls); - assertEquals("strictmode.com", cfgStrict.hostname); - assertEquals(new InetAddress[0], cfgStrict.ips); - } - - @Test - public void testSendDnsConfiguration() throws Exception { - reset(mMockDnsResolver); - mDnsManager.updatePrivateDns(new Network(TEST_NETID), - mDnsManager.getPrivateDnsConfig()); - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(TEST_IFACENAME); - lp.addDnsServer(InetAddress.getByName("3.3.3.3")); - lp.addDnsServer(InetAddress.getByName("4.4.4.4")); - mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.flushVmDnsCache(); - - final ArgumentCaptor resolverParamsParcelCaptor = - ArgumentCaptor.forClass(ResolverParamsParcel.class); - verify(mMockDnsResolver, times(1)).setResolverConfiguration( - resolverParamsParcelCaptor.capture()); - final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue(); - final ResolverParamsParcel expectedParams = new ResolverParamsParcel(); - expectedParams.netId = TEST_NETID; - expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS; - expectedParams.successThreshold = TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT; - expectedParams.minSamples = TEST_DEFAULT_MIN_SAMPLES; - expectedParams.maxSamples = TEST_DEFAULT_MAX_SAMPLES; - expectedParams.servers = new String[]{"3.3.3.3", "4.4.4.4"}; - expectedParams.domains = new String[]{}; - expectedParams.tlsName = ""; - expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"}; - expectedParams.transportTypes = TEST_TRANSPORT_TYPES; - expectedParams.resolverOptions = new ResolverOptionsParcel(); - assertResolverParamsEquals(actualParams, expectedParams); - } - - @Test - public void testTransportTypesEqual() throws Exception { - SparseArray ncTransTypes = MessageUtils.findMessageNames( - new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" }); - SparseArray dnsTransTypes = MessageUtils.findMessageNames( - new Class[] { IDnsResolver.class }, new String[]{ "TRANSPORT_" }); - assertEquals(0, MIN_TRANSPORT); - assertEquals(MAX_TRANSPORT + 1, ncTransTypes.size()); - // TRANSPORT_UNKNOWN in IDnsResolver is defined to -1 and only for resolver. - assertEquals("TRANSPORT_UNKNOWN", dnsTransTypes.get(-1)); - assertEquals(ncTransTypes.size(), dnsTransTypes.size() - 1); - for (int i = MIN_TRANSPORT; i < MAX_TRANSPORT; i++) { - String name = ncTransTypes.get(i, null); - assertNotNull("Could not find NetworkCapabilies.TRANSPORT_* constant equal to " - + i, name); - assertEquals(name, dnsTransTypes.get(i)); - } - } - - @Test - public void testGetPrivateDnsConfigForNetwork() throws Exception { - final Network network = new Network(TEST_NETID); - final InetAddress dnsAddr = InetAddressUtils.parseNumericAddress("3.3.3.3"); - final InetAddress[] tlsAddrs = new InetAddress[]{ - InetAddressUtils.parseNumericAddress("6.6.6.6"), - InetAddressUtils.parseNumericAddress("2001:db8:66:66::1") - }; - final String tlsName = "strictmode.com"; - LinkProperties lp = new LinkProperties(); - lp.addDnsServer(dnsAddr); - - // The PrivateDnsConfig map is empty, so the default PRIVATE_DNS_OFF is returned. - PrivateDnsConfig privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); - assertFalse(privateDnsCfg.useTls); - assertEquals("", privateDnsCfg.hostname); - assertEquals(new InetAddress[0], privateDnsCfg.ips); - - // An entry with default PrivateDnsConfig is added to the PrivateDnsConfig map. - mDnsManager.updatePrivateDns(network, mDnsManager.getPrivateDnsConfig()); - mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.updatePrivateDnsValidation( - new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, dnsAddr, "", - VALIDATION_RESULT_SUCCESS)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); - assertTrue(privateDnsCfg.useTls); - assertEquals("", privateDnsCfg.hostname); - assertEquals(new InetAddress[0], privateDnsCfg.ips); - - // The original entry is overwritten by a new PrivateDnsConfig. - mDnsManager.updatePrivateDns(network, new PrivateDnsConfig(tlsName, tlsAddrs)); - mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp); - privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); - assertTrue(privateDnsCfg.useTls); - assertEquals(tlsName, privateDnsCfg.hostname); - assertEquals(tlsAddrs, privateDnsCfg.ips); - - // The network is removed, so the PrivateDnsConfig map becomes empty again. - mDnsManager.removeNetwork(network); - privateDnsCfg = mDnsManager.getPrivateDnsConfig(network); - assertFalse(privateDnsCfg.useTls); - assertEquals("", privateDnsCfg.hostname); - assertEquals(new InetAddress[0], privateDnsCfg.ips); - } -} diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt deleted file mode 100644 index 45b575a4365d..000000000000 --- a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2021 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.connectivity - -import android.net.NetworkAgentConfig -import android.net.NetworkCapabilities -import android.net.NetworkScore.KEEP_CONNECTED_NONE -import android.text.TextUtils -import android.util.ArraySet -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY -import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED -import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED -import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED -import com.android.server.connectivity.FullScore.POLICY_IS_VPN -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.collections.minOfOrNull -import kotlin.collections.maxOfOrNull -import kotlin.reflect.full.staticProperties -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -@RunWith(AndroidJUnit4::class) -@SmallTest -class FullScoreTest { - // Convenience methods - fun FullScore.withPolicies( - validated: Boolean = false, - vpn: Boolean = false, - onceChosen: Boolean = false, - acceptUnvalidated: Boolean = false - ): FullScore { - val nac = NetworkAgentConfig.Builder().apply { - setUnvalidatedConnectivityAcceptable(acceptUnvalidated) - setExplicitlySelected(onceChosen) - }.build() - val nc = NetworkCapabilities.Builder().apply { - if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN) - if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) - }.build() - return mixInScore(nc, nac, validated, false /* yieldToBadWifi */) - } - - @Test - fun testGetLegacyInt() { - val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE) - assertEquals(10, ns.legacyInt) // -40 penalty for not being validated - assertEquals(50, ns.legacyIntAsValidated) - - val vpnNs = FullScore(101, 0L /* policy */, KEEP_CONNECTED_NONE).withPolicies(vpn = true) - assertEquals(101, vpnNs.legacyInt) // VPNs are not subject to unvalidation penalty - assertEquals(101, vpnNs.legacyIntAsValidated) - assertEquals(101, vpnNs.withPolicies(validated = true).legacyInt) - assertEquals(101, vpnNs.withPolicies(validated = true).legacyIntAsValidated) - - val validatedNs = ns.withPolicies(validated = true) - assertEquals(50, validatedNs.legacyInt) // No penalty, this is validated - assertEquals(50, validatedNs.legacyIntAsValidated) - - val chosenNs = ns.withPolicies(onceChosen = true) - assertEquals(10, chosenNs.legacyInt) - assertEquals(100, chosenNs.legacyIntAsValidated) - assertEquals(10, chosenNs.withPolicies(acceptUnvalidated = true).legacyInt) - assertEquals(50, chosenNs.withPolicies(acceptUnvalidated = true).legacyIntAsValidated) - } - - @Test - fun testToString() { - val string = FullScore(10, 0L /* policy */, KEEP_CONNECTED_NONE) - .withPolicies(vpn = true, acceptUnvalidated = true).toString() - assertTrue(string.contains("Score(10"), string) - assertTrue(string.contains("ACCEPT_UNVALIDATED"), string) - assertTrue(string.contains("IS_VPN"), string) - assertFalse(string.contains("IS_VALIDATED"), string) - val foundNames = ArraySet() - getAllPolicies().forEach { - val name = FullScore.policyNameOf(it.get() as Int) - assertFalse(TextUtils.isEmpty(name)) - assertFalse(foundNames.contains(name)) - foundNames.add(name) - } - assertFailsWith { - FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1) - } - } - - fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex -> - FullScore::class.staticProperties.filter { it.name.matches(nameRegex) } - } - - @Test - fun testHasPolicy() { - val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE) - assertFalse(ns.hasPolicy(POLICY_IS_VALIDATED)) - assertFalse(ns.hasPolicy(POLICY_IS_VPN)) - assertFalse(ns.hasPolicy(POLICY_EVER_USER_SELECTED)) - assertFalse(ns.hasPolicy(POLICY_ACCEPT_UNVALIDATED)) - assertTrue(ns.withPolicies(validated = true).hasPolicy(POLICY_IS_VALIDATED)) - assertTrue(ns.withPolicies(vpn = true).hasPolicy(POLICY_IS_VPN)) - assertTrue(ns.withPolicies(onceChosen = true).hasPolicy(POLICY_EVER_USER_SELECTED)) - assertTrue(ns.withPolicies(acceptUnvalidated = true).hasPolicy(POLICY_ACCEPT_UNVALIDATED)) - } - - @Test - fun testMinMaxPolicyConstants() { - val policies = getAllPolicies() - - policies.forEach { policy -> - assertTrue(policy.get() as Int >= FullScore.MIN_CS_MANAGED_POLICY) - assertTrue(policy.get() as Int <= FullScore.MAX_CS_MANAGED_POLICY) - } - assertEquals(FullScore.MIN_CS_MANAGED_POLICY, - policies.minOfOrNull { it.get() as Int }) - assertEquals(FullScore.MAX_CS_MANAGED_POLICY, - policies.maxOfOrNull { it.get() as Int }) - } -} diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java deleted file mode 100644 index 70495cced536..000000000000 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (C) 2016 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.connectivity; - -import static com.android.server.connectivity.MetricsTestUtil.aLong; -import static com.android.server.connectivity.MetricsTestUtil.aString; -import static com.android.server.connectivity.MetricsTestUtil.aType; -import static com.android.server.connectivity.MetricsTestUtil.anInt; -import static com.android.server.connectivity.MetricsTestUtil.describeIpEvent; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.BLUETOOTH; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.CELLULAR; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE; -import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.net.ConnectivityMetricsEvent; -import android.net.metrics.ApfProgramEvent; -import android.net.metrics.ApfStats; -import android.net.metrics.DefaultNetworkEvent; -import android.net.metrics.DhcpClientEvent; -import android.net.metrics.DhcpErrorEvent; -import android.net.metrics.IpManagerEvent; -import android.net.metrics.IpReachabilityEvent; -import android.net.metrics.NetworkEvent; -import android.net.metrics.RaEvent; -import android.net.metrics.ValidationProbeEvent; -import android.net.metrics.WakeupStats; -import android.test.suitebuilder.annotation.SmallTest; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Arrays; -import java.util.List; - -// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpConnectivityEventBuilderTest { - - @Test - public void testLinkLayerInferrence() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(IpReachabilityEvent.class), - anInt(IpReachabilityEvent.NUD_FAILED)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.netId = 123; - ev.transports = 3; // transports have priority for inferrence of link layer - ev.ifname = "wlan0"; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - String.format(" link_layer: %d", MULTIPLE), - " network_id: 123", - " time_ms: 1", - " transports: 3", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.transports = 1; - ev.ifname = null; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - String.format(" link_layer: %d", CELLULAR), - " network_id: 123", - " time_ms: 1", - " transports: 1", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.transports = 0; - ev.ifname = "not_inferred"; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"not_inferred\"", - " link_layer: 0", - " network_id: 123", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.ifname = "bt-pan"; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - String.format(" link_layer: %d", BLUETOOTH), - " network_id: 123", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.ifname = "rmnet_ipa0"; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - String.format(" link_layer: %d", CELLULAR), - " network_id: 123", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - - ev.ifname = "wlan0"; - want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - String.format(" link_layer: %d", WIFI), - " network_id: 123", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - verifySerialization(want, ev); - } - - @Test - public void testDefaultNetworkEventSerialization() { - DefaultNetworkEvent ev = new DefaultNetworkEvent(1001); - ev.netId = 102; - ev.transports = 2; - ev.previousTransports = 4; - ev.ipv4 = true; - ev.initialScore = 20; - ev.finalScore = 60; - ev.durationMs = 54; - ev.validatedMs = 27; - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 102", - " time_ms: 0", - " transports: 2", - " default_network_event <", - " default_network_duration_ms: 54", - " final_score: 60", - " initial_score: 20", - " ip_support: 1", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 1", - " previous_network_ip_support: 0", - " validation_duration_ms: 27", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, IpConnectivityEventBuilder.toProto(ev)); - } - - @Test - public void testDhcpClientEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(DhcpClientEvent.class), - aString("SomeState"), - anInt(192)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " dhcp_event <", - " duration_ms: 192", - " if_name: \"\"", - " state_transition: \"SomeState\"", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testDhcpErrorEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(DhcpErrorEvent.class), - anInt(DhcpErrorEvent.L4_NOT_UDP)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " dhcp_event <", - " duration_ms: 0", - " if_name: \"\"", - " error_code: 50397184", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testIpManagerEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(IpManagerEvent.class), - anInt(IpManagerEvent.PROVISIONING_OK), - aLong(5678)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " ip_provisioning_event <", - " event_type: 1", - " if_name: \"\"", - " latency_ms: 5678", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testIpReachabilityEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(IpReachabilityEvent.class), - anInt(IpReachabilityEvent.NUD_FAILED)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testNetworkEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(NetworkEvent.class), - anInt(5), - aLong(20410)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " network_event <", - " event_type: 5", - " latency_ms: 20410", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testValidationProbeEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(ValidationProbeEvent.class), - aLong(40730), - anInt(ValidationProbeEvent.PROBE_HTTP), - anInt(204)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " validation_probe_event <", - " latency_ms: 40730", - " probe_result: 204", - " probe_type: 1", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testApfProgramEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(ApfProgramEvent.class), - aLong(200), - aLong(18), - anInt(7), - anInt(9), - anInt(2048), - anInt(3)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " apf_program_event <", - " current_ras: 9", - " drop_multicast: true", - " effective_lifetime: 18", - " filtered_ras: 7", - " has_ipv4_addr: true", - " lifetime: 200", - " program_length: 2048", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testApfStatsSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(ApfStats.class), - aLong(45000), - anInt(10), - anInt(2), - anInt(2), - anInt(1), - anInt(2), - anInt(4), - anInt(7), - anInt(3), - anInt(2048)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " apf_statistics <", - " dropped_ras: 2", - " duration_ms: 45000", - " matching_ras: 2", - " max_program_size: 2048", - " parse_errors: 2", - " program_updates: 4", - " program_updates_all: 7", - " program_updates_allowing_multicast: 3", - " received_ras: 10", - " total_packet_dropped: 0", - " total_packet_processed: 0", - " zero_lifetime_ras: 1", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testRaEventSerialization() { - ConnectivityMetricsEvent ev = describeIpEvent( - aType(RaEvent.class), - aLong(2000), - aLong(400), - aLong(300), - aLong(-1), - aLong(1000), - aLong(-1)); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 1", - " transports: 0", - " ra_event <", - " dnssl_lifetime: -1", - " prefix_preferred_lifetime: 300", - " prefix_valid_lifetime: 400", - " rdnss_lifetime: 1000", - " route_info_lifetime: -1", - " router_lifetime: 2000", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, ev); - } - - @Test - public void testWakeupStatsSerialization() { - WakeupStats stats = new WakeupStats("wlan0"); - stats.totalWakeups = 14; - stats.applicationWakeups = 5; - stats.nonApplicationWakeups = 1; - stats.rootWakeups = 2; - stats.systemWakeups = 3; - stats.noUidWakeups = 3; - stats.l2UnicastCount = 5; - stats.l2MulticastCount = 1; - stats.l2BroadcastCount = 2; - stats.ethertypes.put(0x800, 3); - stats.ethertypes.put(0x86dd, 3); - stats.ipNextHeaders.put(6, 5); - - - IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats); - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " wakeup_stats <", - " application_wakeups: 5", - " duration_sec: 0", - " ethertype_counts <", - " key: 2048", - " value: 3", - " >", - " ethertype_counts <", - " key: 34525", - " value: 3", - " >", - " ip_next_header_counts <", - " key: 6", - " value: 5", - " >", - " l2_broadcast_count: 2", - " l2_multicast_count: 1", - " l2_unicast_count: 5", - " no_uid_wakeups: 3", - " non_application_wakeups: 1", - " root_wakeups: 2", - " system_wakeups: 3", - " total_wakeups: 14", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, got); - } - - static void verifySerialization(String want, ConnectivityMetricsEvent... input) { - List protoInput = - IpConnectivityEventBuilder.toProto(Arrays.asList(input)); - verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0])); - } - - static void verifySerialization(String want, IpConnectivityEvent... input) { - try { - byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input)); - IpConnectivityLog log = IpConnectivityLog.parseFrom(got); - assertEquals(want, log.toString()); - } catch (Exception e) { - fail(e.toString()); - } - } -} diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java deleted file mode 100644 index 8b072c49de82..000000000000 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (C) 2016, 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.connectivity; - -import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; -import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.ConnectivityMetricsEvent; -import android.net.IIpConnectivityMetrics; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.RouteInfo; -import android.net.metrics.ApfProgramEvent; -import android.net.metrics.ApfStats; -import android.net.metrics.DhcpClientEvent; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.IpManagerEvent; -import android.net.metrics.IpReachabilityEvent; -import android.net.metrics.RaEvent; -import android.net.metrics.ValidationProbeEvent; -import android.os.Parcelable; -import android.system.OsConstants; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Base64; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.BitUtils; -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.PrintWriter; -import java.io.StringWriter; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpConnectivityMetricsTest { - static final IpReachabilityEvent FAKE_EV = - new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); - - private static final String EXAMPLE_IPV4 = "192.0.2.1"; - private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; - - private static final byte[] MAC_ADDR = - {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b}; - - @Mock Context mCtx; - @Mock IIpConnectivityMetrics mMockService; - @Mock ConnectivityManager mCm; - - IpConnectivityMetrics mService; - NetdEventListenerService mNetdListener; - private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(); - private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .build(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); - mNetdListener = new NetdEventListenerService(mCm); - mService.mNetdListener = mNetdListener; - } - - @Test - public void testBufferFlushing() { - String output1 = getdump("flush"); - assertEquals("", output1); - - new IpConnectivityLog(mService.impl).log(1, FAKE_EV); - String output2 = getdump("flush"); - assertFalse("".equals(output2)); - - String output3 = getdump("flush"); - assertEquals("", output3); - } - - @Test - public void testRateLimiting() { - final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); - final ApfProgramEvent ev = new ApfProgramEvent.Builder().build(); - final long fakeTimestamp = 1; - - int attempt = 100; // More than burst quota, but less than buffer size. - for (int i = 0; i < attempt; i++) { - logger.log(ev); - } - - String output1 = getdump("flush"); - assertFalse("".equals(output1)); - - for (int i = 0; i < attempt; i++) { - assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev)); - } - - String output2 = getdump("flush"); - assertEquals("", output2); - } - - private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai, - NetworkAgentInfo oldNai) { - final Network network = (nai != null) ? nai.network() : null; - final int score = (nai != null) ? nai.getCurrentScore() : 0; - final boolean validated = (nai != null) ? nai.lastValidated : false; - final LinkProperties lp = (nai != null) ? nai.linkProperties : null; - final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null; - - final Network prevNetwork = (oldNai != null) ? oldNai.network() : null; - final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0; - final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null; - final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null; - - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated, - lp, nc, prevNetwork, prevScore, prevLp, prevNc); - } - @Test - public void testDefaultNetworkEvents() throws Exception { - final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); - final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); - - NetworkAgentInfo[][] defaultNetworks = { - // nothing -> cell - {null, makeNai(100, 10, false, true, cell)}, - // cell -> wifi - {makeNai(100, 50, true, true, cell), makeNai(101, 20, true, false, wifi)}, - // wifi -> nothing - {makeNai(101, 60, true, false, wifi), null}, - // nothing -> cell - {null, makeNai(102, 10, true, true, cell)}, - // cell -> wifi - {makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)}, - }; - - long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; - long durationMs = 1001; - for (NetworkAgentInfo[] pair : defaultNetworks) { - timeMs += durationMs; - durationMs += durationMs; - logDefaultNetworkEvent(timeMs, pair[1], pair[0]); - } - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 5", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " default_network_event <", - " default_network_duration_ms: 1001", - " final_score: 0", - " initial_score: 0", - " ip_support: 0", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 0", - " previous_network_ip_support: 0", - " validation_duration_ms: 0", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 100", - " time_ms: 0", - " transports: 1", - " default_network_event <", - " default_network_duration_ms: 2002", - " final_score: 50", - " initial_score: 10", - " ip_support: 3", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 0", - " previous_network_ip_support: 0", - " validation_duration_ms: 2002", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 101", - " time_ms: 0", - " transports: 2", - " default_network_event <", - " default_network_duration_ms: 4004", - " final_score: 60", - " initial_score: 20", - " ip_support: 1", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 2", - " previous_network_ip_support: 0", - " validation_duration_ms: 4004", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 5", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " default_network_event <", - " default_network_duration_ms: 8008", - " final_score: 0", - " initial_score: 0", - " ip_support: 0", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 4", - " previous_network_ip_support: 0", - " validation_duration_ms: 0", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 102", - " time_ms: 0", - " transports: 1", - " default_network_event <", - " default_network_duration_ms: 16016", - " final_score: 50", - " initial_score: 10", - " ip_support: 3", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 4", - " previous_network_ip_support: 0", - " validation_duration_ms: 16016", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, getdump("flush")); - } - - @Test - public void testEndToEndLogging() throws Exception { - // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. - IpConnectivityLog logger = new IpConnectivityLog(mService.impl); - - ApfStats apfStats = new ApfStats.Builder() - .setDurationMs(45000) - .setReceivedRas(10) - .setMatchingRas(2) - .setDroppedRas(2) - .setParseErrors(2) - .setZeroLifetimeRas(1) - .setProgramUpdates(4) - .setProgramUpdatesAll(7) - .setProgramUpdatesAllowingMulticast(3) - .setMaxProgramSize(2048) - .build(); - - final ValidationProbeEvent validationEv = new ValidationProbeEvent.Builder() - .setDurationMs(40730) - .setProbeType(ValidationProbeEvent.PROBE_HTTP, true) - .setReturnCode(204) - .build(); - - final DhcpClientEvent event = new DhcpClientEvent.Builder() - .setMsg("SomeState") - .setDurationMs(192) - .build(); - Parcelable[] events = { - new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), event, - new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678), - validationEv, - apfStats, - new RaEvent(2000, 400, 300, -1, 1000, -1) - }; - - for (int i = 0; i < events.length; i++) { - ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); - ev.timestamp = 100 * (i + 1); - ev.ifname = "wlan0"; - ev.data = events[i]; - logger.log(ev); - } - - // netId, errno, latency, destination - connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); - connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); - connectEvent(100, 0, 110, EXAMPLE_IPV4); - connectEvent(101, 0, 23, EXAMPLE_IPV4); - connectEvent(101, 0, 45, EXAMPLE_IPV6); - connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); - - // netId, type, return code, latency - dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); - dnsEvent(100, EVENT_GETADDRINFO, 3, 45); - dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); - dnsEvent(101, EVENT_GETADDRINFO, 0, 56); - dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); - - // iface, uid - final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b}; - final String srcIp = "192.168.2.1"; - final String dstIp = "192.168.2.23"; - final int sport = 2356; - final int dport = 13489; - final long now = 1001L; - final int v4 = 0x800; - final int tcp = 6; - final int udp = 17; - wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); - wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); - wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); - wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); - wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L); - wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L); - - long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs; - final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); - final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); - NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); - NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); - logDefaultNetworkEvent(timeMs + 200L, cellNai, null); - logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai); - - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 100", - " transports: 0", - " ip_reachability_event <", - " event_type: 512", - " if_name: \"\"", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 200", - " transports: 0", - " dhcp_event <", - " duration_ms: 192", - " if_name: \"\"", - " state_transition: \"SomeState\"", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 300", - " transports: 0", - " ip_provisioning_event <", - " event_type: 1", - " if_name: \"\"", - " latency_ms: 5678", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 400", - " transports: 0", - " validation_probe_event <", - " latency_ms: 40730", - " probe_result: 204", - " probe_type: 257", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 500", - " transports: 0", - " apf_statistics <", - " dropped_ras: 2", - " duration_ms: 45000", - " matching_ras: 2", - " max_program_size: 2048", - " parse_errors: 2", - " program_updates: 4", - " program_updates_all: 7", - " program_updates_allowing_multicast: 3", - " received_ras: 10", - " total_packet_dropped: 0", - " total_packet_processed: 0", - " zero_lifetime_ras: 1", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 600", - " transports: 0", - " ra_event <", - " dnssl_lifetime: -1", - " prefix_preferred_lifetime: 300", - " prefix_valid_lifetime: 400", - " rdnss_lifetime: 1000", - " route_info_lifetime: -1", - " router_lifetime: 2000", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 5", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " default_network_event <", - " default_network_duration_ms: 200", - " final_score: 0", - " initial_score: 0", - " ip_support: 0", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 0", - " previous_network_ip_support: 0", - " validation_duration_ms: 0", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 100", - " time_ms: 0", - " transports: 1", - " default_network_event <", - " default_network_duration_ms: 100", - " final_score: 50", - " initial_score: 50", - " ip_support: 2", - " no_default_network_duration_ms: 0", - " previous_default_network_link_layer: 0", - " previous_network_ip_support: 0", - " validation_duration_ms: 100", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 100", - " time_ms: 0", - " transports: 2", - " connect_statistics <", - " connect_blocking_count: 1", - " connect_count: 3", - " errnos_counters <", - " key: 11", - " value: 1", - " >", - " ipv6_addr_count: 1", - " latencies_ms: 110", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 101", - " time_ms: 0", - " transports: 1", - " connect_statistics <", - " connect_blocking_count: 2", - " connect_count: 2", - " ipv6_addr_count: 1", - " latencies_ms: 23", - " latencies_ms: 45", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 100", - " time_ms: 0", - " transports: 2", - " dns_lookup_batch <", - " event_types: 1", - " event_types: 1", - " event_types: 2", - " getaddrinfo_error_count: 0", - " getaddrinfo_query_count: 0", - " gethostbyname_error_count: 0", - " gethostbyname_query_count: 0", - " latencies_ms: 3456", - " latencies_ms: 45", - " latencies_ms: 638", - " return_codes: 0", - " return_codes: 3", - " return_codes: 0", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 101", - " time_ms: 0", - " transports: 1", - " dns_lookup_batch <", - " event_types: 1", - " event_types: 2", - " getaddrinfo_error_count: 0", - " getaddrinfo_query_count: 0", - " gethostbyname_error_count: 0", - " gethostbyname_query_count: 0", - " latencies_ms: 56", - " latencies_ms: 34", - " return_codes: 0", - " return_codes: 0", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " wakeup_stats <", - " application_wakeups: 3", - " duration_sec: 0", - " ethertype_counts <", - " key: 2048", - " value: 6", - " >", - " ip_next_header_counts <", - " key: 6", - " value: 3", - " >", - " ip_next_header_counts <", - " key: 17", - " value: 3", - " >", - " l2_broadcast_count: 0", - " l2_multicast_count: 0", - " l2_unicast_count: 6", - " no_uid_wakeups: 1", - " non_application_wakeups: 0", - " root_wakeups: 0", - " system_wakeups: 2", - " total_wakeups: 6", - " >", - ">", - "version: 2\n"); - - verifySerialization(want, getdump("flush")); - } - - String getdump(String ... command) { - StringWriter buffer = new StringWriter(); - PrintWriter writer = new PrintWriter(buffer); - mService.impl.dump(null, writer, command); - return buffer.toString(); - } - - private void setCapabilities(int netId) { - final ArgumentCaptor networkCallback = - ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); - verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); - networkCallback.getValue().onCapabilitiesChanged(new Network(netId), - netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); - } - - void connectEvent(int netId, int error, int latencyMs, String ipAddr) throws Exception { - setCapabilities(netId); - mNetdListener.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); - } - - void dnsEvent(int netId, int type, int result, int latency) throws Exception { - setCapabilities(netId); - mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); - } - - void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, - String dstIp, int sport, int dport, long now) throws Exception { - String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; - mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now); - } - - NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) { - NetworkAgentInfo nai = mock(NetworkAgentInfo.class); - when(nai.network()).thenReturn(new Network(netId)); - when(nai.getCurrentScore()).thenReturn(score); - nai.linkProperties = new LinkProperties(); - nai.networkCapabilities = new NetworkCapabilities(); - nai.lastValidated = true; - for (int t : BitUtils.unpackBits(transports)) { - nai.networkCapabilities.addTransportType(t); - } - if (ipv4) { - nai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.12/24")); - nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"))); - } - if (ipv6) { - nai.linkProperties.addLinkAddress(new LinkAddress("2001:db8:dead:beef:f00::a0/64")); - nai.linkProperties.addRoute(new RouteInfo(new IpPrefix("::/0"))); - } - return nai; - } - - - - static void verifySerialization(String want, String output) { - try { - byte[] got = Base64.decode(output, Base64.DEFAULT); - IpConnectivityLogClass.IpConnectivityLog log = - IpConnectivityLogClass.IpConnectivityLog.parseFrom(got); - assertEquals(want, log.toString()); - } catch (Exception e) { - fail(e.toString()); - } - } -} diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java deleted file mode 100644 index 36e229d8aa73..000000000000 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright (C) 2016, 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.connectivity; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.ConnectivityResources; -import android.net.IDnsResolver; -import android.net.INetd; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkProvider; -import android.net.NetworkScore; -import android.os.Binder; -import android.text.format.DateUtils; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.R; -import com.android.server.ConnectivityService; -import com.android.server.connectivity.NetworkNotificationManager.NotificationType; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class LingerMonitorTest { - static final String CELLULAR = "CELLULAR"; - static final String WIFI = "WIFI"; - - static final long LOW_RATE_LIMIT = DateUtils.MINUTE_IN_MILLIS; - static final long HIGH_RATE_LIMIT = 0; - - static final int LOW_DAILY_LIMIT = 2; - static final int HIGH_DAILY_LIMIT = 1000; - - private static final int TEST_LINGER_DELAY_MS = 400; - - LingerMonitor mMonitor; - - @Mock ConnectivityService mConnService; - @Mock IDnsResolver mDnsResolver; - @Mock INetd mNetd; - @Mock Context mCtx; - @Mock NetworkNotificationManager mNotifier; - @Mock Resources mResources; - @Mock QosCallbackTracker mQosCallbackTracker; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mCtx.getResources()).thenReturn(mResources); - when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity"); - ConnectivityResources.setResourcesContextForTest(mCtx); - - mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT); - } - - @After - public void tearDown() { - ConnectivityResources.setResourcesContextForTest(null); - } - - @Test - public void testTransitions() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - NetworkAgentInfo nai1 = wifiNai(100); - NetworkAgentInfo nai2 = cellNai(101); - - assertTrue(mMonitor.isNotificationEnabled(nai1, nai2)); - assertFalse(mMonitor.isNotificationEnabled(nai2, nai1)); - } - - @Test - public void testNotificationOnLinger() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNotification(from, to); - } - - @Test - public void testToastOnLinger() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyToast(from, to); - } - - @Test - public void testNotificationClearedAfterDisconnect() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNotification(from, to); - - mMonitor.noteDisconnect(to); - verify(mNotifier, times(1)).clearNotification(100); - } - - @Test - public void testNotificationClearedAfterSwitchingBack() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNotification(from, to); - - mMonitor.noteLingerDefaultNetwork(to, from); - verify(mNotifier, times(1)).clearNotification(100); - } - - @Test - public void testUniqueToast() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyToast(from, to); - - mMonitor.noteLingerDefaultNetwork(to, from); - verify(mNotifier, times(1)).clearNotification(100); - - reset(mNotifier); - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - @Test - public void testMultipleNotifications() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo wifi1 = wifiNai(100); - NetworkAgentInfo wifi2 = wifiNai(101); - NetworkAgentInfo cell = cellNai(102); - - mMonitor.noteLingerDefaultNetwork(wifi1, cell); - verifyNotification(wifi1, cell); - - mMonitor.noteLingerDefaultNetwork(cell, wifi2); - verify(mNotifier, times(1)).clearNotification(100); - - reset(mNotifier); - mMonitor.noteLingerDefaultNetwork(wifi2, cell); - verifyNotification(wifi2, cell); - } - - @Test - public void testRateLimiting() throws InterruptedException { - mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, LOW_RATE_LIMIT); - - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo wifi1 = wifiNai(100); - NetworkAgentInfo wifi2 = wifiNai(101); - NetworkAgentInfo wifi3 = wifiNai(102); - NetworkAgentInfo cell = cellNai(103); - - mMonitor.noteLingerDefaultNetwork(wifi1, cell); - verifyNotification(wifi1, cell); - reset(mNotifier); - - Thread.sleep(50); - mMonitor.noteLingerDefaultNetwork(cell, wifi2); - mMonitor.noteLingerDefaultNetwork(wifi2, cell); - verifyNoNotifications(); - - Thread.sleep(50); - mMonitor.noteLingerDefaultNetwork(cell, wifi3); - mMonitor.noteLingerDefaultNetwork(wifi3, cell); - verifyNoNotifications(); - } - - @Test - public void testDailyLimiting() throws InterruptedException { - mMonitor = new TestableLingerMonitor(mCtx, mNotifier, LOW_DAILY_LIMIT, HIGH_RATE_LIMIT); - - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo wifi1 = wifiNai(100); - NetworkAgentInfo wifi2 = wifiNai(101); - NetworkAgentInfo wifi3 = wifiNai(102); - NetworkAgentInfo cell = cellNai(103); - - mMonitor.noteLingerDefaultNetwork(wifi1, cell); - verifyNotification(wifi1, cell); - reset(mNotifier); - - Thread.sleep(50); - mMonitor.noteLingerDefaultNetwork(cell, wifi2); - mMonitor.noteLingerDefaultNetwork(wifi2, cell); - verifyNotification(wifi2, cell); - reset(mNotifier); - - Thread.sleep(50); - mMonitor.noteLingerDefaultNetwork(cell, wifi3); - mMonitor.noteLingerDefaultNetwork(wifi3, cell); - verifyNoNotifications(); - } - - @Test - public void testUniqueNotification() { - setNotificationSwitch(transition(WIFI, CELLULAR)); - setNotificationType(LingerMonitor.NOTIFY_TYPE_NOTIFICATION); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNotification(from, to); - - mMonitor.noteLingerDefaultNetwork(to, from); - verify(mNotifier, times(1)).clearNotification(100); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNotification(from, to); - } - - @Test - public void testIgnoreNeverValidatedNetworks() { - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - setNotificationSwitch(transition(WIFI, CELLULAR)); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - from.everValidated = false; - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - @Test - public void testIgnoreCurrentlyValidatedNetworks() { - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - setNotificationSwitch(transition(WIFI, CELLULAR)); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - from.lastValidated = true; - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - @Test - public void testNoNotificationType() { - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - setNotificationSwitch(); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - @Test - public void testNoTransitionToNotify() { - setNotificationType(LingerMonitor.NOTIFY_TYPE_NONE); - setNotificationSwitch(transition(WIFI, CELLULAR)); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - @Test - public void testDifferentTransitionToNotify() { - setNotificationType(LingerMonitor.NOTIFY_TYPE_TOAST); - setNotificationSwitch(transition(CELLULAR, WIFI)); - NetworkAgentInfo from = wifiNai(100); - NetworkAgentInfo to = cellNai(101); - - mMonitor.noteLingerDefaultNetwork(from, to); - verifyNoNotifications(); - } - - void setNotificationSwitch(String... transitions) { - when(mResources.getStringArray(R.array.config_networkNotifySwitches)) - .thenReturn(transitions); - } - - String transition(String from, String to) { - return from + "-" + to; - } - - void setNotificationType(int type) { - when(mResources.getInteger(R.integer.config_networkNotifySwitchType)).thenReturn(type); - } - - void verifyNoToast() { - verify(mNotifier, never()).showToast(any(), any()); - } - - void verifyNoNotification() { - verify(mNotifier, never()) - .showNotification(anyInt(), any(), any(), any(), any(), anyBoolean()); - } - - void verifyNoNotifications() { - verifyNoToast(); - verifyNoNotification(); - } - - void verifyToast(NetworkAgentInfo from, NetworkAgentInfo to) { - verifyNoNotification(); - verify(mNotifier, times(1)).showToast(from, to); - } - - void verifyNotification(NetworkAgentInfo from, NetworkAgentInfo to) { - verifyNoToast(); - verify(mNotifier, times(1)).showNotification(eq(from.network.netId), - eq(NotificationType.NETWORK_SWITCH), eq(from), eq(to), any(), eq(true)); - } - - NetworkAgentInfo nai(int netId, int transport, int networkType, String networkTypeName) { - NetworkInfo info = new NetworkInfo(networkType, 0, networkTypeName, ""); - NetworkCapabilities caps = new NetworkCapabilities(); - caps.addCapability(0); - caps.addTransportType(transport); - NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, - new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), - mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, - mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS, - mQosCallbackTracker, new ConnectivityService.Dependencies()); - nai.everValidated = true; - return nai; - } - - NetworkAgentInfo wifiNai(int netId) { - return nai(netId, NetworkCapabilities.TRANSPORT_WIFI, - ConnectivityManager.TYPE_WIFI, WIFI); - } - - NetworkAgentInfo cellNai(int netId) { - return nai(netId, NetworkCapabilities.TRANSPORT_CELLULAR, - ConnectivityManager.TYPE_MOBILE, CELLULAR); - } - - public static class TestableLingerMonitor extends LingerMonitor { - public TestableLingerMonitor(Context c, NetworkNotificationManager n, int l, long r) { - super(c, n, l, r); - } - @Override protected PendingIntent createNotificationIntent() { - return null; - } - } -} diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java deleted file mode 100644 index 5064b9bd91b9..000000000000 --- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2016 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.connectivity; - -import android.net.ConnectivityMetricsEvent; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.function.Consumer; - -abstract public class MetricsTestUtil { - private MetricsTestUtil() { - } - - static ConnectivityMetricsEvent ev(Parcelable p) { - ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); - ev.timestamp = 1L; - ev.data = p; - return ev; - } - - static ConnectivityMetricsEvent describeIpEvent(Consumer... fs) { - Parcel p = Parcel.obtain(); - for (Consumer f : fs) { - f.accept(p); - } - p.setDataPosition(0); - return ev(p.readParcelable(ClassLoader.getSystemClassLoader())); - } - - static Consumer aType(Class c) { - return aString(c.getName()); - } - - static Consumer aBool(boolean b) { - return aByte((byte) (b ? 1 : 0)); - } - - static Consumer aByte(byte b) { - return (p) -> p.writeByte(b); - } - - static Consumer anInt(int i) { - return (p) -> p.writeInt(i); - } - - static Consumer aLong(long l) { - return (p) -> p.writeLong(l); - } - - static Consumer aString(String s) { - return (p) -> p.writeString(s); - } - - static Consumer aByteArray(byte... ary) { - return (p) -> p.writeByteArray(ary); - } - - static Consumer anIntArray(int... ary) { - return (p) -> p.writeIntArray(ary); - } - - static byte b(int i) { - return (byte) i; - } -} diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java deleted file mode 100644 index 38f6d7f3172d..000000000000 --- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2018 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.connectivity; - -import static android.content.Intent.ACTION_CONFIGURATION_CHANGED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkPolicy.LIMIT_DISABLED; -import static android.net.NetworkPolicy.SNOOZE_NEVER; -import static android.net.NetworkPolicy.WARNING_DISABLED; -import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; - -import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; -import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; - -import static junit.framework.TestCase.assertNotNull; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.usage.NetworkStatsManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.EthernetNetworkSpecifier; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkPolicy; -import android.net.NetworkPolicyManager; -import android.net.NetworkTemplate; -import android.net.TelephonyNetworkSpecifier; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; -import android.telephony.TelephonyManager; -import android.test.mock.MockContentResolver; -import android.util.DataUnit; -import android.util.RecurrenceRule; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.R; -import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.LocalServices; -import com.android.server.net.NetworkPolicyManagerInternal; -import com.android.server.net.NetworkStatsManagerInternal; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.time.Clock; -import java.time.Instant; -import java.time.Period; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class MultipathPolicyTrackerTest { - private static final Network TEST_NETWORK = new Network(123); - private static final int POLICY_SNOOZED = -100; - - @Mock private Context mContext; - @Mock private Context mUserAllContext; - @Mock private Resources mResources; - @Mock private Handler mHandler; - @Mock private MultipathPolicyTracker.Dependencies mDeps; - @Mock private Clock mClock; - @Mock private ConnectivityManager mCM; - @Mock private NetworkPolicyManager mNPM; - @Mock private NetworkStatsManager mStatsManager; - @Mock private NetworkPolicyManagerInternal mNPMI; - @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal; - @Mock private TelephonyManager mTelephonyManager; - private MockContentResolver mContentResolver; - - private ArgumentCaptor mConfigChangeReceiverCaptor; - - private MultipathPolicyTracker mTracker; - - private Clock mPreviousRecurrenceRuleClock; - private boolean mRecurrenceRuleClockMocked; - - private void mockService(String serviceName, Class serviceClass, T service) { - when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName); - when(mContext.getSystemService(serviceName)).thenReturn(service); - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mPreviousRecurrenceRuleClock = RecurrenceRule.sClock; - RecurrenceRule.sClock = mClock; - mRecurrenceRuleClockMocked = true; - - mConfigChangeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - - when(mContext.getResources()).thenReturn(mResources); - when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); - // Mock user id to all users that Context#registerReceiver will register with all users too. - doReturn(UserHandle.ALL.getIdentifier()).when(mUserAllContext).getUserId(); - when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())) - .thenReturn(mUserAllContext); - when(mUserAllContext.registerReceiver(mConfigChangeReceiverCaptor.capture(), - argThat(f -> f.hasAction(ACTION_CONFIGURATION_CHANGED)), any(), any())) - .thenReturn(null); - - when(mDeps.getClock()).thenReturn(mClock); - - when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); - - mContentResolver = Mockito.spy(new MockContentResolver(mContext)); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - Settings.Global.clearProviderForTest(); - when(mContext.getContentResolver()).thenReturn(mContentResolver); - - mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, mCM); - mockService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, mNPM); - mockService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, mStatsManager); - mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager); - - LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); - LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI); - - LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class); - LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal); - - mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps); - } - - @After - public void tearDown() { - // Avoid setting static clock to null (which should normally not be the case) - // if MockitoAnnotations.initMocks threw an exception - if (mRecurrenceRuleClockMocked) { - RecurrenceRule.sClock = mPreviousRecurrenceRuleClock; - } - mRecurrenceRuleClockMocked = false; - } - - private void setDefaultQuotaGlobalSetting(long setting) { - Settings.Global.putInt(mContentResolver, NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES, - (int) setting); - } - - private void testGetMultipathPreference( - long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit, - long defaultGlobalSetting, long defaultResSetting, boolean roaming) { - - // TODO: tests should not use ZoneId.systemDefault() once code handles TZ correctly. - final ZonedDateTime now = ZonedDateTime.ofInstant( - Instant.parse("2017-04-02T10:11:12Z"), ZoneId.systemDefault()); - final ZonedDateTime startOfDay = now.truncatedTo(ChronoUnit.DAYS); - when(mClock.millis()).thenReturn(now.toInstant().toEpochMilli()); - when(mClock.instant()).thenReturn(now.toInstant()); - when(mClock.getZone()).thenReturn(ZoneId.systemDefault()); - - // Setup plan quota - when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH)) - .thenReturn(subscriptionQuota); - - // Setup user policy warning / limit - if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) { - final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z"); - final RecurrenceRule recurrenceRule = new RecurrenceRule( - ZonedDateTime.ofInstant( - recurrenceStart, - ZoneId.systemDefault()), - null /* end */, - Period.ofMonths(1)); - final boolean snoozeWarning = policyWarning == POLICY_SNOOZED; - final boolean snoozeLimit = policyLimit == POLICY_SNOOZED; - when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] { - new NetworkPolicy( - NetworkTemplate.buildTemplateMobileWildcard(), - recurrenceRule, - snoozeWarning ? 0 : policyWarning, - snoozeLimit ? 0 : policyLimit, - snoozeWarning ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER, - snoozeLimit ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER, - SNOOZE_NEVER, - true /* metered */, - false /* inferred */) - }); - } else { - when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); - } - - // Setup default quota in settings and resources - if (defaultGlobalSetting > 0) { - setDefaultQuotaGlobalSetting(defaultGlobalSetting); - } - when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes)) - .thenReturn((int) defaultResSetting); - - when(mNetworkStatsManagerInternal.getNetworkTotalBytes( - any(), - eq(startOfDay.toInstant().toEpochMilli()), - eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday); - - ArgumentCaptor networkCallback = - ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); - mTracker.start(); - verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any()); - - // Simulate callback after capability changes - NetworkCapabilities capabilities = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addTransportType(TRANSPORT_CELLULAR) - .setNetworkSpecifier(new EthernetNetworkSpecifier("eth234")); - if (!roaming) { - capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING); - } - networkCallback.getValue().onCapabilitiesChanged( - TEST_NETWORK, - capabilities); - - // make sure it also works with the new introduced TelephonyNetworkSpecifier - capabilities = new NetworkCapabilities() - .addCapability(NET_CAPABILITY_INTERNET) - .addTransportType(TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() - .setSubscriptionId(234).build()); - if (!roaming) { - capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING); - } - networkCallback.getValue().onCapabilitiesChanged( - TEST_NETWORK, - capabilities); - } - - @Test - public void testGetMultipathPreference_SubscriptionQuota() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, - DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */, - DataUnit.MEGABYTES.toBytes(100) /* policyWarning */, - LIMIT_DISABLED, - DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, - 2_500_000 /* defaultResSetting */, - false /* roaming */); - - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); - } - - @Test - public void testGetMultipathPreference_UserWarningQuota() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, - OPPORTUNISTIC_QUOTA_UNKNOWN, - // 29 days from Apr. 2nd to May 1st - DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */, - LIMIT_DISABLED, - DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, - 2_500_000 /* defaultResSetting */, - false /* roaming */); - - // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); - } - - @Test - public void testGetMultipathPreference_SnoozedWarningQuota() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, - OPPORTUNISTIC_QUOTA_UNKNOWN, - // 29 days from Apr. 2nd to May 1st - POLICY_SNOOZED /* policyWarning */, - DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */, - DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, - 2_500_000 /* defaultResSetting */, - false /* roaming */); - - // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); - } - - @Test - public void testGetMultipathPreference_SnoozedBothQuota() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */, - OPPORTUNISTIC_QUOTA_UNKNOWN, - // 29 days from Apr. 2nd to May 1st - POLICY_SNOOZED /* policyWarning */, - POLICY_SNOOZED /* policyLimit */, - DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */, - 2_500_000 /* defaultResSetting */, - false /* roaming */); - - // Default global setting should be used: 12 - 7 = 5 - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any()); - } - - @Test - public void testGetMultipathPreference_SettingChanged() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, - OPPORTUNISTIC_QUOTA_UNKNOWN, - WARNING_DISABLED, - LIMIT_DISABLED, - -1 /* defaultGlobalSetting */, - DataUnit.MEGABYTES.toBytes(10) /* defaultResSetting */, - false /* roaming */); - - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any()); - - // Update setting - setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14)); - mTracker.mSettingsObserver.onChange( - false, Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES)); - - // Callback must have been re-registered with new setting - verify(mStatsManager, times(1)).unregisterUsageCallback(any()); - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); - } - - @Test - public void testGetMultipathPreference_ResourceChanged() { - testGetMultipathPreference( - DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */, - OPPORTUNISTIC_QUOTA_UNKNOWN, - WARNING_DISABLED, - LIMIT_DISABLED, - -1 /* defaultGlobalSetting */, - DataUnit.MEGABYTES.toBytes(14) /* defaultResSetting */, - false /* roaming */); - - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any()); - - when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes)) - .thenReturn((int) DataUnit.MEGABYTES.toBytes(16)); - - final BroadcastReceiver configChangeReceiver = mConfigChangeReceiverCaptor.getValue(); - assertNotNull(configChangeReceiver); - configChangeReceiver.onReceive(mContext, new Intent()); - - // Uses the new setting (16 - 2 = 14MB) - verify(mStatsManager, times(1)).registerUsageCallback( - any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any()); - } -} diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java deleted file mode 100644 index 9b2a638f8b39..000000000000 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright (C) 2017 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.connectivity; - -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.net.ConnectivityManager; -import android.net.IDnsResolver; -import android.net.INetd; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NetworkAgentConfig; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.os.Handler; -import android.os.test.TestLooper; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.ConnectivityService; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class Nat464XlatTest { - - static final String BASE_IFACE = "test0"; - static final String STACKED_IFACE = "v4-test0"; - static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64"); - static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); - static final String NAT64_PREFIX = "64:ff9b::/96"; - static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96"; - static final int NETID = 42; - - @Mock ConnectivityService mConnectivity; - @Mock IDnsResolver mDnsResolver; - @Mock INetd mNetd; - @Mock NetworkAgentInfo mNai; - - TestLooper mLooper; - Handler mHandler; - NetworkAgentConfig mAgentConfig = new NetworkAgentConfig(); - - Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) { - return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) { - @Override protected int getNetId() { - return NETID; - } - - @Override protected boolean isCellular464XlatEnabled() { - return isCellular464XlatEnabled; - } - }; - } - - private void markNetworkConnected() { - mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", ""); - } - - private void markNetworkDisconnected() { - mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", ""); - } - - @Before - public void setUp() throws Exception { - mLooper = new TestLooper(); - mHandler = new Handler(mLooper.getLooper()); - - MockitoAnnotations.initMocks(this); - - mNai.linkProperties = new LinkProperties(); - mNai.linkProperties.setInterfaceName(BASE_IFACE); - mNai.networkInfo = new NetworkInfo(null); - mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); - mNai.networkCapabilities = new NetworkCapabilities(); - markNetworkConnected(); - when(mNai.connService()).thenReturn(mConnectivity); - when(mNai.netAgentConfig()).thenReturn(mAgentConfig); - when(mNai.handler()).thenReturn(mHandler); - final InterfaceConfigurationParcel mConfig = new InterfaceConfigurationParcel(); - when(mNetd.interfaceGetCfg(eq(STACKED_IFACE))).thenReturn(mConfig); - mConfig.ipv4Addr = ADDR.getAddress().getHostAddress(); - mConfig.prefixLength = ADDR.getPrefixLength(); - } - - private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) { - Nat464Xlat nat = makeNat464Xlat(true); - String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b " - + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), - nai.networkInfo.getDetailedState(), - mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), - nai.linkProperties.getLinkAddresses()); - assertEquals(msg, expected, nat.requiresClat(nai)); - } - - private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) { - Nat464Xlat nat = makeNat464Xlat(true); - String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b " - + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), - nai.networkInfo.getDetailedState(), - mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(), - nai.linkProperties.getLinkAddresses()); - assertEquals(msg, expected, nat.shouldStartClat(nai)); - } - - @Test - public void testRequiresClat() throws Exception { - final int[] supportedTypes = { - ConnectivityManager.TYPE_MOBILE, - ConnectivityManager.TYPE_WIFI, - ConnectivityManager.TYPE_ETHERNET, - }; - - // NetworkInfo doesn't allow setting the State directly, but rather - // requires setting DetailedState in order set State as a side-effect. - final NetworkInfo.DetailedState[] supportedDetailedStates = { - NetworkInfo.DetailedState.CONNECTED, - NetworkInfo.DetailedState.SUSPENDED, - }; - - LinkProperties oldLp = new LinkProperties(mNai.linkProperties); - for (int type : supportedTypes) { - mNai.networkInfo.setType(type); - for (NetworkInfo.DetailedState state : supportedDetailedStates) { - mNai.networkInfo.setDetailedState(state, "reason", "extraInfo"); - - mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX)); - assertRequiresClat(false, mNai); - assertShouldStartClat(false, mNai); - - mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64")); - assertRequiresClat(false, mNai); - assertShouldStartClat(false, mNai); - - mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - assertRequiresClat(true, mNai); - assertShouldStartClat(true, mNai); - - mAgentConfig.skip464xlat = true; - assertRequiresClat(false, mNai); - assertShouldStartClat(false, mNai); - - mAgentConfig.skip464xlat = false; - assertRequiresClat(true, mNai); - assertShouldStartClat(true, mNai); - - mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24")); - assertRequiresClat(false, mNai); - assertShouldStartClat(false, mNai); - - mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24")); - assertRequiresClat(true, mNai); - assertShouldStartClat(true, mNai); - - mNai.linkProperties.setNat64Prefix(null); - assertRequiresClat(true, mNai); - assertShouldStartClat(false, mNai); - - mNai.linkProperties = new LinkProperties(oldLp); - } - } - } - - private void makeClatUnnecessary(boolean dueToDisconnect) { - if (dueToDisconnect) { - markNetworkDisconnected(); - } else { - mNai.linkProperties.addLinkAddress(ADDR); - } - } - - private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception { - Nat464Xlat nat = makeNat464Xlat(true); - ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); - - mNai.linkProperties.addLinkAddress(V6ADDR); - - nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); - - // Start clat. - nat.start(); - - verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - // Stacked interface up notification arrives. - nat.interfaceLinkStateChanged(STACKED_IFACE, true); - mLooper.dispatchNext(); - - verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE)); - verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertFalse(c.getValue().getStackedLinks().isEmpty()); - assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertRunning(nat); - - // Stop clat (Network disconnects, IPv4 addr appears, ...). - makeClatUnnecessary(dueToDisconnect); - nat.stop(); - - verify(mNetd).clatdStop(eq(BASE_IFACE)); - verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertTrue(c.getValue().getStackedLinks().isEmpty()); - assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); - assertIdle(nat); - - // Stacked interface removed notification arrives and is ignored. - nat.interfaceRemoved(STACKED_IFACE); - mLooper.dispatchNext(); - - verifyNoMoreInteractions(mNetd, mConnectivity); - } - - @Test - public void testNormalStartAndStopDueToDisconnect() throws Exception { - checkNormalStartAndStop(true); - } - - @Test - public void testNormalStartAndStopDueToIpv4Addr() throws Exception { - checkNormalStartAndStop(false); - } - - private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception { - Nat464Xlat nat = makeNat464Xlat(true); - ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); - InOrder inOrder = inOrder(mNetd, mConnectivity); - - mNai.linkProperties.addLinkAddress(V6ADDR); - - nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); - - nat.start(); - - inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - // Stacked interface up notification arrives. - nat.interfaceLinkStateChanged(STACKED_IFACE, true); - mLooper.dispatchNext(); - - inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertFalse(c.getValue().getStackedLinks().isEmpty()); - assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertRunning(nat); - - // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). - nat.stop(); - - inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); - - inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertTrue(c.getValue().getStackedLinks().isEmpty()); - assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertIdle(nat); - - if (interfaceRemovedFirst) { - // Stacked interface removed notification arrives and is ignored. - nat.interfaceRemoved(STACKED_IFACE); - mLooper.dispatchNext(); - nat.interfaceLinkStateChanged(STACKED_IFACE, false); - mLooper.dispatchNext(); - } - - assertTrue(c.getValue().getStackedLinks().isEmpty()); - assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertIdle(nat); - inOrder.verifyNoMoreInteractions(); - - nat.start(); - - inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - if (!interfaceRemovedFirst) { - // Stacked interface removed notification arrives and is ignored. - nat.interfaceRemoved(STACKED_IFACE); - mLooper.dispatchNext(); - nat.interfaceLinkStateChanged(STACKED_IFACE, false); - mLooper.dispatchNext(); - } - - // Stacked interface up notification arrives. - nat.interfaceLinkStateChanged(STACKED_IFACE, true); - mLooper.dispatchNext(); - - inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertFalse(c.getValue().getStackedLinks().isEmpty()); - assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertRunning(nat); - - // ConnectivityService stops clat again. - nat.stop(); - - inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); - - inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertTrue(c.getValue().getStackedLinks().isEmpty()); - assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertIdle(nat); - - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void testStartStopStart() throws Exception { - checkStartStopStart(true); - } - - @Test - public void testStartStopStartBeforeInterfaceRemoved() throws Exception { - checkStartStopStart(false); - } - - @Test - public void testClatdCrashWhileRunning() throws Exception { - Nat464Xlat nat = makeNat464Xlat(true); - ArgumentCaptor c = ArgumentCaptor.forClass(LinkProperties.class); - - nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); - - nat.start(); - - verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - // Stacked interface up notification arrives. - nat.interfaceLinkStateChanged(STACKED_IFACE, true); - mLooper.dispatchNext(); - - verify(mNetd).interfaceGetCfg(eq(STACKED_IFACE)); - verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); - assertFalse(c.getValue().getStackedLinks().isEmpty()); - assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertRunning(nat); - - // Stacked interface removed notification arrives (clatd crashed, ...). - nat.interfaceRemoved(STACKED_IFACE); - mLooper.dispatchNext(); - - verify(mNetd).clatdStop(eq(BASE_IFACE)); - verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); - verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); - assertTrue(c.getValue().getStackedLinks().isEmpty()); - assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); - assertIdle(nat); - - // ConnectivityService stops clat: no-op. - nat.stop(); - - verifyNoMoreInteractions(mNetd, mConnectivity); - } - - private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception { - Nat464Xlat nat = makeNat464Xlat(true); - - mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - - nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); - - nat.start(); - - verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) - makeClatUnnecessary(dueToDisconnect); - nat.stop(); - - verify(mNetd).clatdStop(eq(BASE_IFACE)); - verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); - assertIdle(nat); - - // In-flight interface up notification arrives: no-op - nat.interfaceLinkStateChanged(STACKED_IFACE, true); - mLooper.dispatchNext(); - - // Interface removed notification arrives after stopClatd() takes effect: no-op. - nat.interfaceRemoved(STACKED_IFACE); - mLooper.dispatchNext(); - - assertIdle(nat); - - verifyNoMoreInteractions(mNetd, mConnectivity); - } - - @Test - public void testStopDueToDisconnectBeforeClatdStarts() throws Exception { - checkStopBeforeClatdStarts(true); - } - - @Test - public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception { - checkStopBeforeClatdStarts(false); - } - - private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception { - Nat464Xlat nat = makeNat464Xlat(true); - - mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); - - nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); - - nat.start(); - - verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); - - // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) - makeClatUnnecessary(dueToDisconnect); - nat.stop(); - - verify(mNetd).clatdStop(eq(BASE_IFACE)); - verify(mDnsResolver).stopPrefix64Discovery(eq(NETID)); - assertIdle(nat); - - verifyNoMoreInteractions(mNetd, mConnectivity); - } - - @Test - public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception { - checkStopAndClatdNeverStarts(true); - } - - @Test - public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception { - checkStopAndClatdNeverStarts(false); - } - - @Test - public void testNat64PrefixPreference() throws Exception { - final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX); - final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX); - - Nat464Xlat nat = makeNat464Xlat(true); - - final LinkProperties emptyLp = new LinkProperties(); - LinkProperties fixedupLp; - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromDns(prefixFromDns); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(prefixFromDns, fixedupLp.getNat64Prefix()); - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromRa(prefixFromRa); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromRa(null); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(prefixFromDns, fixedupLp.getNat64Prefix()); - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromRa(prefixFromRa); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromDns(null); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(prefixFromRa, fixedupLp.getNat64Prefix()); - - fixedupLp = new LinkProperties(); - nat.setNat64PrefixFromRa(null); - nat.fixupLinkProperties(emptyLp, fixedupLp); - assertEquals(null, fixedupLp.getNat64Prefix()); - } - - private void checkClatDisabledOnCellular(boolean onCellular) throws Exception { - // Disable 464xlat on cellular networks. - Nat464Xlat nat = makeNat464Xlat(false); - mNai.linkProperties.addLinkAddress(V6ADDR); - mNai.networkCapabilities.setTransportType(TRANSPORT_CELLULAR, onCellular); - nat.update(); - - final IpPrefix nat64Prefix = new IpPrefix(NAT64_PREFIX); - if (onCellular) { - // Prefix discovery is never started. - verify(mDnsResolver, never()).startPrefix64Discovery(eq(NETID)); - assertIdle(nat); - - // If a NAT64 prefix comes in from an RA, clat is not started either. - mNai.linkProperties.setNat64Prefix(nat64Prefix); - nat.setNat64PrefixFromRa(nat64Prefix); - nat.update(); - verify(mNetd, never()).clatdStart(anyString(), anyString()); - assertIdle(nat); - } else { - // Prefix discovery is started. - verify(mDnsResolver).startPrefix64Discovery(eq(NETID)); - assertIdle(nat); - - // If a NAT64 prefix comes in from an RA, clat is started. - mNai.linkProperties.setNat64Prefix(nat64Prefix); - nat.setNat64PrefixFromRa(nat64Prefix); - nat.update(); - verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX); - assertStarting(nat); - } - } - - @Test - public void testClatDisabledOnCellular() throws Exception { - checkClatDisabledOnCellular(true); - } - - @Test - public void testClatDisabledOnNonCellular() throws Exception { - checkClatDisabledOnCellular(false); - } - - static void assertIdle(Nat464Xlat nat) { - assertTrue("Nat464Xlat was not IDLE", !nat.isStarted()); - } - - static void assertStarting(Nat464Xlat nat) { - assertTrue("Nat464Xlat was not STARTING", nat.isStarting()); - } - - static void assertRunning(Nat464Xlat nat) { - assertTrue("Nat464Xlat was not RUNNING", nat.isRunning()); - } -} diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java deleted file mode 100644 index 50aaaee24418..000000000000 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Copyright (C) 2016, 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.connectivity; - -import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; -import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; - -import static com.android.testutils.MiscAsserts.assertStringContains; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.system.OsConstants; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Base64; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; -import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; - -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetdEventListenerServiceTest { - private static final String EXAMPLE_IPV4 = "192.0.2.1"; - private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; - - private static final byte[] MAC_ADDR = - {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b}; - - NetdEventListenerService mService; - ConnectivityManager mCm; - private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(); - private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .build(); - - @Before - public void setUp() { - mCm = mock(ConnectivityManager.class); - mService = new NetdEventListenerService(mCm); - } - - @Test - public void testWakeupEventLogging() throws Exception { - final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH; - final long now = System.currentTimeMillis(); - final String iface = "wlan0"; - final byte[] mac = MAC_ADDR; - final String srcIp = "192.168.2.1"; - final String dstIp = "192.168.2.23"; - final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4"; - final String dstIp6 = "2001:db8:4006:807::200a"; - final int sport = 2356; - final int dport = 13489; - - final int v4 = 0x800; - final int v6 = 0x86dd; - final int tcp = 6; - final int udp = 17; - final int icmp6 = 58; - - // Baseline without any event - String[] baseline = listNetdEvent(); - - int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004}; - wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - - String[] events2 = remove(listNetdEvent(), baseline); - int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line - assertEquals(expectedLength2, events2.length); - assertStringContains(events2[0], "WakeupStats"); - assertStringContains(events2[0], "wlan0"); - assertStringContains(events2[0], "0x800"); - assertStringContains(events2[0], "0x86dd"); - for (int i = 0; i < uids.length; i++) { - String got = events2[i+1]; - assertStringContains(got, "WakeupEvent"); - assertStringContains(got, "wlan0"); - assertStringContains(got, "uid: " + uids[i]); - } - - int uid = 20000; - for (int i = 0; i < BUFFER_LENGTH * 2; i++) { - long ts = now + 10; - wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts); - } - - String[] events3 = remove(listNetdEvent(), baseline); - int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line - assertEquals(expectedLength3, events3.length); - assertStringContains(events2[0], "WakeupStats"); - assertStringContains(events2[0], "wlan0"); - for (int i = 1; i < expectedLength3; i++) { - String got = events3[i]; - assertStringContains(got, "WakeupEvent"); - assertStringContains(got, "wlan0"); - assertStringContains(got, "uid: " + uid); - } - - uid = 45678; - wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now); - - String[] events4 = remove(listNetdEvent(), baseline); - String lastEvent = events4[events4.length - 1]; - assertStringContains(lastEvent, "WakeupEvent"); - assertStringContains(lastEvent, "wlan0"); - assertStringContains(lastEvent, "uid: " + uid); - } - - @Test - public void testWakeupStatsLogging() throws Exception { - final byte[] mac = MAC_ADDR; - final String srcIp = "192.168.2.1"; - final String dstIp = "192.168.2.23"; - final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4"; - final String dstIp6 = "2404:6800:4006:807::200a"; - final int sport = 2356; - final int dport = 13489; - final long now = 1001L; - - final int v4 = 0x800; - final int v6 = 0x86dd; - final int tcp = 6; - final int udp = 17; - final int icmp6 = 58; - - wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now); - wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now); - wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now); - - String got = flushStatistics(); - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " wakeup_stats <", - " application_wakeups: 3", - " duration_sec: 0", - " ethertype_counts <", - " key: 2048", - " value: 4", - " >", - " ethertype_counts <", - " key: 34525", - " value: 1", - " >", - " ip_next_header_counts <", - " key: 6", - " value: 5", - " >", - " l2_broadcast_count: 0", - " l2_multicast_count: 0", - " l2_unicast_count: 5", - " no_uid_wakeups: 0", - " non_application_wakeups: 0", - " root_wakeups: 0", - " system_wakeups: 2", - " total_wakeups: 5", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " wakeup_stats <", - " application_wakeups: 2", - " duration_sec: 0", - " ethertype_counts <", - " key: 2048", - " value: 5", - " >", - " ethertype_counts <", - " key: 34525", - " value: 5", - " >", - " ip_next_header_counts <", - " key: 6", - " value: 3", - " >", - " ip_next_header_counts <", - " key: 17", - " value: 5", - " >", - " ip_next_header_counts <", - " key: 58", - " value: 2", - " >", - " l2_broadcast_count: 0", - " l2_multicast_count: 0", - " l2_unicast_count: 10", - " no_uid_wakeups: 2", - " non_application_wakeups: 1", - " root_wakeups: 2", - " system_wakeups: 3", - " total_wakeups: 10", - " >", - ">", - "version: 2\n"); - assertEquals(want, got); - } - - @Test - public void testDnsLogging() throws Exception { - asyncDump(100); - - dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); - dnsEvent(100, EVENT_GETADDRINFO, 0, 267); - dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230); - dnsEvent(100, EVENT_GETADDRINFO, 3, 45); - dnsEvent(100, EVENT_GETADDRINFO, 1, 2111); - dnsEvent(100, EVENT_GETADDRINFO, 0, 450); - dnsEvent(100, EVENT_GETHOSTBYNAME, 200, 638); - dnsEvent(100, EVENT_GETHOSTBYNAME, 178, 1300); - dnsEvent(101, EVENT_GETADDRINFO, 0, 56); - dnsEvent(101, EVENT_GETADDRINFO, 0, 78); - dnsEvent(101, EVENT_GETADDRINFO, 0, 14); - dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 56); - dnsEvent(101, EVENT_GETADDRINFO, 0, 78); - dnsEvent(101, EVENT_GETADDRINFO, 0, 14); - - String got = flushStatistics(); - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 100", - " time_ms: 0", - " transports: 2", - " dns_lookup_batch <", - " event_types: 1", - " event_types: 1", - " event_types: 2", - " event_types: 1", - " event_types: 1", - " event_types: 1", - " event_types: 2", - " event_types: 2", - " getaddrinfo_error_count: 0", - " getaddrinfo_query_count: 0", - " gethostbyname_error_count: 0", - " gethostbyname_query_count: 0", - " latencies_ms: 3456", - " latencies_ms: 267", - " latencies_ms: 1230", - " latencies_ms: 45", - " latencies_ms: 2111", - " latencies_ms: 450", - " latencies_ms: 638", - " latencies_ms: 1300", - " return_codes: 0", - " return_codes: 0", - " return_codes: 22", - " return_codes: 3", - " return_codes: 1", - " return_codes: 0", - " return_codes: 200", - " return_codes: 178", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 101", - " time_ms: 0", - " transports: 1", - " dns_lookup_batch <", - " event_types: 1", - " event_types: 1", - " event_types: 1", - " event_types: 2", - " event_types: 1", - " event_types: 1", - " getaddrinfo_error_count: 0", - " getaddrinfo_query_count: 0", - " gethostbyname_error_count: 0", - " gethostbyname_query_count: 0", - " latencies_ms: 56", - " latencies_ms: 78", - " latencies_ms: 14", - " latencies_ms: 56", - " latencies_ms: 78", - " latencies_ms: 14", - " return_codes: 0", - " return_codes: 0", - " return_codes: 0", - " return_codes: 0", - " return_codes: 0", - " return_codes: 0", - " >", - ">", - "version: 2\n"); - assertEquals(want, got); - } - - @Test - public void testConnectLogging() throws Exception { - asyncDump(100); - - final int OK = 0; - Thread[] logActions = { - // ignored - connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4), - connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6), - connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), - connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), - connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), - // valid latencies - connectEventAction(100, OK, 110, EXAMPLE_IPV4), - connectEventAction(100, OK, 23, EXAMPLE_IPV4), - connectEventAction(100, OK, 45, EXAMPLE_IPV4), - connectEventAction(101, OK, 56, EXAMPLE_IPV4), - connectEventAction(101, OK, 523, EXAMPLE_IPV6), - connectEventAction(101, OK, 214, EXAMPLE_IPV6), - connectEventAction(101, OK, 67, EXAMPLE_IPV6), - // errors - connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4), - connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6), - connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), - connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), - connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), - }; - - for (Thread t : logActions) { - t.start(); - } - for (Thread t : logActions) { - t.join(); - } - - String got = flushStatistics(); - String want = String.join("\n", - "dropped_events: 0", - "events <", - " if_name: \"\"", - " link_layer: 4", - " network_id: 100", - " time_ms: 0", - " transports: 2", - " connect_statistics <", - " connect_blocking_count: 3", - " connect_count: 6", - " errnos_counters <", - " key: 1", - " value: 1", - " >", - " errnos_counters <", - " key: 11", - " value: 1", - " >", - " errnos_counters <", - " key: 13", - " value: 1", - " >", - " errnos_counters <", - " key: 98", - " value: 1", - " >", - " errnos_counters <", - " key: 110", - " value: 2", - " >", - " ipv6_addr_count: 1", - " latencies_ms: 23", - " latencies_ms: 45", - " latencies_ms: 110", - " >", - ">", - "events <", - " if_name: \"\"", - " link_layer: 2", - " network_id: 101", - " time_ms: 0", - " transports: 1", - " connect_statistics <", - " connect_blocking_count: 4", - " connect_count: 6", - " errnos_counters <", - " key: 1", - " value: 1", - " >", - " errnos_counters <", - " key: 13", - " value: 2", - " >", - " errnos_counters <", - " key: 110", - " value: 1", - " >", - " errnos_counters <", - " key: 111", - " value: 1", - " >", - " ipv6_addr_count: 5", - " latencies_ms: 56", - " latencies_ms: 67", - " latencies_ms: 214", - " latencies_ms: 523", - " >", - ">", - "version: 2\n"); - assertEquals(want, got); - } - - private void setCapabilities(int netId) { - final ArgumentCaptor networkCallback = - ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); - verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); - networkCallback.getValue().onCapabilitiesChanged(new Network(netId), - netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); - } - - Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) { - setCapabilities(netId); - return new Thread(() -> { - try { - mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); - } catch (Exception e) { - fail(e.toString()); - } - }); - } - - void dnsEvent(int netId, int type, int result, int latency) throws Exception { - setCapabilities(netId); - mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0); - } - - void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp, - String dstIp, int sport, int dport, long now) throws Exception { - String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface; - mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now); - } - - void asyncDump(long durationMs) throws Exception { - final long stop = System.currentTimeMillis() + durationMs; - final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null")); - new Thread(() -> { - while (System.currentTimeMillis() < stop) { - mService.list(pw); - } - }).start(); - } - - // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. - String flushStatistics() throws Exception { - IpConnectivityMetrics metricsService = - new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000); - metricsService.mNetdListener = mService; - - StringWriter buffer = new StringWriter(); - PrintWriter writer = new PrintWriter(buffer); - metricsService.impl.dump(null, writer, new String[]{"flush"}); - byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT); - IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes); - for (IpConnectivityEvent ev : log.events) { - if (ev.getConnectStatistics() == null) { - continue; - } - // Sort repeated fields of connect() events arriving in non-deterministic order. - Arrays.sort(ev.getConnectStatistics().latenciesMs); - Arrays.sort(ev.getConnectStatistics().errnosCounters, - Comparator.comparingInt((p) -> p.key)); - } - return log.toString(); - } - - String[] listNetdEvent() throws Exception { - StringWriter buffer = new StringWriter(); - PrintWriter writer = new PrintWriter(buffer); - mService.list(writer); - return buffer.toString().split("\\n"); - } - - static T[] remove(T[] array, T[] filtered) { - List c = Arrays.asList(filtered); - int next = 0; - for (int i = 0; i < array.length; i++) { - if (c.contains(array[i])) { - continue; - } - array[next++] = array[i]; - } - return Arrays.copyOf(array, next); - } -} diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java deleted file mode 100644 index c353cea266bb..000000000000 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2016 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.connectivity; - -import static android.app.Notification.FLAG_ONGOING_EVENT; - -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.LOST_INTERNET; -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NETWORK_SWITCH; -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NO_INTERNET; -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PARTIAL_CONNECTIVITY; -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PRIVATE_DNS_BROKEN; -import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.SIGN_IN; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.net.ConnectivityResources; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.os.UserHandle; -import android.telephony.TelephonyManager; -import android.util.DisplayMetrics; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.connectivity.resources.R; -import com.android.server.connectivity.NetworkNotificationManager.NotificationType; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.AdditionalAnswers; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkNotificationManagerTest { - - private static final String TEST_SSID = "Test SSID"; - private static final String TEST_EXTRA_INFO = "extra"; - static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities(); - static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities(); - static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities(); - static { - CELL_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - CELL_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - - WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - WIFI_CAPABILITIES.setSSID(TEST_SSID); - - // Set the underyling network to wifi. - VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_VPN); - VPN_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - VPN_CAPABILITIES.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - } - - @Mock Context mCtx; - @Mock Resources mResources; - @Mock DisplayMetrics mDisplayMetrics; - @Mock PackageManager mPm; - @Mock TelephonyManager mTelephonyManager; - @Mock NotificationManager mNotificationManager; - @Mock NetworkAgentInfo mWifiNai; - @Mock NetworkAgentInfo mCellNai; - @Mock NetworkAgentInfo mVpnNai; - @Mock NetworkInfo mNetworkInfo; - ArgumentCaptor mCaptor; - - NetworkNotificationManager mManager; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mCaptor = ArgumentCaptor.forClass(Notification.class); - mWifiNai.networkCapabilities = WIFI_CAPABILITIES; - mWifiNai.networkInfo = mNetworkInfo; - mCellNai.networkCapabilities = CELL_CAPABILITIES; - mCellNai.networkInfo = mNetworkInfo; - mVpnNai.networkCapabilities = VPN_CAPABILITIES; - mVpnNai.networkInfo = mNetworkInfo; - mDisplayMetrics.density = 2.275f; - doReturn(true).when(mVpnNai).isVPN(); - when(mCtx.getResources()).thenReturn(mResources); - when(mCtx.getPackageManager()).thenReturn(mPm); - when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo()); - final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mCtx)); - doReturn(UserHandle.ALL).when(asUserCtx).getUser(); - when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); - when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE))) - .thenReturn(mNotificationManager); - when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO); - ConnectivityResources.setResourcesContextForTest(mCtx); - when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); - when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); - - // Come up with some credible-looking transport names. The actual values do not matter. - String[] transportNames = new String[NetworkCapabilities.MAX_TRANSPORT + 1]; - for (int transport = 0; transport <= NetworkCapabilities.MAX_TRANSPORT; transport++) { - transportNames[transport] = NetworkCapabilities.transportNameOf(transport); - } - when(mResources.getStringArray(R.array.network_switch_type_name)) - .thenReturn(transportNames); - - mManager = new NetworkNotificationManager(mCtx, mTelephonyManager); - } - - @After - public void tearDown() { - ConnectivityResources.setResourcesContextForTest(null); - } - - private void verifyTitleByNetwork(final int id, final NetworkAgentInfo nai, final int title) { - final String tag = NetworkNotificationManager.tagFor(id); - mManager.showNotification(id, PRIVATE_DNS_BROKEN, nai, null, null, true); - verify(mNotificationManager, times(1)) - .notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any()); - final int transportType = NetworkNotificationManager.approximateTransportType(nai); - if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { - verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO)); - } else { - verify(mResources, times(1)).getString(title); - } - verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed)); - } - - @Test - public void testTitleOfPrivateDnsBroken() { - // Test the title of mobile data. - verifyTitleByNetwork(100, mCellNai, R.string.mobile_no_internet); - clearInvocations(mResources); - - // Test the title of wifi. - verifyTitleByNetwork(101, mWifiNai, R.string.wifi_no_internet); - clearInvocations(mResources); - - // Test the title of other networks. - verifyTitleByNetwork(102, mVpnNai, R.string.other_networks_no_internet); - clearInvocations(mResources); - } - - @Test - public void testNotificationsShownAndCleared() { - final int NETWORK_ID_BASE = 100; - List types = Arrays.asList(NotificationType.values()); - List ids = new ArrayList<>(types.size()); - for (int i = 0; i < types.size(); i++) { - ids.add(NETWORK_ID_BASE + i); - } - Collections.shuffle(ids); - Collections.shuffle(types); - - for (int i = 0; i < ids.size(); i++) { - mManager.showNotification(ids.get(i), types.get(i), mWifiNai, mCellNai, null, false); - } - - List idsToClear = new ArrayList<>(ids); - Collections.shuffle(idsToClear); - for (int i = 0; i < ids.size(); i++) { - mManager.clearNotification(idsToClear.get(i)); - } - - for (int i = 0; i < ids.size(); i++) { - final int id = ids.get(i); - final int eventId = types.get(i).eventId; - final String tag = NetworkNotificationManager.tagFor(id); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(eventId)); - } - } - - @Test - @Ignore - // Ignored because the code under test calls Log.wtf, which crashes the tests on eng builds. - // TODO: re-enable after fixing this (e.g., turn Log.wtf into exceptions that this test catches) - public void testNoInternetNotificationsNotShownForCellular() { - mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false); - mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false); - - verify(mNotificationManager, never()).notify(any(), anyInt(), any()); - - mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false); - - final int eventId = NO_INTERNET.eventId; - final String tag = NetworkNotificationManager.tagFor(102); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(eventId), any()); - } - - @Test - public void testNotificationsNotShownIfNoInternetCapability() { - mWifiNai.networkCapabilities = new NetworkCapabilities(); - mWifiNai.networkCapabilities .addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false); - mManager.showNotification(103, LOST_INTERNET, mWifiNai, mCellNai, null, false); - mManager.showNotification(104, NETWORK_SWITCH, mWifiNai, mCellNai, null, false); - - verify(mNotificationManager, never()).notify(any(), anyInt(), any()); - } - - private void assertNotification(NotificationType type, boolean ongoing) { - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - final ArgumentCaptor noteCaptor = ArgumentCaptor.forClass(Notification.class); - mManager.showNotification(id, type, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(type.eventId), - noteCaptor.capture()); - - assertEquals("Notification ongoing flag should be " + (ongoing ? "set" : "unset"), - ongoing, (noteCaptor.getValue().flags & FLAG_ONGOING_EVENT) != 0); - } - - @Test - public void testDuplicatedNotificationsNoInternetThenSignIn() { - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - - // Show first NO_INTERNET - assertNotification(NO_INTERNET, false /* ongoing */); - - // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET - assertNotification(SIGN_IN, false /* ongoing */); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); - - // Network disconnects - mManager.clearNotification(id); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); - } - - @Test - public void testOngoingSignInNotification() { - doReturn(true).when(mResources).getBoolean(R.bool.config_ongoingSignInNotification); - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - - // Show first NO_INTERNET - assertNotification(NO_INTERNET, false /* ongoing */); - - // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET - assertNotification(SIGN_IN, true /* ongoing */); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); - - // Network disconnects - mManager.clearNotification(id); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); - } - - @Test - public void testDuplicatedNotificationsSignInThenNoInternet() { - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - - // Show first SIGN_IN - mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); - reset(mNotificationManager); - - // NO_INTERNET arrives after, but is ignored. - mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, never()).cancel(any(), anyInt()); - verify(mNotificationManager, never()).notify(any(), anyInt(), any()); - - // Network disconnects - mManager.clearNotification(id); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId)); - } - - @Test - public void testClearNotificationByType() { - final int id = 101; - final String tag = NetworkNotificationManager.tagFor(id); - - // clearNotification(int id, NotificationType notifyType) will check if given type is equal - // to previous type or not. If they are equal then clear the notification; if they are not - // equal then return. - mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(NO_INTERNET.eventId), any()); - - // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification - // should be cleared. - mManager.clearNotification(id, NO_INTERNET); - verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId)); - - // SIGN_IN is popped-up. - mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false); - verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any()); - - // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be - // cleared. - mManager.clearNotification(id, PARTIAL_CONNECTIVITY); - verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId)); - } -} diff --git a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt b/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt deleted file mode 100644 index 409f8c309935..000000000000 --- a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2021 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.connectivity - -import android.net.INetworkOfferCallback -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.net.NetworkScore.KEEP_CONNECTED_NONE -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mockito.mock -import org.mockito.Mockito.verify -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -const val POLICY_NONE = 0L - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NetworkOfferTest { - val mockCallback = mock(INetworkOfferCallback::class.java) - - @Test - fun testOfferNeededUnneeded() { - val score = FullScore(50, POLICY_NONE, KEEP_CONNECTED_NONE) - val offer = NetworkOffer(score, NetworkCapabilities.Builder().build(), mockCallback, - 1 /* providerId */) - val request1 = mock(NetworkRequest::class.java) - val request2 = mock(NetworkRequest::class.java) - offer.onNetworkNeeded(request1) - verify(mockCallback).onNetworkNeeded(eq(request1)) - assertTrue(offer.neededFor(request1)) - assertFalse(offer.neededFor(request2)) - - offer.onNetworkNeeded(request2) - verify(mockCallback).onNetworkNeeded(eq(request2)) - assertTrue(offer.neededFor(request1)) - assertTrue(offer.neededFor(request2)) - - // Note that the framework never calls onNetworkNeeded multiple times with the same - // request without calling onNetworkUnneeded first. It would be incorrect usage and the - // behavior would be undefined, so there is nothing to test. - - offer.onNetworkUnneeded(request1) - verify(mockCallback).onNetworkUnneeded(eq(request1)) - assertFalse(offer.neededFor(request1)) - assertTrue(offer.neededFor(request2)) - - offer.onNetworkUnneeded(request2) - verify(mockCallback).onNetworkUnneeded(eq(request2)) - assertFalse(offer.neededFor(request1)) - assertFalse(offer.neededFor(request2)) - } -} diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt deleted file mode 100644 index 551b94c7668e..000000000000 --- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.connectivity - -import android.net.NetworkCapabilities -import android.net.NetworkRequest -import android.net.NetworkScore.KEEP_CONNECTED_NONE -import androidx.test.filters.SmallTest -import androidx.test.runner.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import kotlin.test.assertEquals -import kotlin.test.assertNull - -@RunWith(AndroidJUnit4::class) -@SmallTest -class NetworkRankerTest { - private val ranker = NetworkRanker() - - private fun makeNai(satisfy: Boolean, legacyScore: Int) = - mock(NetworkAgentInfo::class.java).also { - doReturn(satisfy).`when`(it).satisfies(any()) - val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE) - doReturn(fs).`when`(it).getScore() - val nc = NetworkCapabilities.Builder().build() - doReturn(nc).`when`(it).getCapsNoCopy() - } - - @Test - fun testGetBestNetwork() { - val scores = listOf(20, 50, 90, 60, 23, 68) - val nais = scores.map { makeNai(true, it) } - val bestNetwork = nais[2] // The one with the top score - val someRequest = mock(NetworkRequest::class.java) - assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, bestNetwork)) - } - - @Test - fun testIgnoreNonSatisfying() { - val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90), - makeNai(false, 60), makeNai(true, 23), makeNai(false, 68)) - val bestNetwork = nais[1] // Top score that's satisfying - val someRequest = mock(NetworkRequest::class.java) - assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, nais[1])) - } - - @Test - fun testNoMatch() { - val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90)) - val someRequest = mock(NetworkRequest::class.java) - assertNull(ranker.getBestNetwork(someRequest, nais, null)) - } - - @Test - fun testEmpty() { - val someRequest = mock(NetworkRequest::class.java) - assertNull(ranker.getBestNetwork(someRequest, emptyList(), null)) - } - - // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST - // network satisfying the request if multiple of them have the same score. - @Test - fun testStable() { - val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30), - makeNai(true, 30), makeNai(true, 30), makeNai(true, 30)) - val someRequest = mock(NetworkRequest::class.java) - assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1, nais1[0])) - - val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20), - makeNai(true, 50), makeNai(true, 50), makeNai(true, 40)) - assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2, nais2[1])) - } -} diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java deleted file mode 100644 index 02a58080fefd..000000000000 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright (C) 2018 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.connectivity; - -import static android.Manifest.permission.CHANGE_NETWORK_STATE; -import static android.Manifest.permission.CHANGE_WIFI_STATE; -import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; -import static android.Manifest.permission.INTERNET; -import static android.Manifest.permission.NETWORK_STACK; -import static android.Manifest.permission.UPDATE_DEVICE_STATS; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; -import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; -import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED; -import static android.content.pm.PackageManager.GET_PERMISSIONS; -import static android.content.pm.PackageManager.MATCH_ANY_USER; -import static android.os.Process.SYSTEM_UID; - -import static com.android.server.connectivity.PermissionMonitor.NETWORK; -import static com.android.server.connectivity.PermissionMonitor.SYSTEM; - -import static junit.framework.Assert.fail; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.aryEq; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.net.INetd; -import android.net.UidRange; -import android.net.Uri; -import android.os.Build; -import android.os.SystemConfigManager; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.SparseIntArray; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.AdditionalAnswers; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class PermissionMonitorTest { - private static final UserHandle MOCK_USER1 = UserHandle.of(0); - private static final UserHandle MOCK_USER2 = UserHandle.of(1); - private static final int MOCK_UID1 = 10001; - private static final int MOCK_UID2 = 10086; - private static final int SYSTEM_UID1 = 1000; - private static final int SYSTEM_UID2 = 1008; - private static final int VPN_UID = 10002; - private static final String REAL_SYSTEM_PACKAGE_NAME = "android"; - private static final String MOCK_PACKAGE1 = "appName1"; - private static final String MOCK_PACKAGE2 = "appName2"; - private static final String SYSTEM_PACKAGE1 = "sysName1"; - private static final String SYSTEM_PACKAGE2 = "sysName2"; - private static final String PARTITION_SYSTEM = "system"; - private static final String PARTITION_OEM = "oem"; - private static final String PARTITION_PRODUCT = "product"; - private static final String PARTITION_VENDOR = "vendor"; - private static final int VERSION_P = Build.VERSION_CODES.P; - private static final int VERSION_Q = Build.VERSION_CODES.Q; - - @Mock private Context mContext; - @Mock private PackageManager mPackageManager; - @Mock private INetd mNetdService; - @Mock private UserManager mUserManager; - @Mock private PermissionMonitor.Dependencies mDeps; - @Mock private SystemConfigManager mSystemConfigManager; - - private PermissionMonitor mPermissionMonitor; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); - when(mUserManager.getUserHandles(eq(true))).thenReturn( - Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 })); - when(mContext.getSystemServiceName(SystemConfigManager.class)) - .thenReturn(Context.SYSTEM_CONFIG_SERVICE); - when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) - .thenReturn(mSystemConfigManager); - when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); - final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); - doReturn(UserHandle.ALL).when(asUserCtx).getUser(); - when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); - - mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); - - when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); - mPermissionMonitor.startMonitoring(); - } - - private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, - String... permissions) { - final PackageInfo packageInfo = - packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition); - packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; - packageInfo.applicationInfo.uid = uid; - return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo); - } - - private static PackageInfo systemPackageInfoWithPermissions(String... permissions) { - return packageInfoWithPermissions( - REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM); - } - - private static PackageInfo vendorPackageInfoWithPermissions(String... permissions) { - return packageInfoWithPermissions( - REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_VENDOR); - } - - private static PackageInfo packageInfoWithPermissions(int permissionsFlags, - String[] permissions, String partition) { - int[] requestedPermissionsFlags = new int[permissions.length]; - for (int i = 0; i < permissions.length; i++) { - requestedPermissionsFlags[i] = permissionsFlags; - } - final PackageInfo packageInfo = new PackageInfo(); - packageInfo.requestedPermissions = permissions; - packageInfo.applicationInfo = new ApplicationInfo(); - packageInfo.requestedPermissionsFlags = requestedPermissionsFlags; - int privateFlags = 0; - switch (partition) { - case PARTITION_OEM: - privateFlags = PRIVATE_FLAG_OEM; - break; - case PARTITION_PRODUCT: - privateFlags = PRIVATE_FLAG_PRODUCT; - break; - case PARTITION_VENDOR: - privateFlags = PRIVATE_FLAG_VENDOR; - break; - } - packageInfo.applicationInfo.privateFlags = privateFlags; - return packageInfo; - } - - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, - UserHandle user) { - final PackageInfo pkgInfo; - if (hasSystemPermission) { - pkgInfo = systemPackageInfoWithPermissions( - CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS); - } else { - pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, ""); - } - pkgInfo.applicationInfo.uid = user.getUid(UserHandle.getAppId(uid)); - return pkgInfo; - } - - @Test - public void testHasPermission() { - PackageInfo app = systemPackageInfoWithPermissions(); - assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); - - app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE, NETWORK_STACK); - assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); - - app = systemPackageInfoWithPermissions( - CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL); - assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); - assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); - - app = packageInfoWithPermissions(REQUESTED_PERMISSION_REQUIRED, new String[] { - CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL, NETWORK_STACK }, - PARTITION_SYSTEM); - assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL)); - - app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); - app.requestedPermissions = null; - assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - - app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); - app.requestedPermissionsFlags = null; - assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE)); - } - - @Test - public void testIsVendorApp() { - PackageInfo app = systemPackageInfoWithPermissions(); - assertFalse(mPermissionMonitor.isVendorApp(app.applicationInfo)); - app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, - new String[] {}, PARTITION_OEM); - assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); - app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, - new String[] {}, PARTITION_PRODUCT); - assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); - app = vendorPackageInfoWithPermissions(); - assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo)); - } - - @Test - public void testHasNetworkPermission() { - PackageInfo app = systemPackageInfoWithPermissions(); - assertFalse(mPermissionMonitor.hasNetworkPermission(app)); - app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE); - assertTrue(mPermissionMonitor.hasNetworkPermission(app)); - app = systemPackageInfoWithPermissions(NETWORK_STACK); - assertFalse(mPermissionMonitor.hasNetworkPermission(app)); - app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS); - assertFalse(mPermissionMonitor.hasNetworkPermission(app)); - app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL); - assertFalse(mPermissionMonitor.hasNetworkPermission(app)); - } - - @Test - public void testHasRestrictedNetworkPermission() { - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); - } - - @Test - public void testHasRestrictedNetworkPermissionSystemUid() { - doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt(); - assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - - doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt(); - assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - } - - @Test - public void testHasRestrictedNetworkPermissionVendorApp() { - assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertTrue(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - - assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL)); - assertFalse(hasRestrictedNetworkPermission( - PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE)); - } - - private void assertBackgroundPermission(boolean hasPermission, String name, int uid, - String... permissions) throws Exception { - when(mPackageManager.getPackageInfo(eq(name), anyInt())) - .thenReturn(packageInfoWithPermissions( - REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM)); - mPermissionMonitor.onPackageAdded(name, uid); - assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid)); - } - - @Test - public void testHasUseBackgroundNetworksPermission() throws Exception { - assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID)); - assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID); - assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL); - assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE); - assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK); - - assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1)); - assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1); - assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1, - CONNECTIVITY_USE_RESTRICTED_NETWORKS); - - assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2)); - assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2); - assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2, - CONNECTIVITY_INTERNAL); - assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK); - } - - private class NetdMonitor { - private final HashMap mApps = new HashMap<>(); - - NetdMonitor(INetd mockNetd) throws Exception { - // Add hook to verify and track result of setPermission. - doAnswer((InvocationOnMock invocation) -> { - final Object[] args = invocation.getArguments(); - final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM); - for (final int uid : (int[]) args[1]) { - // TODO: Currently, permission monitor will send duplicate commands for each uid - // corresponding to each user. Need to fix that and uncomment below test. - // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) { - // fail("uid " + uid + " is already set to " + isSystem); - // } - mApps.put(uid, isSystem); - } - return null; - }).when(mockNetd).networkSetPermissionForUser(anyInt(), any(int[].class)); - - // Add hook to verify and track result of clearPermission. - doAnswer((InvocationOnMock invocation) -> { - final Object[] args = invocation.getArguments(); - for (final int uid : (int[]) args[0]) { - // TODO: Currently, permission monitor will send duplicate commands for each uid - // corresponding to each user. Need to fix that and uncomment below test. - // if (!mApps.containsKey(uid)) { - // fail("uid " + uid + " does not exist."); - // } - mApps.remove(uid); - } - return null; - }).when(mockNetd).networkClearPermissionForUser(any(int[].class)); - } - - public void expectPermission(Boolean permission, UserHandle[] users, int[] apps) { - for (final UserHandle user : users) { - for (final int app : apps) { - final int uid = user.getUid(app); - if (!mApps.containsKey(uid)) { - fail("uid " + uid + " does not exist."); - } - if (mApps.get(uid) != permission) { - fail("uid " + uid + " has wrong permission: " + permission); - } - } - } - } - - public void expectNoPermission(UserHandle[] users, int[] apps) { - for (final UserHandle user : users) { - for (final int app : apps) { - final int uid = user.getUid(app); - if (mApps.containsKey(uid)) { - fail("uid " + uid + " has listed permissions, expected none."); - } - } - } - } - } - - @Test - public void testUserAndPackageAddRemove() throws Exception { - final NetdMonitor mNetdMonitor = new NetdMonitor(mNetdService); - - // MOCK_UID1: MOCK_PACKAGE1 only has network permission. - // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission. - // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission. - doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString()); - doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(), - eq(SYSTEM_PACKAGE1)); - doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), - eq(SYSTEM_PACKAGE2)); - doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), - eq(MOCK_PACKAGE1)); - - // Add SYSTEM_PACKAGE2, expect only have network permission. - mPermissionMonitor.onUserAdded(MOCK_USER1); - addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID); - mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID}); - - // Add SYSTEM_PACKAGE1, expect permission escalate. - addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID); - mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{SYSTEM_UID}); - - mPermissionMonitor.onUserAdded(MOCK_USER2); - mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{SYSTEM_UID}); - - addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); - mNetdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{SYSTEM_UID}); - mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{MOCK_UID1}); - - // Remove MOCK_UID1, expect no permission left for all user. - mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); - mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{MOCK_UID1}); - - // Remove SYSTEM_PACKAGE1, expect permission downgrade. - when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2}); - removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, - SYSTEM_PACKAGE1, SYSTEM_UID); - mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{SYSTEM_UID}); - - mPermissionMonitor.onUserRemoved(MOCK_USER1); - mNetdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER2}, new int[]{SYSTEM_UID}); - - // Remove all packages, expect no permission left. - when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{}); - removePackageForUsers(new UserHandle[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID); - mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{SYSTEM_UID, MOCK_UID1}); - - // Remove last user, expect no redundant clearPermission is invoked. - mPermissionMonitor.onUserRemoved(MOCK_USER2); - mNetdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2}, - new int[]{SYSTEM_UID, MOCK_UID1}); - } - - @Test - public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception { - when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - Arrays.asList(new PackageInfo[] { - buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) - })); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), - eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); - mPermissionMonitor.startMonitoring(); - // Every app on user 0 except MOCK_UID2 are under VPN. - final Set vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] { - new UidRange(0, MOCK_UID2 - 1), - new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1)})); - final Set vpnRange2 = Collections.singleton(new UidRange(MOCK_UID2, MOCK_UID2)); - - // When VPN is connected, expect a rule to be set up for user app MOCK_UID1 - mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID); - verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), - aryEq(new int[] {MOCK_UID1})); - - reset(mNetdService); - - // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated - mPermissionMonitor.onPackageRemoved( - MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); - verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); - mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); - verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), - aryEq(new int[] {MOCK_UID1})); - - reset(mNetdService); - - // During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the - // old UID rules then adds the new ones. Expect netd to be updated - mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID); - verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); - mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID); - verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), - aryEq(new int[] {MOCK_UID2})); - - reset(mNetdService); - - // When VPN is disconnected, expect rules to be torn down - mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID); - verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2})); - assertNull(mPermissionMonitor.getVpnUidRanges("tun0")); - } - - @Test - public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception { - when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - Arrays.asList(new PackageInfo[] { - buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1), - buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1) - })); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), - eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( - buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1)); - - mPermissionMonitor.startMonitoring(); - final Set vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1)); - mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID); - - // Newly-installed package should have uid rules added - mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); - verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), - aryEq(new int[] {MOCK_UID1})); - - // Removed package should have its uid rules removed - mPermissionMonitor.onPackageRemoved( - MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1)); - verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1})); - } - - - // Normal package add/remove operations will trigger multiple intent for uids corresponding to - // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be - // called multiple times with the uid corresponding to each user. - private void addPackageForUsers(UserHandle[] users, String packageName, int uid) { - for (final UserHandle user : users) { - mPermissionMonitor.onPackageAdded(packageName, user.getUid(uid)); - } - } - - private void removePackageForUsers(UserHandle[] users, String packageName, int uid) { - for (final UserHandle user : users) { - mPermissionMonitor.onPackageRemoved(packageName, user.getUid(uid)); - } - } - - private class NetdServiceMonitor { - private final HashMap mPermissions = new HashMap<>(); - - NetdServiceMonitor(INetd mockNetdService) throws Exception { - // Add hook to verify and track result of setPermission. - doAnswer((InvocationOnMock invocation) -> { - final Object[] args = invocation.getArguments(); - final int permission = (int) args[0]; - for (final int uid : (int[]) args[1]) { - mPermissions.put(uid, permission); - } - return null; - }).when(mockNetdService).trafficSetNetPermForUids(anyInt(), any(int[].class)); - } - - public void expectPermission(int permission, int[] apps) { - for (final int app : apps) { - if (!mPermissions.containsKey(app)) { - fail("uid " + app + " does not exist."); - } - if (mPermissions.get(app) != permission) { - fail("uid " + app + " has wrong permission: " + mPermissions.get(app)); - } - } - } - } - - @Test - public void testPackagePermissionUpdate() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - // MOCK_UID1: MOCK_PACKAGE1 only has internet permission. - // MOCK_UID2: MOCK_PACKAGE2 does not have any permission. - // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission. - // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission. - - SparseIntArray netdPermissionsAppIds = new SparseIntArray(); - netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET); - netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE); - netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS); - netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS); - - // Send the permission information to netd, expect permission updated. - mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds); - - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, - new int[]{MOCK_UID1}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS, - new int[]{SYSTEM_UID2}); - - // Update permission of MOCK_UID1, expect new permission show up. - mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, - INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - // Change permissions of SYSTEM_UID2, expect new permission show up and old permission - // revoked. - mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, - INetd.PERMISSION_INTERNET); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2}); - - // Revoke permission from SYSTEM_UID1, expect no permission stored. - mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1}); - } - - private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions) - throws Exception { - PackageInfo packageInfo = packageInfoWithPermissions( - REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM); - when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo); - when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName}); - return packageInfo; - } - - private PackageInfo addPackage(String packageName, int uid, String[] permissions) - throws Exception { - PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions); - mPermissionMonitor.onPackageAdded(packageName, uid); - return packageInfo; - } - - @Test - public void testPackageInstall() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2}); - } - - @Test - public void testPackageInstallSharedUid() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1, - new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - // Install another package with the same uid and no permissions should not cause the UID to - // lose permissions. - PackageInfo packageInfo2 = systemPackageInfoWithPermissions(); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); - when(mPackageManager.getPackagesForUid(MOCK_UID1)) - .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2}); - mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - } - - @Test - public void testPackageUninstallBasic() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); - mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); - } - - @Test - public void testPackageRemoveThenAdd() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{}); - mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1}); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); - } - - @Test - public void testPackageUpdate() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1}); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); - } - - @Test - public void testPackageUninstallWithMultiplePackages() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - - addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS}); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1}); - - // Mock another package with the same uid but different permissions. - PackageInfo packageInfo2 = systemPackageInfoWithPermissions(INTERNET); - when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2); - when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{ - MOCK_PACKAGE2}); - - mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); - } - - @Test - public void testRealSystemPermission() throws Exception { - // Use the real context as this test must ensure the *real* system package holds the - // necessary permission. - final Context realContext = InstrumentationRegistry.getContext(); - final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService); - final PackageManager manager = realContext.getPackageManager(); - final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME, - GET_PERMISSIONS | MATCH_ANY_USER); - assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - } - - @Test - public void testUpdateUidPermissionsFromSystemConfig() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>()); - when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET))) - .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 }); - when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS))) - .thenReturn(new int[]{ MOCK_UID2 }); - - mPermissionMonitor.startMonitoring(); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{ MOCK_UID1 }); - mNetdServiceMonitor.expectPermission( - INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, - new int[]{ MOCK_UID2 }); - } - - @Test - public void testIntentReceiver() throws Exception { - final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); - final ArgumentCaptor receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any()); - final BroadcastReceiver receiver = receiverCaptor.getValue(); - - // Verify receiving PACKAGE_ADDED intent. - final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED, - Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); - addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); - setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, - new String[] { INTERNET, UPDATE_DEVICE_STATS }); - receiver.onReceive(mContext, addedIntent); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET - | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 }); - - // Verify receiving PACKAGE_REMOVED intent. - when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null); - final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED, - Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); - removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); - receiver.onReceive(mContext, removedIntent); - mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 }); - } - -} diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java deleted file mode 100644 index b725b826b14f..000000000000 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ /dev/null @@ -1,1283 +0,0 @@ -/* - * Copyright (C) 2016 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.connectivity; - -import static android.content.pm.UserInfo.FLAG_ADMIN; -import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; -import static android.content.pm.UserInfo.FLAG_PRIMARY; -import static android.content.pm.UserInfo.FLAG_RESTRICTED; -import static android.net.ConnectivityManager.NetworkCallback; -import static android.net.INetd.IF_STATE_DOWN; -import static android.net.INetd.IF_STATE_UP; -import static android.os.UserHandle.PER_USER_RANGE; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.UserIdInt; -import android.app.AppOpsManager; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.INetd; -import android.net.Ikev2VpnProfile; -import android.net.InetAddresses; -import android.net.InterfaceConfigurationParcel; -import android.net.IpPrefix; -import android.net.IpSecManager; -import android.net.IpSecTunnelInterfaceResponse; -import android.net.LinkProperties; -import android.net.LocalSocket; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo.DetailedState; -import android.net.RouteInfo; -import android.net.UidRangeParcel; -import android.net.VpnManager; -import android.net.VpnService; -import android.net.VpnTransportInfo; -import android.net.ipsec.ike.IkeSessionCallback; -import android.net.ipsec.ike.exceptions.IkeProtocolException; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.INetworkManagementService; -import android.os.Process; -import android.os.UserHandle; -import android.os.UserManager; -import android.os.test.TestLooper; -import android.provider.Settings; -import android.security.Credentials; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Range; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.R; -import com.android.internal.net.LegacyVpnInfo; -import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnProfile; -import com.android.server.IpSecService; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.AdditionalAnswers; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; - -/** - * Tests for {@link Vpn}. - * - * Build, install and run with: - * runtest frameworks-net -c com.android.server.connectivity.VpnTest - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class VpnTest { - private static final String TAG = "VpnTest"; - - // Mock users - static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY); - static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN); - static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED); - static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED); - static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE); - static { - restrictedProfileA.restrictedProfileParentId = primaryUser.id; - restrictedProfileB.restrictedProfileParentId = secondaryUser.id; - managedProfileA.profileGroupId = primaryUser.id; - } - - static final Network EGRESS_NETWORK = new Network(101); - static final String EGRESS_IFACE = "wlan0"; - static final String TEST_VPN_PKG = "com.testvpn.vpn"; - private static final String TEST_VPN_SERVER = "1.2.3.4"; - private static final String TEST_VPN_IDENTITY = "identity"; - private static final byte[] TEST_VPN_PSK = "psk".getBytes(); - - private static final Network TEST_NETWORK = new Network(Integer.MAX_VALUE); - private static final String TEST_IFACE_NAME = "TEST_IFACE"; - private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345; - private static final long TEST_TIMEOUT_MS = 500L; - - /** - * Names and UIDs for some fake packages. Important points: - * - UID is ordered increasing. - * - One pair of packages have consecutive UIDs. - */ - static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"}; - static final int[] PKG_UIDS = {66, 77, 78, 400}; - - // Mock packages - static final Map mPackages = new ArrayMap<>(); - static { - for (int i = 0; i < PKGS.length; i++) { - mPackages.put(PKGS[i], PKG_UIDS[i]); - } - } - private static final Range PRI_USER_RANGE = uidRangeForUser(primaryUser.id); - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; - @Mock private UserManager mUserManager; - @Mock private PackageManager mPackageManager; - @Mock private INetworkManagementService mNetService; - @Mock private INetd mNetd; - @Mock private AppOpsManager mAppOps; - @Mock private NotificationManager mNotificationManager; - @Mock private Vpn.SystemServices mSystemServices; - @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator; - @Mock private ConnectivityManager mConnectivityManager; - @Mock private IpSecService mIpSecService; - @Mock private VpnProfileStore mVpnProfileStore; - private final VpnProfile mVpnProfile; - - private IpSecManager mIpSecManager; - - public VpnTest() throws Exception { - // Build an actual VPN profile that is capable of being converted to and from an - // Ikev2VpnProfile - final Ikev2VpnProfile.Builder builder = - new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY); - builder.setAuthPsk(TEST_VPN_PSK); - mVpnProfile = builder.build().toVpnProfile(); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mIpSecManager = new IpSecManager(mContext, mIpSecService); - - when(mContext.getPackageManager()).thenReturn(mPackageManager); - setMockedPackages(mPackages); - - when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG); - when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG); - when(mContext.getSystemServiceName(UserManager.class)) - .thenReturn(Context.USER_SERVICE); - when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); - when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps); - when(mContext.getSystemServiceName(NotificationManager.class)) - .thenReturn(Context.NOTIFICATION_SERVICE); - when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) - .thenReturn(mNotificationManager); - when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) - .thenReturn(mConnectivityManager); - when(mContext.getSystemServiceName(eq(ConnectivityManager.class))) - .thenReturn(Context.CONNECTIVITY_SERVICE); - when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager); - when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) - .thenReturn(Resources.getSystem().getString( - R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)); - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) - .thenReturn(true); - - // Used by {@link Notification.Builder} - ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; - when(mContext.getApplicationInfo()).thenReturn(applicationInfo); - when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) - .thenReturn(applicationInfo); - - doNothing().when(mNetService).registerObserver(any()); - - // Deny all appops by default. - when(mAppOps.noteOpNoThrow(anyString(), anyInt(), anyString(), any(), any())) - .thenReturn(AppOpsManager.MODE_IGNORED); - - // Setup IpSecService - final IpSecTunnelInterfaceResponse tunnelResp = - new IpSecTunnelInterfaceResponse( - IpSecManager.Status.OK, TEST_TUNNEL_RESOURCE_ID, TEST_IFACE_NAME); - when(mIpSecService.createTunnelInterface(any(), any(), any(), any(), any())) - .thenReturn(tunnelResp); - } - - private Set> rangeSet(Range ... ranges) { - final Set> range = new ArraySet<>(); - for (Range r : ranges) range.add(r); - - return range; - } - - private static Range uidRangeForUser(int userId) { - return new Range(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1); - } - - private Range uidRange(int start, int stop) { - return new Range(start, stop); - } - - @Test - public void testRestrictedProfilesAreAddedToVpn() { - setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB); - - final Vpn vpn = createVpn(primaryUser.id); - - // Assume the user can have restricted profiles. - doReturn(true).when(mUserManager).canHaveRestrictedProfile(); - final Set> ranges = - vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); - - assertEquals(rangeSet(PRI_USER_RANGE, uidRangeForUser(restrictedProfileA.id)), ranges); - } - - @Test - public void testManagedProfilesAreNotAddedToVpn() { - setMockedUsers(primaryUser, managedProfileA); - - final Vpn vpn = createVpn(primaryUser.id); - final Set> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, - null, null); - - assertEquals(rangeSet(PRI_USER_RANGE), ranges); - } - - @Test - public void testAddUserToVpnOnlyAddsOneUser() { - setMockedUsers(primaryUser, restrictedProfileA, managedProfileA); - - final Vpn vpn = createVpn(primaryUser.id); - final Set> ranges = new ArraySet<>(); - vpn.addUserToRanges(ranges, primaryUser.id, null, null); - - assertEquals(rangeSet(PRI_USER_RANGE), ranges); - } - - @Test - public void testUidAllowAndDenylist() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; - final int userStart = user.getLower(); - final int userStop = user.getUpper(); - final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; - - // Allowed list - final Set> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, - Arrays.asList(packages), null /* disallowedApplications */); - assertEquals(rangeSet( - uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]), - uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2])), - allow); - - // Denied list - final Set> disallow = - vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, - null /* allowedApplications */, Arrays.asList(packages)); - assertEquals(rangeSet( - uidRange(userStart, userStart + PKG_UIDS[0] - 1), - uidRange(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), - /* Empty range between UIDS[1] and UIDS[2], should be excluded, */ - uidRange(userStart + PKG_UIDS[2] + 1, userStop)), - disallow); - } - - @Test - public void testGetAlwaysAndOnGetLockDown() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - - // Default state. - assertFalse(vpn.getAlwaysOn()); - assertFalse(vpn.getLockdown()); - - // Set always-on without lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList())); - assertTrue(vpn.getAlwaysOn()); - assertFalse(vpn.getLockdown()); - - // Set always-on with lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList())); - assertTrue(vpn.getAlwaysOn()); - assertTrue(vpn.getLockdown()); - - // Remove always-on configuration. - assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList())); - assertFalse(vpn.getAlwaysOn()); - assertFalse(vpn.getLockdown()); - } - - @Test - public void testLockdownChangingPackage() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; - final int userStart = user.getLower(); - final int userStop = user.getUpper(); - // Set always-on without lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null)); - - // Set always-on with lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null)); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) - })); - - // Switch to another app. - assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null)); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[3] - 1), - new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) - })); - } - - @Test - public void testLockdownAllowlist() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final Range user = PRI_USER_RANGE; - final int userStart = user.getLower(); - final int userStop = user.getUpper(); - // Set always-on with lockdown and allow app PKGS[2] from lockdown. - assertTrue(vpn.setAlwaysOnPackage( - PKGS[1], true, Collections.singletonList(PKGS[2]))); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) - })); - // Change allowed app list to PKGS[3]. - assertTrue(vpn.setAlwaysOnPackage( - PKGS[1], true, Collections.singletonList(PKGS[3]))); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1), - new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) - })); - - // Change the VPN app. - assertTrue(vpn.setAlwaysOnPackage( - PKGS[0], true, Collections.singletonList(PKGS[3]))); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart, userStart + PKG_UIDS[0] - 1), - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1) - })); - - // Remove the list of allowed packages. - assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null)); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1), - new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop), - })); - - // Add the list of allowed packages. - assertTrue(vpn.setAlwaysOnPackage( - PKGS[0], true, Collections.singletonList(PKGS[1]))); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) - })); - - // Try allowing a package with a comma, should be rejected. - assertFalse(vpn.setAlwaysOnPackage( - PKGS[0], true, Collections.singletonList("a.b,c.d"))); - - // Pass a non-existent packages in the allowlist, they (and only they) should be ignored. - // allowed package should change from PGKS[1] to PKGS[2]. - assertTrue(vpn.setAlwaysOnPackage( - PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"))); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1), - new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop) - })); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[2] - 1), - new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop) - })); - } - - @Test - public void testLockdownRuleRepeatability() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { - new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper())}; - // Given legacy lockdown is already enabled, - vpn.setLockdown(true); - verify(mConnectivityManager, times(1)).setRequireVpnForUids(true, - toRanges(primaryUserRangeParcel)); - - // Enabling legacy lockdown twice should do nothing. - vpn.setLockdown(true); - verify(mConnectivityManager, times(1)).setRequireVpnForUids(anyBoolean(), any()); - - // And disabling should remove the rules exactly once. - vpn.setLockdown(false); - verify(mConnectivityManager, times(1)).setRequireVpnForUids(false, - toRanges(primaryUserRangeParcel)); - - // Removing the lockdown again should have no effect. - vpn.setLockdown(false); - verify(mConnectivityManager, times(2)).setRequireVpnForUids(anyBoolean(), any()); - } - - private ArrayList> toRanges(UidRangeParcel[] ranges) { - ArrayList> rangesArray = new ArrayList<>(ranges.length); - for (int i = 0; i < ranges.length; i++) { - rangesArray.add(new Range<>(ranges[i].start, ranges[i].stop)); - } - return rangesArray; - } - - @Test - public void testLockdownRuleReversibility() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - final UidRangeParcel[] entireUser = { - new UidRangeParcel(PRI_USER_RANGE.getLower(), PRI_USER_RANGE.getUpper()) - }; - final UidRangeParcel[] exceptPkg0 = { - new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1), - new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop) - }; - - final InOrder order = inOrder(mConnectivityManager); - - // Given lockdown is enabled with no package (legacy VPN), - vpn.setLockdown(true); - order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser)); - - // When a new VPN package is set the rules should change to cover that package. - vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE); - order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(entireUser)); - order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(exceptPkg0)); - - // When that VPN package is unset, everything should be undone again in reverse. - vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE); - order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(exceptPkg0)); - order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser)); - } - - @Test - public void testIsAlwaysOnPackageSupported() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - - ApplicationInfo appInfo = new ApplicationInfo(); - when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id))) - .thenReturn(appInfo); - - ServiceInfo svcInfo = new ServiceInfo(); - ResolveInfo resInfo = new ResolveInfo(); - resInfo.serviceInfo = svcInfo; - when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA), - eq(primaryUser.id))) - .thenReturn(Collections.singletonList(resInfo)); - - // null package name should return false - assertFalse(vpn.isAlwaysOnPackageSupported(null)); - - // Pre-N apps are not supported - appInfo.targetSdkVersion = VERSION_CODES.M; - assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); - - // N+ apps are supported by default - appInfo.targetSdkVersion = VERSION_CODES.N; - assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0])); - - // Apps that opt out explicitly are not supported - appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT; - Bundle metaData = new Bundle(); - metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false); - svcInfo.metaData = metaData; - assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0])); - } - - @Test - public void testNotificationShownForAlwaysOnApp() throws Exception { - final UserHandle userHandle = UserHandle.of(primaryUser.id); - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - final InOrder order = inOrder(mNotificationManager); - - // Don't show a notification for regular disconnected states. - vpn.updateState(DetailedState.DISCONNECTED, TAG); - order.verify(mNotificationManager, atLeastOnce()).cancel(anyString(), anyInt()); - - // Start showing a notification for disconnected once always-on. - vpn.setAlwaysOnPackage(PKGS[0], false, null); - order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); - - // Stop showing the notification once connected. - vpn.updateState(DetailedState.CONNECTED, TAG); - order.verify(mNotificationManager).cancel(anyString(), anyInt()); - - // Show the notification if we disconnect again. - vpn.updateState(DetailedState.DISCONNECTED, TAG); - order.verify(mNotificationManager).notify(anyString(), anyInt(), any()); - - // Notification should be cleared after unsetting always-on package. - vpn.setAlwaysOnPackage(null, false, null); - order.verify(mNotificationManager).cancel(anyString(), anyInt()); - } - - /** - * The profile name should NOT change between releases for backwards compatibility - * - *

If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST - * be updated to ensure backward compatibility. - */ - @Test - public void testGetProfileNameForPackage() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG; - assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG)); - } - - private Vpn createVpnAndSetupUidChecks(String... grantedOps) throws Exception { - return createVpnAndSetupUidChecks(primaryUser, grantedOps); - } - - private Vpn createVpnAndSetupUidChecks(UserInfo user, String... grantedOps) throws Exception { - final Vpn vpn = createVpn(user.id); - setMockedUsers(user); - - when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt())) - .thenReturn(Process.myUid()); - - for (final String opStr : grantedOps) { - when(mAppOps.noteOpNoThrow(opStr, Process.myUid(), TEST_VPN_PKG, - null /* attributionTag */, null /* message */)) - .thenReturn(AppOpsManager.MODE_ALLOWED); - } - - return vpn; - } - - private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, String... checkedOps) { - assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile)); - - // The profile should always be stored, whether or not consent has been previously granted. - verify(mVpnProfileStore) - .put( - eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), - eq(mVpnProfile.encode())); - - for (final String checkedOpStr : checkedOps) { - verify(mAppOps).noteOpNoThrow(checkedOpStr, Process.myUid(), TEST_VPN_PKG, - null /* attributionTag */, null /* message */); - } - } - - @Test - public void testProvisionVpnProfileNoIpsecTunnels() throws Exception { - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)) - .thenReturn(false); - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - try { - checkProvisionVpnProfile( - vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - fail("Expected exception due to missing feature"); - } catch (UnsupportedOperationException expected) { - } - } - - @Test - public void testProvisionVpnProfilePreconsented() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - checkProvisionVpnProfile( - vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - } - - @Test - public void testProvisionVpnProfileNotPreconsented() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller - // had neither. - checkProvisionVpnProfile(vpn, false /* expectedResult */, - AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN, AppOpsManager.OPSTR_ACTIVATE_VPN); - } - - @Test - public void testProvisionVpnProfileVpnServicePreconsented() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN); - - checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OPSTR_ACTIVATE_VPN); - } - - @Test - public void testProvisionVpnProfileTooLarge() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - final VpnProfile bigProfile = new VpnProfile(""); - bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]); - - try { - vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile); - fail("Expected IAE due to profile size"); - } catch (IllegalArgumentException expected) { - } - } - - @Test - public void testProvisionVpnProfileRestrictedUser() throws Exception { - final Vpn vpn = - createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - try { - vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile); - fail("Expected SecurityException due to restricted user"); - } catch (SecurityException expected) { - } - } - - @Test - public void testDeleteVpnProfile() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - vpn.deleteVpnProfile(TEST_VPN_PKG); - - verify(mVpnProfileStore) - .remove(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - } - - @Test - public void testDeleteVpnProfileRestrictedUser() throws Exception { - final Vpn vpn = - createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - try { - vpn.deleteVpnProfile(TEST_VPN_PKG); - fail("Expected SecurityException due to restricted user"); - } catch (SecurityException expected) { - } - } - - @Test - public void testGetVpnProfilePrivileged() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) - .thenReturn(new VpnProfile("").encode()); - - vpn.getVpnProfilePrivileged(TEST_VPN_PKG); - - verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - } - - @Test - public void testStartVpnProfile() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) - .thenReturn(mVpnProfile.encode()); - - vpn.startVpnProfile(TEST_VPN_PKG); - - verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - verify(mAppOps) - .noteOpNoThrow( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); - } - - @Test - public void testStartVpnProfileVpnServicePreconsented() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN); - - when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) - .thenReturn(mVpnProfile.encode()); - - vpn.startVpnProfile(TEST_VPN_PKG); - - // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown. - verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(), - TEST_VPN_PKG, null /* attributionTag */, null /* message */); - } - - @Test - public void testStartVpnProfileNotConsented() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - try { - vpn.startVpnProfile(TEST_VPN_PKG); - fail("Expected failure due to no user consent"); - } catch (SecurityException expected) { - } - - // Verify both appops were checked. - verify(mAppOps) - .noteOpNoThrow( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); - verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(), - TEST_VPN_PKG, null /* attributionTag */, null /* message */); - - // Keystore should never have been accessed. - verify(mVpnProfileStore, never()).get(any()); - } - - @Test - public void testStartVpnProfileMissingProfile() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null); - - try { - vpn.startVpnProfile(TEST_VPN_PKG); - fail("Expected failure due to missing profile"); - } catch (IllegalArgumentException expected) { - } - - verify(mVpnProfileStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG)); - verify(mAppOps) - .noteOpNoThrow( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(null) /* attributionTag */, - eq(null) /* message */); - } - - @Test - public void testStartVpnProfileRestrictedUser() throws Exception { - final Vpn vpn = - createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - try { - vpn.startVpnProfile(TEST_VPN_PKG); - fail("Expected SecurityException due to restricted user"); - } catch (SecurityException expected) { - } - } - - @Test - public void testStopVpnProfileRestrictedUser() throws Exception { - final Vpn vpn = - createVpnAndSetupUidChecks( - restrictedProfileA, AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN); - - try { - vpn.stopVpnProfile(TEST_VPN_PKG); - fail("Expected SecurityException due to restricted user"); - } catch (SecurityException expected) { - } - } - - @Test - public void testSetPackageAuthorizationVpnService() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE)); - verify(mAppOps) - .setMode( - eq(AppOpsManager.OPSTR_ACTIVATE_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(AppOpsManager.MODE_ALLOWED)); - } - - @Test - public void testSetPackageAuthorizationPlatformVpn() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM)); - verify(mAppOps) - .setMode( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(AppOpsManager.MODE_ALLOWED)); - } - - @Test - public void testSetPackageAuthorizationRevokeAuthorization() throws Exception { - final Vpn vpn = createVpnAndSetupUidChecks(); - - assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE)); - verify(mAppOps) - .setMode( - eq(AppOpsManager.OPSTR_ACTIVATE_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(AppOpsManager.MODE_IGNORED)); - verify(mAppOps) - .setMode( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), - eq(Process.myUid()), - eq(TEST_VPN_PKG), - eq(AppOpsManager.MODE_IGNORED)); - } - - private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception { - final ArgumentCaptor networkCallbackCaptor = - ArgumentCaptor.forClass(NetworkCallback.class); - verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)) - .requestNetwork(any(), networkCallbackCaptor.capture()); - - // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be - // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException. - final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); - config.flags = new String[] {IF_STATE_DOWN}; - when(mNetd.interfaceGetCfg(anyString())).thenReturn(config); - final NetworkCallback cb = networkCallbackCaptor.getValue(); - cb.onAvailable(TEST_NETWORK); - return cb; - } - - private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception { - // Add a timeout for waiting for interfaceSetCfg to be called. - verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat( - config -> Arrays.asList(config.flags).contains(flag))); - } - - @Test - public void testStartPlatformVpnAuthenticationFailed() throws Exception { - final ArgumentCaptor captor = - ArgumentCaptor.forClass(IkeSessionCallback.class); - final IkeProtocolException exception = mock(IkeProtocolException.class); - when(exception.getErrorType()) - .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); - - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); - final NetworkCallback cb = triggerOnAvailableAndGetCallback(); - - verifyInterfaceSetCfgWithFlags(IF_STATE_UP); - - // Wait for createIkeSession() to be called before proceeding in order to ensure consistent - // state - verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)) - .createIkeSession(any(), any(), any(), any(), captor.capture(), any()); - final IkeSessionCallback ikeCb = captor.getValue(); - ikeCb.onClosedExceptionally(exception); - - verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); - } - - @Test - public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { - when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) - .thenThrow(new IllegalArgumentException()); - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); - final NetworkCallback cb = triggerOnAvailableAndGetCallback(); - - verifyInterfaceSetCfgWithFlags(IF_STATE_UP); - - // Wait for createIkeSession() to be called before proceeding in order to ensure consistent - // state - verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); - } - - private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { - assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null)); - - verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG))); - verify(mAppOps).setMode( - eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG), - eq(AppOpsManager.MODE_ALLOWED)); - - verify(mSystemServices).settingsSecurePutStringForUser( - eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id)); - verify(mSystemServices).settingsSecurePutIntForUser( - eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0), - eq(primaryUser.id)); - verify(mSystemServices).settingsSecurePutStringForUser( - eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id)); - } - - @Test - public void testSetAndStartAlwaysOnVpn() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - // UID checks must return a different UID; otherwise it'll be treated as already prepared. - final int uid = Process.myUid() + 1; - when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt())) - .thenReturn(uid); - when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))) - .thenReturn(mVpnProfile.encode()); - - setAndVerifyAlwaysOnPackage(vpn, uid, false); - assertTrue(vpn.startAlwaysOnVpn()); - - // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in - // a subsequent CL. - } - - private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { - setMockedUsers(primaryUser); - - // Dummy egress interface - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(EGRESS_IFACE); - - final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE); - lp.addRoute(defaultRoute); - - vpn.startLegacyVpn(vpnProfile, EGRESS_NETWORK, lp); - return vpn; - } - - @Test - public void testStartPlatformVpn() throws Exception { - startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); - // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in - // a subsequent patch. - } - - @Test - public void testStartRacoonNumericAddress() throws Exception { - startRacoon("1.2.3.4", "1.2.3.4"); - } - - @Test - public void testStartRacoonHostname() throws Exception { - startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve - } - - private void assertTransportInfoMatches(NetworkCapabilities nc, int type) { - assertNotNull(nc); - VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo(); - assertNotNull(ti); - assertEquals(type, ti.getType()); - } - - public void startRacoon(final String serverAddr, final String expectedAddr) - throws Exception { - final ConditionVariable legacyRunnerReady = new ConditionVariable(); - final VpnProfile profile = new VpnProfile("testProfile" /* key */); - profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK; - profile.name = "testProfileName"; - profile.username = "userName"; - profile.password = "thePassword"; - profile.server = serverAddr; - profile.ipsecIdentifier = "id"; - profile.ipsecSecret = "secret"; - profile.l2tpSecret = "l2tpsecret"; - - when(mConnectivityManager.getAllNetworks()) - .thenReturn(new Network[] { new Network(101) }); - - when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), - any(), any(), anyInt())).thenAnswer(invocation -> { - // The runner has registered an agent and is now ready. - legacyRunnerReady.open(); - return new Network(102); - }); - final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); - final TestDeps deps = (TestDeps) vpn.mDeps; - try { - // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK - assertArrayEquals( - new String[] { EGRESS_IFACE, expectedAddr, "udppsk", - profile.ipsecIdentifier, profile.ipsecSecret, "1701" }, - deps.racoonArgs.get(10, TimeUnit.SECONDS)); - // literal values are hardcoded in Vpn.java for mtpd args - assertArrayEquals( - new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret, - "name", profile.username, "password", profile.password, - "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", - "idle", "1800", "mtu", "1270", "mru", "1270" }, - deps.mtpdArgs.get(10, TimeUnit.SECONDS)); - - // Now wait for the runner to be ready before testing for the route. - ArgumentCaptor lpCaptor = ArgumentCaptor.forClass(LinkProperties.class); - ArgumentCaptor ncCaptor = - ArgumentCaptor.forClass(NetworkCapabilities.class); - verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt()); - - // In this test the expected address is always v4 so /32. - // Note that the interface needs to be specified because RouteInfo objects stored in - // LinkProperties objects always acquire the LinkProperties' interface. - final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), - null, EGRESS_IFACE, RouteInfo.RTN_THROW); - final List actualRoutes = lpCaptor.getValue().getRoutes(); - assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes, - actualRoutes.contains(expectedRoute)); - - assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY); - } finally { - // Now interrupt the thread, unblock the runner and clean up. - vpn.mVpnRunner.exitVpnRunner(); - deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier - vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup - } - } - - private static final class TestDeps extends Vpn.Dependencies { - public final CompletableFuture racoonArgs = new CompletableFuture(); - public final CompletableFuture mtpdArgs = new CompletableFuture(); - public final File mStateFile; - - private final HashMap mRunningServices = new HashMap<>(); - - TestDeps() { - try { - mStateFile = File.createTempFile("vpnTest", ".tmp"); - mStateFile.deleteOnExit(); - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean isCallerSystem() { - return true; - } - - @Override - public void startService(final String serviceName) { - mRunningServices.put(serviceName, true); - } - - @Override - public void stopService(final String serviceName) { - mRunningServices.put(serviceName, false); - } - - @Override - public boolean isServiceRunning(final String serviceName) { - return mRunningServices.getOrDefault(serviceName, false); - } - - @Override - public boolean isServiceStopped(final String serviceName) { - return !isServiceRunning(serviceName); - } - - @Override - public File getStateFile() { - return mStateFile; - } - - @Override - public PendingIntent getIntentForStatusPanel(Context context) { - return null; - } - - @Override - public void sendArgumentsToDaemon( - final String daemon, final LocalSocket socket, final String[] arguments, - final Vpn.RetryScheduler interruptChecker) throws IOException { - if ("racoon".equals(daemon)) { - racoonArgs.complete(arguments); - } else if ("mtpd".equals(daemon)) { - writeStateFile(arguments); - mtpdArgs.complete(arguments); - } else { - throw new UnsupportedOperationException("Unsupported daemon : " + daemon); - } - } - - private void writeStateFile(final String[] arguments) throws IOException { - mStateFile.delete(); - mStateFile.createNewFile(); - mStateFile.deleteOnExit(); - final BufferedWriter writer = new BufferedWriter( - new FileWriter(mStateFile, false /* append */)); - writer.write(EGRESS_IFACE); - writer.write("\n"); - // addresses - writer.write("10.0.0.1/24\n"); - // routes - writer.write("192.168.6.0/24\n"); - // dns servers - writer.write("192.168.6.1\n"); - // search domains - writer.write("vpn.searchdomains.com\n"); - // endpoint - intentionally empty - writer.write("\n"); - writer.flush(); - writer.close(); - } - - @Override - @NonNull - public InetAddress resolve(final String endpoint) { - try { - // If a numeric IP address, return it. - return InetAddress.parseNumericAddress(endpoint); - } catch (IllegalArgumentException e) { - // Otherwise, return some token IP to test for. - return InetAddress.parseNumericAddress("5.6.7.8"); - } - } - - @Override - public boolean isInterfacePresent(final Vpn vpn, final String iface) { - return true; - } - } - - /** - * Mock some methods of vpn object. - */ - private Vpn createVpn(@UserIdInt int userId) { - final Context asUserContext = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); - doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); - when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) - .thenReturn(asUserContext); - final TestLooper testLooper = new TestLooper(); - final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, - mNetd, userId, mVpnProfileStore, mSystemServices, mIkev2SessionCreator); - verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat( - provider -> provider.getName().contains("VpnNetworkProvider") - )); - return vpn; - } - - /** - * Populate {@link #mUserManager} with a list of fake users. - */ - private void setMockedUsers(UserInfo... users) { - final Map userMap = new ArrayMap<>(); - for (UserInfo user : users) { - userMap.put(user.id, user); - } - - /** - * @see UserManagerService#getUsers(boolean) - */ - doAnswer(invocation -> { - final ArrayList result = new ArrayList<>(users.length); - for (UserInfo ui : users) { - if (ui.isEnabled() && !ui.partial) { - result.add(ui); - } - } - return result; - }).when(mUserManager).getAliveUsers(); - - doAnswer(invocation -> { - final int id = (int) invocation.getArguments()[0]; - return userMap.get(id); - }).when(mUserManager).getUserInfo(anyInt()); - } - - /** - * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping. - */ - private void setMockedPackages(final Map packages) { - try { - doAnswer(invocation -> { - final String appName = (String) invocation.getArguments()[0]; - final int userId = (int) invocation.getArguments()[1]; - Integer appId = packages.get(appName); - if (appId == null) throw new PackageManager.NameNotFoundException(appName); - return UserHandle.getUid(userId, appId); - }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt()); - } catch (Exception e) { - } - } - - private void setMockedNetworks(final Map networks) { - doAnswer(invocation -> { - final Network network = (Network) invocation.getArguments()[0]; - return networks.get(network); - }).when(mConnectivityManager).getNetworkCapabilities(any()); - } - - // Need multiple copies of this, but Java's Stream objects can't be reused or - // duplicated. - private Stream publicIpV4Routes() { - return Stream.of( - "0.0.0.0/5", "8.0.0.0/7", "11.0.0.0/8", "12.0.0.0/6", "16.0.0.0/4", - "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/3", "160.0.0.0/5", "168.0.0.0/6", - "172.0.0.0/12", "172.32.0.0/11", "172.64.0.0/10", "172.128.0.0/9", - "173.0.0.0/8", "174.0.0.0/7", "176.0.0.0/4", "192.0.0.0/9", "192.128.0.0/11", - "192.160.0.0/13", "192.169.0.0/16", "192.170.0.0/15", "192.172.0.0/14", - "192.176.0.0/12", "192.192.0.0/10", "193.0.0.0/8", "194.0.0.0/7", - "196.0.0.0/6", "200.0.0.0/5", "208.0.0.0/4"); - } - - private Stream publicIpV6Routes() { - return Stream.of( - "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6", - "fe00::/8", "2605:ef80:e:af1d::/64"); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java deleted file mode 100644 index 8b730af76951..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2015 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.net; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -import android.Manifest; -import android.Manifest.permission; -import android.app.AppOpsManager; -import android.app.admin.DevicePolicyManagerInternal; -import android.content.Context; -import android.content.pm.PackageManager; -import android.telephony.TelephonyManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.LocalServices; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsAccessTest { - private static final String TEST_PKG = "com.example.test"; - private static final int TEST_UID = 12345; - - @Mock private Context mContext; - @Mock private DevicePolicyManagerInternal mDpmi; - @Mock private TelephonyManager mTm; - @Mock private AppOpsManager mAppOps; - - // Hold the real service so we can restore it when tearing down the test. - private DevicePolicyManagerInternal mSystemDpmi; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi); - - when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm); - when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps); - } - - @After - public void tearDown() throws Exception { - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi); - } - - @Test - public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception { - setHasCarrierPrivileges(true); - setIsDeviceOwner(false); - setIsProfileOwner(false); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEVICE, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_isDeviceOwner() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(true); - setIsProfileOwner(false); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEVICE, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_isProfileOwner() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(true); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.USER, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(true); - setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(true); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(true); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); - setHasReadHistoryPermission(true); - assertEquals(NetworkStatsAccess.Level.DEVICESUMMARY, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_deniedAppOpsBit() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(false); - setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEFAULT, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - @Test - public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception { - setHasCarrierPrivileges(false); - setIsDeviceOwner(false); - setIsProfileOwner(false); - setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false); - setHasReadHistoryPermission(false); - assertEquals(NetworkStatsAccess.Level.DEFAULT, - NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG)); - } - - private void setHasCarrierPrivileges(boolean hasPrivileges) { - when(mTm.checkCarrierPrivilegesForPackageAnyPhone(TEST_PKG)).thenReturn( - hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS - : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS); - } - - private void setIsDeviceOwner(boolean isOwner) { - when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner); - } - - private void setIsProfileOwner(boolean isOwner) { - when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner); - } - - private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) { - when(mAppOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG)) - .thenReturn(appOpsMode); - when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn( - hasPermission ? PackageManager.PERMISSION_GRANTED - : PackageManager.PERMISSION_DENIED); - } - - private void setHasReadHistoryPermission(boolean hasPermission) { - when(mContext.checkCallingOrSelfPermission(permission.READ_NETWORK_USAGE_HISTORY)) - .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED - : PackageManager.PERMISSION_DENIED); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java deleted file mode 100644 index a058a466a4ff..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2011 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.net; - -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.DEFAULT_NETWORK_YES; -import static android.net.NetworkStats.METERED_ALL; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.METERED_YES; -import static android.net.NetworkStats.ROAMING_ALL; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.ROAMING_YES; -import static android.net.NetworkStats.SET_ALL; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.SET_FOREGROUND; -import static android.net.NetworkStats.TAG_NONE; - -import static org.junit.Assert.assertEquals; - -import android.net.NetworkStats; -import android.net.UnderlyingNetworkInfo; - -import java.util.Arrays; - -/** Superclass with utilities for NetworkStats(Service|Factory)Test */ -abstract class NetworkStatsBaseTest { - static final String TEST_IFACE = "test0"; - static final String TEST_IFACE2 = "test1"; - static final String TUN_IFACE = "test_nss_tun0"; - static final String TUN_IFACE2 = "test_nss_tun1"; - - static final int UID_RED = 1001; - static final int UID_BLUE = 1002; - static final int UID_GREEN = 1003; - static final int UID_VPN = 1004; - - void assertValues(NetworkStats stats, String iface, int uid, long rxBytes, - long rxPackets, long txBytes, long txPackets) { - assertValues( - stats, iface, uid, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, - rxBytes, rxPackets, txBytes, txPackets, 0); - } - - void assertValues(NetworkStats stats, String iface, int uid, int set, int tag, - int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, - long txBytes, long txPackets, long operations) { - final NetworkStats.Entry entry = new NetworkStats.Entry(); - final int[] sets; - if (set == SET_ALL) { - sets = new int[] {SET_ALL, SET_DEFAULT, SET_FOREGROUND}; - } else { - sets = new int[] {set}; - } - - final int[] roamings; - if (roaming == ROAMING_ALL) { - roamings = new int[] {ROAMING_ALL, ROAMING_YES, ROAMING_NO}; - } else { - roamings = new int[] {roaming}; - } - - final int[] meterings; - if (metered == METERED_ALL) { - meterings = new int[] {METERED_ALL, METERED_YES, METERED_NO}; - } else { - meterings = new int[] {metered}; - } - - final int[] defaultNetworks; - if (defaultNetwork == DEFAULT_NETWORK_ALL) { - defaultNetworks = - new int[] {DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, DEFAULT_NETWORK_NO}; - } else { - defaultNetworks = new int[] {defaultNetwork}; - } - - for (int s : sets) { - for (int r : roamings) { - for (int m : meterings) { - for (int d : defaultNetworks) { - final int i = stats.findIndex(iface, uid, s, tag, m, r, d); - if (i != -1) { - entry.add(stats.getValues(i, null)); - } - } - } - } - } - - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - assertEquals("unexpected operations", operations, entry.operations); - } - - static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) { - return createVpnInfo(TUN_IFACE, underlyingIfaces); - } - - static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { - return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces)); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java deleted file mode 100644 index 505ff9b6a34b..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Copyright (C) 2012 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.net; - -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.NetworkIdentity.OEM_NONE; -import static android.net.NetworkStats.SET_ALL; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStatsHistory.FIELD_ALL; -import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.os.Process.myUid; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; - -import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational; -import static com.android.testutils.MiscAsserts.assertThrows; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.NetworkIdentity; -import android.net.NetworkStats; -import android.net.NetworkStatsHistory; -import android.net.NetworkTemplate; -import android.os.Process; -import android.os.UserHandle; -import android.telephony.SubscriptionPlan; -import android.telephony.TelephonyManager; -import android.text.format.DateUtils; -import android.util.RecurrenceRule; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.frameworks.tests.net.R; - -import libcore.io.IoUtils; -import libcore.io.Streams; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.List; - -/** - * Tests for {@link NetworkStatsCollection}. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsCollectionTest { - - private static final String TEST_FILE = "test.bin"; - private static final String TEST_IMSI = "310260000000000"; - - private static final long TIME_A = 1326088800000L; // UTC: Monday 9th January 2012 06:00:00 AM - private static final long TIME_B = 1326110400000L; // UTC: Monday 9th January 2012 12:00:00 PM - private static final long TIME_C = 1326132000000L; // UTC: Monday 9th January 2012 06:00:00 PM - - private static Clock sOriginalClock; - - @Before - public void setUp() throws Exception { - sOriginalClock = RecurrenceRule.sClock; - // ignore any device overlay while testing - NetworkTemplate.forceAllNetworkTypes(); - } - - @After - public void tearDown() throws Exception { - RecurrenceRule.sClock = sOriginalClock; - NetworkTemplate.resetForceAllNetworkTypes(); - } - - private void setClock(Instant instant) { - RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault()); - } - - @Test - public void testReadLegacyNetwork() throws Exception { - final File testFile = - new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); - stageFile(R.raw.netstats_v1, testFile); - - final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); - collection.readLegacyNetwork(testFile); - - // verify that history read correctly - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); - - // now export into a unified format - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(bos); - - // clear structure completely - collection.reset(); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); - - // and read back into structure, verifying that totals are same - collection.read(new ByteArrayInputStream(bos.toByteArray())); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); - } - - @Test - public void testReadLegacyUid() throws Exception { - final File testFile = - new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); - stageFile(R.raw.netstats_uid_v4, testFile); - - final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); - collection.readLegacyUid(testFile, false); - - // verify that history read correctly - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); - - // now export into a unified format - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(bos); - - // clear structure completely - collection.reset(); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE); - - // and read back into structure, verifying that totals are same - collection.read(new ByteArrayInputStream(bos.toByteArray())); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); - } - - @Test - public void testReadLegacyUidTags() throws Exception { - final File testFile = - new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); - stageFile(R.raw.netstats_uid_v4, testFile); - - final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); - collection.readLegacyUid(testFile, true); - - // verify that history read correctly - assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), - 77017831L, 100995L, 35436758L, 92344L); - - // now export into a unified format - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - collection.write(bos); - - // clear structure completely - collection.reset(); - assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), - 0L, 0L, 0L, 0L); - - // and read back into structure, verifying that totals are same - collection.read(new ByteArrayInputStream(bos.toByteArray())); - assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI), - 77017831L, 100995L, 35436758L, 92344L); - } - - @Test - public void testStartEndAtomicBuckets() throws Exception { - final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); - - // record empty data straddling between buckets - final NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.rxBytes = 32; - collection.recordData(null, UID_ALL, SET_DEFAULT, TAG_NONE, 30 * MINUTE_IN_MILLIS, - 90 * MINUTE_IN_MILLIS, entry); - - // assert that we report boundary in atomic buckets - assertEquals(0, collection.getStartMillis()); - assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis()); - } - - @Test - public void testAccessLevels() throws Exception { - final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS); - final NetworkStats.Entry entry = new NetworkStats.Entry(); - final NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - TEST_IMSI, null, false, true, true, OEM_NONE)); - - int myUid = Process.myUid(); - int otherUidInSameUser = Process.myUid() + 1; - int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE; - - // Record one entry for the current UID. - entry.rxBytes = 32; - collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS, - entry); - - // Record one entry for another UID in this user. - entry.rxBytes = 64; - collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0, - 60 * MINUTE_IN_MILLIS, entry); - - // Record one entry for the system UID. - entry.rxBytes = 128; - collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0, - 60 * MINUTE_IN_MILLIS, entry); - - // Record one entry for a UID in a different user. - entry.rxBytes = 256; - collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0, - 60 * MINUTE_IN_MILLIS, entry); - - // Verify the set of relevant UIDs for each access level. - assertArrayEquals(new int[] { myUid }, - collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT)); - assertArrayEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser }, - collection.getRelevantUids(NetworkStatsAccess.Level.USER)); - assertArrayEquals( - new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser }, - collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE)); - - // Verify security check in getHistory. - assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT, - TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid)); - try { - collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser, - SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid); - fail("Should have thrown SecurityException for accessing different UID"); - } catch (SecurityException e) { - // expected - } - - // Verify appropriate aggregation in getSummary. - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0, - NetworkStatsAccess.Level.DEFAULT); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0, - NetworkStatsAccess.Level.USER); - assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0, - 0, NetworkStatsAccess.Level.DEVICE); - } - - @Test - public void testAugmentPlan() throws Exception { - final File testFile = - new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE); - stageFile(R.raw.netstats_v1, testFile); - - final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); - final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS); - collection.readLegacyNetwork(testFile); - - // We're in the future, but not that far off - setClock(Instant.parse("2012-06-01T00:00:00.00Z")); - - // Test a bunch of plans that should result in no augmentation - final List plans = new ArrayList<>(); - - // No plan - plans.add(null); - // No usage anchor - plans.add(SubscriptionPlan.Builder - .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")).build()); - // Usage anchor far in past - plans.add(SubscriptionPlan.Builder - .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) - .setDataUsage(1000L, TIME_A - DateUtils.YEAR_IN_MILLIS).build()); - // Usage anchor far in future - plans.add(SubscriptionPlan.Builder - .createRecurringMonthly(ZonedDateTime.parse("2011-01-14T00:00:00.00Z")) - .setDataUsage(1000L, TIME_A + DateUtils.YEAR_IN_MILLIS).build()); - // Usage anchor near but outside cycle - plans.add(SubscriptionPlan.Builder - .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), - ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) - .setDataUsage(1000L, TIME_C).build()); - - for (SubscriptionPlan plan : plans) { - int i; - NetworkStatsHistory history; - - // Empty collection should be untouched - history = getHistory(emptyCollection, plan, TIME_A, TIME_C); - assertEquals(0L, history.getTotalBytes()); - - // Normal collection should be untouched - history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; - assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); - assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); - assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); - assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); - assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); - assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); - assertEntry(10747, 50, 16839, 55, history.getValues(i++, null)); - assertEntry(10747, 49, 16837, 54, history.getValues(i++, null)); - assertEntry(89191, 151, 18021, 140, history.getValues(i++, null)); - assertEntry(89190, 150, 18020, 139, history.getValues(i++, null)); - assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); - assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); - assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); - assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); - assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); - assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); - assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); - assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); - assertEquals(history.size(), i); - - // Slice from middle should be untouched - history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, - TIME_B + HOUR_IN_MILLIS); i = 0; - assertEntry(3821, 23, 4525, 26, history.getValues(i++, null)); - assertEntry(3820, 21, 4524, 26, history.getValues(i++, null)); - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEquals(history.size(), i); - } - - // Lower anchor in the middle of plan - { - int i; - NetworkStatsHistory history; - - final SubscriptionPlan plan = SubscriptionPlan.Builder - .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), - ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) - .setDataUsage(200000L, TIME_B).build(); - - // Empty collection should be augmented - history = getHistory(emptyCollection, plan, TIME_A, TIME_C); - assertEquals(200000L, history.getTotalBytes()); - - // Normal collection should be augmented - history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; - assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); - assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); - assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); - assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); - assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); - assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); - // Cycle point; start data normalization - assertEntry(7507, 0, 11763, 0, history.getValues(i++, null)); - assertEntry(7507, 0, 11762, 0, history.getValues(i++, null)); - assertEntry(62309, 0, 12589, 0, history.getValues(i++, null)); - assertEntry(62309, 0, 12588, 0, history.getValues(i++, null)); - assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); - assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); - // Anchor point; end data normalization - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); - assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); - assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); - assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); - // Cycle point - assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); - assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); - assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); - assertEquals(history.size(), i); - - // Slice from middle should be augmented - history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, - TIME_B + HOUR_IN_MILLIS); i = 0; - assertEntry(2669, 0, 3161, 0, history.getValues(i++, null)); - assertEntry(2668, 0, 3160, 0, history.getValues(i++, null)); - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEquals(history.size(), i); - } - - // Higher anchor in the middle of plan - { - int i; - NetworkStatsHistory history; - - final SubscriptionPlan plan = SubscriptionPlan.Builder - .createNonrecurring(ZonedDateTime.parse("2012-01-09T09:00:00.00Z"), - ZonedDateTime.parse("2012-01-09T15:00:00.00Z")) - .setDataUsage(400000L, TIME_B + MINUTE_IN_MILLIS).build(); - - // Empty collection should be augmented - history = getHistory(emptyCollection, plan, TIME_A, TIME_C); - assertEquals(400000L, history.getTotalBytes()); - - // Normal collection should be augmented - history = getHistory(collection, plan, TIME_A, TIME_C); i = 0; - assertEntry(100647, 197, 23649, 185, history.getValues(i++, null)); - assertEntry(100647, 196, 23648, 185, history.getValues(i++, null)); - assertEntry(18323, 76, 15032, 76, history.getValues(i++, null)); - assertEntry(18322, 75, 15031, 75, history.getValues(i++, null)); - assertEntry(527798, 761, 78570, 652, history.getValues(i++, null)); - assertEntry(527797, 760, 78570, 651, history.getValues(i++, null)); - // Cycle point; start data normalization - assertEntry(15015, 0, 23527, 0, history.getValues(i++, null)); - assertEntry(15015, 0, 23524, 0, history.getValues(i++, null)); - assertEntry(124619, 0, 25179, 0, history.getValues(i++, null)); - assertEntry(124618, 0, 25177, 0, history.getValues(i++, null)); - assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); - assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); - // Anchor point; end data normalization - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEntry(8289, 36, 6864, 39, history.getValues(i++, null)); - assertEntry(8289, 34, 6862, 37, history.getValues(i++, null)); - assertEntry(113914, 174, 18364, 157, history.getValues(i++, null)); - assertEntry(113913, 173, 18364, 157, history.getValues(i++, null)); - // Cycle point - assertEntry(11378, 49, 9261, 50, history.getValues(i++, null)); - assertEntry(11377, 48, 9261, 48, history.getValues(i++, null)); - assertEntry(201766, 328, 41808, 291, history.getValues(i++, null)); - assertEntry(201764, 328, 41807, 290, history.getValues(i++, null)); - assertEntry(106106, 219, 39918, 202, history.getValues(i++, null)); - assertEntry(106105, 216, 39916, 200, history.getValues(i++, null)); - - // Slice from middle should be augmented - history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS, - TIME_B + HOUR_IN_MILLIS); i = 0; - assertEntry(5338, 0, 6322, 0, history.getValues(i++, null)); - assertEntry(5337, 0, 6320, 0, history.getValues(i++, null)); - assertEntry(91686, 159, 18576, 146, history.getValues(i++, null)); - assertEntry(91685, 159, 18574, 146, history.getValues(i++, null)); - assertEquals(history.size(), i); - } - } - - @Test - public void testAugmentPlanGigantic() throws Exception { - // We're in the future, but not that far off - setClock(Instant.parse("2012-06-01T00:00:00.00Z")); - - // Create a simple history with a ton of measured usage - final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS); - final NetworkIdentitySet ident = new NetworkIdentitySet(); - ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null, - false, true, true, OEM_NONE)); - large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B, - new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0)); - - // Verify untouched total - assertEquals(12_730_893_164L, getHistory(large, null, TIME_A, TIME_C).getTotalBytes()); - - // Verify anchor that might cause overflows - final SubscriptionPlan plan = SubscriptionPlan.Builder - .createRecurringMonthly(ZonedDateTime.parse("2012-01-09T00:00:00.00Z")) - .setDataUsage(4_939_212_390L, TIME_B).build(); - assertEquals(4_939_212_386L, getHistory(large, plan, TIME_A, TIME_C).getTotalBytes()); - } - - @Test - public void testRounding() throws Exception { - final NetworkStatsCollection coll = new NetworkStatsCollection(HOUR_IN_MILLIS); - - // Special values should remain unchanged - for (long time : new long[] { - Long.MIN_VALUE, Long.MAX_VALUE, SubscriptionPlan.TIME_UNKNOWN - }) { - assertEquals(time, coll.roundUp(time)); - assertEquals(time, coll.roundDown(time)); - } - - assertEquals(TIME_A, coll.roundUp(TIME_A)); - assertEquals(TIME_A, coll.roundDown(TIME_A)); - - assertEquals(TIME_A + HOUR_IN_MILLIS, coll.roundUp(TIME_A + 1)); - assertEquals(TIME_A, coll.roundDown(TIME_A + 1)); - - assertEquals(TIME_A, coll.roundUp(TIME_A - 1)); - assertEquals(TIME_A - HOUR_IN_MILLIS, coll.roundDown(TIME_A - 1)); - } - - @Test - public void testMultiplySafeRational() { - assertEquals(25, multiplySafeByRational(50, 1, 2)); - assertEquals(100, multiplySafeByRational(50, 2, 1)); - - assertEquals(-10, multiplySafeByRational(30, -1, 3)); - assertEquals(0, multiplySafeByRational(30, 0, 3)); - assertEquals(10, multiplySafeByRational(30, 1, 3)); - assertEquals(20, multiplySafeByRational(30, 2, 3)); - assertEquals(30, multiplySafeByRational(30, 3, 3)); - assertEquals(40, multiplySafeByRational(30, 4, 3)); - - assertEquals(100_000_000_000L, - multiplySafeByRational(300_000_000_000L, 10_000_000_000L, 30_000_000_000L)); - assertEquals(100_000_000_010L, - multiplySafeByRational(300_000_000_000L, 10_000_000_001L, 30_000_000_000L)); - assertEquals(823_202_048L, - multiplySafeByRational(4_939_212_288L, 2_121_815_528L, 12_730_893_165L)); - - assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0)); - } - - /** - * Copy a {@link Resources#openRawResource(int)} into {@link File} for - * testing purposes. - */ - private void stageFile(int rawId, File file) throws Exception { - new File(file.getParent()).mkdirs(); - InputStream in = null; - OutputStream out = null; - try { - in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); - out = new FileOutputStream(file); - Streams.copy(in, out); - } finally { - IoUtils.closeQuietly(in); - IoUtils.closeQuietly(out); - } - } - - private static NetworkStatsHistory getHistory(NetworkStatsCollection collection, - SubscriptionPlan augmentPlan, long start, long end) { - return collection.getHistory(buildTemplateMobileAll(TEST_IMSI), augmentPlan, UID_ALL, - SET_ALL, TAG_NONE, FIELD_ALL, start, end, NetworkStatsAccess.Level.DEVICE, myUid()); - } - - private static void assertSummaryTotal(NetworkStatsCollection collection, - NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets, - @NetworkStatsAccess.Level int accessLevel) { - final NetworkStats.Entry actual = collection.getSummary( - template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel, myUid()) - .getTotal(null); - assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); - } - - private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection, - NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) { - final NetworkStats.Entry actual = collection.getSummary( - template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE, myUid()) - .getTotalIncludingTags(null); - assertEntry(rxBytes, rxPackets, txBytes, txPackets, actual); - } - - private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, - NetworkStats.Entry actual) { - assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); - } - - private static void assertEntry(long rxBytes, long rxPackets, long txBytes, long txPackets, - NetworkStatsHistory.Entry actual) { - assertEntry(new NetworkStats.Entry(rxBytes, rxPackets, txBytes, txPackets, 0L), actual); - } - - private static void assertEntry(NetworkStats.Entry expected, - NetworkStatsHistory.Entry actual) { - assertEntry(expected, new NetworkStats.Entry(actual.rxBytes, actual.rxPackets, - actual.txBytes, actual.txPackets, 0L)); - } - - private static void assertEntry(NetworkStats.Entry expected, - NetworkStats.Entry actual) { - assertEquals("unexpected rxBytes", expected.rxBytes, actual.rxBytes); - assertEquals("unexpected rxPackets", expected.rxPackets, actual.rxPackets); - assertEquals("unexpected txBytes", expected.txBytes, actual.txBytes); - assertEquals("unexpected txPackets", expected.txPackets, actual.txPackets); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java deleted file mode 100644 index f3ae9b051e7c..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Copyright (C) 2011 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.net; - -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.METERED_ALL; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_ALL; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_ALL; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.SET_FOREGROUND; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; - -import static com.android.server.NetworkManagementSocketTagger.kernelToTag; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.content.res.Resources; -import android.net.NetworkStats; -import android.net.TrafficStats; -import android.net.UnderlyingNetworkInfo; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.frameworks.tests.net.R; - -import libcore.io.IoUtils; -import libcore.io.Streams; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.OutputStream; - -/** Tests for {@link NetworkStatsFactory}. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { - private static final String CLAT_PREFIX = "v4-"; - - private File mTestProc; - private NetworkStatsFactory mFactory; - - @Before - public void setUp() throws Exception { - mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc"); - if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); - } - - // The libandroid_servers which have the native method is not available to - // applications. So in order to have a test support native library, the native code - // related to networkStatsFactory is compiled to a minimal native library and loaded here. - System.loadLibrary("networkstatsfactorytestjni"); - mFactory = new NetworkStatsFactory(mTestProc, false); - mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]); - } - - @After - public void tearDown() throws Exception { - mFactory = null; - - if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); - } - } - - @Test - public void testNetworkStatsDetail() throws Exception { - final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical); - - assertEquals(70, stats.size()); - assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L); - assertStatsEntry(stats, "wlan0", 10011, SET_DEFAULT, 0x0, 35777L, 5718L); - assertStatsEntry(stats, "wlan0", 10021, SET_DEFAULT, 0x7fffff01, 562386L, 49228L); - assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 227423L); - assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L); - } - - @Test - public void testVpnRewriteTrafficThroughItself() throws Exception { - UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // - // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic - - final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self); - - assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0); - assertValues(tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 0); - assertValues(tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 0); - - assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 260L, 26L); - } - - @Test - public void testVpnWithClat() throws Exception { - final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { - createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over v4-WiFi, and clat - // added 20 bytes per packet of extra overhead - // - // For 1650 bytes sent over v4-WiFi, 4650 bytes were actually sent over WiFi, which is - // expected to be split as follows: - // UID_RED: 1000 bytes, 100 packets - // UID_BLUE: 500 bytes, 50 packets - // UID_VPN: 3150 bytes, 0 packets - // - // For 3300 bytes received over v4-WiFi, 9300 bytes were actually sent over WiFi, which is - // expected to be split as follows: - // UID_RED: 2000 bytes, 200 packets - // UID_BLUE: 1000 bytes, 100 packets - // UID_VPN: 6300 bytes, 0 packets - final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat); - - assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L); - assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); - assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_VPN, 6300L, 0L, 3150L, 0L); - } - - @Test - public void testVpnWithOneUnderlyingIface() throws Exception { - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi. - // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. - // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes - // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. - final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying); - - assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L); - } - - @Test - public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun - // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500 - // packets) from it. Including overhead that is 6600/5500 bytes. - // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi. - // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN. - // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes - // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN. - final NetworkStats tunStats = - parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic); - - assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 5800L, 500L, 6750L, 600L); - } - - @Test - public void testVpnWithOneUnderlyingIface_withCompression() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN. - // VPN sent/received 1000 bytes (100 packets) over WiFi. - // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE, - // with nothing attributed to UID_VPN for both rx/tx traffic. - final NetworkStats tunStats = - parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression); - - assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); - } - - @Test - public void testVpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is duplicating traffic across both WiFi and Cell. - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN. - // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total). - // Of 8800 bytes over WiFi/Cell, expect: - // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE. - // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID. - final NetworkStats tunStats = - parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication); - - assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 1200L, 100L, 1200L, 100L); - assertValues(tunStats, TEST_IFACE2, UID_RED, 500L, 50L, 500L, 50L); - assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 500L, 50L); - assertValues(tunStats, TEST_IFACE2, UID_VPN, 1200L, 100L, 1200L, 100L); - } - - @Test - public void testConcurrentVpns() throws Exception { - // Assume two VPNs are connected on two different network interfaces. VPN1 is using - // TEST_IFACE and VPN2 is using TEST_IFACE2. - final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { - createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}), - createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN1. - // 700 bytes (70 packets) were sent, and 3000 bytes (300 packets) were received by UID_RED - // over VPN2. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN1. - // 250 bytes (25 packets) were sent, and 500 bytes (50 packets) were received by UID_BLUE - // over VPN2. - // VPN1 sent 1650 bytes (150 packets), and received 3300 (300 packets) over TEST_IFACE. - // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. - // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes - // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. - // VPN2 sent 1045 bytes (95 packets), and received 3850 (350 packets) over TEST_IFACE2. - // Of 1045 bytes sent over Cell, expect 700 bytes attributed to UID_RED, 250 bytes - // attributed to UID_BLUE, and 95 bytes attributed to UID_VPN. - // Of 3850 bytes received over Cell, expect 3000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 350 bytes attributed to UID_VPN. - final NetworkStats tunStats = - parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn); - - assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); - assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); - assertValues(tunStats, TEST_IFACE2, UID_RED, 3000L, 300L, 700L, 70L); - assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 250L, 25L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L); - assertValues(tunStats, TEST_IFACE2, UID_VPN, 350L, 0L, 95L, 0L); - } - - @Test - public void testVpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over - // VPN. - // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell. - // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx) - // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell. - // - // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic. - // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic. - final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split); - - assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L); - assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 400L, 40L); - assertValues(tunStats, TEST_IFACE2, UID_VPN, 20L, 0L, 40L, 0L); - } - - @Test - public void testVpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface: - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both - // rx/tx. - // UID_VPN gets nothing attributed to it (avoiding negative stats). - final NetworkStats tunStats = - parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression); - - assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); - assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 200L, 20L); - assertValues(tunStats, TEST_IFACE2, UID_VPN, 0L, 0L, 0L, 0L); - } - - @Test - public void testVpnWithIncorrectUnderlyingIface() throws Exception { - // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), - // but has declared only WiFi (TEST_IFACE) in its underlying network set. - final UnderlyingNetworkInfo[] underlyingNetworkInfos = - new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); - - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 1100 bytes (100 packets) over Cell. - // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic. - final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface); - - assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L); - assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); - assertValues(tunStats, TEST_IFACE2, UID_RED, 0L, 0L, 0L, 0L); - assertValues(tunStats, TEST_IFACE2, UID_VPN, 1100L, 100L, 1100L, 100L); - } - - @Test - public void testKernelTags() throws Exception { - assertEquals(0, kernelToTag("0x0000000000000000")); - assertEquals(0x32, kernelToTag("0x0000003200000000")); - assertEquals(2147483647, kernelToTag("0x7fffffff00000000")); - assertEquals(0, kernelToTag("0x0000000000000000")); - assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000")); - - assertEquals(0, kernelToTag("0x0")); - assertEquals(0, kernelToTag("0xf00d")); - assertEquals(1, kernelToTag("0x100000000")); - assertEquals(14438007, kernelToTag("0xdc4e7700000000")); - assertEquals(TrafficStats.TAG_SYSTEM_DOWNLOAD, kernelToTag("0xffffff0100000000")); - } - - @Test - public void testNetworkStatsWithSet() throws Exception { - final NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_typical); - assertEquals(70, stats.size()); - assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L, - 676L); - assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L); - } - - @Test - public void testNetworkStatsSingle() throws Exception { - stageFile(R.raw.xt_qtaguid_iface_typical, file("net/xt_qtaguid/iface_stat_all")); - - final NetworkStats stats = mFactory.readNetworkStatsSummaryDev(); - assertEquals(6, stats.size()); - assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 2112L, 24L, 700L, 10L); - assertStatsEntry(stats, "test1", UID_ALL, SET_ALL, TAG_NONE, 6L, 8L, 10L, 12L); - assertStatsEntry(stats, "test2", UID_ALL, SET_ALL, TAG_NONE, 1L, 2L, 3L, 4L); - } - - @Test - public void testNetworkStatsXt() throws Exception { - stageFile(R.raw.xt_qtaguid_iface_fmt_typical, file("net/xt_qtaguid/iface_stat_fmt")); - - final NetworkStats stats = mFactory.readNetworkStatsSummaryXt(); - assertEquals(3, stats.size()); - assertStatsEntry(stats, "rmnet0", UID_ALL, SET_ALL, TAG_NONE, 6824L, 16L, 5692L, 10L); - assertStatsEntry(stats, "rmnet1", UID_ALL, SET_ALL, TAG_NONE, 11153922L, 8051L, 190226L, - 2468L); - assertStatsEntry(stats, "rmnet2", UID_ALL, SET_ALL, TAG_NONE, 4968L, 35L, 3081L, 39L); - } - - @Test - public void testDoubleClatAccountingSimple() throws Exception { - mFactory.noteStackedIface("v4-wlan0", "wlan0"); - - // xt_qtaguid_with_clat_simple is a synthetic file that simulates - // - 213 received 464xlat packets of size 200 bytes - // - 41 sent 464xlat packets of size 100 bytes - // - no other traffic on base interface for root uid. - NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple); - assertEquals(3, stats.size()); - - assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L); - assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); - } - - @Test - public void testDoubleClatAccounting() throws Exception { - mFactory.noteStackedIface("v4-wlan0", "wlan0"); - - NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat); - assertEquals(42, stats.size()); - - assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L); - assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L); - assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L); - assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L); - assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); - assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L); - assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L); - assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L); - assertStatsEntry(stats, "wlan0", 10060, SET_DEFAULT, 0x0, 134356L, 8705L); - assertStatsEntry(stats, "wlan0", 10079, SET_DEFAULT, 0x0, 10926L, 1507L); - assertStatsEntry(stats, "wlan0", 10102, SET_DEFAULT, 0x0, 25038L, 8245L); - assertStatsEntry(stats, "wlan0", 10103, SET_DEFAULT, 0x0, 0L, 192L); - assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L); - assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L); - - assertNoStatsEntry(stats, "wlan0", 1029, SET_DEFAULT, 0x0); - } - - @Test - public void testDoubleClatAccounting100MBDownload() throws Exception { - // Downloading 100mb from an ipv4 only destination in a foreground activity - - long appRxBytesBefore = 328684029L; - long appRxBytesAfter = 439237478L; - assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore); - - long rootRxBytes = 330187296L; - - mFactory.noteStackedIface("v4-wlan0", "wlan0"); - NetworkStats stats; - - // Stats snapshot before the download - stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before); - assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L); - assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L); - - // Stats snapshot after the download - stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after); - assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L); - assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L); - } - - /** - * Copy a {@link Resources#openRawResource(int)} into {@link File} for - * testing purposes. - */ - private void stageFile(int rawId, File file) throws Exception { - new File(file.getParent()).mkdirs(); - InputStream in = null; - OutputStream out = null; - try { - in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); - out = new FileOutputStream(file); - Streams.copy(in, out); - } finally { - IoUtils.closeQuietly(in); - IoUtils.closeQuietly(out); - } - } - - private void stageLong(long value, File file) throws Exception { - new File(file.getParent()).mkdirs(); - FileWriter out = null; - try { - out = new FileWriter(file); - out.write(Long.toString(value)); - } finally { - IoUtils.closeQuietly(out); - } - } - - private File file(String path) throws Exception { - return new File(mTestProc, path); - } - - private NetworkStats parseDetailedStats(int resourceId) throws Exception { - stageFile(resourceId, file("net/xt_qtaguid/stats")); - return mFactory.readNetworkStatsDetail(); - } - - private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, - int tag, long rxBytes, long txBytes) { - final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO); - if (i < 0) { - fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", - iface, uid, set, tag)); - } - final NetworkStats.Entry entry = stats.getValues(i, null); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - } - - private static void assertNoStatsEntry(NetworkStats stats, String iface, int uid, int set, - int tag) { - final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO); - if (i >= 0) { - fail("unexpected NetworkStats entry at " + i); - } - } - - private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, - int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { - assertStatsEntry(stats, iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, - rxBytes, rxPackets, txBytes, txPackets); - } - - private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, - int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, - long txBytes, long txPackets) { - final int i = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); - - if (i < 0) { - fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d, metered:" - + " %d, roaming: %d, defaultNetwork: %d)", - iface, uid, set, tag, metered, roaming, defaultNetwork)); - } - final NetworkStats.Entry entry = stats.getValues(i, null); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java deleted file mode 100644 index 9fa1c50423d9..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2016 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.net; - -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.NetworkIdentity.OEM_NONE; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.DEFAULT_NETWORK_YES; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.net.NetworkTemplate.buildTemplateWifiWildcard; -import static android.net.TrafficStats.MB_IN_BYTES; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; - -import android.app.usage.NetworkStatsManager; -import android.net.DataUsageRequest; -import android.net.NetworkIdentity; -import android.net.NetworkStats; -import android.net.NetworkTemplate; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Messenger; -import android.os.Process; -import android.os.UserHandle; -import android.telephony.TelephonyManager; -import android.util.ArrayMap; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.net.NetworkStatsServiceTest.LatchedHandler; -import com.android.testutils.HandlerUtils; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.Objects; - -/** - * Tests for {@link NetworkStatsObservers}. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsObserversTest { - private static final String TEST_IFACE = "test0"; - private static final String TEST_IFACE2 = "test1"; - private static final long TEST_START = 1194220800000L; - - private static final String IMSI_1 = "310004"; - private static final String IMSI_2 = "310260"; - private static final String TEST_SSID = "AndroidAP"; - - private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard(); - private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); - private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); - - private static final int UID_RED = UserHandle.PER_USER_RANGE + 1; - private static final int UID_BLUE = UserHandle.PER_USER_RANGE + 2; - private static final int UID_GREEN = UserHandle.PER_USER_RANGE + 3; - private static final int UID_ANOTHER_USER = 2 * UserHandle.PER_USER_RANGE + 4; - - private static final long WAIT_TIMEOUT_MS = 500; - private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES; - private static final long BASE_BYTES = 7 * MB_IN_BYTES; - private static final int INVALID_TYPE = -1; - - private long mElapsedRealtime; - - private HandlerThread mObserverHandlerThread; - private Handler mObserverNoopHandler; - - private LatchedHandler mHandler; - - private NetworkStatsObservers mStatsObservers; - private Messenger mMessenger; - private ArrayMap mActiveIfaces; - private ArrayMap mActiveUidIfaces; - - @Mock private IBinder mockBinder; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mObserverHandlerThread = new HandlerThread("HandlerThread"); - mObserverHandlerThread.start(); - final Looper observerLooper = mObserverHandlerThread.getLooper(); - mStatsObservers = new NetworkStatsObservers() { - @Override - protected Looper getHandlerLooperLocked() { - return observerLooper; - } - }; - - mHandler = new LatchedHandler(Looper.getMainLooper(), new ConditionVariable()); - mMessenger = new Messenger(mHandler); - - mActiveIfaces = new ArrayMap<>(); - mActiveUidIfaces = new ArrayMap<>(); - } - - @Test - public void testRegister_thresholdTooLow_setsDefaultThreshold() throws Exception { - long thresholdTooLowBytes = 1L; - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdTooLowBytes); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateWifi, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - } - - @Test - public void testRegister_highThreshold_accepted() throws Exception { - long highThresholdBytes = 2 * THRESHOLD_BYTES; - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, highThresholdBytes); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateWifi, request.template)); - assertEquals(highThresholdBytes, request.thresholdInBytes); - } - - @Test - public void testRegister_twoRequests_twoIds() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES); - - DataUsageRequest request1 = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request1.requestId > 0); - assertTrue(Objects.equals(sTemplateWifi, request1.template)); - assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes); - - DataUsageRequest request2 = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request2.requestId > request1.requestId); - assertTrue(Objects.equals(sTemplateWifi, request2.template)); - assertEquals(THRESHOLD_BYTES, request2.thresholdInBytes); - } - - @Test - public void testUnregister_unknownRequest_noop() throws Exception { - DataUsageRequest unknownRequest = new DataUsageRequest( - 123456 /* id */, sTemplateWifi, THRESHOLD_BYTES); - - mStatsObservers.unregister(unknownRequest, UID_RED); - } - - @Test - public void testUnregister_knownRequest_releasesCaller() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - - mStatsObservers.unregister(request, Process.SYSTEM_UID); - waitForObserverToIdle(); - - Mockito.verify(mockBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - } - - @Test - public void testUnregister_knownRequest_invalidUid_doesNotUnregister() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - UID_RED, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - Mockito.verify(mockBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - - mStatsObservers.unregister(request, UID_BLUE); - waitForObserverToIdle(); - - Mockito.verifyZeroInteractions(mockBinder); - } - - private NetworkIdentitySet makeTestIdentSet() { - NetworkIdentitySet identSet = new NetworkIdentitySet(); - identSet.add(new NetworkIdentity( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - IMSI_1, null /* networkId */, false /* roaming */, true /* metered */, - true /* defaultNetwork */, OEM_NONE)); - return identSet; - } - - @Test - public void testUpdateStats_initialSample_doesNotNotify() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) - .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); - NetworkStats uidSnapshot = null; - - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - } - - @Test - public void testUpdateStats_belowThreshold_doesNotNotify() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) - .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); - NetworkStats uidSnapshot = null; - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) - .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - } - - - @Test - public void testUpdateStats_deviceAccess_notifies() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) - .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); - NetworkStats uidSnapshot = null; - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */) - .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L, - BASE_BYTES + THRESHOLD_BYTES, 22L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); - } - - @Test - public void testUpdateStats_defaultAccess_notifiesSameUid() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - UID_RED, NetworkStatsAccess.Level.DEFAULT); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveUidIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = null; - NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, - BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); - } - - @Test - public void testUpdateStats_defaultAccess_usageOtherUid_doesNotNotify() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - UID_BLUE, NetworkStatsAccess.Level.DEFAULT); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveUidIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = null; - NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, - BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - } - - @Test - public void testUpdateStats_userAccess_usageSameUser_notifies() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - UID_BLUE, NetworkStatsAccess.Level.USER); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveUidIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = null; - NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, - BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); - } - - @Test - public void testUpdateStats_userAccess_usageAnotherUser_doesNotNotify() throws Exception { - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES); - - DataUsageRequest request = mStatsObservers.register(inputRequest, mMessenger, mockBinder, - UID_RED, NetworkStatsAccess.Level.USER); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateImsi1, request.template)); - assertEquals(THRESHOLD_BYTES, request.thresholdInBytes); - - NetworkIdentitySet identSet = makeTestIdentSet(); - mActiveUidIfaces.put(TEST_IFACE, identSet); - - // Baseline - NetworkStats xtSnapshot = null; - NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - - // Delta - uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) - .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, - ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, - BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); - mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); - waitForObserverToIdle(); - } - - private void waitForObserverToIdle() { - HandlerUtils.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS); - HandlerUtils.waitForIdle(mHandler, WAIT_TIMEOUT_MS); - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java deleted file mode 100644 index fd374bc9e68f..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ /dev/null @@ -1,1767 +0,0 @@ -/* - * Copyright (C) 2011 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.net; - -import static android.content.Intent.ACTION_UID_REMOVED; -import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.NetworkIdentity.OEM_PAID; -import static android.net.NetworkIdentity.OEM_PRIVATE; -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; -import static android.net.NetworkStats.DEFAULT_NETWORK_YES; -import static android.net.NetworkStats.IFACE_ALL; -import static android.net.NetworkStats.INTERFACES_ALL; -import static android.net.NetworkStats.METERED_ALL; -import static android.net.NetworkStats.METERED_NO; -import static android.net.NetworkStats.METERED_YES; -import static android.net.NetworkStats.ROAMING_ALL; -import static android.net.NetworkStats.ROAMING_NO; -import static android.net.NetworkStats.ROAMING_YES; -import static android.net.NetworkStats.SET_ALL; -import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.SET_FOREGROUND; -import static android.net.NetworkStats.STATS_PER_UID; -import static android.net.NetworkStats.TAG_ALL; -import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; -import static android.net.NetworkStatsHistory.FIELD_ALL; -import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; -import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; -import static android.net.NetworkTemplate.OEM_MANAGED_NO; -import static android.net.NetworkTemplate.OEM_MANAGED_YES; -import static android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT; -import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; -import static android.net.NetworkTemplate.buildTemplateWifi; -import static android.net.NetworkTemplate.buildTemplateWifiWildcard; -import static android.net.TrafficStats.MB_IN_BYTES; -import static android.net.TrafficStats.UID_REMOVED; -import static android.net.TrafficStats.UID_TETHERING; -import static android.text.format.DateUtils.DAY_IN_MILLIS; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; -import static android.text.format.DateUtils.WEEK_IN_MILLIS; - -import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.AlarmManager; -import android.app.usage.NetworkStatsManager; -import android.content.Context; -import android.content.Intent; -import android.database.ContentObserver; -import android.net.DataUsageRequest; -import android.net.INetworkManagementEventObserver; -import android.net.INetworkStatsSession; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkStateSnapshot; -import android.net.NetworkStats; -import android.net.NetworkStatsHistory; -import android.net.NetworkTemplate; -import android.net.TelephonyNetworkSpecifier; -import android.net.UnderlyingNetworkInfo; -import android.net.netstats.provider.INetworkStatsProviderCallback; -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.Message; -import android.os.Messenger; -import android.os.PowerManager; -import android.os.SimpleClock; -import android.provider.Settings; -import android.telephony.TelephonyManager; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.test.BroadcastInterceptingContext; -import com.android.server.net.NetworkStatsService.NetworkStatsSettings; -import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; -import com.android.testutils.HandlerUtils; -import com.android.testutils.TestableNetworkStatsProviderBinder; - -import libcore.io.IoUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.time.Clock; -import java.time.ZoneOffset; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * Tests for {@link NetworkStatsService}. - * - * TODO: This test used to be really brittle because it used Easymock - it uses Mockito now, but - * still uses the Easymock structure, which could be simplified. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkStatsServiceTest extends NetworkStatsBaseTest { - private static final String TAG = "NetworkStatsServiceTest"; - - private static final long TEST_START = 1194220800000L; - - private static final String IMSI_1 = "310004"; - private static final String IMSI_2 = "310260"; - private static final String TEST_SSID = "AndroidAP"; - - private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID); - private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); - private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); - - private static final Network WIFI_NETWORK = new Network(100); - private static final Network MOBILE_NETWORK = new Network(101); - private static final Network VPN_NETWORK = new Network(102); - - private static final Network[] NETWORKS_WIFI = new Network[]{ WIFI_NETWORK }; - private static final Network[] NETWORKS_MOBILE = new Network[]{ MOBILE_NETWORK }; - - private static final long WAIT_TIMEOUT = 2 * 1000; // 2 secs - private static final int INVALID_TYPE = -1; - - private long mElapsedRealtime; - - private File mStatsDir; - private MockContext mServiceContext; - private @Mock TelephonyManager mTelephonyManager; - private @Mock INetworkManagementService mNetManager; - private @Mock NetworkStatsFactory mStatsFactory; - private @Mock NetworkStatsSettings mSettings; - private @Mock IBinder mBinder; - private @Mock AlarmManager mAlarmManager; - @Mock - private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor; - private HandlerThread mHandlerThread; - - private NetworkStatsService mService; - private INetworkStatsSession mSession; - private INetworkManagementEventObserver mNetworkObserver; - private ContentObserver mContentObserver; - private Handler mHandler; - - private class MockContext extends BroadcastInterceptingContext { - private final Context mBaseContext; - - MockContext(Context base) { - super(base); - mBaseContext = base; - } - - @Override - public Object getSystemService(String name) { - if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; - return mBaseContext.getSystemService(name); - } - } - - private final Clock mClock = new SimpleClock(ZoneOffset.UTC) { - @Override - public long millis() { - return currentTimeMillis(); - } - }; - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - final Context context = InstrumentationRegistry.getContext(); - mServiceContext = new MockContext(context); - mStatsDir = context.getFilesDir(); - if (mStatsDir.exists()) { - IoUtils.deleteContents(mStatsDir); - } - - PowerManager powerManager = (PowerManager) mServiceContext.getSystemService( - Context.POWER_SERVICE); - PowerManager.WakeLock wakeLock = - powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - - mHandlerThread = new HandlerThread("HandlerThread"); - final NetworkStatsService.Dependencies deps = makeDependencies(); - mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock, - mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir, - getBaseDir(mStatsDir), deps); - - mElapsedRealtime = 0L; - - expectDefaultSettings(); - expectNetworkStatsUidDetail(buildEmptyStats()); - expectSystemReady(); - mService.systemReady(); - // Verify that system ready fetches realtime stats - verify(mStatsFactory).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL); - // Wait for posting onChange() event to handler thread and verify that when system ready, - // start monitoring data usage per RAT type because the settings value is mock as false - // by default in expectSettings(). - waitForIdle(); - verify(mNetworkStatsSubscriptionsMonitor).start(); - reset(mNetworkStatsSubscriptionsMonitor); - - doReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS).when(mTelephonyManager) - .checkCarrierPrivilegesForPackageAnyPhone(anyString()); - - mSession = mService.openSession(); - assertNotNull("openSession() failed", mSession); - - // catch INetworkManagementEventObserver during systemReady() - ArgumentCaptor networkObserver = - ArgumentCaptor.forClass(INetworkManagementEventObserver.class); - verify(mNetManager).registerObserver(networkObserver.capture()); - mNetworkObserver = networkObserver.getValue(); - } - - @NonNull - private NetworkStatsService.Dependencies makeDependencies() { - return new NetworkStatsService.Dependencies() { - @Override - public HandlerThread makeHandlerThread() { - return mHandlerThread; - } - - @Override - public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor( - @NonNull Context context, @NonNull Looper looper, @NonNull Executor executor, - @NonNull NetworkStatsService service) { - - return mNetworkStatsSubscriptionsMonitor; - } - - @Override - public ContentObserver makeContentObserver(Handler handler, - NetworkStatsSettings settings, NetworkStatsSubscriptionsMonitor monitor) { - mHandler = handler; - return mContentObserver = super.makeContentObserver(handler, settings, monitor); - } - - }; - } - - @After - public void tearDown() throws Exception { - IoUtils.deleteContents(mStatsDir); - - mServiceContext = null; - mStatsDir = null; - - mNetManager = null; - mSettings = null; - - mSession.close(); - mService = null; - - mHandlerThread.quitSafely(); - } - - @Test - public void testNetworkStatsWifi() throws Exception { - // pretend that wifi network comes online; service should ask about full - // network state, and poll any existing interfaces before updating. - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // verify service has empty history for wifi - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - - // modify some number on wifi, and trigger poll event - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); - - - // and bump forward again, with counters going higher. this is - // important, since polling should correctly subtract last snapshot. - incrementCurrentTime(DAY_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); - - } - - @Test - public void testStatsRebootPersist() throws Exception { - assertStatsFilesExist(false); - - // pretend that wifi network comes online; service should ask about full - // network state, and poll any existing interfaces before updating. - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // verify service has empty history for wifi - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - - - // modify some number on wifi, and trigger poll event - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L)); - mService.setUidForeground(UID_RED, false); - mService.incrementOperationCount(UID_RED, 0xFAAD, 4); - mService.setUidForeground(UID_RED, true); - mService.incrementOperationCount(UID_RED, 0xFAAD, 6); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); - assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); - assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); - assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); - assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); - - - // graceful shutdown system, which should trigger persist of stats, and - // clear any values in memory. - expectDefaultSettings(); - mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN)); - assertStatsFilesExist(true); - - // boot through serviceReady() again - expectDefaultSettings(); - expectNetworkStatsUidDetail(buildEmptyStats()); - expectSystemReady(); - - mService.systemReady(); - - // after systemReady(), we should have historical stats loaded again - assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0); - assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10); - assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 4); - assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 512L, 4L, 256L, 2L, 6); - assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0); - - } - - // TODO: simulate reboot to test bucket resize - @Test - @Ignore - public void testStatsBucketResize() throws Exception { - NetworkStatsHistory history = null; - - assertStatsFilesExist(false); - - // pretend that wifi network comes online; service should ask about full - // network state, and poll any existing interfaces before updating. - expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // modify some number on wifi, and trigger poll event - incrementCurrentTime(2 * HOUR_IN_MILLIS); - expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify service recorded history - history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); - assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); - assertEquals(HOUR_IN_MILLIS, history.getBucketDuration()); - assertEquals(2, history.size()); - - - // now change bucket duration setting and trigger another poll with - // exact same values, which should resize existing buckets. - expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify identical stats, but spread across 4 buckets now - history = mSession.getHistoryForNetwork(sTemplateWifi, FIELD_ALL); - assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0); - assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration()); - assertEquals(4, history.size()); - - } - - @Test - public void testUidStatsAcrossNetworks() throws Exception { - // pretend first mobile network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some traffic on first network - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); - mService.incrementOperationCount(UID_RED, 0xF00D, 10); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 10); - assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 0); - - - // now switch networks; this also tests that we're okay with interfaces - // disappearing, to verify we don't count backwards. - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_2)}; - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - forcePollAndWaitForIdle(); - - - // create traffic on second network - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L)); - mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10); - - forcePollAndWaitForIdle(); - - // verify original history still intact - assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); - assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 10); - assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 0); - - // and verify new history also recorded under different template, which - // verifies that we didn't cross the streams. - assertNetworkTotal(sTemplateImsi2, 128L, 1L, 1024L, 8L, 0); - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1L, 1024L, 8L, 10); - - } - - @Test - public void testUidRemovedIsMoved() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, - 4096L, 258L, 512L, 32L, 0L) - .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); - mService.incrementOperationCount(UID_RED, 0xFAAD, 10); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0); - assertUidTotal(sTemplateWifi, UID_RED, 16L, 1L, 16L, 1L, 10); - assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 258L, 512L, 32L, 0); - assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0); - - - // now pretend two UIDs are uninstalled, which should migrate stats to - // special "removed" bucket. - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L)); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, - 4096L, 258L, 512L, 32L, 0L) - .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)); - final Intent intent = new Intent(ACTION_UID_REMOVED); - intent.putExtra(EXTRA_UID, UID_BLUE); - mServiceContext.sendBroadcast(intent); - intent.putExtra(EXTRA_UID, UID_RED); - mServiceContext.sendBroadcast(intent); - - // existing uid and total should remain unchanged; but removed UID - // should be gone completely. - assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0); - assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0); - assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L, 0L, 0L, 0); - assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 0); - assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 259L, 528L, 33L, 10); - - } - - @Test - public void testMobileStatsByRatType() throws Exception { - final NetworkTemplate template3g = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); - final NetworkTemplate template4g = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE); - final NetworkTemplate template5g = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR); - final NetworkStateSnapshot[] states = - new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; - - // 3G network comes online. - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 12L, 18L, 14L, 1L, 0L))); - forcePollAndWaitForIdle(); - - // Verify 3g templates gets stats. - assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0); - assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); - assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0); - assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); - - // 4G network comes online. - incrementCurrentTime(MINUTE_IN_MILLIS); - setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - // Append more traffic on existing 3g stats entry. - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 16L, 22L, 17L, 2L, 0L)) - // Add entry that is new on 4g. - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, - 33L, 27L, 8L, 10L, 1L))); - forcePollAndWaitForIdle(); - - // Verify ALL_MOBILE template gets all. 3g template counters do not increase. - assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1); - assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); - // Verify 4g template counts appended stats on existing entry and newly created entry. - assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); - // Verify 5g template doesn't get anything since no traffic is generated on 5g. - assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0); - - // 5g network comes online. - incrementCurrentTime(MINUTE_IN_MILLIS); - setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - // Existing stats remains. - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 16L, 22L, 17L, 2L, 0L)) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, - 33L, 27L, 8L, 10L, 1L)) - // Add some traffic on 5g. - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 5L, 13L, 31L, 9L, 2L))); - forcePollAndWaitForIdle(); - - // Verify ALL_MOBILE template gets all. - assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3); - // 3g/4g template counters do not increase. - assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); - assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1); - // Verify 5g template gets the 5g count. - assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2); - } - - @Test - public void testMobileStatsOemManaged() throws Exception { - final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID, - SUBSCRIBER_ID_MATCH_RULE_EXACT); - - final NetworkTemplate templateOemPrivate = new NetworkTemplate(MATCH_MOBILE_WILDCARD, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE, - SUBSCRIBER_ID_MATCH_RULE_EXACT); - - final NetworkTemplate templateOemAll = new NetworkTemplate(MATCH_MOBILE_WILDCARD, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - OEM_PAID | OEM_PRIVATE, SUBSCRIBER_ID_MATCH_RULE_EXACT); - - final NetworkTemplate templateOemYes = new NetworkTemplate(MATCH_MOBILE_WILDCARD, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES, - SUBSCRIBER_ID_MATCH_RULE_EXACT); - - final NetworkTemplate templateOemNone = new NetworkTemplate(MATCH_MOBILE_WILDCARD, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO, - SUBSCRIBER_ID_MATCH_RULE_EXACT); - - // OEM_PAID network comes online. - NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ - buildOemManagedMobileState(IMSI_1, false, - new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 36L, 41L, 24L, 96L, 0L))); - forcePollAndWaitForIdle(); - - // OEM_PRIVATE network comes online. - states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, - new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 49L, 71L, 72L, 48L, 0L))); - forcePollAndWaitForIdle(); - - // OEM_PAID + OEM_PRIVATE network comes online. - states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, - new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, - NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 57L, 86L, 83L, 93L, 0L))); - forcePollAndWaitForIdle(); - - // OEM_NONE network comes online. - states = new NetworkStateSnapshot[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 29L, 73L, 34L, 31L, 0L))); - forcePollAndWaitForIdle(); - - // Verify OEM_PAID template gets only relevant stats. - assertUidTotal(templateOemPaid, UID_RED, 36L, 41L, 24L, 96L, 0); - - // Verify OEM_PRIVATE template gets only relevant stats. - assertUidTotal(templateOemPrivate, UID_RED, 49L, 71L, 72L, 48L, 0); - - // Verify OEM_PAID + OEM_PRIVATE template gets only relevant stats. - assertUidTotal(templateOemAll, UID_RED, 57L, 86L, 83L, 93L, 0); - - // Verify OEM_NONE sees only non-OEM managed stats. - assertUidTotal(templateOemNone, UID_RED, 29L, 73L, 34L, 31L, 0); - - // Verify OEM_MANAGED_YES sees all OEM managed stats. - assertUidTotal(templateOemYes, UID_RED, - 36L + 49L + 57L, - 41L + 71L + 86L, - 24L + 72L + 83L, - 96L + 48L + 93L, 0); - - // Verify ALL_MOBILE template gets both OEM managed and non-OEM managed stats. - assertUidTotal(sTemplateImsi1, UID_RED, - 36L + 49L + 57L + 29L, - 41L + 71L + 86L + 73L, - 24L + 72L + 83L + 34L, - 96L + 48L + 93L + 31L, 0); - } - - // TODO: support per IMSI state - private void setMobileRatTypeAndWaitForIdle(int ratType) { - when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString())) - .thenReturn(ratType); - mService.handleOnCollapsedRatTypeChanged(); - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - } - - @Test - public void testSummaryForAllUid() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some traffic for two apps - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L)); - mService.incrementOperationCount(UID_RED, 0xF00D, 1); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 8L, 512L, 4L, 0); - - - // now create more traffic in next hour, but only for one app - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, - 2048L, 16L, 1024L, 8L, 0L)); - forcePollAndWaitForIdle(); - - // first verify entire history present - NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(3, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 50L, 5L, 50L, 5L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 10L, 1L, 10L, 1L, 1); - assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 2048L, 16L, 1024L, 8L, 0); - - // now verify that recent history only contains one uid - final long currentTime = currentTimeMillis(); - stats = mSession.getSummaryForAllUid( - sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true); - assertEquals(1, stats.size()); - assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1024L, 8L, 512L, 4L, 0); - } - - @Test - public void testDetailedUidStats() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - NetworkStats.Entry entry1 = new NetworkStats.Entry( - TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); - NetworkStats.Entry entry2 = new NetworkStats.Entry( - TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 50L, 5L, 50L, 5L, 0L); - NetworkStats.Entry entry3 = new NetworkStats.Entry( - TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, 1024L, 8L, 512L, 4L, 0L); - - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .insertEntry(entry1) - .insertEntry(entry2) - .insertEntry(entry3)); - mService.incrementOperationCount(UID_RED, 0xF00D, 1); - - NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL); - - assertEquals(3, stats.size()); - entry1.operations = 1; - assertEquals(entry1, stats.getValues(0, null)); - entry2.operations = 1; - assertEquals(entry2, stats.getValues(1, null)); - assertEquals(entry3, stats.getValues(2, null)); - } - - @Test - public void testDetailedUidStats_Filtered() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - - final String stackedIface = "stacked-test0"; - final LinkProperties stackedProp = new LinkProperties(); - stackedProp.setInterfaceName(stackedIface); - final NetworkStateSnapshot wifiState = buildWifiState(); - wifiState.getLinkProperties().addStackedLink(stackedProp); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {wifiState}; - - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - NetworkStats.Entry uidStats = new NetworkStats.Entry( - TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); - // Stacked on matching interface - NetworkStats.Entry tetheredStats1 = new NetworkStats.Entry( - stackedIface, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); - // Different interface - NetworkStats.Entry tetheredStats2 = new NetworkStats.Entry( - "otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); - - final String[] ifaceFilter = new String[] { TEST_IFACE }; - final String[] augmentedIfaceFilter = new String[] { stackedIface, TEST_IFACE }; - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - when(mStatsFactory.augmentWithStackedInterfaces(eq(ifaceFilter))) - .thenReturn(augmentedIfaceFilter); - when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL))) - .thenReturn(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(uidStats)); - when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)) - .thenReturn(new NetworkStats(getElapsedRealtime(), 2) - .insertEntry(tetheredStats1) - .insertEntry(tetheredStats2)); - - NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); - - // mStatsFactory#readNetworkStatsDetail() has the following invocations: - // 1) NetworkStatsService#systemReady from #setUp. - // 2) mService#forceUpdateIfaces in the test above. - // - // Additionally, we should have one call from the above call to mService#getDetailedUidStats - // with the augmented ifaceFilter. - verify(mStatsFactory, times(2)).readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL); - verify(mStatsFactory, times(1)).readNetworkStatsDetail( - eq(UID_ALL), - eq(augmentedIfaceFilter), - eq(TAG_ALL)); - assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE)); - assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface)); - assertEquals(2, stats.size()); - assertEquals(uidStats, stats.getValues(0, null)); - assertEquals(tetheredStats1, stats.getValues(1, null)); - } - - @Test - public void testForegroundBackground() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some initial traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)); - mService.incrementOperationCount(UID_RED, 0xF00D, 1); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); - - - // now switch to foreground - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L)); - mService.setUidForeground(UID_RED, true); - mService.incrementOperationCount(UID_RED, 0xFAAD, 1); - - forcePollAndWaitForIdle(); - - // test that we combined correctly - assertUidTotal(sTemplateWifi, UID_RED, 160L, 4L, 160L, 4L, 2); - - // verify entire history present - final NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(4, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 32L, 2L, 32L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1L, 1L, 1L, 1L, 1); - } - - @Test - public void testMetered() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = - new NetworkStateSnapshot[] {buildWifiState(true /* isMetered */, TEST_IFACE)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some initial traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO - // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer. - // We layer them on top by inspecting the iface properties. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); - mService.incrementOperationCount(UID_RED, 0xF00D, 1); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); - // verify entire history present - final NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(2, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1); - } - - @Test - public void testRoaming() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkStateSnapshot[] states = - new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1, true /* isRoaming */)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - // Note that all traffic from NetworkManagementService is tagged as METERED_NO and - // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it - // on top by inspecting the iface properties. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L)); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0); - - // verify entire history present - final NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(2, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_YES, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_YES, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0); - } - - @Test - public void testTethering() throws Exception { - // pretend first mobile network comes online - expectDefaultSettings(); - final NetworkStateSnapshot[] states = - new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // create some tethering traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - - // Register custom provider and retrieve callback. - final TestableNetworkStatsProviderBinder provider = - new TestableNetworkStatsProviderBinder(); - final INetworkStatsProviderCallback cb = - mService.registerNetworkStatsProvider("TEST-TETHERING-OFFLOAD", provider); - assertNotNull(cb); - final long now = getElapsedRealtime(); - - // Traffic seen by kernel counters (includes software tethering). - final NetworkStats swIfaceStats = new NetworkStats(now, 1) - .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L); - // Hardware tethering traffic, not seen by kernel counters. - final NetworkStats tetherHwIfaceStats = new NetworkStats(now, 1) - .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_ALL, SET_DEFAULT, - TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 512L, 4L, 128L, 1L, 0L)); - final NetworkStats tetherHwUidStats = new NetworkStats(now, 1) - .insertEntry(new NetworkStats.Entry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, - TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 512L, 4L, 128L, 1L, 0L)); - cb.notifyStatsUpdated(0 /* unused */, tetherHwIfaceStats, tetherHwUidStats); - - // Fake some traffic done by apps on the device (as opposed to tethering), and record it - // into UID stats (as opposed to iface stats). - final NetworkStats localUidStats = new NetworkStats(now, 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L); - // Software per-uid tethering traffic. - final NetworkStats tetherSwUidStats = new NetworkStats(now, 1) - .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1408L, 10L, 256L, 1L, - 0L); - - expectNetworkStatsSummary(swIfaceStats); - expectNetworkStatsUidDetail(localUidStats, tetherSwUidStats); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0); - assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 0); - assertUidTotal(sTemplateImsi1, UID_TETHERING, 1920L, 14L, 384L, 2L, 0); - } - - @Test - public void testRegisterUsageCallback() throws Exception { - // pretend that wifi network comes online; service should ask about full - // network state, and poll any existing interfaces before updating. - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // verify service has empty history for wifi - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - long thresholdInBytes = 1L; // very small; should be overriden by framework - DataUsageRequest inputRequest = new DataUsageRequest( - DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdInBytes); - - // Create a messenger that waits for callback activity - ConditionVariable cv = new ConditionVariable(false); - LatchedHandler latchedHandler = new LatchedHandler(Looper.getMainLooper(), cv); - Messenger messenger = new Messenger(latchedHandler); - - // Force poll - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - // Register and verify request and that binder was called - DataUsageRequest request = - mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest, - messenger, mBinder); - assertTrue(request.requestId > 0); - assertTrue(Objects.equals(sTemplateWifi, request.template)); - long minThresholdInBytes = 2 * 1024 * 1024; // 2 MB - assertEquals(minThresholdInBytes, request.thresholdInBytes); - - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - - // Make sure that the caller binder gets connected - verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - - // modify some number on wifi, and trigger poll event - // not enough traffic to call data usage callback - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); - - // make sure callback has not being called - assertEquals(INVALID_TYPE, latchedHandler.lastMessageType); - - // and bump forward again, with counters going higher. this is - // important, since it will trigger the data usage callback - incrementCurrentTime(DAY_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); - - // verify service recorded history - assertNetworkTotal(sTemplateWifi, 4096000L, 4L, 8192000L, 8L, 0); - - - // Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED - assertTrue(cv.block(WAIT_TIMEOUT)); - assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.lastMessageType); - cv.close(); - - // Allow binder to disconnect - when(mBinder.unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt())).thenReturn(true); - - // Unregister request - mService.unregisterUsageRequest(request); - - // Wait for the caller to ack receipt of CALLBACK_RELEASED - assertTrue(cv.block(WAIT_TIMEOUT)); - assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.lastMessageType); - - // Make sure that the caller binder gets disconnected - verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - } - - @Test - public void testUnregisterUsageCallback_unknown_noop() throws Exception { - String callingPackage = "the.calling.package"; - long thresholdInBytes = 10 * 1024 * 1024; // 10 MB - DataUsageRequest unknownRequest = new DataUsageRequest( - 2 /* requestId */, sTemplateImsi1, thresholdInBytes); - - mService.unregisterUsageRequest(unknownRequest); - } - - @Test - public void testStatsProviderUpdateStats() throws Exception { - // Pretend that network comes online. - expectDefaultSettings(); - final NetworkStateSnapshot[] states = - new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - // Register custom provider and retrieve callback. - final TestableNetworkStatsProviderBinder provider = - new TestableNetworkStatsProviderBinder(); - final INetworkStatsProviderCallback cb = - mService.registerNetworkStatsProvider("TEST", provider); - assertNotNull(cb); - - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Verifies that one requestStatsUpdate will be called during iface update. - provider.expectOnRequestStatsUpdate(0 /* unused */); - - // Create some initial traffic and report to the service. - incrementCurrentTime(HOUR_IN_MILLIS); - final NetworkStats expectedStats = new NetworkStats(0L, 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, - TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 128L, 2L, 128L, 2L, 1L)) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, - 0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 64L, 1L, 64L, 1L, 1L)); - cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); - - // Make another empty mutable stats object. This is necessary since the new NetworkStats - // object will be used to compare with the old one in NetworkStatsRecoder, two of them - // cannot be the same object. - expectNetworkStatsUidDetail(buildEmptyStats()); - - forcePollAndWaitForIdle(); - - // Verifies that one requestStatsUpdate and setAlert will be called during polling. - provider.expectOnRequestStatsUpdate(0 /* unused */); - provider.expectOnSetAlert(MB_IN_BYTES); - - // Verifies that service recorded history, does not verify uid tag part. - assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1); - - // Verifies that onStatsUpdated updates the stats accordingly. - final NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(2, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); - - // Verifies that unregister the callback will remove the provider from service. - cb.unregister(); - forcePollAndWaitForIdle(); - provider.assertNoCallback(); - } - - @Test - public void testDualVilteProviderStats() throws Exception { - // Pretend that network comes online. - expectDefaultSettings(); - final int subId1 = 1; - final int subId2 = 2; - final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ - buildImsState(IMSI_1, subId1, TEST_IFACE), - buildImsState(IMSI_2, subId2, TEST_IFACE2)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - // Register custom provider and retrieve callback. - final TestableNetworkStatsProviderBinder provider = - new TestableNetworkStatsProviderBinder(); - final INetworkStatsProviderCallback cb = - mService.registerNetworkStatsProvider("TEST", provider); - assertNotNull(cb); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Verifies that one requestStatsUpdate will be called during iface update. - provider.expectOnRequestStatsUpdate(0 /* unused */); - - // Create some initial traffic and report to the service. - incrementCurrentTime(HOUR_IN_MILLIS); - final String vtIface1 = NetworkStats.IFACE_VT + subId1; - final String vtIface2 = NetworkStats.IFACE_VT + subId2; - final NetworkStats expectedStats = new NetworkStats(0L, 1) - .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT, - TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 128L, 2L, 128L, 2L, 1L)) - .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT, - TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, - 64L, 1L, 64L, 1L, 1L)); - cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); - - // Make another empty mutable stats object. This is necessary since the new NetworkStats - // object will be used to compare with the old one in NetworkStatsRecoder, two of them - // cannot be the same object. - expectNetworkStatsUidDetail(buildEmptyStats()); - - forcePollAndWaitForIdle(); - - // Verifies that one requestStatsUpdate and setAlert will be called during polling. - provider.expectOnRequestStatsUpdate(0 /* unused */); - provider.expectOnSetAlert(MB_IN_BYTES); - - // Verifies that service recorded history, does not verify uid tag part. - assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1); - - // Verifies that onStatsUpdated updates the stats accordingly. - NetworkStats stats = mSession.getSummaryForAllUid( - sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(1, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); - - stats = mSession.getSummaryForAllUid( - sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertEquals(1, stats.size()); - assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, - DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); - - // Verifies that unregister the callback will remove the provider from service. - cb.unregister(); - forcePollAndWaitForIdle(); - provider.assertNoCallback(); - } - - @Test - public void testStatsProviderSetAlert() throws Exception { - // Pretend that network comes online. - expectDefaultSettings(); - NetworkStateSnapshot[] states = - new NetworkStateSnapshot[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Register custom provider and retrieve callback. - final TestableNetworkStatsProviderBinder provider = - new TestableNetworkStatsProviderBinder(); - final INetworkStatsProviderCallback cb = - mService.registerNetworkStatsProvider("TEST", provider); - assertNotNull(cb); - - // Simulates alert quota of the provider has been reached. - cb.notifyAlertReached(); - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - - // Verifies that polling is triggered by alert reached. - provider.expectOnRequestStatsUpdate(0 /* unused */); - // Verifies that global alert will be re-armed. - provider.expectOnSetAlert(MB_IN_BYTES); - } - - private void setCombineSubtypeEnabled(boolean enable) { - when(mSettings.getCombineSubtypeEnabled()).thenReturn(enable); - mHandler.post(() -> mContentObserver.onChange(false, Settings.Global - .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED))); - waitForIdle(); - if (enable) { - verify(mNetworkStatsSubscriptionsMonitor).stop(); - } else { - verify(mNetworkStatsSubscriptionsMonitor).start(); - } - } - - @Test - public void testDynamicWatchForNetworkRatTypeChanges() throws Exception { - // Build 3G template, type unknown template to get stats while network type is unknown - // and type all template to get the sum of all network type stats. - final NetworkTemplate template3g = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); - final NetworkTemplate templateUnknown = - buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN); - final NetworkTemplate templateAll = - buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL); - final NetworkStateSnapshot[] states = - new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)}; - - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - // 3G network comes online. - setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 12L, 18L, 14L, 1L, 0L))); - forcePollAndWaitForIdle(); - - // Since CombineSubtypeEnabled is false by default in unit test, the generated traffic - // will be split by RAT type. Verify 3G templates gets stats, while template with unknown - // RAT type gets nothing, and template with NETWORK_TYPE_ALL gets all stats. - assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); - assertUidTotal(templateUnknown, UID_RED, 0L, 0L, 0L, 0L, 0); - assertUidTotal(templateAll, UID_RED, 12L, 18L, 14L, 1L, 0); - - // Stop monitoring data usage per RAT type changes NetworkStatsService records data - // to {@link TelephonyManager#NETWORK_TYPE_UNKNOWN}. - setCombineSubtypeEnabled(true); - - // Call handleOnCollapsedRatTypeChanged manually to simulate the callback fired - // when stopping monitor, this is needed by NetworkStatsService to trigger updateIfaces. - mService.handleOnCollapsedRatTypeChanged(); - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - // Append more traffic on existing snapshot. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 12L + 4L, 18L + 4L, 14L + 3L, 1L + 1L, 0L)) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, - 35L, 29L, 7L, 11L, 1L))); - forcePollAndWaitForIdle(); - - // Verify 3G counters do not increase, while template with unknown RAT type gets new - // traffic and template with NETWORK_TYPE_ALL gets all stats. - assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0); - assertUidTotal(templateUnknown, UID_RED, 4L + 35L, 4L + 29L, 3L + 7L, 1L + 11L, 1); - assertUidTotal(templateAll, UID_RED, 16L + 35L, 22L + 29L, 17L + 7L, 2L + 11L, 1); - - // Start monitoring data usage per RAT type changes and NetworkStatsService records data - // by a granular subtype representative of the actual subtype - setCombineSubtypeEnabled(false); - - mService.handleOnCollapsedRatTypeChanged(); - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - // Create some traffic. - incrementCurrentTime(MINUTE_IN_MILLIS); - // Append more traffic on existing snapshot. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, - 22L, 26L, 19L, 5L, 0L)) - .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, - 35L, 29L, 7L, 11L, 1L))); - forcePollAndWaitForIdle(); - - // Verify traffic is split by RAT type, no increase on template with unknown RAT type - // and template with NETWORK_TYPE_ALL gets all stats. - assertUidTotal(template3g, UID_RED, 6L + 12L , 4L + 18L, 2L + 14L, 3L + 1L, 0); - assertUidTotal(templateUnknown, UID_RED, 4L + 35L, 4L + 29L, 3L + 7L, 1L + 11L, 1); - assertUidTotal(templateAll, UID_RED, 22L + 35L, 26L + 29L, 19L + 7L, 5L + 11L, 1); - } - - @Test - public void testOperationCount_nonDefault_traffic() throws Exception { - // Pretend mobile network comes online, but wifi is the default network. - expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ - buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)}; - expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), - new UnderlyingNetworkInfo[0]); - - // Create some traffic on mobile network. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 2L, 1L, 3L, 4L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_YES, 1L, 3L, 2L, 1L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 5L, 4L, 1L, 4L, 0L)); - // Increment operation count, which must have a specific tag. - mService.incrementOperationCount(UID_RED, 0xF00D, 2); - forcePollAndWaitForIdle(); - - // Verify mobile summary is not changed by the operation count. - final NetworkTemplate templateMobile = - buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL); - final NetworkStats statsMobile = mSession.getSummaryForAllUid( - templateMobile, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 3L, 4L, 5L, 5L, 0); - assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, 0xF00D, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 5L, 4L, 1L, 4L, 0); - - // Verify the operation count is blamed onto the default network. - // TODO: Blame onto the default network is not very reasonable. Consider blame onto the - // network that generates the traffic. - final NetworkTemplate templateWifi = buildTemplateWifiWildcard(); - final NetworkStats statsWifi = mSession.getSummaryForAllUid( - templateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true); - assertValues(statsWifi, IFACE_ALL, UID_RED, SET_ALL, 0xF00D, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 2); - } - - private static File getBaseDir(File statsDir) { - File baseDir = new File(statsDir, "netstats"); - baseDir.mkdirs(); - return baseDir; - } - - private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets, - long txBytes, long txPackets, int operations) throws Exception { - assertNetworkTotal(template, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, - txPackets, operations); - } - - private void assertNetworkTotal(NetworkTemplate template, long start, long end, long rxBytes, - long rxPackets, long txBytes, long txPackets, int operations) throws Exception { - // verify history API - final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELD_ALL); - assertValues(history, start, end, rxBytes, rxPackets, txBytes, txPackets, operations); - - // verify summary API - final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end); - assertValues(stats, IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, rxBytes, rxPackets, txBytes, txPackets, operations); - } - - private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets, - long txBytes, long txPackets, int operations) throws Exception { - assertUidTotal(template, uid, SET_ALL, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, - rxBytes, rxPackets, txBytes, txPackets, operations); - } - - private void assertUidTotal(NetworkTemplate template, int uid, int set, int metered, - int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes, - long txPackets, int operations) throws Exception { - // verify history API - final NetworkStatsHistory history = mSession.getHistoryForUid( - template, uid, set, TAG_NONE, FIELD_ALL); - assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes, - txPackets, operations); - - // verify summary API - final NetworkStats stats = mSession.getSummaryForAllUid( - template, Long.MIN_VALUE, Long.MAX_VALUE, false); - assertValues(stats, IFACE_ALL, uid, set, TAG_NONE, metered, roaming, defaultNetwork, - rxBytes, rxPackets, txBytes, txPackets, operations); - } - - private void expectSystemReady() throws Exception { - expectNetworkStatsSummary(buildEmptyStats()); - } - - private String getActiveIface(NetworkStateSnapshot... states) throws Exception { - if (states == null || states.length == 0 || states[0].getLinkProperties() == null) { - return null; - } - return states[0].getLinkProperties().getInterfaceName(); - } - - private void expectNetworkStatsSummary(NetworkStats summary) throws Exception { - expectNetworkStatsSummaryDev(summary.clone()); - expectNetworkStatsSummaryXt(summary.clone()); - } - - private void expectNetworkStatsSummaryDev(NetworkStats summary) throws Exception { - when(mStatsFactory.readNetworkStatsSummaryDev()).thenReturn(summary); - } - - private void expectNetworkStatsSummaryXt(NetworkStats summary) throws Exception { - when(mStatsFactory.readNetworkStatsSummaryXt()).thenReturn(summary); - } - - private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception { - expectNetworkStatsUidDetail(detail, new NetworkStats(0L, 0)); - } - - private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats) - throws Exception { - when(mStatsFactory.readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL)) - .thenReturn(detail); - - // also include tethering details, since they are folded into UID - when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats); - } - - private void expectDefaultSettings() throws Exception { - expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); - } - - private void expectSettings(long persistBytes, long bucketDuration, long deleteAge) - throws Exception { - when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS); - when(mSettings.getPollDelay()).thenReturn(0L); - when(mSettings.getSampleEnabled()).thenReturn(true); - when(mSettings.getCombineSubtypeEnabled()).thenReturn(false); - - final Config config = new Config(bucketDuration, deleteAge, deleteAge); - when(mSettings.getDevConfig()).thenReturn(config); - when(mSettings.getXtConfig()).thenReturn(config); - when(mSettings.getUidConfig()).thenReturn(config); - when(mSettings.getUidTagConfig()).thenReturn(config); - - when(mSettings.getGlobalAlertBytes(anyLong())).thenReturn(MB_IN_BYTES); - when(mSettings.getDevPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); - when(mSettings.getXtPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); - when(mSettings.getUidPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); - when(mSettings.getUidTagPersistBytes(anyLong())).thenReturn(MB_IN_BYTES); - } - - private void assertStatsFilesExist(boolean exist) { - final File basePath = new File(mStatsDir, "netstats"); - if (exist) { - assertTrue(basePath.list().length > 0); - } else { - assertTrue(basePath.list().length == 0); - } - } - - private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, - long rxPackets, long txBytes, long txPackets, int operations) { - final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - assertEquals("unexpected operations", operations, entry.operations); - } - - private static NetworkStateSnapshot buildWifiState() { - return buildWifiState(false, TEST_IFACE); - } - - private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(iface); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); - capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - capabilities.setSSID(TEST_SSID); - return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI); - } - - private static NetworkStateSnapshot buildMobile3gState(String subscriberId) { - return buildMobile3gState(subscriberId, false /* isRoaming */); - } - - private static NetworkStateSnapshot buildMobile3gState(String subscriberId, boolean isRoaming) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_IFACE); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); - capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new NetworkStateSnapshot( - MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); - } - - private NetworkStats buildEmptyStats() { - return new NetworkStats(getElapsedRealtime(), 0); - } - - private static NetworkStateSnapshot buildOemManagedMobileState( - String subscriberId, boolean isRoaming, int[] oemNetCapabilities) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(TEST_IFACE); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); - for (int nc : oemNetCapabilities) { - capabilities.setCapability(nc, true); - } - capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new NetworkStateSnapshot(MOBILE_NETWORK, capabilities, prop, subscriberId, - TYPE_MOBILE); - } - - private static NetworkStateSnapshot buildImsState( - String subscriberId, int subId, String ifaceName) { - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(ifaceName); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true); - capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)); - return new NetworkStateSnapshot( - MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); - } - - private long getElapsedRealtime() { - return mElapsedRealtime; - } - - private long startTimeMillis() { - return TEST_START; - } - - private long currentTimeMillis() { - return startTimeMillis() + mElapsedRealtime; - } - - private void incrementCurrentTime(long duration) { - mElapsedRealtime += duration; - } - - private void forcePollAndWaitForIdle() { - mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - waitForIdle(); - } - - private void waitForIdle() { - HandlerUtils.waitForIdle(mHandlerThread, WAIT_TIMEOUT); - } - - static class LatchedHandler extends Handler { - private final ConditionVariable mCv; - int lastMessageType = INVALID_TYPE; - - LatchedHandler(Looper looper, ConditionVariable cv) { - super(looper); - mCv = cv; - } - - @Override - public void handleMessage(Message msg) { - lastMessageType = msg.what; - mCv.open(); - super.handleMessage(msg); - } - } -} diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java deleted file mode 100644 index 6d2c7dc39ffd..000000000000 --- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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.net; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.net.NetworkTemplate; -import android.os.test.TestLooper; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; - -import com.android.internal.util.CollectionUtils; -import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -@RunWith(JUnit4.class) -public final class NetworkStatsSubscriptionsMonitorTest { - private static final int TEST_SUBID1 = 3; - private static final int TEST_SUBID2 = 5; - private static final String TEST_IMSI1 = "466921234567890"; - private static final String TEST_IMSI2 = "466920987654321"; - private static final String TEST_IMSI3 = "466929999999999"; - - @Mock private Context mContext; - @Mock private SubscriptionManager mSubscriptionManager; - @Mock private TelephonyManager mTelephonyManager; - @Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate; - private final List mTestSubList = new ArrayList<>(); - - private final Executor mExecutor = Executors.newSingleThreadExecutor(); - private NetworkStatsSubscriptionsMonitor mMonitor; - private TestLooper mTestLooper = new TestLooper(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); - - when(mContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE))) - .thenReturn(mSubscriptionManager); - when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))) - .thenReturn(mTelephonyManager); - - mMonitor = new NetworkStatsSubscriptionsMonitor(mContext, mTestLooper.getLooper(), - mExecutor, mDelegate); - } - - @Test - public void testStartStop() { - // Verify that addOnSubscriptionsChangedListener() is never called before start(). - verify(mSubscriptionManager, never()) - .addOnSubscriptionsChangedListener(mExecutor, mMonitor); - mMonitor.start(); - verify(mSubscriptionManager).addOnSubscriptionsChangedListener(mExecutor, mMonitor); - - // Verify that removeOnSubscriptionsChangedListener() is never called before stop() - verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(mMonitor); - mMonitor.stop(); - verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mMonitor); - } - - @NonNull - private static int[] convertArrayListToIntArray(@NonNull List arrayList) { - final int[] list = new int[arrayList.size()]; - for (int i = 0; i < arrayList.size(); i++) { - list[i] = arrayList.get(i); - } - return list; - } - - private void setRatTypeForSub(List listeners, - int subId, int type) { - final ServiceState serviceState = mock(ServiceState.class); - when(serviceState.getDataNetworkType()).thenReturn(type); - final RatTypeListener match = CollectionUtils - .find(listeners, it -> it.getSubId() == subId); - if (match == null) { - fail("Could not find listener with subId: " + subId); - } - match.onServiceStateChanged(serviceState); - } - - private void addTestSub(int subId, String subscriberId) { - // add SubId to TestSubList. - if (mTestSubList.contains(subId)) fail("The subscriber list already contains this ID"); - - mTestSubList.add(subId); - - final int[] subList = convertArrayListToIntArray(mTestSubList); - when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList); - when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId); - mMonitor.onSubscriptionsChanged(); - } - - private void updateSubscriberIdForTestSub(int subId, @Nullable final String subscriberId) { - when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId); - mMonitor.onSubscriptionsChanged(); - } - - private void removeTestSub(int subId) { - // Remove subId from TestSubList. - mTestSubList.removeIf(it -> it == subId); - final int[] subList = convertArrayListToIntArray(mTestSubList); - when(mSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(subList); - mMonitor.onSubscriptionsChanged(); - } - - private void assertRatTypeChangedForSub(String subscriberId, int ratType) { - assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId)); - final ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(Integer.class); - // Verify callback with the subscriberId and the RAT type should be as expected. - // It will fail if get a callback with an unexpected RAT type. - verify(mDelegate).onCollapsedRatTypeChanged(eq(subscriberId), typeCaptor.capture()); - final int type = typeCaptor.getValue(); - assertEquals(ratType, type); - } - - private void assertRatTypeNotChangedForSub(String subscriberId, int ratType) { - assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType); - // Should never get callback with any RAT type. - verify(mDelegate, never()).onCollapsedRatTypeChanged(eq(subscriberId), anyInt()); - } - - @Test - public void testSubChangedAndRatTypeChanged() { - final ArgumentCaptor ratTypeListenerCaptor = - ArgumentCaptor.forClass(RatTypeListener.class); - - mMonitor.start(); - // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback - // before changing RAT type. - addTestSub(TEST_SUBID1, TEST_IMSI1); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - - // Insert sim2. - addTestSub(TEST_SUBID2, TEST_IMSI2); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - verify(mTelephonyManager, times(2)).listen(ratTypeListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - reset(mDelegate); - - // Set RAT type of sim1 to UMTS. - // Verify RAT type of sim1 after subscription gets onCollapsedRatTypeChanged() callback - // and others remain untouched. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); - assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - - // Set RAT type of sim2 to LTE. - // Verify RAT type of sim2 after subscription gets onCollapsedRatTypeChanged() callback - // and others remain untouched. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID2, - TelephonyManager.NETWORK_TYPE_LTE); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_LTE); - assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - - // Remove sim2 and verify that callbacks are fired and RAT type is correct for sim2. - // while the other two remain untouched. - removeTestSub(TEST_SUBID2); - verify(mTelephonyManager).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); - assertRatTypeNotChangedForSub(TEST_IMSI3, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - - // Set RAT type of sim1 to UNKNOWN. Then stop monitoring subscription changes - // and verify that the listener for sim1 is removed. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_UNKNOWN); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - - mMonitor.stop(); - verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE)); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - } - - - @Test - public void test5g() { - mMonitor.start(); - // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback - // before changing RAT type. Also capture listener for later use. - addTestSub(TEST_SUBID1, TEST_IMSI1); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - final ArgumentCaptor ratTypeListenerCaptor = - ArgumentCaptor.forClass(RatTypeListener.class); - verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - final RatTypeListener listener = CollectionUtils - .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1); - assertNotNull(listener); - - // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs - // NETWORK_TYPE_5G_NSA. - final ServiceState serviceState = mock(ServiceState.class); - when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); - when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); - listener.onServiceStateChanged(serviceState); - assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA); - reset(mDelegate); - - // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE. - when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); - listener.onServiceStateChanged(serviceState); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); - reset(mDelegate); - - // Verify NR connected with other RAT type does not take effect. - when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS); - when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED); - listener.onServiceStateChanged(serviceState); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - reset(mDelegate); - - // Set RAT type to 5G standalone mode, the RAT type should be NR. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_NR); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); - reset(mDelegate); - - // Set NR state to none in standalone mode does not change anything. - when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR); - when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE); - listener.onServiceStateChanged(serviceState); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR); - } - - @Test - public void testSubscriberIdUnavailable() { - final ArgumentCaptor ratTypeListenerCaptor = - ArgumentCaptor.forClass(RatTypeListener.class); - - mMonitor.start(); - // Insert sim1, set subscriberId to null which is normal in SIM PIN locked case. - // Verify RAT type is NETWORK_TYPE_UNKNOWN and service will not perform listener - // registration. - addTestSub(TEST_SUBID1, null); - verify(mTelephonyManager, never()).listen(any(), anyInt()); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - - // Set IMSI for sim1, verify the listener will be registered. - updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1); - verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - reset(mTelephonyManager); - when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); - - // Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - reset(mDelegate); - - // Set IMSI to null again to simulate somehow IMSI is not available, such as - // modem crash. Verify service should unregister listener. - updateSubscriberIdForTestSub(TEST_SUBID1, null); - verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), - eq(PhoneStateListener.LISTEN_NONE)); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - clearInvocations(mTelephonyManager); - - // Simulate somehow IMSI is back. Verify service will register with - // another listener and fire callback accordingly. - final ArgumentCaptor ratTypeListenerCaptor2 = - ArgumentCaptor.forClass(RatTypeListener.class); - updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1); - verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - clearInvocations(mTelephonyManager); - - // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works. - setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_LTE); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE); - reset(mDelegate); - - mMonitor.stop(); - verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()), - eq(PhoneStateListener.LISTEN_NONE)); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - } - - /** - * Verify that when IMSI suddenly changed for a given subId, the service will register a new - * listener and unregister the old one, and report changes on updated IMSI. This is for modem - * feature that may be enabled for certain carrier, which changes to use a different IMSI while - * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same. - */ - @Test - public void testSubscriberIdChanged() { - mMonitor.start(); - // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback - // before changing RAT type. - addTestSub(TEST_SUBID1, TEST_IMSI1); - final ArgumentCaptor ratTypeListenerCaptor = - ArgumentCaptor.forClass(RatTypeListener.class); - verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - - // Set RAT type of sim1 to UMTS. - // Verify RAT type of sim1 changes accordingly. - setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS); - reset(mDelegate); - clearInvocations(mTelephonyManager); - - // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with - // another listener and remove the old one. The RAT type of new IMSI stays at - // NETWORK_TYPE_UNKNOWN until received initial callback from telephony. - final ArgumentCaptor ratTypeListenerCaptor2 = - ArgumentCaptor.forClass(RatTypeListener.class); - updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2); - verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(), - eq(PhoneStateListener.LISTEN_SERVICE_STATE)); - verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()), - eq(PhoneStateListener.LISTEN_NONE)); - assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN); - reset(mDelegate); - - // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received - // from telephony after registration. Verify RAT type of sim1 changes with IMSI2 - // accordingly. - setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1, - TelephonyManager.NETWORK_TYPE_UMTS); - assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN); - assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS); - reset(mDelegate); - } -} diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java deleted file mode 100644 index ebbc0ef62548..000000000000 --- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 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.net.ipmemorystore; - -import static org.junit.Assert.assertEquals; - -import android.net.ipmemorystore.NetworkAttributes; -import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.lang.reflect.Field; -import java.net.Inet4Address; -import java.net.UnknownHostException; -import java.util.Arrays; - -/** Unit tests for {@link NetworkAttributes}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class NetworkAttributesTest { - private static final String WEIGHT_FIELD_NAME_PREFIX = "WEIGHT_"; - private static final float EPSILON = 0.0001f; - - // This is running two tests to make sure the total weight is the sum of all weights. To be - // sure this is not fireproof, but you'd kind of need to do it on purpose to pass. - @Test - public void testTotalWeight() throws IllegalAccessException, UnknownHostException { - // Make sure that TOTAL_WEIGHT is equal to the sum of the fields starting with WEIGHT_ - float sum = 0f; - final Field[] fieldList = NetworkAttributes.class.getDeclaredFields(); - for (final Field field : fieldList) { - if (!field.getName().startsWith(WEIGHT_FIELD_NAME_PREFIX)) continue; - field.setAccessible(true); - sum += (float) field.get(null); - } - assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON); - - final IPv6ProvisioningLossQuirk ipv6ProvisioningLossQuirk = - new IPv6ProvisioningLossQuirk(3, System.currentTimeMillis() + 7_200_000); - // Use directly the constructor with all attributes, and make sure that when compared - // to itself the score is a clean 1.0f. - final NetworkAttributes na = - new NetworkAttributes( - (Inet4Address) Inet4Address.getByAddress(new byte[] {1, 2, 3, 4}), - System.currentTimeMillis() + 7_200_000, - "some hint", - Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}), - Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})), - 98, ipv6ProvisioningLossQuirk); - assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON); - } -} diff --git a/tests/net/jni/Android.bp b/tests/net/jni/Android.bp deleted file mode 100644 index 22a04f5c0945..000000000000 --- a/tests/net/jni/Android.bp +++ /dev/null @@ -1,32 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -cc_library_shared { - name: "libnetworkstatsfactorytestjni", - - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wthread-safety", - ], - - srcs: [ - ":lib_networkStatsFactory_native", - "test_onload.cpp", - ], - - shared_libs: [ - "libbpf_android", - "liblog", - "libnativehelper", - "libnetdbpf", - "libnetdutils", - ], -} diff --git a/tests/net/jni/test_onload.cpp b/tests/net/jni/test_onload.cpp deleted file mode 100644 index 5194ddb0d882..000000000000 --- a/tests/net/jni/test_onload.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -/* - * this is a mini native libaray for NetworkStatsFactoryTest to run properly. It - * load all the native method related to NetworkStatsFactory when test run - */ -#include -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" - -namespace android { -int register_android_server_net_NetworkStatsFactory(JNIEnv* env); -}; - -using namespace android; - -extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) -{ - JNIEnv* env = NULL; - jint result = -1; - - if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { - ALOGE("GetEnv failed!"); - return result; - } - ALOG_ASSERT(env, "Could not retrieve the env!"); - register_android_server_net_NetworkStatsFactory(env); - return JNI_VERSION_1_4; -} diff --git a/tests/net/res/raw/history_v1 b/tests/net/res/raw/history_v1 deleted file mode 100644 index de79491c032e..000000000000 Binary files a/tests/net/res/raw/history_v1 and /dev/null differ diff --git a/tests/net/res/raw/net_dev_typical b/tests/net/res/raw/net_dev_typical deleted file mode 100644 index 290bf03eb9b4..000000000000 --- a/tests/net/res/raw/net_dev_typical +++ /dev/null @@ -1,8 +0,0 @@ -Inter-| Receive | Transmit - face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed - lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0 -rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0 - ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 - ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0 - sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0 -ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0 diff --git a/tests/net/res/raw/netstats_uid_v4 b/tests/net/res/raw/netstats_uid_v4 deleted file mode 100644 index e75fc1ca5c2e..000000000000 Binary files a/tests/net/res/raw/netstats_uid_v4 and /dev/null differ diff --git a/tests/net/res/raw/netstats_v1 b/tests/net/res/raw/netstats_v1 deleted file mode 100644 index e80860a6b959..000000000000 Binary files a/tests/net/res/raw/netstats_v1 and /dev/null differ diff --git a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical b/tests/net/res/raw/xt_qtaguid_iface_fmt_typical deleted file mode 100644 index 656d5bb82da4..000000000000 --- a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical +++ /dev/null @@ -1,4 +0,0 @@ -ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets -rmnet2 4968 35 3081 39 -rmnet1 11153922 8051 190226 2468 -rmnet0 6824 16 5692 10 diff --git a/tests/net/res/raw/xt_qtaguid_iface_typical b/tests/net/res/raw/xt_qtaguid_iface_typical deleted file mode 100644 index 610723aef2f2..000000000000 --- a/tests/net/res/raw/xt_qtaguid_iface_typical +++ /dev/null @@ -1,6 +0,0 @@ -rmnet3 1 0 0 0 0 20822 501 1149991 815 -rmnet2 1 0 0 0 0 1594 15 1313 15 -rmnet1 1 0 0 0 0 207398 458 166918 565 -rmnet0 1 0 0 0 0 2112 24 700 10 -test1 1 1 2 3 4 5 6 7 8 -test2 0 1 2 3 4 5 6 7 8 diff --git a/tests/net/res/raw/xt_qtaguid_typical b/tests/net/res/raw/xt_qtaguid_typical deleted file mode 100644 index c1b0d259955c..000000000000 --- a/tests/net/res/raw/xt_qtaguid_typical +++ /dev/null @@ -1,71 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 wlan0 0x0 0 0 18621 96 2898 44 312 6 15897 58 2412 32 312 6 1010 16 1576 22 -3 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -4 wlan0 0x0 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -5 wlan0 0x0 1000 1 1949 13 1078 14 0 0 1600 10 349 3 0 0 600 10 478 4 -6 wlan0 0x0 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -7 wlan0 0x0 10005 1 32081 38 5315 50 32081 38 0 0 0 0 5315 50 0 0 0 0 -8 wlan0 0x0 10011 0 35777 53 5718 57 0 0 0 0 35777 53 0 0 0 0 5718 57 -9 wlan0 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -10 wlan0 0x0 10014 0 0 0 1098 13 0 0 0 0 0 0 0 0 0 0 1098 13 -11 wlan0 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -12 wlan0 0x0 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549 -13 wlan0 0x0 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -14 wlan0 0x0 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6 -15 wlan0 0x0 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -16 wlan0 0x7fffff0100000000 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549 -17 wlan0 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -18 wlan0 0x7fffff0100000000 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6 -19 wlan0 0x7fffff0100000000 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -20 rmnet2 0x0 0 0 547 5 118 2 40 1 243 1 264 3 0 0 62 1 56 1 -21 rmnet2 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -22 rmnet2 0x0 10001 0 1125899906842624 5 984 11 632 5 0 0 0 0 984 11 0 0 0 0 -23 rmnet2 0x0 10001 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -24 rmnet1 0x0 0 0 26736 174 7098 130 7210 97 18382 64 1144 13 2932 64 4054 64 112 2 -25 rmnet1 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -26 rmnet1 0x0 1000 0 75774 77 18038 78 75335 72 439 5 0 0 17668 73 370 5 0 0 -27 rmnet1 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -28 rmnet1 0x0 10007 0 269945 578 111632 586 269945 578 0 0 0 0 111632 586 0 0 0 0 -29 rmnet1 0x0 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -30 rmnet1 0x0 10011 0 1741256 6918 769778 7019 1741256 6918 0 0 0 0 769778 7019 0 0 0 0 -31 rmnet1 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -32 rmnet1 0x0 10014 0 0 0 786 12 0 0 0 0 0 0 786 12 0 0 0 0 -33 rmnet1 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -34 rmnet1 0x0 10021 0 433533 1454 393420 1604 433533 1454 0 0 0 0 393420 1604 0 0 0 0 -35 rmnet1 0x0 10021 1 21215 33 10278 33 21215 33 0 0 0 0 10278 33 0 0 0 0 -36 rmnet1 0x0 10036 0 6310 25 3284 29 6310 25 0 0 0 0 3284 29 0 0 0 0 -37 rmnet1 0x0 10036 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -38 rmnet1 0x0 10047 0 34264 47 3936 34 34264 47 0 0 0 0 3936 34 0 0 0 0 -39 rmnet1 0x0 10047 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -40 rmnet1 0x4e7700000000 10011 0 9187 27 4248 33 9187 27 0 0 0 0 4248 33 0 0 0 0 -41 rmnet1 0x4e7700000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -42 rmnet1 0x1000000000000000 10007 0 2109 4 791 4 2109 4 0 0 0 0 791 4 0 0 0 0 -43 rmnet1 0x1000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -44 rmnet1 0x1000000400000000 10007 0 9811 22 6286 22 9811 22 0 0 0 0 6286 22 0 0 0 0 -45 rmnet1 0x1000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -46 rmnet1 0x1010000000000000 10021 0 164833 426 135392 527 164833 426 0 0 0 0 135392 527 0 0 0 0 -47 rmnet1 0x1010000000000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -48 rmnet1 0x1144000400000000 10011 0 10112 18 3334 17 10112 18 0 0 0 0 3334 17 0 0 0 0 -49 rmnet1 0x1144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -50 rmnet1 0x1244000400000000 10011 0 1300 3 848 2 1300 3 0 0 0 0 848 2 0 0 0 0 -51 rmnet1 0x1244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -52 rmnet1 0x3000000000000000 10007 0 10389 14 1521 12 10389 14 0 0 0 0 1521 12 0 0 0 0 -53 rmnet1 0x3000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -54 rmnet1 0x3000000400000000 10007 0 238070 380 93938 404 238070 380 0 0 0 0 93938 404 0 0 0 0 -55 rmnet1 0x3000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -56 rmnet1 0x3010000000000000 10021 0 219110 578 227423 676 219110 578 0 0 0 0 227423 676 0 0 0 0 -57 rmnet1 0x3010000000000000 10021 1 742 3 1265 3 742 3 0 0 0 0 1265 3 0 0 0 0 -58 rmnet1 0x3020000000000000 10021 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -59 rmnet1 0x3020000000000000 10021 1 20473 30 9013 30 20473 30 0 0 0 0 9013 30 0 0 0 0 -60 rmnet1 0x3144000400000000 10011 0 43963 92 34414 116 43963 92 0 0 0 0 34414 116 0 0 0 0 -61 rmnet1 0x3144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -62 rmnet1 0x3244000400000000 10011 0 3486 8 1520 9 3486 8 0 0 0 0 1520 9 0 0 0 0 -63 rmnet1 0x3244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -64 rmnet1 0x7fffff0100000000 10021 0 29102 56 8865 60 29102 56 0 0 0 0 8865 60 0 0 0 0 -65 rmnet1 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -66 rmnet1 0x7fffff0300000000 1000 0 995 13 14145 14 995 13 0 0 0 0 14145 14 0 0 0 0 -67 rmnet1 0x7fffff0300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -68 rmnet0 0x0 0 0 4312 49 1288 23 0 0 0 0 4312 49 0 0 0 0 1288 23 -69 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -70 rmnet0 0x0 10080 0 22266 30 20976 30 0 0 0 0 22266 30 0 0 0 0 20976 30 -71 rmnet0 0x0 10080 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface deleted file mode 100644 index fc92715253ed..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface +++ /dev/null @@ -1,3 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying deleted file mode 100644 index 1ef18894b669..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying +++ /dev/null @@ -1,5 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 -4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression deleted file mode 100644 index 6d6bf550bbfa..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression +++ /dev/null @@ -1,4 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0 -4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic deleted file mode 100644 index 2c2e5d2555f6..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic +++ /dev/null @@ -1,6 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 -4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0 -5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn deleted file mode 100644 index eb0513b10049..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn +++ /dev/null @@ -1,9 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 -4 test_nss_tun1 0x0 1001 0 3000 300 700 70 0 0 0 0 0 0 0 0 0 0 0 0 -5 test_nss_tun1 0x0 1002 0 500 50 250 25 0 0 0 0 0 0 0 0 0 0 0 0 -6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -7 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 -8 test1 0x0 1004 0 3850 350 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self deleted file mode 100644 index afcdd7199026..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self +++ /dev/null @@ -1,6 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 -4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0 -5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0 -6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication deleted file mode 100644 index d7c7eb9f4ae8..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication +++ /dev/null @@ -1,5 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0 -5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split deleted file mode 100644 index 38a3dce4a834..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split +++ /dev/null @@ -1,4 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0 -4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression deleted file mode 100644 index d35244b3b4f2..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression +++ /dev/null @@ -1,4 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0 -4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/tests/net/res/raw/xt_qtaguid_vpn_with_clat deleted file mode 100644 index 0d893d515ca2..000000000000 --- a/tests/net/res/raw/xt_qtaguid_vpn_with_clat +++ /dev/null @@ -1,8 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 -3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 -4 v4-test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 -6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat deleted file mode 100644 index f04b32f08332..000000000000 --- a/tests/net/res/raw/xt_qtaguid_with_clat +++ /dev/null @@ -1,43 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 v4-wlan0 0x0 0 0 256 5 196 4 256 5 0 0 0 0 196 4 0 0 0 0 -3 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -4 v4-wlan0 0x0 1000 0 30312 25 1770 27 30236 24 76 1 0 0 1694 26 76 1 0 0 -5 v4-wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6 v4-wlan0 0x0 10060 0 9398432 6717 169412 4235 9398432 6717 0 0 0 0 169412 4235 0 0 0 0 -7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0 -8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0 -9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -10 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0 -13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -14 wlan0 0x0 10013 0 0 0 144 2 0 0 0 0 0 0 144 2 0 0 0 0 -15 wlan0 0x0 10013 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -16 wlan0 0x0 10018 0 5980263 4715 167667 1922 5972583 4709 0 0 7680 6 167667 1922 0 0 0 0 -17 wlan0 0x0 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0 -18 wlan0 0x0 10060 0 134356 133 8705 74 134356 133 0 0 0 0 8705 74 0 0 0 0 -19 wlan0 0x0 10060 1 294709 326 26448 256 294709 326 0 0 0 0 26448 256 0 0 0 0 -20 wlan0 0x0 10079 0 10926 13 1507 13 10926 13 0 0 0 0 1507 13 0 0 0 0 -21 wlan0 0x0 10079 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -22 wlan0 0x0 10102 0 25038 42 8245 57 25038 42 0 0 0 0 8245 57 0 0 0 0 -23 wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -24 wlan0 0x0 10103 0 0 0 192 2 0 0 0 0 0 0 0 0 192 2 0 0 -25 wlan0 0x0 10103 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -26 wlan0 0x1000040700000000 10018 0 831 6 655 5 831 6 0 0 0 0 655 5 0 0 0 0 -27 wlan0 0x1000040700000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -28 wlan0 0x1000040b00000000 10018 0 1714 8 1561 7 1714 8 0 0 0 0 1561 7 0 0 0 0 -29 wlan0 0x1000040b00000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -30 wlan0 0x1000120300000000 10018 0 8243 11 2234 12 8243 11 0 0 0 0 2234 12 0 0 0 0 -31 wlan0 0x1000120300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -32 wlan0 0x1000180300000000 10018 0 56368 49 4790 39 56368 49 0 0 0 0 4790 39 0 0 0 0 -33 wlan0 0x1000180300000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -34 wlan0 0x1000300000000000 10018 0 9488 17 18813 25 1808 11 0 0 7680 6 18813 25 0 0 0 0 -35 wlan0 0x1000300000000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -36 wlan0 0x3000180400000000 10018 0 131262 103 7416 103 131262 103 0 0 0 0 7416 103 0 0 0 0 -37 wlan0 0x3000180400000000 10018 1 43995 37 2766 27 43995 37 0 0 0 0 2766 27 0 0 0 0 -38 wlan0 0xffffff0100000000 10018 0 5771986 4518 131190 1725 5771986 4518 0 0 0 0 131190 1725 0 0 0 0 -39 wlan0 0xffffff0100000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -40 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 -41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8 -43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after deleted file mode 100644 index 12d98ca29f57..000000000000 --- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after +++ /dev/null @@ -1,189 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6 -3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -4 v4-wlan0 0x0 0 0 58952 2072 2888 65 264 6 0 0 58688 2066 132 3 0 0 2756 62 -5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0 -7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0 -10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0 -11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0 -12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0 -13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0 -15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0 -16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 -17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0 -19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0 -21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -22 wlan0 0x0 10020 0 21163552 34395 2351650 15326 21162947 34390 605 5 0 0 2351045 15321 605 5 0 0 -23 wlan0 0x0 10020 1 13835740 12938 1548795 6365 13833754 12920 1986 18 0 0 1546809 6347 1986 18 0 0 -24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0 -25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0 -27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0 -28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0 -29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0 -30 wlan0 0x0 10057 0 12312074 9339 436098 5450 12248060 9263 64014 76 0 0 414224 5388 21874 62 0 0 -31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0 -32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0 -33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0 -34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0 -35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0 -37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0 -39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0 -40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0 -41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0 -43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0 -45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0 -47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 -48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0 -49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0 -50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0 -51 wlan0 0x0 10106 1 88923475 64963 1606962 35612 88917201 64886 3586 29 2688 48 1602032 35535 4930 77 0 0 -52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0 -53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0 -54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0 -55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0 -57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0 -59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0 -61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 -62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0 -63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0 -65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0 -67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0 -69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 -70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0 -71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0 -73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0 -75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 -76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0 -77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0 -79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0 -80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0 -81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0 -83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0 -84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0 -85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0 -86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0 -87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0 -89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0 -90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0 -91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0 -92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0 -93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0 -95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0 -97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0 -100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0 -101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0 -102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0 -103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0 -104 wlan0 0x3000040100000000 10020 0 113809 342 135666 308 113809 342 0 0 0 0 135666 308 0 0 0 0 -105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0 -106 wlan0 0x3000040700000000 10020 0 365815 5119 213340 2733 365815 5119 0 0 0 0 213340 2733 0 0 0 0 -107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0 -108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0 -109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0 -110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0 -111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0 -112 wlan0 0x3000090000000000 10020 0 11826 67 7309 52 11826 67 0 0 0 0 7309 52 0 0 0 0 -113 wlan0 0x3000090000000000 10020 1 24805 41 4785 41 24805 41 0 0 0 0 4785 41 0 0 0 0 -114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0 -116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0 -117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0 -118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0 -119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0 -120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0 -121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0 -122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0 -123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0 -124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0 -125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0 -126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0 -127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0 -128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 -129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0 -130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0 -131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0 -132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0 -133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0 -134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 -135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0 -136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0 -138 wlan0 0x3000320100000000 10020 0 6844 14 3745 13 6844 14 0 0 0 0 3745 13 0 0 0 0 -139 wlan0 0x3000320100000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -140 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0 -141 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0 -142 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0 -143 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -144 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0 -145 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0 -146 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -147 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0 -148 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0 -149 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -150 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -151 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0 -152 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -153 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0 -154 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -155 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0 -156 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -157 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0 -158 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0 -159 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -160 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -161 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0 -162 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0 -163 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0 -164 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0 -165 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0 -166 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 -167 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -168 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0 -169 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0 -170 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0 -171 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0 -172 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0 -173 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -174 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0 -175 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -176 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0 -177 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -178 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -179 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0 -180 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0 -181 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0 -182 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0 -183 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0 -184 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0 -185 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -186 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 -187 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -188 wlan0 0x0 1029 0 0 0 8524052 130894 0 0 0 0 0 0 7871216 121284 108568 1325 544268 8285 -189 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before deleted file mode 100644 index ce4bcc3a3b43..000000000000 --- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before +++ /dev/null @@ -1,187 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 r_rmnet_data0 0x0 0 0 0 0 392 6 0 0 0 0 0 0 0 0 0 0 392 6 -3 r_rmnet_data0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -4 v4-wlan0 0x0 0 0 58848 2070 2836 64 160 4 0 0 58688 2066 80 2 0 0 2756 62 -5 v4-wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6 v4-wlan0 0x0 10034 0 6192 11 1445 11 6192 11 0 0 0 0 1445 11 0 0 0 0 -7 v4-wlan0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -8 v4-wlan0 0x0 10057 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0 -10 v4-wlan0 0x0 10106 0 1488 12 1488 12 0 0 1488 12 0 0 0 0 1488 12 0 0 -11 v4-wlan0 0x0 10106 1 323981189 235142 3509032 84542 323979453 235128 1736 14 0 0 3502676 84363 6356 179 0 0 -12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0 -13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0 -15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0 -16 wlan0 0x0 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 -17 wlan0 0x0 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -18 wlan0 0x0 10015 0 4390 7 14824 252 4390 7 0 0 0 0 14824 252 0 0 0 0 -19 wlan0 0x0 10015 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -20 wlan0 0x0 10018 0 4928 11 1741 14 4928 11 0 0 0 0 1741 14 0 0 0 0 -21 wlan0 0x0 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -22 wlan0 0x0 10020 0 21141412 34316 2329881 15262 21140807 34311 605 5 0 0 2329276 15257 605 5 0 0 -23 wlan0 0x0 10020 1 13835740 12938 1548555 6362 13833754 12920 1986 18 0 0 1546569 6344 1986 18 0 0 -24 wlan0 0x0 10023 0 13405 40 5042 44 13405 40 0 0 0 0 5042 44 0 0 0 0 -25 wlan0 0x0 10023 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -26 wlan0 0x0 10034 0 436394741 342648 6237981 80442 436394741 342648 0 0 0 0 6237981 80442 0 0 0 0 -27 wlan0 0x0 10034 1 64860872 51297 1335539 15546 64860872 51297 0 0 0 0 1335539 15546 0 0 0 0 -28 wlan0 0x0 10044 0 17614444 14774 521004 5694 17329882 14432 284562 342 0 0 419974 5408 101030 286 0 0 -29 wlan0 0x0 10044 1 17701 33 3100 28 17701 33 0 0 0 0 3100 28 0 0 0 0 -30 wlan0 0x0 10057 0 12311735 9335 435954 5448 12247721 9259 64014 76 0 0 414080 5386 21874 62 0 0 -31 wlan0 0x0 10057 1 1332953195 954797 31849632 457698 1331933207 953569 1019988 1228 0 0 31702284 456899 147348 799 0 0 -32 wlan0 0x0 10060 0 32972 200 433705 380 32972 200 0 0 0 0 433705 380 0 0 0 0 -33 wlan0 0x0 10060 1 32106 66 37789 87 32106 66 0 0 0 0 37789 87 0 0 0 0 -34 wlan0 0x0 10061 0 7675 23 2509 22 7675 23 0 0 0 0 2509 22 0 0 0 0 -35 wlan0 0x0 10061 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -36 wlan0 0x0 10074 0 38355 82 10447 97 38355 82 0 0 0 0 10447 97 0 0 0 0 -37 wlan0 0x0 10074 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -38 wlan0 0x0 10078 0 49013 79 7167 69 49013 79 0 0 0 0 7167 69 0 0 0 0 -39 wlan0 0x0 10078 1 5872 8 1236 10 5872 8 0 0 0 0 1236 10 0 0 0 0 -40 wlan0 0x0 10082 0 8301 13 1981 15 8301 13 0 0 0 0 1981 15 0 0 0 0 -41 wlan0 0x0 10082 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -42 wlan0 0x0 10086 0 7001 14 1579 15 7001 14 0 0 0 0 1579 15 0 0 0 0 -43 wlan0 0x0 10086 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -44 wlan0 0x0 10090 0 24327795 20224 920502 14661 24327795 20224 0 0 0 0 920502 14661 0 0 0 0 -45 wlan0 0x0 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -46 wlan0 0x0 10092 0 36849 78 12449 81 36849 78 0 0 0 0 12449 81 0 0 0 0 -47 wlan0 0x0 10092 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 -48 wlan0 0x0 10095 0 131962 223 37069 241 131962 223 0 0 0 0 37069 241 0 0 0 0 -49 wlan0 0x0 10095 1 12949 21 3930 21 12949 21 0 0 0 0 3930 21 0 0 0 0 -50 wlan0 0x0 10106 0 30899554 22679 632476 12296 30895334 22645 4220 34 0 0 628256 12262 4220 34 0 0 -51 wlan0 0x0 10106 1 88922349 64952 1605126 35599 88916075 64875 3586 29 2688 48 1600196 35522 4930 77 0 0 -52 wlan0 0x40700000000 10020 0 705732 10589 404428 5504 705732 10589 0 0 0 0 404428 5504 0 0 0 0 -53 wlan0 0x40700000000 10020 1 2376 36 1296 18 2376 36 0 0 0 0 1296 18 0 0 0 0 -54 wlan0 0x40800000000 10020 0 34624 146 122525 160 34624 146 0 0 0 0 122525 160 0 0 0 0 -55 wlan0 0x40800000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -56 wlan0 0x40b00000000 10020 0 22411 85 7364 57 22411 85 0 0 0 0 7364 57 0 0 0 0 -57 wlan0 0x40b00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -58 wlan0 0x120300000000 10020 0 76641 241 32783 169 76641 241 0 0 0 0 32783 169 0 0 0 0 -59 wlan0 0x120300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -60 wlan0 0x130100000000 10020 0 73101 287 23236 203 73101 287 0 0 0 0 23236 203 0 0 0 0 -61 wlan0 0x130100000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 -62 wlan0 0x180300000000 10020 0 330648 399 24736 232 330648 399 0 0 0 0 24736 232 0 0 0 0 -63 wlan0 0x180300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -64 wlan0 0x180400000000 10020 0 21865 59 5022 42 21865 59 0 0 0 0 5022 42 0 0 0 0 -65 wlan0 0x180400000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -66 wlan0 0x300000000000 10020 0 15984 65 26927 57 15984 65 0 0 0 0 26927 57 0 0 0 0 -67 wlan0 0x300000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -68 wlan0 0x1065fff00000000 10020 0 131871 599 93783 445 131871 599 0 0 0 0 93783 445 0 0 0 0 -69 wlan0 0x1065fff00000000 10020 1 264 4 144 2 264 4 0 0 0 0 144 2 0 0 0 0 -70 wlan0 0x1b24f4600000000 10034 0 15445 42 23329 45 15445 42 0 0 0 0 23329 45 0 0 0 0 -71 wlan0 0x1b24f4600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -72 wlan0 0x1000010000000000 10020 0 5542 9 1364 10 5542 9 0 0 0 0 1364 10 0 0 0 0 -73 wlan0 0x1000010000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -74 wlan0 0x1000040100000000 10020 0 47196 184 213319 257 47196 184 0 0 0 0 213319 257 0 0 0 0 -75 wlan0 0x1000040100000000 10020 1 60 1 103 1 60 1 0 0 0 0 103 1 0 0 0 0 -76 wlan0 0x1000040700000000 10020 0 11599 50 10786 47 11599 50 0 0 0 0 10786 47 0 0 0 0 -77 wlan0 0x1000040700000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -78 wlan0 0x1000040800000000 10020 0 21902 145 174139 166 21902 145 0 0 0 0 174139 166 0 0 0 0 -79 wlan0 0x1000040800000000 10020 1 8568 88 105743 90 8568 88 0 0 0 0 105743 90 0 0 0 0 -80 wlan0 0x1000100300000000 10020 0 55213 118 194551 199 55213 118 0 0 0 0 194551 199 0 0 0 0 -81 wlan0 0x1000100300000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -82 wlan0 0x1000120300000000 10020 0 50826 74 21153 70 50826 74 0 0 0 0 21153 70 0 0 0 0 -83 wlan0 0x1000120300000000 10020 1 72 1 175 2 72 1 0 0 0 0 175 2 0 0 0 0 -84 wlan0 0x1000180300000000 10020 0 744198 657 65437 592 744198 657 0 0 0 0 65437 592 0 0 0 0 -85 wlan0 0x1000180300000000 10020 1 144719 132 10989 108 144719 132 0 0 0 0 10989 108 0 0 0 0 -86 wlan0 0x1000180600000000 10020 0 4599 8 1928 10 4599 8 0 0 0 0 1928 10 0 0 0 0 -87 wlan0 0x1000180600000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -88 wlan0 0x1000250000000000 10020 0 57740 98 13076 88 57740 98 0 0 0 0 13076 88 0 0 0 0 -89 wlan0 0x1000250000000000 10020 1 328 3 414 4 207 2 121 1 0 0 293 3 121 1 0 0 -90 wlan0 0x1000300000000000 10020 0 7675 30 31331 32 7675 30 0 0 0 0 31331 32 0 0 0 0 -91 wlan0 0x1000300000000000 10020 1 30173 97 101335 100 30173 97 0 0 0 0 101335 100 0 0 0 0 -92 wlan0 0x1000310200000000 10020 0 1681 9 2194 9 1681 9 0 0 0 0 2194 9 0 0 0 0 -93 wlan0 0x1000310200000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -94 wlan0 0x1000360000000000 10020 0 5606 20 2831 20 5606 20 0 0 0 0 2831 20 0 0 0 0 -95 wlan0 0x1000360000000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -96 wlan0 0x11065fff00000000 10020 0 18363 91 83367 104 18363 91 0 0 0 0 83367 104 0 0 0 0 -97 wlan0 0x11065fff00000000 10020 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -98 wlan0 0x3000009600000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -99 wlan0 0x3000009600000000 10020 1 6163 18 2424 18 6163 18 0 0 0 0 2424 18 0 0 0 0 -100 wlan0 0x3000009800000000 10020 0 23337 46 8723 39 23337 46 0 0 0 0 8723 39 0 0 0 0 -101 wlan0 0x3000009800000000 10020 1 33744 93 72437 89 33744 93 0 0 0 0 72437 89 0 0 0 0 -102 wlan0 0x3000020000000000 10020 0 4124 11 8969 19 4124 11 0 0 0 0 8969 19 0 0 0 0 -103 wlan0 0x3000020000000000 10020 1 5993 11 3815 14 5993 11 0 0 0 0 3815 14 0 0 0 0 -104 wlan0 0x3000040100000000 10020 0 106718 322 121557 287 106718 322 0 0 0 0 121557 287 0 0 0 0 -105 wlan0 0x3000040100000000 10020 1 142508 642 500579 637 142508 642 0 0 0 0 500579 637 0 0 0 0 -106 wlan0 0x3000040700000000 10020 0 365419 5113 213124 2730 365419 5113 0 0 0 0 213124 2730 0 0 0 0 -107 wlan0 0x3000040700000000 10020 1 30747 130 18408 100 30747 130 0 0 0 0 18408 100 0 0 0 0 -108 wlan0 0x3000040800000000 10020 0 34672 112 68623 92 34672 112 0 0 0 0 68623 92 0 0 0 0 -109 wlan0 0x3000040800000000 10020 1 78443 199 140944 192 78443 199 0 0 0 0 140944 192 0 0 0 0 -110 wlan0 0x3000040b00000000 10020 0 14949 33 4017 26 14949 33 0 0 0 0 4017 26 0 0 0 0 -111 wlan0 0x3000040b00000000 10020 1 996 15 576 8 996 15 0 0 0 0 576 8 0 0 0 0 -112 wlan0 0x3000090000000000 10020 0 4017 28 3610 25 4017 28 0 0 0 0 3610 25 0 0 0 0 -113 wlan0 0x3000090000000000 10020 1 24805 41 4545 38 24805 41 0 0 0 0 4545 38 0 0 0 0 -114 wlan0 0x3000100300000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -115 wlan0 0x3000100300000000 10020 1 3112 10 1628 10 3112 10 0 0 0 0 1628 10 0 0 0 0 -116 wlan0 0x3000120300000000 10020 0 38249 107 20374 85 38249 107 0 0 0 0 20374 85 0 0 0 0 -117 wlan0 0x3000120300000000 10020 1 122581 174 36792 143 122581 174 0 0 0 0 36792 143 0 0 0 0 -118 wlan0 0x3000130100000000 10020 0 2700 41 1524 21 2700 41 0 0 0 0 1524 21 0 0 0 0 -119 wlan0 0x3000130100000000 10020 1 22515 59 8366 52 22515 59 0 0 0 0 8366 52 0 0 0 0 -120 wlan0 0x3000180200000000 10020 0 6411 18 14511 20 6411 18 0 0 0 0 14511 20 0 0 0 0 -121 wlan0 0x3000180200000000 10020 1 336 5 319 4 336 5 0 0 0 0 319 4 0 0 0 0 -122 wlan0 0x3000180300000000 10020 0 129301 136 17622 97 129301 136 0 0 0 0 17622 97 0 0 0 0 -123 wlan0 0x3000180300000000 10020 1 464787 429 41703 336 464787 429 0 0 0 0 41703 336 0 0 0 0 -124 wlan0 0x3000180400000000 10020 0 11014 39 2787 25 11014 39 0 0 0 0 2787 25 0 0 0 0 -125 wlan0 0x3000180400000000 10020 1 144040 139 7540 80 144040 139 0 0 0 0 7540 80 0 0 0 0 -126 wlan0 0x3000210100000000 10020 0 10278 44 4579 33 10278 44 0 0 0 0 4579 33 0 0 0 0 -127 wlan0 0x3000210100000000 10020 1 31151 73 14159 47 31151 73 0 0 0 0 14159 47 0 0 0 0 -128 wlan0 0x3000250000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 -129 wlan0 0x3000250000000000 10020 1 76614 143 17711 130 76080 137 534 6 0 0 17177 124 534 6 0 0 -130 wlan0 0x3000260100000000 10020 0 9426 26 3535 20 9426 26 0 0 0 0 3535 20 0 0 0 0 -131 wlan0 0x3000260100000000 10020 1 468 7 288 4 468 7 0 0 0 0 288 4 0 0 0 0 -132 wlan0 0x3000300000000000 10020 0 7241 29 12055 26 7241 29 0 0 0 0 12055 26 0 0 0 0 -133 wlan0 0x3000300000000000 10020 1 3273 23 11232 21 3273 23 0 0 0 0 11232 21 0 0 0 0 -134 wlan0 0x3000310000000000 10020 0 132 2 72 1 132 2 0 0 0 0 72 1 0 0 0 0 -135 wlan0 0x3000310000000000 10020 1 53425 64 8721 62 53425 64 0 0 0 0 8721 62 0 0 0 0 -136 wlan0 0x3000310500000000 10020 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -137 wlan0 0x3000310500000000 10020 1 9929 16 3879 18 9929 16 0 0 0 0 3879 18 0 0 0 0 -138 wlan0 0x3000360000000000 10020 0 8855 43 4749 31 8855 43 0 0 0 0 4749 31 0 0 0 0 -139 wlan0 0x3000360000000000 10020 1 5597 19 2456 19 5597 19 0 0 0 0 2456 19 0 0 0 0 -140 wlan0 0x3010000000000000 10090 0 605140 527 38435 429 605140 527 0 0 0 0 38435 429 0 0 0 0 -141 wlan0 0x3010000000000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -142 wlan0 0x31065fff00000000 10020 0 22011 67 29665 64 22011 67 0 0 0 0 29665 64 0 0 0 0 -143 wlan0 0x31065fff00000000 10020 1 10695 34 18347 35 10695 34 0 0 0 0 18347 35 0 0 0 0 -144 wlan0 0x32e544f900000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -145 wlan0 0x32e544f900000000 10034 1 40143 54 7299 61 40143 54 0 0 0 0 7299 61 0 0 0 0 -146 wlan0 0x58872a4400000000 10018 0 4928 11 1669 13 4928 11 0 0 0 0 1669 13 0 0 0 0 -147 wlan0 0x58872a4400000000 10018 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -148 wlan0 0x5caeaa7b00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -149 wlan0 0x5caeaa7b00000000 10034 1 74971 73 7103 75 74971 73 0 0 0 0 7103 75 0 0 0 0 -150 wlan0 0x9e00923800000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -151 wlan0 0x9e00923800000000 10034 1 72385 98 13072 110 72385 98 0 0 0 0 13072 110 0 0 0 0 -152 wlan0 0xb972bdd400000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -153 wlan0 0xb972bdd400000000 10034 1 15282 24 3034 27 15282 24 0 0 0 0 3034 27 0 0 0 0 -154 wlan0 0xc7c9f7ba00000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -155 wlan0 0xc7c9f7ba00000000 10034 1 194915 185 13316 138 194915 185 0 0 0 0 13316 138 0 0 0 0 -156 wlan0 0xc9395b2600000000 10034 0 6991 13 6215 14 6991 13 0 0 0 0 6215 14 0 0 0 0 -157 wlan0 0xc9395b2600000000 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -158 wlan0 0xdaddf21100000000 10034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -159 wlan0 0xdaddf21100000000 10034 1 928676 849 81570 799 928676 849 0 0 0 0 81570 799 0 0 0 0 -160 wlan0 0xe8d195d100000000 10020 0 516 8 288 4 516 8 0 0 0 0 288 4 0 0 0 0 -161 wlan0 0xe8d195d100000000 10020 1 5905 15 2622 15 5905 15 0 0 0 0 2622 15 0 0 0 0 -162 wlan0 0xe8d195d100000000 10034 0 236640 524 312523 555 236640 524 0 0 0 0 312523 555 0 0 0 0 -163 wlan0 0xe8d195d100000000 10034 1 319028 539 188776 553 319028 539 0 0 0 0 188776 553 0 0 0 0 -164 wlan0 0xffffff0100000000 10006 0 80755 92 9122 99 80755 92 0 0 0 0 9122 99 0 0 0 0 -165 wlan0 0xffffff0100000000 10006 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -166 wlan0 0xffffff0100000000 10020 0 17874405 14068 223987 3065 17874405 14068 0 0 0 0 223987 3065 0 0 0 0 -167 wlan0 0xffffff0100000000 10020 1 11011258 8672 177693 2407 11011258 8672 0 0 0 0 177693 2407 0 0 0 0 -168 wlan0 0xffffff0100000000 10034 0 436062595 341880 5843990 79630 436062595 341880 0 0 0 0 5843990 79630 0 0 0 0 -169 wlan0 0xffffff0100000000 10034 1 63201220 49447 1005882 13713 63201220 49447 0 0 0 0 1005882 13713 0 0 0 0 -170 wlan0 0xffffff0100000000 10044 0 17159287 13702 356212 4778 17159287 13702 0 0 0 0 356212 4778 0 0 0 0 -171 wlan0 0xffffff0100000000 10044 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -172 wlan0 0xffffff0100000000 10078 0 10439 17 1665 15 10439 17 0 0 0 0 1665 15 0 0 0 0 -173 wlan0 0xffffff0100000000 10078 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -174 wlan0 0xffffff0100000000 10090 0 23722655 19697 881995 14231 23722655 19697 0 0 0 0 881995 14231 0 0 0 0 -175 wlan0 0xffffff0100000000 10090 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -176 wlan0 0xffffff0500000000 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -177 wlan0 0xffffff0500000000 1000 1 1592 5 314 1 0 0 1592 5 0 0 0 0 314 1 0 0 -178 wlan0 0xffffff0600000000 1000 0 0 0 36960 385 0 0 0 0 0 0 0 0 36960 385 0 0 -179 wlan0 0xffffff0600000000 1000 1 96 1 480 5 0 0 96 1 0 0 0 0 480 5 0 0 -180 wlan0 0xffffff0700000000 1000 0 38732 229 16567 163 38732 229 0 0 0 0 16567 163 0 0 0 0 -181 wlan0 0xffffff0700000000 1000 1 18539 74 7562 66 18539 74 0 0 0 0 7562 66 0 0 0 0 -182 wlan0 0xffffff0900000000 1000 0 38381 43 2624 27 38381 43 0 0 0 0 2624 27 0 0 0 0 -183 wlan0 0xffffff0900000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -184 dummy0 0x0 0 0 0 0 168 3 0 0 0 0 0 0 0 0 0 0 168 3 -185 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -186 wlan0 0x0 1029 0 0 0 5855801 94173 0 0 0 0 0 0 5208040 84634 103637 1256 544124 8283 -187 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple deleted file mode 100644 index a1d6d411bad8..000000000000 --- a/tests/net/res/raw/xt_qtaguid_with_clat_simple +++ /dev/null @@ -1,4 +0,0 @@ -idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets -2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0 -3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -4 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp deleted file mode 100644 index 1535f3ddcb38..000000000000 --- a/tests/net/smoketest/Android.bp +++ /dev/null @@ -1,31 +0,0 @@ -// This test exists only because the jni_libs list for these tests is difficult to -// maintain: the test itself only depends on libnetworkstatsfactorytestjni, but the test -// fails to load that library unless *all* the dependencies of that library are explicitly -// listed in jni_libs. This means that whenever any of the dependencies changes the test -// starts failing and breaking presubmits in frameworks/base. We cannot easily put -// FrameworksNetTests into global presubmit because they are at times flaky, but this -// test is effectively empty beyond validating that the libraries load correctly, and -// thus should be stable enough to put in global presubmit. -// -// TODO: remove this hack when there is a better solution for jni_libs that includes -// dependent libraries. -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "FrameworksNetSmokeTests", - defaults: ["FrameworksNetTests-jni-defaults"], - srcs: ["java/SmokeTest.java"], - test_suites: ["device-tests"], - static_libs: [ - "androidx.test.rules", - "mockito-target-minus-junit4", - "services.core", - ], -} diff --git a/tests/net/smoketest/AndroidManifest.xml b/tests/net/smoketest/AndroidManifest.xml deleted file mode 100644 index f1b9febb9f57..000000000000 --- a/tests/net/smoketest/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - diff --git a/tests/net/smoketest/AndroidTest.xml b/tests/net/smoketest/AndroidTest.xml deleted file mode 100644 index ac366e4ac544..000000000000 --- a/tests/net/smoketest/AndroidTest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/tests/net/smoketest/java/SmokeTest.java b/tests/net/smoketest/java/SmokeTest.java deleted file mode 100644 index 7d6655fde15e..000000000000 --- a/tests/net/smoketest/java/SmokeTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2019 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.net; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public final class SmokeTest { - - @Test - public void testLoadJni() { - System.loadLibrary("networkstatsfactorytestjni"); - assertEquals(0, 0x00); - } -} -- cgit v1.2.3-59-g8ed1b