• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 com.google.snippet.connectivity
18 
19 import android.Manifest.permission.NETWORK_SETTINGS
20 import android.Manifest.permission.OVERRIDE_WIFI_CONFIG
21 import android.content.pm.PackageManager.FEATURE_TELEPHONY
22 import android.content.pm.PackageManager.FEATURE_WIFI
23 import android.net.ConnectivityManager
24 import android.net.Network
25 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
26 import android.net.NetworkCapabilities.TRANSPORT_WIFI
27 import android.net.NetworkRequest
28 import android.net.cts.util.CtsNetUtils
29 import android.net.cts.util.CtsTetheringUtils
30 import android.net.wifi.SoftApConfiguration
31 import android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
32 import android.net.wifi.WifiConfiguration
33 import android.net.wifi.WifiInfo
34 import android.net.wifi.WifiManager
35 import android.net.wifi.WifiSsid
36 import android.os.Build.VERSION.CODENAME
37 import android.os.Build.VERSION.SDK_INT
38 import androidx.test.platform.app.InstrumentationRegistry
39 import com.android.compatibility.common.util.PropertyUtil
40 import com.android.modules.utils.build.SdkLevel
41 import com.android.testutils.AutoReleaseNetworkCallbackRule
42 import com.android.testutils.ConnectUtil
43 import com.android.testutils.NetworkCallbackHelper
44 import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
45 import com.android.testutils.TestableNetworkCallback
46 import com.android.testutils.runAsShell
47 import com.google.android.mobly.snippet.Snippet
48 import com.google.android.mobly.snippet.rpc.Rpc
49 import org.junit.Rule
50 
51 class ConnectivityMultiDevicesSnippet : Snippet {
52     @get:Rule
53     val networkCallbackRule = AutoReleaseNetworkCallbackRule()
54     private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
55     private val wifiManager = context.getSystemService(WifiManager::class.java)!!
56     private val cm = context.getSystemService(ConnectivityManager::class.java)!!
57     private val pm = context.packageManager
58     private val ctsNetUtils = CtsNetUtils(context)
59     private val cbHelper = NetworkCallbackHelper()
60     private val ctsTetheringUtils = CtsTetheringUtils(context)
61     private var oldSoftApConfig: SoftApConfiguration? = null
62 
shutdownnull63     override fun shutdown() {
64         cbHelper.unregisterAll()
65     }
66 
isAtLeastPreReleaseCodenamenull67     private fun isAtLeastPreReleaseCodename(codeName: String): Boolean {
68         // Special case "REL", which means the build is not a pre-release build.
69         if ("REL".equals(CODENAME)) {
70             return false
71         }
72 
73         // Otherwise lexically compare them. Return true if the build codename is equal to or
74         // greater than the requested codename.
75         return CODENAME.compareTo(codeName) >= 0
76     }
77 
78     @Rpc(description = "Check whether the device has wifi feature.")
hasWifiFeaturenull79     fun hasWifiFeature() = pm.hasSystemFeature(FEATURE_WIFI)
80 
81     @Rpc(description = "Check whether the device has telephony feature.")
82     fun hasTelephonyFeature() = pm.hasSystemFeature(FEATURE_TELEPHONY)
83 
84     @Rpc(description = "Check whether the device supporters AP + STA concurrency.")
85     fun isStaApConcurrencySupported() = wifiManager.isStaApConcurrencySupported()
86 
87     @Rpc(description = "Check whether the device SDK is as least T")
88     fun isAtLeastT() = SdkLevel.isAtLeastT()
89 
90     @Rpc(description = "Return whether the Sdk level is at least V.")
91     fun isAtLeastV() = SdkLevel.isAtLeastV()
92 
93     @Rpc(description = "Check whether the device is at least B.")
94     fun isAtLeastB(): Boolean {
95         return SDK_INT >= 36 || (SDK_INT == 35 && isAtLeastPreReleaseCodename("Baklava"))
96     }
97 
98     @Rpc(description = "Return the API level that the VSR requirement must be fulfilled.")
getVsrApiLevelnull99     fun getVsrApiLevel() = PropertyUtil.getVsrApiLevel()
100 
101     @Rpc(description = "Request cellular connection and ensure it is the default network.")
102     fun requestCellularAndEnsureDefault() {
103         ctsNetUtils.disableWifi()
104         val network = cbHelper.requestCell()
105         ctsNetUtils.expectNetworkIsSystemDefault(network)
106     }
107 
108     @Rpc(description = "Reconnect to wifi if supported.")
reconnectWifiIfSupportednull109     fun reconnectWifiIfSupported() {
110         ctsNetUtils.reconnectWifiIfSupported()
111     }
112 
113     @Rpc(description = "Unregister all connections.")
unregisterAllnull114     fun unregisterAll() {
115         cbHelper.unregisterAll()
116     }
117 
118     @Rpc(description = "Ensure any wifi is connected and is the default network.")
ensureWifiIsDefaultnull119     fun ensureWifiIsDefault() {
120         val network = ctsNetUtils.ensureWifiConnected()
121         ctsNetUtils.expectNetworkIsSystemDefault(network)
122     }
123 
124     @Rpc(description = "Connect to specified wifi network.")
125     // Suppress warning because WifiManager methods to connect to a config are
126     // documented not to be deprecated for privileged users.
127     @Suppress("DEPRECATION")
connectToWifinull128     fun connectToWifi(ssid: String, passphrase: String, requireValidation: Boolean): Long {
129         val wifiConfig = WifiConfiguration()
130         wifiConfig.SSID = "\"" + ssid + "\""
131         wifiConfig.preSharedKey = "\"" + passphrase + "\""
132         wifiConfig.hiddenSSID = true
133         wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA2_PSK)
134         wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
135         wifiConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
136 
137         // Add the test configuration and connect to it.
138         val connectUtil = ConnectUtil(context)
139         connectUtil.connectToWifiConfig(wifiConfig)
140 
141         // Implement manual SSID matching. Specifying the SSID in
142         // NetworkSpecifier is ineffective
143         // (see WifiNetworkAgentSpecifier#canBeSatisfiedBy for details).
144         // Note that holding permission is necessary when waiting for
145         // the callbacks. The handler thread checks permission; if
146         // it's not present, the SSID will be redacted.
147         val networkCallback = TestableNetworkCallback()
148         val wifiRequest = NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build()
149         return runAsShell(NETWORK_SETTINGS) {
150             // Register the network callback is needed here.
151             // This is to avoid the race condition where callback is fired before
152             // acquiring permission.
153             networkCallbackRule.registerNetworkCallback(wifiRequest, networkCallback)
154             return@runAsShell networkCallback.eventuallyExpect<CapabilitiesChanged> {
155                 // Remove double quotes.
156                 val ssidFromCaps = (WifiInfo::sanitizeSsid)(it.caps.ssid)
157                 ssidFromCaps == ssid && (!requireValidation ||
158                         it.caps.hasCapability(NET_CAPABILITY_VALIDATED))
159             }.network.networkHandle
160         }
161     }
162 
163     @Rpc(description = "Get interface name from NetworkHandle")
getInterfaceNameFromNetworkHandlenull164     fun getInterfaceNameFromNetworkHandle(networkHandle: Long): String {
165         val network = Network.fromNetworkHandle(networkHandle)
166         return cm.getLinkProperties(network)!!.getInterfaceName()!!
167     }
168 
169     @Rpc(description = "Check whether the device supports hotspot feature.")
hasHotspotFeaturenull170     fun hasHotspotFeature(): Boolean {
171         val tetheringCallback = ctsTetheringUtils.registerTetheringEventCallback()
172         try {
173             return tetheringCallback.isWifiTetheringSupported(context)
174         } finally {
175             ctsTetheringUtils.unregisterTetheringEventCallback(tetheringCallback)
176         }
177     }
178 
179     @Rpc(description = "Start a hotspot with given SSID and passphrase.")
startHotspotnull180     fun startHotspot(ssid: String, passphrase: String): String {
181         // Store old config.
182         runAsShell(OVERRIDE_WIFI_CONFIG) {
183             oldSoftApConfig = wifiManager.getSoftApConfiguration()
184         }
185 
186         val softApConfig = SoftApConfiguration.Builder()
187             .setWifiSsid(WifiSsid.fromBytes(ssid.toByteArray()))
188             .setPassphrase(passphrase, SECURITY_TYPE_WPA2_PSK)
189             .setBand(SoftApConfiguration.BAND_2GHZ)
190             .build()
191         runAsShell(OVERRIDE_WIFI_CONFIG) {
192             wifiManager.setSoftApConfiguration(softApConfig)
193         }
194         val tetheringCallback = ctsTetheringUtils.registerTetheringEventCallback()
195         try {
196             tetheringCallback.expectNoTetheringActive()
197             return ctsTetheringUtils.startWifiTethering(tetheringCallback).getInterface()
198         } finally {
199             ctsTetheringUtils.unregisterTetheringEventCallback(tetheringCallback)
200         }
201     }
202 
203     @Rpc(description = "Stop all tethering.")
stopAllTetheringnull204     fun stopAllTethering() {
205         ctsTetheringUtils.stopAllTethering()
206 
207         // Restore old config.
208         oldSoftApConfig?.let {
209             runAsShell(OVERRIDE_WIFI_CONFIG) {
210                 wifiManager.setSoftApConfiguration(it)
211             }
212         }
213     }
214 }
215