Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1ecc1159 authored by Junyu Lai's avatar Junyu Lai Committed by Automerger Merge Worker
Browse files

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

Change-Id: I16e45c86f4f7c5654dd0061c6fdb3fbf66986124
parents 31f2f386 0cc8431a
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
@@ -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
+0 −1
Original line number Diff line number Diff line
@@ -308,7 +308,6 @@ public class OffloadHardwareInterface {
            return stats;
        }

        mLog.log(logmsg + YIELDS + stats);
        return stats;
    }