Add ConnectivitySettingsManagerTest
- Add tests for ConnectivitySettingsManager.
- Also address leftover javadoc comments.
Bug: 182538166
Test: atest FrameworksNetTests
Test: atest CtsNetTestCases
Original-Change: https://android-review.googlesource.com/1738353
Merged-In: I8161c1bca84b41964578d59ee5d6ab07b2380707
Change-Id: I8161c1bca84b41964578d59ee5d6ab07b2380707
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 4644e4f..085de6b 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -562,7 +562,7 @@
public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context,
@IntRange(from = 0) int count) {
if (count < 0) {
- throw new IllegalArgumentException("Count must be 0~10.");
+ throw new IllegalArgumentException("Count must be more than 0.");
}
Settings.Global.putInt(
context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count);
@@ -585,6 +585,7 @@
/**
* Set minimum duration (to {@link Settings}) between each switching network notifications.
+ * The duration will be rounded down to the next millisecond, and must be positive.
*
* @param context The {@link Context} to set the setting.
* @param duration The minimum duration between notifications when switching networks.
@@ -612,10 +613,11 @@
/**
* Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection.
- * This URL should respond with a 204 response to a GET request to indicate no captive portal is
- * present. And this URL must be HTTP as redirect responses are used to find captive portal
- * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal
- * detection failed and lost the connection.
+ * The URL is accessed to check for connectivity and presence of a captive portal on a network.
+ * The URL should respond with HTTP status 204 to a GET request, and the stack will use
+ * redirection status as a signal for captive portal detection.
+ * If the URL is set to null or is otherwise incorrect or inaccessible, the stack will fail to
+ * detect connectivity and portals. This will often result in loss of connectivity.
*
* @param context The {@link Context} to set the setting.
* @param url The URL used for HTTP captive portal detection upon a new connection.
@@ -819,6 +821,7 @@
/**
* Set duration (to {@link Settings}) to keep a PendingIntent-based request.
+ * The duration will be rounded down to the next millisecond, and must be positive.
*
* @param context The {@link Context} to set the setting.
* @param duration The duration to keep a PendingIntent-based request.
diff --git a/tests/common/java/android/net/ConnectivitySettingsManagerTest.kt b/tests/common/java/android/net/ConnectivitySettingsManagerTest.kt
new file mode 100644
index 0000000..ebaa787
--- /dev/null
+++ b/tests/common/java/android/net/ConnectivitySettingsManagerTest.kt
@@ -0,0 +1,295 @@
+/*
+ * 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.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE
+import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID
+import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_IGNORE
+import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT
+import android.net.ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
+import android.net.ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE
+import android.net.ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_WIFI
+import android.net.ConnectivitySettingsManager.DNS_RESOLVER_MAX_SAMPLES
+import android.net.ConnectivitySettingsManager.DNS_RESOLVER_MIN_SAMPLES
+import android.net.ConnectivitySettingsManager.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS
+import android.net.ConnectivitySettingsManager.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT
+import android.net.ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON
+import android.net.ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT
+import android.net.ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS
+import android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE
+import android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF
+import android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC
+import android.net.ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED
+import android.net.ConnectivitySettingsManager.getCaptivePortalMode
+import android.net.ConnectivitySettingsManager.getConnectivityKeepPendingIntentDuration
+import android.net.ConnectivitySettingsManager.getDnsResolverSampleRanges
+import android.net.ConnectivitySettingsManager.getDnsResolverSampleValidityDuration
+import android.net.ConnectivitySettingsManager.getDnsResolverSuccessThresholdPercent
+import android.net.ConnectivitySettingsManager.getMobileDataActivityTimeout
+import android.net.ConnectivitySettingsManager.getMobileDataAlwaysOn
+import android.net.ConnectivitySettingsManager.getNetworkSwitchNotificationMaximumDailyCount
+import android.net.ConnectivitySettingsManager.getNetworkSwitchNotificationRateDuration
+import android.net.ConnectivitySettingsManager.getPrivateDnsDefaultMode
+import android.net.ConnectivitySettingsManager.getWifiAlwaysRequested
+import android.net.ConnectivitySettingsManager.getWifiDataActivityTimeout
+import android.net.ConnectivitySettingsManager.setCaptivePortalMode
+import android.net.ConnectivitySettingsManager.setConnectivityKeepPendingIntentDuration
+import android.net.ConnectivitySettingsManager.setDnsResolverSampleRanges
+import android.net.ConnectivitySettingsManager.setDnsResolverSampleValidityDuration
+import android.net.ConnectivitySettingsManager.setDnsResolverSuccessThresholdPercent
+import android.net.ConnectivitySettingsManager.setMobileDataActivityTimeout
+import android.net.ConnectivitySettingsManager.setMobileDataAlwaysOn
+import android.net.ConnectivitySettingsManager.setNetworkSwitchNotificationMaximumDailyCount
+import android.net.ConnectivitySettingsManager.setNetworkSwitchNotificationRateDuration
+import android.net.ConnectivitySettingsManager.setPrivateDnsDefaultMode
+import android.net.ConnectivitySettingsManager.setWifiAlwaysRequested
+import android.net.ConnectivitySettingsManager.setWifiDataActivityTimeout
+import android.os.Build
+import android.platform.test.annotations.AppModeFull
+import android.provider.Settings
+import android.util.Range
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SmallTest
+import com.android.net.module.util.ConnectivitySettingsUtils.getPrivateDnsModeAsString
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.time.Duration
+import java.util.Objects
+import kotlin.test.assertFailsWith
+
+/**
+ * Tests for [ConnectivitySettingsManager].
+ *
+ * Build, install and run with:
+ * atest android.net.ConnectivitySettingsManagerTest
+ */
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@SmallTest
+@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
+class ConnectivitySettingsManagerTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context = instrumentation.context
+ private val resolver = context.contentResolver
+
+ private val defaultDuration = Duration.ofSeconds(0L)
+ private val testTime1 = 5L
+ private val testTime2 = 10L
+ private val settingsTypeGlobal = "global"
+ private val settingsTypeSecure = "secure"
+
+ /*** Reset setting value or delete setting if the setting was not existed before testing. */
+ private fun resetSettings(names: Array<String>, type: String, values: Array<String?>) {
+ for (i in names.indices) {
+ if (Objects.equals(values[i], null)) {
+ instrumentation.uiAutomation.executeShellCommand(
+ "settings delete $type ${names[i]}")
+ } else {
+ if (settingsTypeSecure.equals(type)) {
+ Settings.Secure.putString(resolver, names[i], values[i])
+ } else {
+ Settings.Global.putString(resolver, names[i], values[i])
+ }
+ }
+ }
+ }
+
+ fun <T> testIntSetting(
+ names: Array<String>,
+ type: String,
+ value1: T,
+ value2: T,
+ getter: () -> T,
+ setter: (value: T) -> Unit,
+ testIntValues: IntArray
+ ) {
+ val originals: Array<String?> = Array(names.size) { i ->
+ if (settingsTypeSecure.equals(type)) {
+ Settings.Secure.getString(resolver, names[i])
+ } else {
+ Settings.Global.getString(resolver, names[i])
+ }
+ }
+
+ try {
+ for (i in names.indices) {
+ if (settingsTypeSecure.equals(type)) {
+ Settings.Secure.putString(resolver, names[i], testIntValues[i].toString())
+ } else {
+ Settings.Global.putString(resolver, names[i], testIntValues[i].toString())
+ }
+ }
+ assertEquals(value1, getter())
+
+ setter(value2)
+ assertEquals(value2, getter())
+ } finally {
+ resetSettings(names, type, originals)
+ }
+ }
+
+ @Test
+ fun testMobileDataActivityTimeout() {
+ testIntSetting(names = arrayOf(DATA_ACTIVITY_TIMEOUT_MOBILE), type = settingsTypeGlobal,
+ value1 = Duration.ofSeconds(testTime1), value2 = Duration.ofSeconds(testTime2),
+ getter = { getMobileDataActivityTimeout(context, defaultDuration) },
+ setter = { setMobileDataActivityTimeout(context, it) },
+ testIntValues = intArrayOf(testTime1.toInt()))
+ }
+
+ @Test
+ fun testWifiDataActivityTimeout() {
+ testIntSetting(names = arrayOf(DATA_ACTIVITY_TIMEOUT_WIFI), type = settingsTypeGlobal,
+ value1 = Duration.ofSeconds(testTime1), value2 = Duration.ofSeconds(testTime2),
+ getter = { getWifiDataActivityTimeout(context, defaultDuration) },
+ setter = { setWifiDataActivityTimeout(context, it) },
+ testIntValues = intArrayOf(testTime1.toInt()))
+ }
+
+ @Test
+ fun testDnsResolverSampleValidityDuration() {
+ testIntSetting(names = arrayOf(DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS),
+ type = settingsTypeGlobal, value1 = Duration.ofSeconds(testTime1),
+ value2 = Duration.ofSeconds(testTime2),
+ getter = { getDnsResolverSampleValidityDuration(context, defaultDuration) },
+ setter = { setDnsResolverSampleValidityDuration(context, it) },
+ testIntValues = intArrayOf(testTime1.toInt()))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setDnsResolverSampleValidityDuration(context, Duration.ofSeconds(-1L)) }
+ }
+
+ @Test
+ fun testDnsResolverSuccessThresholdPercent() {
+ testIntSetting(names = arrayOf(DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT),
+ type = settingsTypeGlobal, value1 = 5, value2 = 10,
+ getter = { getDnsResolverSuccessThresholdPercent(context, 0 /* def */) },
+ setter = { setDnsResolverSuccessThresholdPercent(context, it) },
+ testIntValues = intArrayOf(5))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setDnsResolverSuccessThresholdPercent(context, -1) }
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setDnsResolverSuccessThresholdPercent(context, 120) }
+ }
+
+ @Test
+ fun testDnsResolverSampleRanges() {
+ testIntSetting(names = arrayOf(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_MAX_SAMPLES),
+ type = settingsTypeGlobal, value1 = Range(1, 63), value2 = Range(2, 62),
+ getter = { getDnsResolverSampleRanges(context) },
+ setter = { setDnsResolverSampleRanges(context, it) },
+ testIntValues = intArrayOf(1, 63))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setDnsResolverSampleRanges(context, Range(-1, 62)) }
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setDnsResolverSampleRanges(context, Range(2, 65)) }
+ }
+
+ @Test
+ fun testNetworkSwitchNotificationMaximumDailyCount() {
+ testIntSetting(names = arrayOf(NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT),
+ type = settingsTypeGlobal, value1 = 5, value2 = 15,
+ getter = { getNetworkSwitchNotificationMaximumDailyCount(context, 0 /* def */) },
+ setter = { setNetworkSwitchNotificationMaximumDailyCount(context, it) },
+ testIntValues = intArrayOf(5))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setNetworkSwitchNotificationMaximumDailyCount(context, -1) }
+ }
+
+ @Test
+ fun testNetworkSwitchNotificationRateDuration() {
+ testIntSetting(names = arrayOf(NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS),
+ type = settingsTypeGlobal, value1 = Duration.ofMillis(testTime1),
+ value2 = Duration.ofMillis(testTime2),
+ getter = { getNetworkSwitchNotificationRateDuration(context, defaultDuration) },
+ setter = { setNetworkSwitchNotificationRateDuration(context, it) },
+ testIntValues = intArrayOf(testTime1.toInt()))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setNetworkSwitchNotificationRateDuration(context, Duration.ofMillis(-1L)) }
+ }
+
+ @Test
+ fun testCaptivePortalMode() {
+ testIntSetting(names = arrayOf(CAPTIVE_PORTAL_MODE), type = settingsTypeGlobal,
+ value1 = CAPTIVE_PORTAL_MODE_AVOID, value2 = CAPTIVE_PORTAL_MODE_PROMPT,
+ getter = { getCaptivePortalMode(context, CAPTIVE_PORTAL_MODE_IGNORE) },
+ setter = { setCaptivePortalMode(context, it) },
+ testIntValues = intArrayOf(CAPTIVE_PORTAL_MODE_AVOID))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setCaptivePortalMode(context, 5 /* mode */) }
+ }
+
+ @Test
+ fun testPrivateDnsDefaultMode() {
+ val original = Settings.Global.getString(resolver, PRIVATE_DNS_DEFAULT_MODE)
+
+ try {
+ val mode = getPrivateDnsModeAsString(PRIVATE_DNS_MODE_OPPORTUNISTIC)
+ Settings.Global.putString(resolver, PRIVATE_DNS_DEFAULT_MODE, mode)
+ assertEquals(mode, getPrivateDnsDefaultMode(context))
+
+ setPrivateDnsDefaultMode(context, PRIVATE_DNS_MODE_OFF)
+ assertEquals(getPrivateDnsModeAsString(PRIVATE_DNS_MODE_OFF),
+ getPrivateDnsDefaultMode(context))
+ } finally {
+ resetSettings(names = arrayOf(PRIVATE_DNS_DEFAULT_MODE), type = settingsTypeGlobal,
+ values = arrayOf(original))
+ }
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setPrivateDnsDefaultMode(context, -1) }
+ }
+
+ @Test
+ fun testConnectivityKeepPendingIntentDuration() {
+ testIntSetting(names = arrayOf(CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS),
+ type = settingsTypeSecure, value1 = Duration.ofMillis(testTime1),
+ value2 = Duration.ofMillis(testTime2),
+ getter = { getConnectivityKeepPendingIntentDuration(context, defaultDuration) },
+ setter = { setConnectivityKeepPendingIntentDuration(context, it) },
+ testIntValues = intArrayOf(testTime1.toInt()))
+
+ assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
+ setConnectivityKeepPendingIntentDuration(context, Duration.ofMillis(-1L)) }
+ }
+
+ @Test
+ fun testMobileDataAlwaysOn() {
+ testIntSetting(names = arrayOf(MOBILE_DATA_ALWAYS_ON), type = settingsTypeGlobal,
+ value1 = false, value2 = true,
+ getter = { getMobileDataAlwaysOn(context, true /* def */) },
+ setter = { setMobileDataAlwaysOn(context, it) },
+ testIntValues = intArrayOf(0))
+ }
+
+ @Test
+ fun testWifiAlwaysRequested() {
+ testIntSetting(names = arrayOf(WIFI_ALWAYS_REQUESTED), type = settingsTypeGlobal,
+ value1 = false, value2 = true,
+ getter = { getWifiAlwaysRequested(context, true /* def */) },
+ setter = { setWifiAlwaysRequested(context, it) },
+ testIntValues = intArrayOf(0))
+ }
+}
\ No newline at end of file