From 236a992cd080d55d0dc0156a7c718fed1d104323 Mon Sep 17 00:00:00 2001 From: paulhu Date: Mon, 29 Mar 2021 10:50:36 +0800 Subject: [PATCH] Add ConnectivitySettingsManagerTest - Add tests for ConnectivitySettingsManager. - Also address leftover javadoc comments. Bug: 182538166 Test: atest FrameworksNetTests Test: atest CtsNetTestCases Change-Id: I8161c1bca84b41964578d59ee5d6ab07b2380707 --- .../net/ConnectivitySettingsManager.java | 13 +- .../net/ConnectivitySettingsManagerTest.kt | 295 ++++++++++++++++++ 2 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 tests/common/java/android/net/ConnectivitySettingsManagerTest.kt diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java index 4644e4f483..085de6b685 100644 --- a/framework/src/android/net/ConnectivitySettingsManager.java +++ b/framework/src/android/net/ConnectivitySettingsManager.java @@ -562,7 +562,7 @@ public class ConnectivitySettingsManager { 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 @@ public class ConnectivitySettingsManager { /** * 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 @@ public class ConnectivitySettingsManager { /** * 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 @@ public class ConnectivitySettingsManager { /** * 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 0000000000..ebaa787cc6 --- /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, type: String, values: Array) { + 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 testIntSetting( + names: Array, + type: String, + value1: T, + value2: T, + getter: () -> T, + setter: (value: T) -> Unit, + testIntValues: IntArray + ) { + val originals: Array = 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("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("Expect fail but argument accepted.") { + setDnsResolverSuccessThresholdPercent(context, -1) } + assertFailsWith("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("Expect fail but argument accepted.") { + setDnsResolverSampleRanges(context, Range(-1, 62)) } + assertFailsWith("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("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("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("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("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("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