1 /* 2 * Copyright (C) 2019 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.android.cts.netpolicy.hostside; 18 19 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; 20 import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 22 23 import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness; 24 import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities; 25 import static com.android.cts.netpolicy.hostside.NetworkPolicyTestUtils.setRestrictBackground; 26 import static com.android.cts.netpolicy.hostside.Property.BATTERY_SAVER_MODE; 27 import static com.android.cts.netpolicy.hostside.Property.DATA_SAVER_MODE; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.fail; 31 import static org.junit.Assume.assumeTrue; 32 33 import android.net.Network; 34 import android.net.NetworkCapabilities; 35 import android.net.NetworkRequest; 36 import android.net.cts.util.CtsNetUtils; 37 import android.os.SystemClock; 38 import android.util.Log; 39 40 import com.android.modules.utils.build.SdkLevel; 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Rule; 45 import org.junit.Test; 46 47 import java.util.ArrayList; 48 import java.util.Objects; 49 import java.util.concurrent.LinkedBlockingQueue; 50 import java.util.concurrent.TimeUnit; 51 52 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase { 53 private Network mNetwork; 54 private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback(); 55 private CtsNetUtils mCtsNetUtils; 56 private static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google"; 57 58 @Rule 59 public final MeterednessConfigurationRule mMeterednessConfiguration 60 = new MeterednessConfigurationRule(); 61 62 enum CallbackState { 63 NONE, 64 AVAILABLE, 65 LOST, 66 BLOCKED_STATUS, 67 CAPABILITIES 68 } 69 70 private static class CallbackInfo { 71 public final CallbackState state; 72 public final Network network; 73 public final Object arg; 74 CallbackInfo(CallbackState s, Network n, Object o)75 CallbackInfo(CallbackState s, Network n, Object o) { 76 state = s; network = n; arg = o; 77 } 78 toString()79 public String toString() { 80 return String.format("%s (%s) (%s)", state, network, arg); 81 } 82 83 @Override equals(Object o)84 public boolean equals(Object o) { 85 if (!(o instanceof CallbackInfo)) return false; 86 // Ignore timeMs, since it's unpredictable. 87 final CallbackInfo other = (CallbackInfo) o; 88 return (state == other.state) && Objects.equals(network, other.network) 89 && Objects.equals(arg, other.arg); 90 } 91 92 @Override hashCode()93 public int hashCode() { 94 return Objects.hash(state, network, arg); 95 } 96 } 97 98 private class TestNetworkCallback extends INetworkCallback.Stub { 99 private static final int TEST_CONNECT_TIMEOUT_MS = 30_000; 100 private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000; 101 102 private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>(); 103 setLastCallback(CallbackState state, Network network, Object o)104 protected void setLastCallback(CallbackState state, Network network, Object o) { 105 mCallbacks.offer(new CallbackInfo(state, network, o)); 106 } 107 nextCallback(int timeoutMs)108 CallbackInfo nextCallback(int timeoutMs) { 109 CallbackInfo cb = null; 110 try { 111 cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS); 112 } catch (InterruptedException e) { 113 } 114 if (cb == null) { 115 fail("Did not receive callback after " + timeoutMs + "ms"); 116 } 117 return cb; 118 } 119 expectCallback(CallbackState state, Network expectedNetwork, Object o)120 CallbackInfo expectCallback(CallbackState state, Network expectedNetwork, Object o) { 121 final CallbackInfo expected = new CallbackInfo(state, expectedNetwork, o); 122 final CallbackInfo actual = nextCallback(TEST_CALLBACK_TIMEOUT_MS); 123 assertEquals("Unexpected callback:", expected, actual); 124 return actual; 125 } 126 127 @Override onAvailable(Network network)128 public void onAvailable(Network network) { 129 setLastCallback(CallbackState.AVAILABLE, network, null); 130 } 131 132 @Override onLost(Network network)133 public void onLost(Network network) { 134 setLastCallback(CallbackState.LOST, network, null); 135 } 136 137 @Override onBlockedStatusChanged(Network network, boolean blocked)138 public void onBlockedStatusChanged(Network network, boolean blocked) { 139 setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked); 140 } 141 142 @Override onCapabilitiesChanged(Network network, NetworkCapabilities cap)143 public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) { 144 setLastCallback(CallbackState.CAPABILITIES, network, cap); 145 } 146 expectAvailableCallbackAndGetNetwork()147 public Network expectAvailableCallbackAndGetNetwork() { 148 final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS); 149 if (cb.state != CallbackState.AVAILABLE) { 150 fail("Network is not available. Instead obtained the following callback :" + cb); 151 } 152 return cb.network; 153 } 154 drainAndWaitForIdle()155 public void drainAndWaitForIdle() { 156 try { 157 do { 158 mCallbacks.drainTo(new ArrayList<>()); 159 } while (mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS) != null); 160 } catch (InterruptedException ie) { 161 Log.e(TAG, "Interrupted while draining callback queue", ie); 162 Thread.currentThread().interrupt(); 163 } 164 } 165 expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked)166 public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) { 167 expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked); 168 } 169 expectBlockedStatusCallbackEventually(Network expectedNetwork, boolean expectBlocked)170 public void expectBlockedStatusCallbackEventually(Network expectedNetwork, 171 boolean expectBlocked) { 172 final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS; 173 do { 174 final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis())); 175 if (cb.state == CallbackState.BLOCKED_STATUS 176 && cb.network.equals(expectedNetwork)) { 177 assertEquals(expectBlocked, cb.arg); 178 return; 179 } 180 } while (System.currentTimeMillis() <= deadline); 181 fail("Didn't receive onBlockedStatusChanged()"); 182 } 183 expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap, int cap)184 public void expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap, 185 int cap) { 186 final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS; 187 do { 188 final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis())); 189 if (cb.state != CallbackState.CAPABILITIES 190 || !expectedNetwork.equals(cb.network) 191 || (hasCap != ((NetworkCapabilities) cb.arg).hasCapability(cap))) { 192 Log.i("NetworkCallbackTest#expectCapabilitiesCallback", 193 "Ignoring non-matching callback : " + cb); 194 continue; 195 } 196 // Found a match, return 197 return; 198 } while (System.currentTimeMillis() <= deadline); 199 fail("Didn't receive the expected callback to onCapabilitiesChanged(). Check the " 200 + "log for a list of received callbacks, if any."); 201 } 202 } 203 204 @Before setUp()205 public void setUp() throws Exception { 206 super.setUp(); 207 208 assumeTrue(canChangeActiveNetworkMeteredness()); 209 210 registerBroadcastReceiver(); 211 212 removeRestrictBackgroundWhitelist(mUid); 213 removeRestrictBackgroundBlacklist(mUid); 214 assertRestrictBackgroundChangedReceived(0); 215 216 // Initial state 217 setBatterySaverMode(false); 218 setRestrictBackground(false); 219 setAppIdle(false); 220 221 // Get transports of the active network, this has to be done before changing meteredness, 222 // since wifi will be disconnected when changing from non-metered to metered. 223 final NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder() 224 .clearCapabilities(); 225 final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities(); 226 for (final int capability : networkCapabilities.getCapabilities()) { 227 networkRequestBuilder.addCapability(capability); 228 } 229 for (final int transportType : networkCapabilities.getTransportTypes()) { 230 networkRequestBuilder.addTransportType(transportType); 231 } 232 networkRequestBuilder.setNetworkSpecifier(networkCapabilities.getNetworkSpecifier()); 233 networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_METERED); 234 235 // Mark network as metered. 236 mMeterednessConfiguration.configureNetworkMeteredness(true); 237 238 // Register callback, copy the capabilities from the active network to expect the "original" 239 // network before disconnecting, but null out some fields to prevent over-specified. 240 registerNetworkCallback(networkRequestBuilder.build(), mTestNetworkCallback); 241 // Wait for onAvailable() callback to ensure network is available before the test 242 // and store the default network. 243 mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork(); 244 // Check that the network is metered. 245 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 246 false /* hasCapability */, NET_CAPABILITY_NOT_METERED); 247 mTestNetworkCallback.drainAndWaitForIdle(); 248 249 // Before Android T, DNS queries over private DNS should be but are not restricted by Power 250 // Saver or Data Saver. The issue is fixed in mainline update and apps can no longer request 251 // DNS queries when its network is restricted by Power Saver. The fix takes effect backwards 252 // starting from Android T. But for Data Saver, the fix is not backward compatible since 253 // there are some platform changes involved. It is only available on devices that a specific 254 // trunk flag is enabled. 255 // 256 // This test can not only verify that the network traffic from apps is blocked at the right 257 // time, but also verify whether it is correctly blocked at the DNS stage, or at a later 258 // socket connection stage. 259 if (SdkLevel.isAtLeastT()) { 260 // Enable private DNS 261 mCtsNetUtils = new CtsNetUtils(mContext); 262 mCtsNetUtils.storePrivateDnsSetting(); 263 mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER); 264 mCtsNetUtils.awaitPrivateDnsSetting( 265 "NetworkCallbackTest wait private DNS setting timeout", mNetwork, 266 GOOGLE_PRIVATE_DNS_SERVER, true); 267 } 268 } 269 270 @After tearDown()271 public void tearDown() throws Exception { 272 super.tearDown(); 273 274 setRestrictBackground(false); 275 setBatterySaverMode(false); 276 unregisterNetworkCallback(); 277 stopApp(); 278 279 if (SdkLevel.isAtLeastT() && (mCtsNetUtils != null)) { 280 mCtsNetUtils.restorePrivateDnsSetting(); 281 } 282 } 283 284 @RequiredProperties({DATA_SAVER_MODE}) 285 @Test testOnBlockedStatusChanged_dataSaver()286 public void testOnBlockedStatusChanged_dataSaver() throws Exception { 287 try { 288 // Enable restrict background 289 setRestrictBackground(true); 290 // TODO: Verify expectedUnavailableError when aconfig support mainline. 291 // (see go/aconfig-in-mainline-problems) 292 assertBackgroundNetworkAccess(false); 293 assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); 294 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 295 296 // Add to whitelist 297 addRestrictBackgroundWhitelist(mUid); 298 assertBackgroundNetworkAccess(true); 299 assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */); 300 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 301 302 // Remove from whitelist 303 removeRestrictBackgroundWhitelist(mUid); 304 // TODO: Verify expectedUnavailableError when aconfig support mainline. 305 assertBackgroundNetworkAccess(false); 306 assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); 307 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 308 } finally { 309 mMeterednessConfiguration.resetNetworkMeteredness(); 310 } 311 312 // Set to non-metered network 313 mMeterednessConfiguration.configureNetworkMeteredness(false); 314 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 315 true /* hasCapability */, NET_CAPABILITY_NOT_METERED); 316 try { 317 assertBackgroundNetworkAccess(true); 318 assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */); 319 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 320 321 // Disable restrict background, should not trigger callback 322 setRestrictBackground(false); 323 assertBackgroundNetworkAccess(true); 324 assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */); 325 } finally { 326 mMeterednessConfiguration.resetNetworkMeteredness(); 327 } 328 } 329 330 @RequiredProperties({BATTERY_SAVER_MODE}) 331 @Test testOnBlockedStatusChanged_powerSaver()332 public void testOnBlockedStatusChanged_powerSaver() throws Exception { 333 try { 334 // Enable Power Saver 335 setBatterySaverMode(true); 336 if (SdkLevel.isAtLeastT()) { 337 assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); 338 assertNetworkAccess(false, "java.net.UnknownHostException"); 339 } else { 340 assertBackgroundNetworkAccess(false); 341 } 342 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 343 assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); 344 345 // Disable Power Saver 346 setBatterySaverMode(false); 347 assertBackgroundNetworkAccess(true); 348 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 349 assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */); 350 } finally { 351 mMeterednessConfiguration.resetNetworkMeteredness(); 352 } 353 354 // Set to non-metered network 355 mMeterednessConfiguration.configureNetworkMeteredness(false); 356 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 357 true /* hasCapability */, NET_CAPABILITY_NOT_METERED); 358 try { 359 // Enable Power Saver 360 setBatterySaverMode(true); 361 if (SdkLevel.isAtLeastT()) { 362 assertProcessStateBelow(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); 363 assertNetworkAccess(false, "java.net.UnknownHostException"); 364 } else { 365 assertBackgroundNetworkAccess(false); 366 } 367 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 368 assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */); 369 370 // Disable Power Saver 371 setBatterySaverMode(false); 372 assertBackgroundNetworkAccess(true); 373 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 374 assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */); 375 } finally { 376 mMeterednessConfiguration.resetNetworkMeteredness(); 377 } 378 } 379 380 @Test testOnBlockedStatusChanged_default()381 public void testOnBlockedStatusChanged_default() throws Exception { 382 try { 383 assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); 384 assertNetworkAccess(false, null); 385 assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); 386 387 launchActivity(); 388 assertTopState(); 389 assertNetworkAccess(true, null); 390 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 391 assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */); 392 393 finishActivity(); 394 assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); 395 SystemClock.sleep(mProcessStateTransitionLongDelayMs); 396 assertNetworkAccess(false, null); 397 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 398 assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); 399 400 } finally { 401 mMeterednessConfiguration.resetNetworkMeteredness(); 402 } 403 404 // Set to non-metered network 405 mMeterednessConfiguration.configureNetworkMeteredness(false); 406 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 407 true /* hasCapability */, NET_CAPABILITY_NOT_METERED); 408 try { 409 assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); 410 assertNetworkAccess(false, null); 411 assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */); 412 413 launchActivity(); 414 assertTopState(); 415 assertNetworkAccess(true, null); 416 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 417 assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */); 418 419 finishActivity(); 420 assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); 421 SystemClock.sleep(mProcessStateTransitionLongDelayMs); 422 assertNetworkAccess(false, null); 423 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 424 assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */); 425 } finally { 426 mMeterednessConfiguration.resetNetworkMeteredness(); 427 } 428 } 429 430 // TODO: 1. test against VPN lockdown. 431 // 2. test against multiple networks. 432 } 433