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