Loading packages/Tethering/res/values/config.xml +7 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,13 @@ <string-array translatable="false" name="config_tether_dhcp_range"> </string-array> <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the data warnings work. 5000(ms) by default. If the device doesn't want to poll tether offload stats, this should be -1. Note that this setting could be override by runtime resource overlays. --> <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer> <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI, WIFI} values allowable for tethering. Loading packages/Tethering/res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ <item type="array" name="config_tether_bluetooth_regexs"/> <item type="array" name="config_tether_dhcp_range"/> <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> <item type="integer" name="config_tether_offload_poll_interval"/> <item type="array" name="config_tether_upstream_types"/> <item type="bool" name="config_tether_upstream_automatic"/> <!-- Configuration values for tethering entitlement check --> Loading packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java +2 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,8 @@ public class OffloadController { private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting enum StatsType { Loading packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +20 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,12 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. */ public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000; public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; Loading @@ -96,6 +102,8 @@ public class TetheringConfiguration { public final int activeDataSubId; private final int mOffloadPollInterval; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); Loading Loading @@ -129,6 +137,10 @@ public class TetheringConfiguration { R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); configLog.log(toString()); } Loading Loading @@ -189,6 +201,9 @@ public class TetheringConfiguration { dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); pw.print("offloadPollInterval: "); pw.println(mOffloadPollInterval); dumpStringArray(pw, "provisioningApp", provisioningApp); pw.print("provisioningAppNoUi: "); pw.println(provisioningAppNoUi); Loading @@ -208,6 +223,7 @@ public class TetheringConfiguration { makeString(tetherableBluetoothRegexs))); sj.add(String.format("isDunRequired:%s", isDunRequired)); sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval)); sj.add(String.format("preferredUpstreamIfaceTypes:%s", toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); Loading Loading @@ -246,6 +262,10 @@ public class TetheringConfiguration { return (tm != null) ? tm.isTetheringApnRequired() : false; } public int getOffloadPollInterval() { return mOffloadPollInterval; } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); Loading packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +79 −32 Original line number Diff line number Diff line Loading @@ -26,18 +26,18 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import static com.android.networkstack.tethering.OffloadController.DEFAULT_PERFORM_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; Loading @@ -63,10 +63,10 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.RouteInfo; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; import android.os.test.TestLooper; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; Loading @@ -75,7 +75,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; import com.android.testutils.HandlerUtilsKt; import com.android.testutils.TestableNetworkStatsProviderCbBinder; import org.junit.After; import org.junit.Before; Loading Loading @@ -109,17 +109,19 @@ public class OffloadControllerTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @Mock private NetworkStatsManager mStatsManager; @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb; // Late init since methods must be called by the thread that created this object. private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; private final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; private final TestLooper mTestLooper = new TestLooper(); private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { return 0; return -1; // Disabled. } }; Loading Loading @@ -150,12 +152,21 @@ public class OffloadControllerTest { Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); } private void setOffloadPollInterval(int interval) { mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { return interval; } }; } private void waitForIdle() { HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT); mTestLooper.dispatchAll(); } private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> tetherStatsProviderCaptor = Loading @@ -164,6 +175,7 @@ public class OffloadControllerTest { tetherStatsProviderCaptor.capture()); mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); assertNotNull(mTetherStatsProvider); mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); return offload; } Loading Loading @@ -459,20 +471,12 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass( NetworkStats.class); final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass( NetworkStats.class); assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); assertNetworkStatsEquals(expectedUidStats, uidStats); // Force pushing stats update to verify the stats reported. mTetherStatsProvider.pushTetherStats(); verify(mTetherStatsProviderCb, times(1)) .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(100000, 100000)); Loading @@ -498,11 +502,10 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); // Verify that only diff of stats is reported. reset(mTetherStatsProviderCb); mTetherStatsProvider.pushTetherStats(); final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) Loading @@ -511,10 +514,8 @@ public class OffloadControllerTest { final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); verify(mTetherStatsProviderCb, times(1)) .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, expectedUidStatsDiff); } @Test Loading Loading @@ -591,7 +592,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); mTetherStatsProviderCb.expectNotifyStatsUpdated(); } @Test Loading Loading @@ -695,8 +696,8 @@ public class OffloadControllerTest { verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); // TODO: verify the exact stats reported. verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); mTetherStatsProviderCb.expectNotifyStatsUpdated(); mTetherStatsProviderCb.assertNoCallback(); verifyNoMoreInteractions(mHardware); } Loading Loading @@ -760,8 +761,8 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); mTetherStatsProviderCb.expectNotifyStatsUpdated(); mTetherStatsProviderCb.assertNoCallback(); // TODO: verify local prefixes and downstreams are also pushed to the HAL. verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); Loading @@ -780,4 +781,50 @@ public class OffloadControllerTest { verifyNoMoreInteractions(mHardware); } @Test public void testOnSetAlert() throws Exception { setupFunctioningHardwareInterface(); enableOffload(); setOffloadPollInterval(DEFAULT_PERFORM_POLL_INTERVAL_MS); final OffloadController offload = makeOffloadController(); offload.start(); // Initialize with fake eth upstream. final String ethernetIface = "eth1"; InOrder inOrder = inOrder(mHardware); final LinkProperties lp = new LinkProperties(); lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); // Previous upstream was null, so no stats are fetched. inOrder.verify(mHardware, never()).getForwardedStats(any()); // Verify that set quota to 0 will immediately triggers an callback. mTetherStatsProvider.onSetAlert(0); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); // Verify that notifyAlertReached never fired if quota is not yet reached. when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(0, 0)); mTetherStatsProvider.onSetAlert(100); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); // Verify that notifyAlertReached fired when quota is reached. when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(50, 50)); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch // any stats since the polling is stopped. reset(mHardware); mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); verify(mHardware, never()).getForwardedStats(any()); } } Loading
packages/Tethering/res/values/config.xml +7 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,13 @@ <string-array translatable="false" name="config_tether_dhcp_range"> </string-array> <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the data warnings work. 5000(ms) by default. If the device doesn't want to poll tether offload stats, this should be -1. Note that this setting could be override by runtime resource overlays. --> <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer> <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI, WIFI} values allowable for tethering. Loading
packages/Tethering/res/values/overlayable.xml +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ <item type="array" name="config_tether_bluetooth_regexs"/> <item type="array" name="config_tether_dhcp_range"/> <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> <item type="integer" name="config_tether_offload_poll_interval"/> <item type="array" name="config_tether_upstream_types"/> <item type="bool" name="config_tether_upstream_automatic"/> <!-- Configuration values for tethering entitlement check --> Loading
packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java +2 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,8 @@ public class OffloadController { private static final boolean DBG = false; private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); private static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting static final int DEFAULT_PERFORM_POLL_INTERVAL_MS = 5000; @VisibleForTesting enum StatsType { Loading
packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +20 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,12 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. */ public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000; public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; Loading @@ -96,6 +102,8 @@ public class TetheringConfiguration { public final int activeDataSubId; private final int mOffloadPollInterval; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); Loading Loading @@ -129,6 +137,10 @@ public class TetheringConfiguration { R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); mOffloadPollInterval = getResourceInteger(res, R.integer.config_tether_offload_poll_interval, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); configLog.log(toString()); } Loading Loading @@ -189,6 +201,9 @@ public class TetheringConfiguration { dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges); dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS); pw.print("offloadPollInterval: "); pw.println(mOffloadPollInterval); dumpStringArray(pw, "provisioningApp", provisioningApp); pw.print("provisioningAppNoUi: "); pw.println(provisioningAppNoUi); Loading @@ -208,6 +223,7 @@ public class TetheringConfiguration { makeString(tetherableBluetoothRegexs))); sj.add(String.format("isDunRequired:%s", isDunRequired)); sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically)); sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval)); sj.add(String.format("preferredUpstreamIfaceTypes:%s", toIntArray(preferredUpstreamIfaceTypes))); sj.add(String.format("provisioningApp:%s", makeString(provisioningApp))); Loading Loading @@ -246,6 +262,10 @@ public class TetheringConfiguration { return (tm != null) ? tm.isTetheringApnRequired() : false; } public int getOffloadPollInterval() { return mOffloadPollInterval; } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); Loading
packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +79 −32 Original line number Diff line number Diff line Loading @@ -26,18 +26,18 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import static com.android.networkstack.tethering.OffloadController.DEFAULT_PERFORM_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals; import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; Loading @@ -63,10 +63,10 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.NetworkStats.Entry; import android.net.RouteInfo; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; import android.net.util.SharedLog; import android.os.Handler; import android.os.Looper; import android.os.test.TestLooper; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; Loading @@ -75,7 +75,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; import com.android.testutils.HandlerUtilsKt; import com.android.testutils.TestableNetworkStatsProviderCbBinder; import org.junit.After; import org.junit.Before; Loading Loading @@ -109,17 +109,19 @@ public class OffloadControllerTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @Mock private NetworkStatsManager mStatsManager; @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb; // Late init since methods must be called by the thread that created this object. private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb; private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; private final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; private final TestLooper mTestLooper = new TestLooper(); private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { return 0; return -1; // Disabled. } }; Loading Loading @@ -150,12 +152,21 @@ public class OffloadControllerTest { Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); } private void setOffloadPollInterval(int interval) { mDeps = new OffloadController.Dependencies() { @Override int getPerformPollInterval() { return interval; } }; } private void waitForIdle() { HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT); mTestLooper.dispatchAll(); } private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()), mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps); final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> tetherStatsProviderCaptor = Loading @@ -164,6 +175,7 @@ public class OffloadControllerTest { tetherStatsProviderCaptor.capture()); mTetherStatsProvider = tetherStatsProviderCaptor.getValue(); assertNotNull(mTetherStatsProvider); mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder(); mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb); return offload; } Loading Loading @@ -459,20 +471,12 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass( NetworkStats.class); final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass( NetworkStats.class); assertNetworkStatsEquals(expectedIfaceStats, ifaceStats); assertNetworkStatsEquals(expectedUidStats, uidStats); // Force pushing stats update to verify the stats reported. mTetherStatsProvider.pushTetherStats(); verify(mTetherStatsProviderCb, times(1)) .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats); when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(100000, 100000)); Loading @@ -498,11 +502,10 @@ public class OffloadControllerTest { .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu); assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu); // Verify that only diff of stats is reported. reset(mTetherStatsProviderCb); mTetherStatsProvider.pushTetherStats(); final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) Loading @@ -511,10 +514,8 @@ public class OffloadControllerTest { final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); verify(mTetherStatsProviderCb, times(1)) .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff, expectedUidStatsDiff); } @Test Loading Loading @@ -591,7 +592,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); mTetherStatsProviderCb.expectNotifyStatsUpdated(); } @Test Loading Loading @@ -695,8 +696,8 @@ public class OffloadControllerTest { verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); // TODO: verify the exact stats reported. verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); mTetherStatsProviderCb.expectNotifyStatsUpdated(); mTetherStatsProviderCb.assertNoCallback(); verifyNoMoreInteractions(mHardware); } Loading Loading @@ -760,8 +761,8 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any()); verifyNoMoreInteractions(mTetherStatsProviderCb); mTetherStatsProviderCb.expectNotifyStatsUpdated(); mTetherStatsProviderCb.assertNoCallback(); // TODO: verify local prefixes and downstreams are also pushed to the HAL. verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); Loading @@ -780,4 +781,50 @@ public class OffloadControllerTest { verifyNoMoreInteractions(mHardware); } @Test public void testOnSetAlert() throws Exception { setupFunctioningHardwareInterface(); enableOffload(); setOffloadPollInterval(DEFAULT_PERFORM_POLL_INTERVAL_MS); final OffloadController offload = makeOffloadController(); offload.start(); // Initialize with fake eth upstream. final String ethernetIface = "eth1"; InOrder inOrder = inOrder(mHardware); final LinkProperties lp = new LinkProperties(); lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); // Previous upstream was null, so no stats are fetched. inOrder.verify(mHardware, never()).getForwardedStats(any()); // Verify that set quota to 0 will immediately triggers an callback. mTetherStatsProvider.onSetAlert(0); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); // Verify that notifyAlertReached never fired if quota is not yet reached. when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(0, 0)); mTetherStatsProvider.onSetAlert(100); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); // Verify that notifyAlertReached fired when quota is reached. when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( new ForwardedStats(50, 50)); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.expectNotifyAlertReached(); // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch // any stats since the polling is stopped. reset(mHardware); mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED); mTestLooper.moveTimeForward(DEFAULT_PERFORM_POLL_INTERVAL_MS); waitForIdle(); mTetherStatsProviderCb.assertNoCallback(); verify(mHardware, never()).getForwardedStats(any()); } }