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.net.hostside; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 20 import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED; 21 22 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness; 23 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getActiveNetworkCapabilities; 24 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground; 25 import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE; 26 import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.fail; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.net.Network; 33 import android.net.NetworkCapabilities; 34 import android.net.NetworkRequest; 35 import android.util.Log; 36 37 import org.junit.After; 38 import org.junit.Before; 39 import org.junit.Rule; 40 import org.junit.Test; 41 42 import java.util.Objects; 43 import java.util.concurrent.LinkedBlockingQueue; 44 import java.util.concurrent.TimeUnit; 45 46 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase { 47 private Network mNetwork; 48 private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback(); 49 @Rule 50 public final MeterednessConfigurationRule mMeterednessConfiguration 51 = new MeterednessConfigurationRule(); 52 53 enum CallbackState { 54 NONE, 55 AVAILABLE, 56 LOST, 57 BLOCKED_STATUS, 58 CAPABILITIES 59 } 60 61 private static class CallbackInfo { 62 public final CallbackState state; 63 public final Network network; 64 public final Object arg; 65 CallbackInfo(CallbackState s, Network n, Object o)66 CallbackInfo(CallbackState s, Network n, Object o) { 67 state = s; network = n; arg = o; 68 } 69 toString()70 public String toString() { 71 return String.format("%s (%s) (%s)", state, network, arg); 72 } 73 74 @Override equals(Object o)75 public boolean equals(Object o) { 76 if (!(o instanceof CallbackInfo)) return false; 77 // Ignore timeMs, since it's unpredictable. 78 final CallbackInfo other = (CallbackInfo) o; 79 return (state == other.state) && Objects.equals(network, other.network) 80 && Objects.equals(arg, other.arg); 81 } 82 83 @Override hashCode()84 public int hashCode() { 85 return Objects.hash(state, network, arg); 86 } 87 } 88 89 private class TestNetworkCallback extends INetworkCallback.Stub { 90 private static final int TEST_CONNECT_TIMEOUT_MS = 30_000; 91 private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000; 92 93 private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>(); 94 setLastCallback(CallbackState state, Network network, Object o)95 protected void setLastCallback(CallbackState state, Network network, Object o) { 96 mCallbacks.offer(new CallbackInfo(state, network, o)); 97 } 98 nextCallback(int timeoutMs)99 CallbackInfo nextCallback(int timeoutMs) { 100 CallbackInfo cb = null; 101 try { 102 cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS); 103 } catch (InterruptedException e) { 104 } 105 if (cb == null) { 106 fail("Did not receive callback after " + timeoutMs + "ms"); 107 } 108 return cb; 109 } 110 expectCallback(CallbackState state, Network expectedNetwork, Object o)111 CallbackInfo expectCallback(CallbackState state, Network expectedNetwork, Object o) { 112 final CallbackInfo expected = new CallbackInfo(state, expectedNetwork, o); 113 final CallbackInfo actual = nextCallback(TEST_CALLBACK_TIMEOUT_MS); 114 assertEquals("Unexpected callback:", expected, actual); 115 return actual; 116 } 117 118 @Override onAvailable(Network network)119 public void onAvailable(Network network) { 120 setLastCallback(CallbackState.AVAILABLE, network, null); 121 } 122 123 @Override onLost(Network network)124 public void onLost(Network network) { 125 setLastCallback(CallbackState.LOST, network, null); 126 } 127 128 @Override onBlockedStatusChanged(Network network, boolean blocked)129 public void onBlockedStatusChanged(Network network, boolean blocked) { 130 setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked); 131 } 132 133 @Override onCapabilitiesChanged(Network network, NetworkCapabilities cap)134 public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) { 135 setLastCallback(CallbackState.CAPABILITIES, network, cap); 136 } 137 expectAvailableCallbackAndGetNetwork()138 public Network expectAvailableCallbackAndGetNetwork() { 139 final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS); 140 if (cb.state != CallbackState.AVAILABLE) { 141 fail("Network is not available. Instead obtained the following callback :" 142 + cb); 143 } 144 return cb.network; 145 } 146 expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked)147 public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) { 148 expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked); 149 } 150 expectBlockedStatusCallbackEventually(Network expectedNetwork, boolean expectBlocked)151 public void expectBlockedStatusCallbackEventually(Network expectedNetwork, 152 boolean expectBlocked) { 153 final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS; 154 do { 155 final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis())); 156 if (cb.state == CallbackState.BLOCKED_STATUS 157 && cb.network.equals(expectedNetwork)) { 158 assertEquals(expectBlocked, cb.arg); 159 return; 160 } 161 } while (System.currentTimeMillis() <= deadline); 162 fail("Didn't receive onBlockedStatusChanged()"); 163 } 164 expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap, int cap)165 public void expectCapabilitiesCallbackEventually(Network expectedNetwork, boolean hasCap, 166 int cap) { 167 final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS; 168 do { 169 final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis())); 170 if (cb.state != CallbackState.CAPABILITIES 171 || !expectedNetwork.equals(cb.network) 172 || (hasCap != ((NetworkCapabilities) cb.arg).hasCapability(cap))) { 173 Log.i("NetworkCallbackTest#expectCapabilitiesCallback", 174 "Ignoring non-matching callback : " + cb); 175 continue; 176 } 177 // Found a match, return 178 return; 179 } while (System.currentTimeMillis() <= deadline); 180 fail("Didn't receive the expected callback to onCapabilitiesChanged(). Check the " 181 + "log for a list of received callbacks, if any."); 182 } 183 } 184 185 @Before setUp()186 public void setUp() throws Exception { 187 super.setUp(); 188 189 assumeTrue(canChangeActiveNetworkMeteredness()); 190 191 registerBroadcastReceiver(); 192 193 removeRestrictBackgroundWhitelist(mUid); 194 removeRestrictBackgroundBlacklist(mUid); 195 assertRestrictBackgroundChangedReceived(0); 196 197 // Initial state 198 setBatterySaverMode(false); 199 setRestrictBackground(false); 200 201 // Get transports of the active network, this has to be done before changing meteredness, 202 // since wifi will be disconnected when changing from non-metered to metered. 203 final NetworkCapabilities networkCapabilities = getActiveNetworkCapabilities(); 204 205 // Mark network as metered. 206 mMeterednessConfiguration.configureNetworkMeteredness(true); 207 208 // Register callback, copy the capabilities from the active network to expect the "original" 209 // network before disconnecting, but null out some fields to prevent over-specified. 210 registerNetworkCallback(new NetworkRequest.Builder() 211 .setCapabilities(networkCapabilities.setTransportInfo(null)) 212 .removeCapability(NET_CAPABILITY_NOT_METERED) 213 .setSignalStrength(SIGNAL_STRENGTH_UNSPECIFIED).build(), mTestNetworkCallback); 214 // Wait for onAvailable() callback to ensure network is available before the test 215 // and store the default network. 216 mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork(); 217 // Check that the network is metered. 218 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 219 false /* hasCapability */, NET_CAPABILITY_NOT_METERED); 220 mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false); 221 } 222 223 @After tearDown()224 public void tearDown() throws Exception { 225 super.tearDown(); 226 227 setRestrictBackground(false); 228 setBatterySaverMode(false); 229 unregisterNetworkCallback(); 230 } 231 232 @RequiredProperties({DATA_SAVER_MODE}) 233 @Test testOnBlockedStatusChanged_dataSaver()234 public void testOnBlockedStatusChanged_dataSaver() throws Exception { 235 try { 236 // Enable restrict background 237 setRestrictBackground(true); 238 assertBackgroundNetworkAccess(false); 239 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 240 241 // Add to whitelist 242 addRestrictBackgroundWhitelist(mUid); 243 assertBackgroundNetworkAccess(true); 244 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 245 246 // Remove from whitelist 247 removeRestrictBackgroundWhitelist(mUid); 248 assertBackgroundNetworkAccess(false); 249 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 250 } finally { 251 mMeterednessConfiguration.resetNetworkMeteredness(); 252 } 253 254 // Set to non-metered network 255 mMeterednessConfiguration.configureNetworkMeteredness(false); 256 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 257 true /* hasCapability */, NET_CAPABILITY_NOT_METERED); 258 try { 259 assertBackgroundNetworkAccess(true); 260 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 261 262 // Disable restrict background, should not trigger callback 263 setRestrictBackground(false); 264 assertBackgroundNetworkAccess(true); 265 } finally { 266 mMeterednessConfiguration.resetNetworkMeteredness(); 267 } 268 } 269 270 @RequiredProperties({BATTERY_SAVER_MODE}) 271 @Test testOnBlockedStatusChanged_powerSaver()272 public void testOnBlockedStatusChanged_powerSaver() throws Exception { 273 try { 274 // Enable Power Saver 275 setBatterySaverMode(true); 276 assertBackgroundNetworkAccess(false); 277 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 278 279 // Disable Power Saver 280 setBatterySaverMode(false); 281 assertBackgroundNetworkAccess(true); 282 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 283 } finally { 284 mMeterednessConfiguration.resetNetworkMeteredness(); 285 } 286 287 // Set to non-metered network 288 mMeterednessConfiguration.configureNetworkMeteredness(false); 289 mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, 290 true /* hasCapability */, NET_CAPABILITY_NOT_METERED); 291 try { 292 // Enable Power Saver 293 setBatterySaverMode(true); 294 assertBackgroundNetworkAccess(false); 295 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); 296 297 // Disable Power Saver 298 setBatterySaverMode(false); 299 assertBackgroundNetworkAccess(true); 300 mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); 301 } finally { 302 mMeterednessConfiguration.resetNetworkMeteredness(); 303 } 304 } 305 306 // TODO: 1. test against VPN lockdown. 307 // 2. test against multiple networks. 308 } 309