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