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