1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net
18 
19 import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE
20 import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_AVOID
21 import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_IGNORE
22 import android.net.ConnectivitySettingsManager.CAPTIVE_PORTAL_MODE_PROMPT
23 import android.net.ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
24 import android.net.ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE
25 import android.net.ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_WIFI
26 import android.net.ConnectivitySettingsManager.DNS_RESOLVER_MAX_SAMPLES
27 import android.net.ConnectivitySettingsManager.DNS_RESOLVER_MIN_SAMPLES
28 import android.net.ConnectivitySettingsManager.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS
29 import android.net.ConnectivitySettingsManager.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT
30 import android.net.ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON
31 import android.net.ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT
32 import android.net.ConnectivitySettingsManager.NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS
33 import android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE
34 import android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF
35 import android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC
36 import android.net.ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED
37 import android.net.ConnectivitySettingsManager.getCaptivePortalMode
38 import android.net.ConnectivitySettingsManager.getConnectivityKeepPendingIntentDuration
39 import android.net.ConnectivitySettingsManager.getDnsResolverSampleRanges
40 import android.net.ConnectivitySettingsManager.getDnsResolverSampleValidityDuration
41 import android.net.ConnectivitySettingsManager.getDnsResolverSuccessThresholdPercent
42 import android.net.ConnectivitySettingsManager.getMobileDataActivityTimeout
43 import android.net.ConnectivitySettingsManager.getMobileDataAlwaysOn
44 import android.net.ConnectivitySettingsManager.getNetworkSwitchNotificationMaximumDailyCount
45 import android.net.ConnectivitySettingsManager.getNetworkSwitchNotificationRateDuration
46 import android.net.ConnectivitySettingsManager.getPrivateDnsDefaultMode
47 import android.net.ConnectivitySettingsManager.getWifiAlwaysRequested
48 import android.net.ConnectivitySettingsManager.getWifiDataActivityTimeout
49 import android.net.ConnectivitySettingsManager.setCaptivePortalMode
50 import android.net.ConnectivitySettingsManager.setConnectivityKeepPendingIntentDuration
51 import android.net.ConnectivitySettingsManager.setDnsResolverSampleRanges
52 import android.net.ConnectivitySettingsManager.setDnsResolverSampleValidityDuration
53 import android.net.ConnectivitySettingsManager.setDnsResolverSuccessThresholdPercent
54 import android.net.ConnectivitySettingsManager.setMobileDataActivityTimeout
55 import android.net.ConnectivitySettingsManager.setMobileDataAlwaysOn
56 import android.net.ConnectivitySettingsManager.setNetworkSwitchNotificationMaximumDailyCount
57 import android.net.ConnectivitySettingsManager.setNetworkSwitchNotificationRateDuration
58 import android.net.ConnectivitySettingsManager.setPrivateDnsDefaultMode
59 import android.net.ConnectivitySettingsManager.setWifiAlwaysRequested
60 import android.net.ConnectivitySettingsManager.setWifiDataActivityTimeout
61 import android.os.Build
62 import android.platform.test.annotations.AppModeFull
63 import android.provider.Settings
64 import android.util.Range
65 import androidx.test.InstrumentationRegistry
66 import androidx.test.filters.SmallTest
67 import com.android.net.module.util.ConnectivitySettingsUtils.getPrivateDnsModeAsString
68 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
69 import com.android.testutils.DevSdkIgnoreRunner
70 import junit.framework.Assert.assertEquals
71 import org.junit.Test
72 import org.junit.runner.RunWith
73 import java.time.Duration
74 import java.util.Objects
75 import kotlin.test.assertFailsWith
76 
77 /**
78  * Tests for [ConnectivitySettingsManager].
79  *
80  * Build, install and run with:
81  * atest android.net.ConnectivitySettingsManagerTest
82  */
83 @RunWith(DevSdkIgnoreRunner::class)
84 @IgnoreUpTo(Build.VERSION_CODES.R)
85 @SmallTest
86 @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
87 class ConnectivitySettingsManagerTest {
88     private val instrumentation = InstrumentationRegistry.getInstrumentation()
89     private val context = instrumentation.context
90     private val resolver = context.contentResolver
91 
92     private val defaultDuration = Duration.ofSeconds(0L)
93     private val testTime1 = 5L
94     private val testTime2 = 10L
95     private val settingsTypeGlobal = "global"
96     private val settingsTypeSecure = "secure"
97 
98     /*** Reset setting value or delete setting if the setting was not existed before testing. */
99     private fun resetSettings(names: Array<String>, type: String, values: Array<String?>) {
100         for (i in names.indices) {
101             if (Objects.equals(values[i], null)) {
102                 instrumentation.uiAutomation.executeShellCommand(
103                         "settings delete $type ${names[i]}")
104             } else {
105                 if (settingsTypeSecure.equals(type)) {
106                     Settings.Secure.putString(resolver, names[i], values[i])
107                 } else {
108                     Settings.Global.putString(resolver, names[i], values[i])
109                 }
110             }
111         }
112     }
113 
114     fun <T> testIntSetting(
115         names: Array<String>,
116         type: String,
117         value1: T,
118         value2: T,
119         getter: () -> T,
120         setter: (value: T) -> Unit,
121         testIntValues: IntArray
122     ) {
123         val originals: Array<String?> = Array(names.size) { i ->
124             if (settingsTypeSecure.equals(type)) {
125                 Settings.Secure.getString(resolver, names[i])
126             } else {
127                 Settings.Global.getString(resolver, names[i])
128             }
129         }
130 
131         try {
132             for (i in names.indices) {
133                 if (settingsTypeSecure.equals(type)) {
134                     Settings.Secure.putString(resolver, names[i], testIntValues[i].toString())
135                 } else {
136                     Settings.Global.putString(resolver, names[i], testIntValues[i].toString())
137                 }
138             }
139             assertEquals(value1, getter())
140 
141             setter(value2)
142             assertEquals(value2, getter())
143         } finally {
144             resetSettings(names, type, originals)
145         }
146     }
147 
148     @Test
149     fun testMobileDataActivityTimeout() {
150         testIntSetting(names = arrayOf(DATA_ACTIVITY_TIMEOUT_MOBILE), type = settingsTypeGlobal,
151                 value1 = Duration.ofSeconds(testTime1), value2 = Duration.ofSeconds(testTime2),
152                 getter = { getMobileDataActivityTimeout(context, defaultDuration) },
153                 setter = { setMobileDataActivityTimeout(context, it) },
154                 testIntValues = intArrayOf(testTime1.toInt()))
155     }
156 
157     @Test
158     fun testWifiDataActivityTimeout() {
159         testIntSetting(names = arrayOf(DATA_ACTIVITY_TIMEOUT_WIFI), type = settingsTypeGlobal,
160                 value1 = Duration.ofSeconds(testTime1), value2 = Duration.ofSeconds(testTime2),
161                 getter = { getWifiDataActivityTimeout(context, defaultDuration) },
162                 setter = { setWifiDataActivityTimeout(context, it) },
163                 testIntValues = intArrayOf(testTime1.toInt()))
164     }
165 
166     @Test
167     fun testDnsResolverSampleValidityDuration() {
168         testIntSetting(names = arrayOf(DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS),
169                 type = settingsTypeGlobal, value1 = Duration.ofSeconds(testTime1),
170                 value2 = Duration.ofSeconds(testTime2),
171                 getter = { getDnsResolverSampleValidityDuration(context, defaultDuration) },
172                 setter = { setDnsResolverSampleValidityDuration(context, it) },
173                 testIntValues = intArrayOf(testTime1.toInt()))
174 
175         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
176             setDnsResolverSampleValidityDuration(context, Duration.ofSeconds(-1L)) }
177     }
178 
179     @Test
180     fun testDnsResolverSuccessThresholdPercent() {
181         testIntSetting(names = arrayOf(DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT),
182                 type = settingsTypeGlobal, value1 = 5, value2 = 10,
183                 getter = { getDnsResolverSuccessThresholdPercent(context, 0 /* def */) },
184                 setter = { setDnsResolverSuccessThresholdPercent(context, it) },
185                 testIntValues = intArrayOf(5))
186 
187         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
188             setDnsResolverSuccessThresholdPercent(context, -1) }
189         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
190             setDnsResolverSuccessThresholdPercent(context, 120) }
191     }
192 
193     @Test
194     fun testDnsResolverSampleRanges() {
195         testIntSetting(names = arrayOf(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_MAX_SAMPLES),
196                 type = settingsTypeGlobal, value1 = Range(1, 63), value2 = Range(2, 62),
197                 getter = { getDnsResolverSampleRanges(context) },
198                 setter = { setDnsResolverSampleRanges(context, it) },
199                 testIntValues = intArrayOf(1, 63))
200 
201         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
202             setDnsResolverSampleRanges(context, Range(-1, 62)) }
203         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
204             setDnsResolverSampleRanges(context, Range(2, 65)) }
205     }
206 
207     @Test
208     fun testNetworkSwitchNotificationMaximumDailyCount() {
209         testIntSetting(names = arrayOf(NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT),
210                 type = settingsTypeGlobal, value1 = 5, value2 = 15,
211                 getter = { getNetworkSwitchNotificationMaximumDailyCount(context, 0 /* def */) },
212                 setter = { setNetworkSwitchNotificationMaximumDailyCount(context, it) },
213                 testIntValues = intArrayOf(5))
214 
215         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
216             setNetworkSwitchNotificationMaximumDailyCount(context, -1) }
217     }
218 
219     @Test
220     fun testNetworkSwitchNotificationRateDuration() {
221         testIntSetting(names = arrayOf(NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS),
222                 type = settingsTypeGlobal, value1 = Duration.ofMillis(testTime1),
223                 value2 = Duration.ofMillis(testTime2),
224                 getter = { getNetworkSwitchNotificationRateDuration(context, defaultDuration) },
225                 setter = { setNetworkSwitchNotificationRateDuration(context, it) },
226                 testIntValues = intArrayOf(testTime1.toInt()))
227 
228         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
229             setNetworkSwitchNotificationRateDuration(context, Duration.ofMillis(-1L)) }
230     }
231 
232     @Test
233     fun testCaptivePortalMode() {
234         testIntSetting(names = arrayOf(CAPTIVE_PORTAL_MODE), type = settingsTypeGlobal,
235                 value1 = CAPTIVE_PORTAL_MODE_AVOID, value2 = CAPTIVE_PORTAL_MODE_PROMPT,
236                 getter = { getCaptivePortalMode(context, CAPTIVE_PORTAL_MODE_IGNORE) },
237                 setter = { setCaptivePortalMode(context, it) },
238                 testIntValues = intArrayOf(CAPTIVE_PORTAL_MODE_AVOID))
239 
240         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
241             setCaptivePortalMode(context, 5 /* mode */) }
242     }
243 
244     @Test
245     fun testPrivateDnsDefaultMode() {
246         val original = Settings.Global.getString(resolver, PRIVATE_DNS_DEFAULT_MODE)
247 
248         try {
249             val mode = getPrivateDnsModeAsString(PRIVATE_DNS_MODE_OPPORTUNISTIC)
250             Settings.Global.putString(resolver, PRIVATE_DNS_DEFAULT_MODE, mode)
251             assertEquals(mode, getPrivateDnsDefaultMode(context))
252 
253             setPrivateDnsDefaultMode(context, PRIVATE_DNS_MODE_OFF)
254             assertEquals(getPrivateDnsModeAsString(PRIVATE_DNS_MODE_OFF),
255                     getPrivateDnsDefaultMode(context))
256         } finally {
257             resetSettings(names = arrayOf(PRIVATE_DNS_DEFAULT_MODE), type = settingsTypeGlobal,
258                     values = arrayOf(original))
259         }
260 
261         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
262             setPrivateDnsDefaultMode(context, -1) }
263     }
264 
265     @Test
266     fun testConnectivityKeepPendingIntentDuration() {
267         testIntSetting(names = arrayOf(CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS),
268                 type = settingsTypeSecure, value1 = Duration.ofMillis(testTime1),
269                 value2 = Duration.ofMillis(testTime2),
270                 getter = { getConnectivityKeepPendingIntentDuration(context, defaultDuration) },
271                 setter = { setConnectivityKeepPendingIntentDuration(context, it) },
272                 testIntValues = intArrayOf(testTime1.toInt()))
273 
274         assertFailsWith<IllegalArgumentException>("Expect fail but argument accepted.") {
275             setConnectivityKeepPendingIntentDuration(context, Duration.ofMillis(-1L)) }
276     }
277 
278     @Test
279     fun testMobileDataAlwaysOn() {
280         testIntSetting(names = arrayOf(MOBILE_DATA_ALWAYS_ON), type = settingsTypeGlobal,
281                 value1 = false, value2 = true,
282                 getter = { getMobileDataAlwaysOn(context, true /* def */) },
283                 setter = { setMobileDataAlwaysOn(context, it) },
284                 testIntValues = intArrayOf(0))
285     }
286 
287     @Test
288     fun testWifiAlwaysRequested() {
289         testIntSetting(names = arrayOf(WIFI_ALWAYS_REQUESTED), type = settingsTypeGlobal,
290                 value1 = false, value2 = true,
291                 getter = { getWifiAlwaysRequested(context, true /* def */) },
292                 setter = { setWifiAlwaysRequested(context, it) },
293                 testIntValues = intArrayOf(0))
294     }
295 }