Pass data usage limits to tethering offload code.
Bug: 29337859
Bug: 32163131
Test: builds
Test: OffloadControllerTest passes
Change-Id: I82d3bee030bafa8fe85855885b5fc3893e699181
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 097202b..3d638be 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1538,6 +1538,17 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+
+ synchronized (mTetheringStatsProviders) {
+ for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+ try {
+ provider.setInterfaceQuota(iface, quotaBytes);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem setting tethering data limit on provider " +
+ mTetheringStatsProviders.get(provider) + ": " + e);
+ }
+ }
+ }
}
}
@@ -1564,6 +1575,17 @@
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+
+ synchronized (mTetheringStatsProviders) {
+ for (ITetheringStatsProvider provider : mTetheringStatsProviders.keySet()) {
+ try {
+ provider.setInterfaceQuota(iface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem removing tethering data limit on provider " +
+ mTetheringStatsProviders.get(provider) + ": " + e);
+ }
+ }
+ }
}
}
@@ -1823,6 +1845,11 @@
}
return stats;
}
+
+ @Override
+ public void setInterfaceQuota(String iface, long quotaBytes) {
+ // Do nothing. netd is already informed of quota changes in setInterfaceQuota.
+ }
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index ccbac31..55e290a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -73,9 +73,17 @@
private Set<String> mLastLocalPrefixStrs;
// Maps upstream interface names to offloaded traffic statistics.
+ // Always contains the latest value received from the hardware for each interface, regardless of
+ // whether offload is currently running on that interface.
private HashMap<String, OffloadHardwareInterface.ForwardedStats>
mForwardedStats = new HashMap<>();
+ // Maps upstream interface names to interface quotas.
+ // Always contains the latest value received from the framework for each interface, regardless
+ // of whether offload is currently running (or is even supported) on that interface. Only
+ // includes upstream interfaces that have a quota set.
+ private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
mHandler = h;
@@ -195,6 +203,17 @@
return stats;
}
+
+ public void setInterfaceQuota(String iface, long quotaBytes) {
+ mHandler.post(() -> {
+ if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+ mInterfaceQuotas.remove(iface);
+ } else {
+ mInterfaceQuotas.put(iface, quotaBytes);
+ }
+ maybeUpdateDataLimit(iface);
+ });
+ }
}
private void maybeUpdateStats(String iface) {
@@ -208,6 +227,22 @@
mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
}
+ private boolean maybeUpdateDataLimit(String iface) {
+ // setDataLimit may only be called while offload is occuring on this upstream.
+ if (!started() ||
+ mUpstreamLinkProperties == null ||
+ !TextUtils.equals(iface, mUpstreamLinkProperties.getInterfaceName())) {
+ return true;
+ }
+
+ Long limit = mInterfaceQuotas.get(iface);
+ if (limit == null) {
+ limit = Long.MAX_VALUE;
+ }
+
+ return mHwInterface.setDataLimit(iface, limit);
+ }
+
private void updateStatsForCurrentUpstream() {
if (mUpstreamLinkProperties != null) {
maybeUpdateStats(mUpstreamLinkProperties.getInterfaceName());
@@ -297,8 +332,21 @@
}
}
- return mHwInterface.setUpstreamParameters(
+ boolean success = mHwInterface.setUpstreamParameters(
iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
+
+ if (!success) {
+ return success;
+ }
+
+ // Data limits can only be set once offload is running on the upstream.
+ success = maybeUpdateDataLimit(iface);
+ if (!success) {
+ mLog.log("Setting data limit for " + iface + " failed, disabling offload.");
+ stop();
+ }
+
+ return success;
}
private boolean computeAndPushLocalPrefixes() {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 4df566f..86ff0a6 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -188,6 +188,27 @@
return results.success;
}
+ public boolean setDataLimit(String iface, long limit) {
+
+ final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ mOffloadControl.setDataLimit(
+ iface, limit,
+ (boolean success, String errMsg) -> {
+ results.success = success;
+ results.errMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.success;
+ }
+
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
iface = (iface != null) ? iface : NO_INTERFACE_NAME;