[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:
@@ -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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user