Merge "[SP18] Poll network stats in OffloadController to support data warning" am: d25d88199f

Change-Id: I16e45c86f4f7c5654dd0061c6fdb3fbf66986124
This commit is contained in:
Junyu Lai
2020-04-29 11:19:40 +00:00
committed by Automerger Merge Worker
2 changed files with 74 additions and 1 deletions

View File

@@ -23,6 +23,7 @@ import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStats.UID_TETHERING;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import android.annotation.NonNull;
@@ -76,6 +77,7 @@ 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
enum StatsType {
@@ -115,6 +117,16 @@ public class OffloadController {
// includes upstream interfaces that have a quota set.
private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
// Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
// quota is interface independent and global for tether offload. Note that this is only
// accessed on the handler thread and in the constructor.
private long mRemainingAlertQuota = QUOTA_UNLIMITED;
// Runnable that used to schedule the next stats poll.
private final Runnable mScheduledPollingTask = () -> {
updateStatsForCurrentUpstream();
maybeSchedulePollingStats();
};
private int mNatUpdateCallbacksReceived;
private int mNatUpdateNetlinkErrors;
@@ -240,6 +252,7 @@ public class OffloadController {
mLog.log("tethering offload started");
mNatUpdateCallbacksReceived = 0;
mNatUpdateNetlinkErrors = 0;
maybeSchedulePollingStats();
}
return isStarted;
}
@@ -255,6 +268,9 @@ public class OffloadController {
mHwInterface.stopOffloadControl();
mControlInitialized = false;
mConfigInitialized = false;
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
mHandler.removeCallbacks(mScheduledPollingTask);
}
if (wasStarted) mLog.log("tethering offload stopped");
}
@@ -345,6 +361,11 @@ public class OffloadController {
@Override
public void onSetAlert(long quotaBytes) {
// TODO: Ask offload HAL to notify alert without stopping traffic.
// Post it to handler thread since it access remaining quota bytes.
mHandler.post(() -> {
updateAlertQuota(quotaBytes);
maybeSchedulePollingStats();
});
}
}
@@ -366,15 +387,66 @@ public class OffloadController {
// the stats for each interface, and does not observe partial writes where rxBytes is
// updated and txBytes is not.
ForwardedStats diff = mHwInterface.getForwardedStats(iface);
final long usedAlertQuota = diff.rxBytes + diff.txBytes;
ForwardedStats base = mForwardedStats.get(iface);
if (base != null) {
diff.add(base);
}
// Update remaining alert quota if it is still positive.
if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
// Trim to zero if overshoot.
final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
updateAlertQuota(newQuota);
}
mForwardedStats.put(iface, diff);
// diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
// mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
}
/**
* Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()}
* callback when it reaches zero. This can be invoked either from service setting the alert, or
* {@code maybeUpdateStats} when updating stats. Note that this can be only called on
* handler thread.
*
* @param newQuota non-negative value to indicate the new quota, or
* {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no
* quota.
*/
private void updateAlertQuota(long newQuota) {
if (newQuota < QUOTA_UNLIMITED) {
throw new IllegalArgumentException("invalid quota value " + newQuota);
}
if (mRemainingAlertQuota == newQuota) return;
mRemainingAlertQuota = newQuota;
if (mRemainingAlertQuota == 0) {
mLog.i("notifyAlertReached");
if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
}
}
/**
* Schedule polling if needed, this will be stopped if offload has been
* stopped or remaining quota reaches zero or upstream is empty.
* Note that this can be only called on handler thread.
*/
private void maybeSchedulePollingStats() {
if (!isPollingStatsNeeded()) return;
if (mHandler.hasCallbacks(mScheduledPollingTask)) {
mHandler.removeCallbacks(mScheduledPollingTask);
}
mHandler.postDelayed(mScheduledPollingTask, DEFAULT_PERFORM_POLL_INTERVAL_MS);
}
private boolean isPollingStatsNeeded() {
return started() && mRemainingAlertQuota > 0
&& !TextUtils.isEmpty(currentUpstreamInterface());
}
private boolean maybeUpdateDataLimit(String iface) {
// setDataLimit may only be called while offload is occurring on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
@@ -414,6 +486,8 @@ public class OffloadController {
final String iface = currentUpstreamInterface();
if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
maybeSchedulePollingStats();
// TODO: examine return code and decide what to do if programming
// upstream parameters fails (probably just wait for a subsequent
// onOffloadEvent() callback to tell us offload is available again and

View File

@@ -308,7 +308,6 @@ public class OffloadHardwareInterface {
return stats;
}
mLog.log(logmsg + YIELDS + stats);
return stats;
}