1 /* 2 * Copyright (C) 2017 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.networkstack.tethering; 18 19 import static android.net.NetworkStats.DEFAULT_NETWORK_NO; 20 import static android.net.NetworkStats.METERED_NO; 21 import static android.net.NetworkStats.ROAMING_NO; 22 import static android.net.NetworkStats.SET_DEFAULT; 23 import static android.net.NetworkStats.TAG_NONE; 24 import static android.net.NetworkStats.UID_ALL; 25 import static android.net.NetworkStats.UID_TETHERING; 26 import static android.net.RouteInfo.RTN_UNICAST; 27 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; 28 29 import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; 30 import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; 31 import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; 32 import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; 33 import static com.android.testutils.MiscAssertsKt.assertContainsAll; 34 import static com.android.testutils.MiscAssertsKt.assertThrows; 35 import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; 36 37 import static junit.framework.Assert.assertNotNull; 38 39 import static org.junit.Assert.assertEquals; 40 import static org.junit.Assert.assertTrue; 41 import static org.mockito.Matchers.any; 42 import static org.mockito.Matchers.anyLong; 43 import static org.mockito.Matchers.anyObject; 44 import static org.mockito.Matchers.anyString; 45 import static org.mockito.Matchers.eq; 46 import static org.mockito.Mockito.clearInvocations; 47 import static org.mockito.Mockito.inOrder; 48 import static org.mockito.Mockito.never; 49 import static org.mockito.Mockito.reset; 50 import static org.mockito.Mockito.times; 51 import static org.mockito.Mockito.verify; 52 import static org.mockito.Mockito.verifyNoMoreInteractions; 53 import static org.mockito.Mockito.when; 54 55 import android.annotation.NonNull; 56 import android.app.usage.NetworkStatsManager; 57 import android.content.Context; 58 import android.content.pm.ApplicationInfo; 59 import android.net.ITetheringStatsProvider; 60 import android.net.IpPrefix; 61 import android.net.LinkAddress; 62 import android.net.LinkProperties; 63 import android.net.NetworkStats; 64 import android.net.NetworkStats.Entry; 65 import android.net.RouteInfo; 66 import android.net.netstats.provider.NetworkStatsProvider; 67 import android.net.util.SharedLog; 68 import android.os.Handler; 69 import android.os.test.TestLooper; 70 import android.provider.Settings; 71 import android.provider.Settings.SettingNotFoundException; 72 import android.test.mock.MockContentResolver; 73 74 import androidx.test.filters.SmallTest; 75 import androidx.test.runner.AndroidJUnit4; 76 77 import com.android.internal.util.test.FakeSettingsProvider; 78 import com.android.testutils.TestableNetworkStatsProviderCbBinder; 79 80 import org.junit.After; 81 import org.junit.Before; 82 import org.junit.Test; 83 import org.junit.runner.RunWith; 84 import org.mockito.ArgumentCaptor; 85 import org.mockito.InOrder; 86 import org.mockito.Mock; 87 import org.mockito.MockitoAnnotations; 88 89 import java.net.InetAddress; 90 import java.util.ArrayList; 91 import java.util.HashSet; 92 import java.util.Set; 93 94 @RunWith(AndroidJUnit4.class) 95 @SmallTest 96 public class OffloadControllerTest { 97 private static final String RNDIS0 = "test_rndis0"; 98 private static final String RMNET0 = "test_rmnet_data0"; 99 private static final String WLAN0 = "test_wlan0"; 100 101 private static final String IPV6_LINKLOCAL = "fe80::/64"; 102 private static final String IPV6_DOC_PREFIX = "2001:db8::/64"; 103 private static final String IPV6_DISCARD_PREFIX = "100::/64"; 104 private static final String USB_PREFIX = "192.168.42.0/24"; 105 private static final String WIFI_PREFIX = "192.168.43.0/24"; 106 private static final long WAIT_FOR_IDLE_TIMEOUT = 2 * 1000; 107 108 @Mock private OffloadHardwareInterface mHardware; 109 @Mock private ApplicationInfo mApplicationInfo; 110 @Mock private Context mContext; 111 @Mock private NetworkStatsManager mStatsManager; 112 @Mock private TetheringConfiguration mTetherConfig; 113 // Late init since methods must be called by the thread that created this object. 114 private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; 115 private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; 116 private final ArgumentCaptor<ArrayList> mStringArrayCaptor = 117 ArgumentCaptor.forClass(ArrayList.class); 118 private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = 119 ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); 120 private MockContentResolver mContentResolver; 121 private final TestLooper mTestLooper = new TestLooper(); 122 private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { 123 @Override 124 public TetheringConfiguration getTetherConfig() { 125 return mTetherConfig; 126 } 127 }; 128 setUp()129 @Before public void setUp() { 130 MockitoAnnotations.initMocks(this); 131 when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); 132 when(mContext.getPackageName()).thenReturn("OffloadControllerTest"); 133 mContentResolver = new MockContentResolver(mContext); 134 mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 135 when(mContext.getContentResolver()).thenReturn(mContentResolver); 136 FakeSettingsProvider.clearSettingsProvider(); 137 when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled. 138 } 139 tearDown()140 @After public void tearDown() throws Exception { 141 FakeSettingsProvider.clearSettingsProvider(); 142 } 143 setupFunctioningHardwareInterface()144 private void setupFunctioningHardwareInterface() { 145 when(mHardware.initOffloadConfig()).thenReturn(true); 146 when(mHardware.initOffloadControl(mControlCallbackCaptor.capture())) 147 .thenReturn(true); 148 when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true); 149 when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); 150 when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); 151 } 152 enableOffload()153 private void enableOffload() { 154 Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); 155 } 156 setOffloadPollInterval(int interval)157 private void setOffloadPollInterval(int interval) { 158 when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval); 159 } 160 waitForIdle()161 private void waitForIdle() { 162 mTestLooper.dispatchAll(); 163 } 164 makeOffloadController()165 private OffloadController makeOffloadController() throws Exception { 166 OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), 167 mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); 168 final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> 169 tetherStatsProviderCaptor = 170 ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); 171 verify(mStatsManager).registerNetworkStatsProvider(anyString(), 172 tetherStatsProviderCaptor.capture()); 173 mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); 174 assertNotNull(mTetherStatsProvider); 175 mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); 176 mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); 177 return offload; 178 } 179 180 @Test testNoSettingsValueDefaultDisabledDoesNotStart()181 public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception { 182 setupFunctioningHardwareInterface(); 183 when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1); 184 assertThrows(SettingNotFoundException.class, () -> 185 Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); 186 187 final OffloadController offload = makeOffloadController(); 188 offload.start(); 189 190 final InOrder inOrder = inOrder(mHardware); 191 inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); 192 inOrder.verify(mHardware, never()).initOffloadConfig(); 193 inOrder.verify(mHardware, never()).initOffloadControl( 194 any(OffloadHardwareInterface.ControlCallback.class)); 195 inOrder.verifyNoMoreInteractions(); 196 } 197 198 @Test testNoSettingsValueDefaultEnabledDoesStart()199 public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception { 200 setupFunctioningHardwareInterface(); 201 when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0); 202 assertThrows(SettingNotFoundException.class, () -> 203 Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); 204 205 final OffloadController offload = makeOffloadController(); 206 offload.start(); 207 208 final InOrder inOrder = inOrder(mHardware); 209 inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); 210 inOrder.verify(mHardware, times(1)).initOffloadConfig(); 211 inOrder.verify(mHardware, times(1)).initOffloadControl( 212 any(OffloadHardwareInterface.ControlCallback.class)); 213 inOrder.verifyNoMoreInteractions(); 214 } 215 216 @Test testSettingsAllowsStart()217 public void testSettingsAllowsStart() throws Exception { 218 setupFunctioningHardwareInterface(); 219 Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); 220 221 final OffloadController offload = makeOffloadController(); 222 offload.start(); 223 224 final InOrder inOrder = inOrder(mHardware); 225 inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); 226 inOrder.verify(mHardware, times(1)).initOffloadConfig(); 227 inOrder.verify(mHardware, times(1)).initOffloadControl( 228 any(OffloadHardwareInterface.ControlCallback.class)); 229 inOrder.verifyNoMoreInteractions(); 230 } 231 232 @Test testSettingsDisablesStart()233 public void testSettingsDisablesStart() throws Exception { 234 setupFunctioningHardwareInterface(); 235 Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1); 236 237 final OffloadController offload = makeOffloadController(); 238 offload.start(); 239 240 final InOrder inOrder = inOrder(mHardware); 241 inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); 242 inOrder.verify(mHardware, never()).initOffloadConfig(); 243 inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); 244 inOrder.verifyNoMoreInteractions(); 245 } 246 247 @Test testSetUpstreamLinkPropertiesWorking()248 public void testSetUpstreamLinkPropertiesWorking() throws Exception { 249 setupFunctioningHardwareInterface(); 250 enableOffload(); 251 252 final OffloadController offload = makeOffloadController(); 253 offload.start(); 254 255 final InOrder inOrder = inOrder(mHardware); 256 inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); 257 inOrder.verify(mHardware, times(1)).initOffloadConfig(); 258 inOrder.verify(mHardware, times(1)).initOffloadControl( 259 any(OffloadHardwareInterface.ControlCallback.class)); 260 inOrder.verifyNoMoreInteractions(); 261 262 // In reality, the UpstreamNetworkMonitor would have passed down to us 263 // a covering set of local prefixes representing a minimum essential 264 // set plus all the prefixes on networks with network agents. 265 // 266 // We simulate that there, and then add upstream elements one by one 267 // and watch what happens. 268 final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); 269 for (String s : new String[]{ 270 "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { 271 minimumLocalPrefixes.add(new IpPrefix(s)); 272 } 273 offload.setLocalPrefixes(minimumLocalPrefixes); 274 inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); 275 ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); 276 assertEquals(4, localPrefixes.size()); 277 assertContainsAll(localPrefixes, 278 "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); 279 inOrder.verifyNoMoreInteractions(); 280 281 offload.setUpstreamLinkProperties(null); 282 // No change in local addresses means no call to setLocalPrefixes(). 283 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 284 // This LinkProperties value does not differ from the default upstream. 285 // There should be no extraneous call to setUpstreamParameters(). 286 inOrder.verify(mHardware, never()).setUpstreamParameters( 287 anyObject(), anyObject(), anyObject(), anyObject()); 288 inOrder.verifyNoMoreInteractions(); 289 290 final LinkProperties lp = new LinkProperties(); 291 292 final String testIfName = "rmnet_data17"; 293 lp.setInterfaceName(testIfName); 294 offload.setUpstreamLinkProperties(lp); 295 // No change in local addresses means no call to setLocalPrefixes(). 296 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 297 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 298 eq(testIfName), eq(null), eq(null), eq(null)); 299 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 300 inOrder.verifyNoMoreInteractions(); 301 302 final String ipv4Addr = "192.0.2.5"; 303 final String linkAddr = ipv4Addr + "/24"; 304 lp.addLinkAddress(new LinkAddress(linkAddr)); 305 lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST)); 306 offload.setUpstreamLinkProperties(lp); 307 // IPv4 prefixes and addresses on the upstream are simply left as whole 308 // prefixes (already passed in from UpstreamNetworkMonitor code). If a 309 // tethering client sends traffic to the IPv4 default router or other 310 // clients on the upstream this will not be hardware-forwarded, and that 311 // should be fine for now. Ergo: no change in local addresses, no call 312 // to setLocalPrefixes(). 313 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 314 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 315 eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); 316 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 317 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 318 inOrder.verifyNoMoreInteractions(); 319 320 final String ipv4Gateway = "192.0.2.1"; 321 lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST)); 322 offload.setUpstreamLinkProperties(lp); 323 // No change in local addresses means no call to setLocalPrefixes(). 324 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 325 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 326 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); 327 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 328 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 329 inOrder.verifyNoMoreInteractions(); 330 331 final String ipv6Gw1 = "fe80::cafe"; 332 lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST)); 333 offload.setUpstreamLinkProperties(lp); 334 // No change in local addresses means no call to setLocalPrefixes(). 335 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 336 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 337 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); 338 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 339 ArrayList<String> v6gws = mStringArrayCaptor.getValue(); 340 assertEquals(1, v6gws.size()); 341 assertTrue(v6gws.contains(ipv6Gw1)); 342 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 343 inOrder.verifyNoMoreInteractions(); 344 345 final String ipv6Gw2 = "fe80::d00d"; 346 lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST)); 347 offload.setUpstreamLinkProperties(lp); 348 // No change in local addresses means no call to setLocalPrefixes(). 349 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 350 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 351 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); 352 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 353 v6gws = mStringArrayCaptor.getValue(); 354 assertEquals(2, v6gws.size()); 355 assertTrue(v6gws.contains(ipv6Gw1)); 356 assertTrue(v6gws.contains(ipv6Gw2)); 357 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 358 inOrder.verifyNoMoreInteractions(); 359 360 final LinkProperties stacked = new LinkProperties(); 361 stacked.setInterfaceName("stacked"); 362 stacked.addLinkAddress(new LinkAddress("192.0.2.129/25")); 363 stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null, 364 RTN_UNICAST)); 365 stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null, 366 RTN_UNICAST)); 367 assertTrue(lp.addStackedLink(stacked)); 368 offload.setUpstreamLinkProperties(lp); 369 // No change in local addresses means no call to setLocalPrefixes(). 370 inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); 371 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 372 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); 373 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 374 v6gws = mStringArrayCaptor.getValue(); 375 assertEquals(2, v6gws.size()); 376 assertTrue(v6gws.contains(ipv6Gw1)); 377 assertTrue(v6gws.contains(ipv6Gw2)); 378 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 379 inOrder.verifyNoMoreInteractions(); 380 381 // Add in some IPv6 upstream info. When there is a tethered downstream 382 // making use of the IPv6 prefix we would expect to see the /64 route 383 // removed from "local prefixes" and /128s added for the upstream IPv6 384 // addresses. This is not yet implemented, and for now we simply 385 // expect to see these /128s. 386 lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST)); 387 // "2001:db8::/64" plus "assigned" ASCII in hex 388 lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); 389 // "2001:db8::/64" plus "random" ASCII in hex 390 lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); 391 offload.setUpstreamLinkProperties(lp); 392 inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); 393 localPrefixes = mStringArrayCaptor.getValue(); 394 assertEquals(6, localPrefixes.size()); 395 assertContainsAll(localPrefixes, 396 "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64", 397 "2001:db8::6173:7369:676e:6564/128", "2001:db8::7261:6e64:6f6d/128"); 398 // The relevant parts of the LinkProperties have not changed, but at the 399 // moment we do not de-dup upstream LinkProperties this carefully. 400 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 401 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); 402 v6gws = mStringArrayCaptor.getValue(); 403 assertEquals(2, v6gws.size()); 404 assertTrue(v6gws.contains(ipv6Gw1)); 405 assertTrue(v6gws.contains(ipv6Gw2)); 406 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName)); 407 inOrder.verify(mHardware, times(1)).setDataLimit(eq(testIfName), eq(Long.MAX_VALUE)); 408 inOrder.verifyNoMoreInteractions(); 409 410 // Completely identical LinkProperties updates are de-duped. 411 offload.setUpstreamLinkProperties(lp); 412 // This LinkProperties value does not differ from the default upstream. 413 // There should be no extraneous call to setUpstreamParameters(). 414 inOrder.verify(mHardware, never()).setUpstreamParameters( 415 anyObject(), anyObject(), anyObject(), anyObject()); 416 inOrder.verifyNoMoreInteractions(); 417 } 418 buildTestEntry(@onNull OffloadController.StatsType how, @NonNull String iface, long rxBytes, long txBytes)419 private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how, 420 @NonNull String iface, long rxBytes, long txBytes) { 421 return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT, 422 TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L, 423 txBytes, 0L, 0L); 424 } 425 426 @Test testGetForwardedStats()427 public void testGetForwardedStats() throws Exception { 428 setupFunctioningHardwareInterface(); 429 enableOffload(); 430 431 final OffloadController offload = makeOffloadController(); 432 offload.start(); 433 434 final String ethernetIface = "eth1"; 435 final String mobileIface = "rmnet_data0"; 436 437 when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( 438 new ForwardedStats(12345, 54321)); 439 when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn( 440 new ForwardedStats(999, 99999)); 441 442 InOrder inOrder = inOrder(mHardware); 443 444 final LinkProperties lp = new LinkProperties(); 445 lp.setInterfaceName(ethernetIface); 446 offload.setUpstreamLinkProperties(lp); 447 // Previous upstream was null, so no stats are fetched. 448 inOrder.verify(mHardware, never()).getForwardedStats(any()); 449 450 lp.setInterfaceName(mobileIface); 451 offload.setUpstreamLinkProperties(lp); 452 // Expect that we fetch stats from the previous upstream. 453 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); 454 455 lp.setInterfaceName(ethernetIface); 456 offload.setUpstreamLinkProperties(lp); 457 // Expect that we fetch stats from the previous upstream. 458 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); 459 460 // Verify that the fetched stats are stored. 461 final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); 462 final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID); 463 final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) 464 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) 465 .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); 466 467 final NetworkStats expectedUidStats = new NetworkStats(0L, 2) 468 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) 469 .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); 470 471 assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); 472 assertNetworkStatsEquals(expectedUidStats, uidStats); 473 474 // Force pushing stats update to verify the stats reported. 475 mTetherStatsProvider.pushTetherStats(); 476 mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); 477 478 when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( 479 new ForwardedStats(100000, 100000)); 480 offload.setUpstreamLinkProperties(null); 481 // Expect that we first clear the HAL's upstream parameters. 482 inOrder.verify(mHardware, times(1)).setUpstreamParameters( 483 eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null)); 484 // Expect that we fetch stats from the previous upstream. 485 inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); 486 487 // There is no current upstream, so no stats are fetched. 488 inOrder.verify(mHardware, never()).getForwardedStats(any()); 489 inOrder.verifyNoMoreInteractions(); 490 491 // Verify that the stored stats is accumulated. 492 final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE); 493 final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID); 494 final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) 495 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) 496 .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); 497 498 final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) 499 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) 500 .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); 501 502 assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); 503 assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); 504 505 // Verify that only diff of stats is reported. 506 mTetherStatsProvider.pushTetherStats(); 507 final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) 508 .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) 509 .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); 510 511 final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) 512 .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) 513 .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); 514 mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, 515 expectedUidStatsDiff); 516 } 517 518 @Test testSetInterfaceQuota()519 public void testSetInterfaceQuota() throws Exception { 520 setupFunctioningHardwareInterface(); 521 enableOffload(); 522 523 final OffloadController offload = makeOffloadController(); 524 offload.start(); 525 526 final String ethernetIface = "eth1"; 527 final String mobileIface = "rmnet_data0"; 528 final long ethernetLimit = 12345; 529 final long mobileLimit = 12345678; 530 531 final LinkProperties lp = new LinkProperties(); 532 lp.setInterfaceName(ethernetIface); 533 offload.setUpstreamLinkProperties(lp); 534 535 final InOrder inOrder = inOrder(mHardware); 536 when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); 537 when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); 538 539 // Applying an interface quota to the current upstream immediately sends it to the hardware. 540 mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); 541 waitForIdle(); 542 inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); 543 inOrder.verifyNoMoreInteractions(); 544 545 // Applying an interface quota to another upstream does not take any immediate action. 546 mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); 547 waitForIdle(); 548 inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); 549 550 // Switching to that upstream causes the quota to be applied if the parameters were applied 551 // correctly. 552 lp.setInterfaceName(mobileIface); 553 offload.setUpstreamLinkProperties(lp); 554 waitForIdle(); 555 inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit); 556 557 // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set 558 // to Long.MAX_VALUE. 559 mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); 560 waitForIdle(); 561 inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); 562 563 // If setting upstream parameters fails, then the data limit is not set. 564 when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); 565 lp.setInterfaceName(ethernetIface); 566 offload.setUpstreamLinkProperties(lp); 567 mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); 568 waitForIdle(); 569 inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); 570 571 // If setting the data limit fails while changing upstreams, offload is stopped. 572 when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); 573 when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); 574 lp.setInterfaceName(mobileIface); 575 offload.setUpstreamLinkProperties(lp); 576 mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); 577 waitForIdle(); 578 inOrder.verify(mHardware).getForwardedStats(ethernetIface); 579 inOrder.verify(mHardware).stopOffloadControl(); 580 } 581 582 @Test testDataLimitCallback()583 public void testDataLimitCallback() throws Exception { 584 setupFunctioningHardwareInterface(); 585 enableOffload(); 586 587 final OffloadController offload = makeOffloadController(); 588 offload.start(); 589 590 OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); 591 callback.onStoppedLimitReached(); 592 mTetherStatsProviderCb.expectNotifyStatsUpdated(); 593 } 594 595 @Test testAddRemoveDownstreams()596 public void testAddRemoveDownstreams() throws Exception { 597 setupFunctioningHardwareInterface(); 598 enableOffload(); 599 600 final OffloadController offload = makeOffloadController(); 601 offload.start(); 602 603 final InOrder inOrder = inOrder(mHardware); 604 inOrder.verify(mHardware, times(1)).initOffloadConfig(); 605 inOrder.verify(mHardware, times(1)).initOffloadControl( 606 any(OffloadHardwareInterface.ControlCallback.class)); 607 inOrder.verifyNoMoreInteractions(); 608 609 // Tethering makes several calls to setLocalPrefixes() before add/remove 610 // downstream calls are made. This is not tested here; only the behavior 611 // of notifyDownstreamLinkProperties() and removeDownstreamInterface() 612 // are tested. 613 614 // [1] USB tethering is started. 615 final LinkProperties usbLinkProperties = new LinkProperties(); 616 usbLinkProperties.setInterfaceName(RNDIS0); 617 usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); 618 usbLinkProperties.addRoute( 619 new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); 620 offload.notifyDownstreamLinkProperties(usbLinkProperties); 621 inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX); 622 inOrder.verifyNoMoreInteractions(); 623 624 // [2] Routes for IPv6 link-local prefixes should never be added. 625 usbLinkProperties.addRoute( 626 new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); 627 offload.notifyDownstreamLinkProperties(usbLinkProperties); 628 inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); 629 inOrder.verifyNoMoreInteractions(); 630 631 // [3] Add an IPv6 prefix for good measure. Only new offload-able 632 // prefixes should be passed to the HAL. 633 usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); 634 usbLinkProperties.addRoute( 635 new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST)); 636 offload.notifyDownstreamLinkProperties(usbLinkProperties); 637 inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); 638 inOrder.verifyNoMoreInteractions(); 639 640 // [4] Adding addresses doesn't affect notifyDownstreamLinkProperties(). 641 // The address is passed in by a separate setLocalPrefixes() invocation. 642 usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64")); 643 offload.notifyDownstreamLinkProperties(usbLinkProperties); 644 inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); 645 646 // [5] Differences in local routes are converted into addDownstream() 647 // and removeDownstream() invocations accordingly. 648 usbLinkProperties.removeRoute( 649 new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST)); 650 usbLinkProperties.addRoute( 651 new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST)); 652 offload.notifyDownstreamLinkProperties(usbLinkProperties); 653 inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); 654 inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); 655 inOrder.verifyNoMoreInteractions(); 656 657 // [6] Removing a downstream interface which was never added causes no 658 // interactions with the HAL. 659 offload.removeDownstreamInterface(WLAN0); 660 inOrder.verifyNoMoreInteractions(); 661 662 // [7] Removing an active downstream removes all remaining prefixes. 663 offload.removeDownstreamInterface(RNDIS0); 664 inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX); 665 inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); 666 inOrder.verifyNoMoreInteractions(); 667 } 668 669 @Test testControlCallbackOnStoppedUnsupportedFetchesAllStats()670 public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception { 671 setupFunctioningHardwareInterface(); 672 enableOffload(); 673 674 final OffloadController offload = makeOffloadController(); 675 offload.start(); 676 677 // Pretend to set a few different upstreams (only the interface name 678 // matters for this test; we're ignoring IP and route information). 679 final LinkProperties upstreamLp = new LinkProperties(); 680 for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { 681 upstreamLp.setInterfaceName(ifname); 682 offload.setUpstreamLinkProperties(upstreamLp); 683 } 684 685 // Clear invocation history, especially the getForwardedStats() calls 686 // that happen with setUpstreamParameters(). 687 clearInvocations(mHardware); 688 689 OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); 690 callback.onStoppedUnsupported(); 691 692 // Verify forwarded stats behaviour. 693 verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); 694 verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); 695 // TODO: verify the exact stats reported. 696 mTetherStatsProviderCb.expectNotifyStatsUpdated(); 697 mTetherStatsProviderCb.assertNoCallback(); 698 verifyNoMoreInteractions(mHardware); 699 } 700 701 @Test testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters()702 public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters() 703 throws Exception { 704 setupFunctioningHardwareInterface(); 705 enableOffload(); 706 707 final OffloadController offload = makeOffloadController(); 708 offload.start(); 709 710 // Pretend to set a few different upstreams (only the interface name 711 // matters for this test; we're ignoring IP and route information). 712 final LinkProperties upstreamLp = new LinkProperties(); 713 for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { 714 upstreamLp.setInterfaceName(ifname); 715 offload.setUpstreamLinkProperties(upstreamLp); 716 } 717 718 // Pretend that some local prefixes and downstreams have been added 719 // (and removed, for good measure). 720 final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); 721 for (String s : new String[]{ 722 "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { 723 minimumLocalPrefixes.add(new IpPrefix(s)); 724 } 725 offload.setLocalPrefixes(minimumLocalPrefixes); 726 727 final LinkProperties usbLinkProperties = new LinkProperties(); 728 usbLinkProperties.setInterfaceName(RNDIS0); 729 usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24")); 730 usbLinkProperties.addRoute( 731 new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); 732 offload.notifyDownstreamLinkProperties(usbLinkProperties); 733 734 final LinkProperties wifiLinkProperties = new LinkProperties(); 735 wifiLinkProperties.setInterfaceName(WLAN0); 736 wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24")); 737 wifiLinkProperties.addRoute( 738 new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST)); 739 wifiLinkProperties.addRoute( 740 new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); 741 // Use a benchmark prefix (RFC 5180 + erratum), since the documentation 742 // prefix is included in the excluded prefix list. 743 wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64")); 744 wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64")); 745 wifiLinkProperties.addRoute( 746 new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST)); 747 offload.notifyDownstreamLinkProperties(wifiLinkProperties); 748 749 offload.removeDownstreamInterface(RNDIS0); 750 751 // Clear invocation history, especially the getForwardedStats() calls 752 // that happen with setUpstreamParameters(). 753 clearInvocations(mHardware); 754 755 OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); 756 callback.onSupportAvailable(); 757 758 // Verify forwarded stats behaviour. 759 verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); 760 verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); 761 mTetherStatsProviderCb.expectNotifyStatsUpdated(); 762 mTetherStatsProviderCb.assertNoCallback(); 763 764 // TODO: verify local prefixes and downstreams are also pushed to the HAL. 765 verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); 766 ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); 767 assertEquals(4, localPrefixes.size()); 768 assertContainsAll(localPrefixes, 769 // TODO: The logic to find and exclude downstream IP prefixes 770 // is currently in Tethering's OffloadWrapper but must be moved 771 // into OffloadController proper. After this, also check for: 772 // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128" 773 "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); 774 verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24"); 775 verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64"); 776 verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any()); 777 verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong()); 778 verifyNoMoreInteractions(mHardware); 779 } 780 781 @Test testOnSetAlert()782 public void testOnSetAlert() throws Exception { 783 setupFunctioningHardwareInterface(); 784 enableOffload(); 785 setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 786 final OffloadController offload = makeOffloadController(); 787 offload.start(); 788 789 // Initialize with fake eth upstream. 790 final String ethernetIface = "eth1"; 791 InOrder inOrder = inOrder(mHardware); 792 final LinkProperties lp = new LinkProperties(); 793 lp.setInterfaceName(ethernetIface); 794 offload.setUpstreamLinkProperties(lp); 795 // Previous upstream was null, so no stats are fetched. 796 inOrder.verify(mHardware, never()).getForwardedStats(any()); 797 798 // Verify that set quota to 0 will immediately triggers an callback. 799 mTetherStatsProvider.onSetAlert(0); 800 waitForIdle(); 801 mTetherStatsProviderCb.expectNotifyAlertReached(); 802 803 // Verify that notifyAlertReached never fired if quota is not yet reached. 804 when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( 805 new ForwardedStats(0, 0)); 806 mTetherStatsProvider.onSetAlert(100); 807 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 808 waitForIdle(); 809 mTetherStatsProviderCb.assertNoCallback(); 810 811 // Verify that notifyAlertReached fired when quota is reached. 812 when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( 813 new ForwardedStats(50, 50)); 814 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 815 waitForIdle(); 816 mTetherStatsProviderCb.expectNotifyAlertReached(); 817 818 // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch 819 // any stats since the polling is stopped. 820 reset(mHardware); 821 mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); 822 mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 823 waitForIdle(); 824 mTetherStatsProviderCb.assertNoCallback(); 825 verify(mHardware, never()).getForwardedStats(any()); 826 } 827 } 828