1 /* <lambda>null2 * Copyright (C) 2020 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.ip 18 19 import android.Manifest.permission.INTERACT_ACROSS_USERS_FULL 20 import android.Manifest.permission.NETWORK_SETTINGS 21 import android.Manifest.permission.READ_DEVICE_CONFIG 22 import android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG 23 import android.Manifest.permission.WRITE_DEVICE_CONFIG 24 import android.net.IIpMemoryStore 25 import android.net.IIpMemoryStoreCallbacks 26 import android.net.NetworkStackIpMemoryStore 27 import android.net.ipmemorystore.NetworkAttributes 28 import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener 29 import android.net.ipmemorystore.OnNetworkEventCountRetrievedListener 30 import android.net.ipmemorystore.Status 31 import android.net.networkstack.TestNetworkStackServiceClient 32 import android.os.Process 33 import android.provider.DeviceConfig 34 import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY 35 import android.util.ArrayMap 36 import android.util.Log 37 import androidx.test.platform.app.InstrumentationRegistry 38 import java.lang.System.currentTimeMillis 39 import java.lang.UnsupportedOperationException 40 import java.util.concurrent.CompletableFuture 41 import java.util.concurrent.CountDownLatch 42 import java.util.concurrent.TimeUnit 43 import java.util.concurrent.TimeoutException 44 import kotlin.test.assertFalse 45 import kotlin.test.assertNotNull 46 import kotlin.test.assertNull 47 import kotlin.test.assertTrue 48 import kotlin.test.fail 49 import org.junit.After 50 import org.junit.AfterClass 51 import org.junit.BeforeClass 52 import org.mockito.ArgumentCaptor 53 import org.mockito.Mockito.timeout 54 import org.mockito.Mockito.verify 55 56 // Stable AIDL method 5 in INetworkStackConnector is allowTestUid 57 private const val ALLOW_TEST_UID_INDEX = 5 58 59 /** 60 * Tests for IpClient, run with root access but no signature permissions. 61 * 62 * Tests run from this class interact with the real network stack process and can affect system 63 * state, e.g. by changing flags. 64 * State should be restored at the end of the test, but is not guaranteed if the test process is 65 * terminated during the run. 66 */ 67 class IpClientRootTest : IpClientIntegrationTestCommon() { 68 companion object { 69 private val TAG = IpClientRootTest::class.java.simpleName 70 private val automation by lazy { InstrumentationRegistry.getInstrumentation().uiAutomation } 71 private lateinit var nsClient: TestNetworkStackServiceClient 72 private lateinit var mStore: NetworkStackIpMemoryStore 73 private val mContext = InstrumentationRegistry.getInstrumentation().getContext() 74 75 private class IpMemoryStoreCallbacks( 76 private val fetchedFuture: CompletableFuture<IIpMemoryStore> 77 ) : IIpMemoryStoreCallbacks.Stub() { 78 override fun onIpMemoryStoreFetched(ipMemoryStore: IIpMemoryStore) { 79 fetchedFuture.complete(ipMemoryStore) 80 } 81 override fun getInterfaceVersion() = IIpMemoryStoreCallbacks.VERSION 82 override fun getInterfaceHash() = IIpMemoryStoreCallbacks.HASH 83 } 84 85 @JvmStatic @BeforeClass 86 fun setUpClass() { 87 // Connect to the NetworkStack only once, as it is relatively expensive (~50ms plus any 88 // polling time waiting for the test UID to be allowed), and there should be minimal 89 // side-effects between tests compared to reconnecting every time. 90 automation.adoptShellPermissionIdentity(NETWORK_SETTINGS, INTERACT_ACROSS_USERS_FULL) 91 try { 92 automation.executeShellCommand("su root service call network_stack " + 93 "$ALLOW_TEST_UID_INDEX i32 " + Process.myUid()) 94 // Connecting to the test service does not require allowing the test UID (binding is 95 // always allowed with NETWORK_SETTINGS permissions on debuggable builds), but 96 // allowing the test UID is required to call any method on the service. 97 nsClient = TestNetworkStackServiceClient.connect() 98 // Wait for oneway call to be processed: unfortunately there is no easy way to wait 99 // for a success callback via the service shell command. 100 // TODO: build a small native util that also waits for the success callback, bundle 101 // it in the test APK, and run it as shell command as root instead. 102 mStore = getIpMemoryStore() 103 } finally { 104 automation.dropShellPermissionIdentity() 105 } 106 } 107 108 private fun getIpMemoryStore(): NetworkStackIpMemoryStore { 109 // Until the test UID is allowed, oneway binder calls will not receive any reply. 110 // Call fetchIpMemoryStore (which has limited side-effects) repeatedly until any call 111 // gets a callback. 112 val limit = currentTimeMillis() + TEST_TIMEOUT_MS 113 val fetchedFuture = CompletableFuture<IIpMemoryStore>() 114 Log.i(TAG, "Starting multiple attempts to fetch IpMemoryStore; failures are expected") 115 while (currentTimeMillis() < limit) { 116 try { 117 nsClient.fetchIpMemoryStore(IpMemoryStoreCallbacks(fetchedFuture)) 118 // The future may be completed by any previous call to fetchIpMemoryStore. 119 val ipMemoryStore = fetchedFuture.get(20, TimeUnit.MILLISECONDS) 120 Log.i(TAG, "Obtained IpMemoryStore: " + ipMemoryStore) 121 return NetworkStackIpMemoryStore(mContext, ipMemoryStore) 122 } catch (e: TimeoutException) { 123 // Fall through 124 } 125 } 126 fail("fail to get the IpMemoryStore instance within timeout") 127 } 128 129 @JvmStatic @AfterClass 130 fun tearDownClass() { 131 nsClient.disconnect() 132 automation.adoptShellPermissionIdentity(NETWORK_SETTINGS) 133 try { 134 // Reset the test UID as -1. 135 // This may not be called if the test process is terminated before completing, 136 // however this is fine as the test UID is only usable on userdebug builds, and 137 // the system does not reuse UIDs across apps until reboot. 138 automation.executeShellCommand("su root service call network_stack " + 139 "$ALLOW_TEST_UID_INDEX i32 -1") 140 } finally { 141 automation.dropShellPermissionIdentity() 142 } 143 } 144 } 145 146 /** 147 * Wrapper class for IIpClientCallbacks. 148 * 149 * Used to delegate method calls to mock interfaces used to verify the calls, while using 150 * real implementations of the binder stub (such as [asBinder]) to properly receive the calls. 151 */ 152 private class BinderCbWrapper(base: IIpClientCallbacks) : 153 IIpClientCallbacks.Stub(), IIpClientCallbacks by base { 154 // asBinder is implemented by both base class and delegate: specify explicitly 155 override fun asBinder() = super.asBinder() 156 override fun getInterfaceVersion() = IIpClientCallbacks.VERSION 157 } 158 159 @After 160 fun tearDownIpMemoryStore() { 161 if (testSkipped()) return 162 val latch = CountDownLatch(1) 163 164 // Delete the IpMemoryStore entry corresponding to TEST_L2KEY, make sure each test starts 165 // from a clean state. 166 mStore.delete(TEST_L2KEY, true) { _, _ -> latch.countDown() } 167 assertTrue(latch.await(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) 168 } 169 170 override fun useNetworkStackSignature() = false 171 172 override fun makeIIpClient(ifaceName: String, cbMock: IIpClientCallbacks): IIpClient { 173 val ipClientCaptor = ArgumentCaptor.forClass(IIpClient::class.java) 174 // Older versions of NetworkStack do not clear the calling identity when creating IpClient, 175 // so READ_DEVICE_CONFIG is required to initialize it (b/168577898). 176 automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG) 177 try { 178 nsClient.makeIpClient(ifaceName, BinderCbWrapper(cbMock)) 179 verify(cbMock, timeout(TEST_TIMEOUT_MS)).onIpClientCreated(ipClientCaptor.capture()) 180 } finally { 181 automation.dropShellPermissionIdentity() 182 } 183 return ipClientCaptor.value 184 } 185 186 // These are not needed in IpClientRootTest because there is no dependency injection and 187 // IpClient always uses the production implementations. 188 override fun getDeviceConfigProperty(name: String) = throw UnsupportedOperationException() 189 override fun isFeatureEnabled(name: String) = throw UnsupportedOperationException() 190 override fun isFeatureNotChickenedOut(name: String) = throw UnsupportedOperationException() 191 192 private val mOriginalPropertyValues = ArrayMap<String, String>() 193 194 override fun setDeviceConfigProperty(name: String?, value: String?) { 195 automation.adoptShellPermissionIdentity( 196 READ_DEVICE_CONFIG, 197 WRITE_DEVICE_CONFIG, 198 WRITE_ALLOWLISTED_DEVICE_CONFIG 199 ) 200 try { 201 // Do not use computeIfAbsent as it would overwrite null values, 202 // property originally unset. 203 if (!mOriginalPropertyValues.containsKey(name)) { 204 mOriginalPropertyValues[name] = DeviceConfig.getProperty( 205 DeviceConfig.NAMESPACE_CONNECTIVITY, 206 (name)!! 207 ) 208 } 209 DeviceConfig.setProperty( 210 DeviceConfig.NAMESPACE_CONNECTIVITY, 211 (name)!!, value, 212 false /* makeDefault */ 213 ) 214 } finally { 215 automation.dropShellPermissionIdentity() 216 } 217 } 218 219 @After 220 fun tearDownDeviceConfigProperties() { 221 if (testSkipped()) return 222 automation.adoptShellPermissionIdentity( 223 READ_DEVICE_CONFIG, 224 WRITE_DEVICE_CONFIG, 225 WRITE_ALLOWLISTED_DEVICE_CONFIG 226 ) 227 try { 228 for (key in mOriginalPropertyValues.keys) { 229 if (key == null) continue 230 DeviceConfig.setProperty( 231 DeviceConfig.NAMESPACE_CONNECTIVITY, key, 232 mOriginalPropertyValues[key], false /* makeDefault */ 233 ) 234 } 235 } finally { 236 automation.dropShellPermissionIdentity() 237 } 238 } 239 240 private class TestAttributesRetrievedListener : OnNetworkAttributesRetrievedListener { 241 private val future = CompletableFuture<NetworkAttributes?>() 242 override fun onNetworkAttributesRetrieved( 243 status: Status, 244 key: String, 245 attr: NetworkAttributes? 246 ) { 247 // NetworkAttributes associated to specific l2key retrieved from IpMemoryStore might be 248 // null according to testcase context, hence, make sure the callback is triggered with 249 // success and the l2key param return from callback matches, which also prevents the 250 // case that the NetworkAttributes haven't been stored within CompletableFuture polling 251 // timeout. 252 if (key != TEST_L2KEY || status.resultCode != Status.SUCCESS) { 253 fail("retrieved the network attributes associated to L2Key: " + key + 254 " status: " + status.resultCode + " attributes: " + attr) 255 } 256 future.complete(attr) 257 } 258 259 fun getBlockingNetworkAttributes(timeout: Long): NetworkAttributes? { 260 return future.get(timeout, TimeUnit.MILLISECONDS) 261 } 262 } 263 264 private class TestNetworkEventCountRetrievedListener : OnNetworkEventCountRetrievedListener { 265 private val future = CompletableFuture<IntArray>() 266 override fun onNetworkEventCountRetrieved( 267 status: Status, 268 counts: IntArray 269 ) { 270 if (status.resultCode != Status.SUCCESS) { 271 fail("retrieved the network event count " + " status: " + status.resultCode) 272 } 273 future.complete(counts) 274 } 275 276 fun getBlockingNetworkEventCount(timeout: Long): IntArray { 277 return future.get(timeout, TimeUnit.MILLISECONDS) 278 } 279 } 280 281 override fun getStoredNetworkAttributes(l2Key: String, timeout: Long): NetworkAttributes { 282 val listener = TestAttributesRetrievedListener() 283 mStore.retrieveNetworkAttributes(l2Key, listener) 284 val na = listener.getBlockingNetworkAttributes(timeout) 285 assertNotNull(na) 286 return na 287 } 288 289 override fun assertIpMemoryNeverStoreNetworkAttributes(l2Key: String, timeout: Long) { 290 val listener = TestAttributesRetrievedListener() 291 mStore.retrieveNetworkAttributes(l2Key, listener) 292 assertNull(listener.getBlockingNetworkAttributes(timeout)) 293 } 294 295 override fun storeNetworkAttributes(l2Key: String, na: NetworkAttributes) { 296 mStore.storeNetworkAttributes(l2Key, na, null /* listener */) 297 } 298 299 override fun storeNetworkEvent(cluster: String, now: Long, expiry: Long, eventType: Int) { 300 mStore.storeNetworkEvent(cluster, now, expiry, eventType, null /* listener */) 301 } 302 303 override fun getStoredNetworkEventCount( 304 cluster: String, 305 sinceTimes: LongArray, 306 eventType: IntArray, 307 timeout: Long 308 ): IntArray { 309 val listener = TestNetworkEventCountRetrievedListener() 310 mStore.retrieveNetworkEventCount(cluster, sinceTimes, eventType, listener) 311 val counts = listener.getBlockingNetworkEventCount(timeout) 312 assertFalse(counts.size == 0) 313 return counts 314 } 315 316 private fun readNudSolicitNumFromResource(name: String): Int { 317 val packageName = nsClient.getNetworkStackPackageName() 318 val resource = mContext.createPackageContext(packageName, 0).getResources() 319 val id = resource.getIdentifier(name, "integer", packageName) 320 return resource.getInteger(id) 321 } 322 323 override fun readNudSolicitNumInSteadyStateFromResource(): Int { 324 return readNudSolicitNumFromResource("config_nud_steadystate_solicit_num") 325 } 326 327 override fun readNudSolicitNumPostRoamingFromResource(): Int { 328 return readNudSolicitNumFromResource("config_nud_postroaming_solicit_num") 329 } 330 } 331