[SP35] Pass data warning to tethering offload

This is supported by:
  1. Utilize the new API from both NetworkStatsProvider
     and IOffloadControl to send data warning quota to hardware.
     And pass the warning reached notification back to NPMS.
  2. Disable software solution introduced in R release for
     V1.1+ hardware, since now we can fully offload data warning
     and limit notification to hardware.

Test: atest TetheringTests
Fix: 149467454
Ignore-AOSP-First: avoid long automerger delay
Change-Id: Ie49461694d77ab7f25a549433b01b5b0167bd489
This commit is contained in:
junyulai
2021-03-10 20:22:30 +08:00
parent 6d8da9f72d
commit a36f33ef90
2 changed files with 168 additions and 25 deletions

View File

@@ -26,6 +26,8 @@ import static android.net.NetworkStats.UID_TETHERING;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -280,6 +282,18 @@ public class OffloadController {
} }
} }
@Override
public void onWarningReached() {
if (!started()) return;
mLog.log("onWarningReached");
updateStatsForCurrentUpstream();
if (mStatsProvider != null) {
mStatsProvider.pushTetherStats();
mStatsProvider.notifyWarningReached();
}
}
@Override @Override
public void onNatTimeoutUpdate(int proto, public void onNatTimeoutUpdate(int proto,
String srcAddr, int srcPort, String srcAddr, int srcPort,
@@ -417,7 +431,11 @@ public class OffloadController {
@Override @Override
public void onSetAlert(long quotaBytes) { public void onSetAlert(long quotaBytes) {
// TODO: Ask offload HAL to notify alert without stopping traffic. // Ignore set alert calls from HAL V1.1 since the hardware supports set warning now.
// Thus, the software polling mechanism is not needed.
if (!useStatsPolling()) {
return;
}
// Post it to handler thread since it access remaining quota bytes. // Post it to handler thread since it access remaining quota bytes.
mHandler.post(() -> { mHandler.post(() -> {
updateAlertQuota(quotaBytes); updateAlertQuota(quotaBytes);
@@ -502,12 +520,17 @@ public class OffloadController {
private boolean isPollingStatsNeeded() { private boolean isPollingStatsNeeded() {
return started() && mRemainingAlertQuota > 0 return started() && mRemainingAlertQuota > 0
&& useStatsPolling()
&& !TextUtils.isEmpty(currentUpstreamInterface()) && !TextUtils.isEmpty(currentUpstreamInterface())
&& mDeps.getTetherConfig() != null && mDeps.getTetherConfig() != null
&& mDeps.getTetherConfig().getOffloadPollInterval() && mDeps.getTetherConfig().getOffloadPollInterval()
>= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
} }
private boolean useStatsPolling() {
return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0;
}
private boolean maybeUpdateDataWarningAndLimit(String iface) { private boolean maybeUpdateDataWarningAndLimit(String iface) {
// setDataLimit or setDataWarningAndLimit may only be called while offload is occurring // setDataLimit or setDataWarningAndLimit may only be called while offload is occurring
// on this upstream. // on this upstream.
@@ -516,7 +539,13 @@ public class OffloadController {
} }
final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE); final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE);
return mHwInterface.setDataLimit(iface, quota.limitBytes); final boolean ret;
if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) {
ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes);
} else {
ret = mHwInterface.setDataLimit(iface, quota.limitBytes);
}
return ret;
} }
private void updateStatsForCurrentUpstream() { private void updateStatsForCurrentUpstream() {

View File

@@ -149,6 +149,7 @@ public class OffloadControllerTest {
when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true); when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
} }
private void enableOffload() { private void enableOffload() {
@@ -503,80 +504,166 @@ public class OffloadControllerTest {
} }
/** /**
* Test OffloadController that uses V1.0 HAL setDataLimit with NetworkStatsProvider#onSetLimit * Test OffloadController with different combinations of HAL and framework versions can set
* which is called by R framework. * data warning and/or limit correctly.
*/ */
@Test @Test
public void testSetDataLimit() throws Exception { public void testSetDataWarningAndLimit() throws Exception {
// Verify the OffloadController is called by R framework, where the framework doesn't send
// warning.
checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_0);
checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_1);
// Verify the OffloadController is called by S+ framework, where the framework sends
// warning along with limit.
checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_0);
checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_1);
}
private void checkSetDataWarningAndLimit(boolean isProviderSetWarning, int controlVersion)
throws Exception {
enableOffload(); enableOffload();
final OffloadController offload = final OffloadController offload =
startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); startOffloadController(controlVersion, true /*expectStart*/);
final String ethernetIface = "eth1"; final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0"; final String mobileIface = "rmnet_data0";
final long ethernetLimit = 12345; final long ethernetLimit = 12345;
final long mobileWarning = 123456;
final long mobileLimit = 12345678; final long mobileLimit = 12345678;
final LinkProperties lp = new LinkProperties(); final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(ethernetIface); lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
final InOrder inOrder = inOrder(mHardware); final InOrder inOrder = inOrder(mHardware);
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); when(mHardware.setUpstreamParameters(
any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
offload.setUpstreamLinkProperties(lp);
// Applying an interface sends the initial quota to the hardware.
if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
Long.MAX_VALUE);
} else {
inOrder.verify(mHardware).setDataLimit(ethernetIface, Long.MAX_VALUE);
}
inOrder.verifyNoMoreInteractions();
// Verify that set to unlimited again won't cause duplicated calls to the hardware.
if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
} else {
mTetherStatsProvider.onSetLimit(ethernetIface, NetworkStatsProvider.QUOTA_UNLIMITED);
}
waitForIdle();
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to the current upstream immediately sends it to the hardware. // Applying an interface quota to the current upstream immediately sends it to the hardware.
mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
NetworkStatsProvider.QUOTA_UNLIMITED, ethernetLimit);
} else {
mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
}
waitForIdle(); waitForIdle();
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
ethernetLimit);
} else {
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
}
inOrder.verifyNoMoreInteractions(); inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action. // Applying an interface quota to another upstream does not take any immediate action.
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
} else {
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
}
waitForIdle(); waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
anyLong());
} else {
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
}
// Switching to that upstream causes the quota to be applied if the parameters were applied // Switching to that upstream causes the quota to be applied if the parameters were applied
// correctly. // correctly.
lp.setInterfaceName(mobileIface); lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp); offload.setUpstreamLinkProperties(lp);
waitForIdle(); waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit); if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface,
isProviderSetWarning ? mobileWarning : Long.MAX_VALUE,
mobileLimit);
} else {
inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
}
// Setting a limit of NetworkStatsProvider.QUOTA_UNLIMITED causes the limit to be set // Setting a limit of NetworkStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE. // to Long.MAX_VALUE.
mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED); if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(mobileIface,
NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
} else {
mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED);
}
waitForIdle(); waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, Long.MAX_VALUE,
Long.MAX_VALUE);
} else {
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
}
// If setting upstream parameters fails, then the data limit is not set. // If setting upstream parameters fails, then the data warning and limit is not set.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface); lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp); offload.setUpstreamLinkProperties(lp);
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
} else {
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
}
waitForIdle(); waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
anyLong());
// If setting the data limit fails while changing upstreams, offload is stopped. // If setting the data warning and/or limit fails while changing upstreams, offload is
// stopped.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface); lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp); offload.setUpstreamLinkProperties(lp);
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); if (isProviderSetWarning) {
mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
} else {
mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
}
waitForIdle(); waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface); inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl(); inOrder.verify(mHardware).stopOffloadControl();
} }
@Test @Test
public void testDataLimitCallback() throws Exception { public void testDataWarningAndLimitCallback() throws Exception {
enableOffload(); enableOffload();
final OffloadController offload = startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached(); callback.onStoppedLimitReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated(); mTetherStatsProviderCb.expectNotifyStatsUpdated();
mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/);
callback = mControlCallbackCaptor.getValue();
callback.onWarningReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated();
mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
} }
@Test @Test
@@ -764,9 +851,7 @@ public class OffloadControllerTest {
// Initialize with fake eth upstream. // Initialize with fake eth upstream.
final String ethernetIface = "eth1"; final String ethernetIface = "eth1";
InOrder inOrder = inOrder(mHardware); InOrder inOrder = inOrder(mHardware);
final LinkProperties lp = new LinkProperties(); offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
// Previous upstream was null, so no stats are fetched. // Previous upstream was null, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any()); inOrder.verify(mHardware, never()).getForwardedStats(any());
@@ -799,4 +884,33 @@ public class OffloadControllerTest {
mTetherStatsProviderCb.assertNoCallback(); mTetherStatsProviderCb.assertNoCallback();
verify(mHardware, never()).getForwardedStats(any()); verify(mHardware, never()).getForwardedStats(any());
} }
private static LinkProperties makeEthernetLinkProperties() {
final String ethernetIface = "eth1";
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(ethernetIface);
return lp;
}
private void checkSoftwarePollingUsed(int controlVersion) throws Exception {
enableOffload();
setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
OffloadController offload =
startOffloadController(controlVersion, true /*expectStart*/);
offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
mTetherStatsProvider.onSetAlert(0);
waitForIdle();
if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
mTetherStatsProviderCb.assertNoCallback();
} else {
mTetherStatsProviderCb.expectNotifyAlertReached();
}
verify(mHardware, never()).getForwardedStats(any());
}
@Test
public void testSoftwarePollingUsed() throws Exception {
checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_0);
checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_1);
}
} }