Merge changes from topic "sp18-TestableNetworkStatsProviderCbBinder"

* changes:
  [SP18.5] Create offload controller poll interval to resource
  [SP18.4] Add unit test for polling network stats in OffloadController
  [SP18.3] Adapt TestableNetworkStatsProviderCbBinder
This commit is contained in:
Junyu Lai
2020-05-05 03:09:58 +00:00
committed by Gerrit Code Review
6 changed files with 128 additions and 33 deletions

View File

@@ -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.

View File

@@ -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 -->

View File

@@ -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 {

View File

@@ -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;
@@ -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");
@@ -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());
}
@@ -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);
@@ -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)));
@@ -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);

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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.
}
};
@@ -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 =
@@ -164,6 +175,7 @@ public class OffloadControllerTest {
tetherStatsProviderCaptor.capture());
mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
assertNotNull(mTetherStatsProvider);
mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
return offload;
}
@@ -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));
@@ -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))
@@ -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
@@ -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
@@ -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);
}
@@ -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());
@@ -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());
}
}

View File

@@ -115,6 +115,8 @@ public class TetheringConfigurationTest {
when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
new String[0]);
when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
@@ -313,6 +315,23 @@ public class TetheringConfigurationTest {
assertFalse(cfg.enableLegacyDhcpServer);
}
@Test
public void testOffloadIntervalByResource() {
final TetheringConfiguration intervalByDefault =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
intervalByDefault.getOffloadPollInterval());
final int[] testOverrides = {0, 3000, -1};
for (final int override : testOverrides) {
when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
override);
final TetheringConfiguration overrideByRes =
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertEquals(override, overrideByRes.getOffloadPollInterval());
}
}
@Test
public void testGetResourcesBySubId() {
setUpResourceForSubId();