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

Commit b6111670 authored by Junyu Lai's avatar Junyu Lai Committed by android-build-merger
Browse files

Merge changes from topic "sp06-offloadcontroller"

am: 01d30a4c

Change-Id: I4549cb9b54ade6a9b45217be133aff535ec60b62
parents 6875770a 01d30a4c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -240,6 +240,7 @@ applications that come with the platform
        <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
        <permission name="android.permission.TETHER_PRIVILEGED"/>
        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
    </privapp-permissions>

    <privapp-permissions package="com.android.server.telecom">
+1 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
    <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <application
+1 −11
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import static android.net.util.TetheringMessageBase.BASE_IPSERVER;

import android.net.INetd;
import android.net.INetworkStackStatusCallback;
import android.net.INetworkStatsService;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -176,7 +175,6 @@ public class IpServer extends StateMachine {

    private final SharedLog mLog;
    private final INetd mNetd;
    private final INetworkStatsService mStatsService;
    private final Callback mCallback;
    private final InterfaceController mInterfaceCtrl;

@@ -208,12 +206,10 @@ public class IpServer extends StateMachine {

    public IpServer(
            String ifaceName, Looper looper, int interfaceType, SharedLog log,
            INetd netd, INetworkStatsService statsService, Callback callback,
            boolean usingLegacyDhcp, Dependencies deps) {
            INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
        super(ifaceName, looper);
        mLog = log.forSubComponent(ifaceName);
        mNetd = netd;
        mStatsService = statsService;
        mCallback = callback;
        mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
        mIfaceName = ifaceName;
@@ -881,12 +877,6 @@ public class IpServer extends StateMachine {
            // Sometimes interfaces are gone before we get
            // to remove their rules, which generates errors.
            // Just do the best we can.
            try {
                // About to tear down NAT; gather remaining statistics.
                mStatsService.forceUpdate();
            } catch (Exception e) {
                mLog.e("Exception in forceUpdate: " + e.toString());
            }
            try {
                mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
            } catch (RemoteException | ServiceSpecificException e) {
+93 −61
Original line number Diff line number Diff line
@@ -16,35 +16,40 @@

package com.android.server.connectivity.tethering;

import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.METERED_NO;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.NetworkStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
import android.content.ContentResolver;
import android.net.ITetheringStatsProvider;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
import android.net.netstats.provider.AbstractNetworkStatsProvider;
import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;

@@ -73,13 +78,19 @@ public class OffloadController {
    private static final String ANYIP = "0.0.0.0";
    private static final ForwardedStats EMPTY_STATS = new ForwardedStats();

    @VisibleForTesting
    enum StatsType {
        STATS_PER_IFACE,
        STATS_PER_UID,
    }

    private enum UpdateType { IF_NEEDED, FORCE };

    private final Handler mHandler;
    private final OffloadHardwareInterface mHwInterface;
    private final ContentResolver mContentResolver;
    private final INetworkManagementService mNms;
    private final ITetheringStatsProvider mStatsProvider;
    private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
    private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
    private final SharedLog mLog;
    private final HashMap<String, LinkProperties> mDownstreams;
    private boolean mConfigInitialized;
@@ -109,22 +120,23 @@ public class OffloadController {
    private int mNatUpdateNetlinkErrors;

    public OffloadController(Handler h, OffloadHardwareInterface hwi,
            ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
            ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) {
        mHandler = h;
        mHwInterface = hwi;
        mContentResolver = contentResolver;
        mNms = nms;
        mStatsProvider = new OffloadTetheringStatsProvider();
        mLog = log.forSubComponent(TAG);
        mDownstreams = new HashMap<>();
        mExemptPrefixes = new HashSet<>();
        mLastLocalPrefixStrs = new HashSet<>();

        NetworkStatsProviderCallback providerCallback = null;
        try {
            mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName());
        } catch (RemoteException e) {
            mLog.e("Cannot register offload stats provider: " + e);
            providerCallback = nsm.registerNetworkStatsProvider(
                    getClass().getSimpleName(), mStatsProvider);
        } catch (RuntimeException e) {
            Log.wtf(TAG, "Cannot register offload stats provider: " + e);
        }
        mStatsProviderCb = providerCallback;
    }

    /** Start hardware offload. */
@@ -173,7 +185,7 @@ public class OffloadController {
                        // and we need to synchronize stats and limits between
                        // software and hardware forwarding.
                        updateStatsForAllUpstreams();
                        forceTetherStatsPoll();
                        mStatsProvider.pushTetherStats();
                    }

                    @Override
@@ -186,7 +198,7 @@ public class OffloadController {
                        // limits set take into account any software tethering
                        // traffic that has been happening in the meantime.
                        updateStatsForAllUpstreams();
                        forceTetherStatsPoll();
                        mStatsProvider.pushTetherStats();
                        // [2] (Re)Push all state.
                        computeAndPushLocalPrefixes(UpdateType.FORCE);
                        pushAllDownstreamState();
@@ -204,14 +216,11 @@ public class OffloadController {
                        // the HAL queued the callback.
                        // TODO: rev the HAL so that it provides an interface name.

                        // Fetch current stats, so that when our notification reaches
                        // NetworkStatsService and triggers a poll, we will respond with
                        // current data (which will be above the limit that was reached).
                        // Note that if we just changed upstream, this is unnecessary but harmless.
                        // The stats for the previous upstream were already updated on this thread
                        // just after the upstream was changed, so they are also up-to-date.
                        updateStatsForCurrentUpstream();
                        forceTetherStatsPoll();
                        mStatsProvider.pushTetherStats();
                        // Push stats to service does not cause the service react to it immediately.
                        // Inform the service about limit reached.
                        if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
                    }

                    @Override
@@ -253,42 +262,37 @@ public class OffloadController {
        return mConfigInitialized && mControlInitialized;
    }

    private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
        @Override
        public NetworkStats getTetherStats(int how) {
            // getTetherStats() is the only function in OffloadController that can be called from
            // a different thread. Do not attempt to update stats by querying the offload HAL
            // synchronously from a different thread than our Handler thread. http://b/64771555.
            Runnable updateStats = () -> {
                updateStatsForCurrentUpstream();
            };
            if (Looper.myLooper() == mHandler.getLooper()) {
                updateStats.run();
            } else {
                mHandler.post(updateStats);
            }
    @VisibleForTesting
    class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
        // These stats must only ever be touched on the handler thread.
        @NonNull
        private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
        @NonNull
        private NetworkStats mUidStats = new NetworkStats(0L, 0);

            NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
            NetworkStats.Entry entry = new NetworkStats.Entry();
            entry.set = SET_DEFAULT;
            entry.tag = TAG_NONE;
            entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
        @VisibleForTesting
        @NonNull
        NetworkStats getTetherStats(@NonNull StatsType how) {
            NetworkStats stats = new NetworkStats(0L, 0);
            final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;

            for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
                ForwardedStats value = kv.getValue();
                entry.iface = kv.getKey();
                entry.rxBytes = value.rxBytes;
                entry.txBytes = value.txBytes;
                stats.addEntry(entry);
            for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
                final ForwardedStats value = kv.getValue();
                final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
                        ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
                stats = stats.addValues(entry);
            }

            return stats;
        }

        @Override
        public void setInterfaceQuota(String iface, long quotaBytes) {
        public void setLimit(String iface, long quotaBytes) {
            mLog.i("setLimit: " + iface + "," + quotaBytes);
            // Listen for all iface is necessary since upstream might be changed after limit
            // is set.
            mHandler.post(() -> {
                if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
                if (quotaBytes == QUOTA_UNLIMITED) {
                    mInterfaceQuotas.remove(iface);
                } else {
                    mInterfaceQuotas.put(iface, quotaBytes);
@@ -296,6 +300,42 @@ public class OffloadController {
                maybeUpdateDataLimit(iface);
            });
        }

        /**
         * Push stats to service, but does not cause a force polling. Note that this can only be
         * called on the handler thread.
         */
        public void pushTetherStats() {
            // TODO: remove the accumulated stats and report the diff from HAL directly.
            if (null == mStatsProviderCb) return;
            final NetworkStats ifaceDiff =
                    getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
            final NetworkStats uidDiff =
                    getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
            try {
                mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
                mIfaceStats = mIfaceStats.add(ifaceDiff);
                mUidStats = mUidStats.add(uidDiff);
            } catch (RuntimeException e) {
                mLog.e("Cannot report network stats: ", e);
            }
        }

        @Override
        public void requestStatsUpdate(int token) {
            mLog.i("requestStatsUpdate: " + token);
            // Do not attempt to update stats by querying the offload HAL
            // synchronously from a different thread than the Handler thread. http://b/64771555.
            mHandler.post(() -> {
                updateStatsForCurrentUpstream();
                pushTetherStats();
            });
        }

        @Override
        public void setAlert(long quotaBytes) {
            // TODO: Ask offload HAL to notify alert without stopping traffic.
        }
    }

    private String currentUpstreamInterface() {
@@ -353,14 +393,6 @@ public class OffloadController {
        }
    }

    private void forceTetherStatsPoll() {
        try {
            mNms.tetherLimitReached(mStatsProvider);
        } catch (RemoteException e) {
            mLog.e("Cannot report data limit reached: " + e);
        }
    }

    /** Set current tethering upstream LinkProperties. */
    public void setUpstreamLinkProperties(LinkProperties lp) {
        if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
+8 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.Handler;
import android.os.RemoteException;
import android.system.OsConstants;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;


@@ -91,6 +93,12 @@ public class OffloadHardwareInterface {
            txBytes = 0;
        }

        @VisibleForTesting
        public ForwardedStats(long rxBytes, long txBytes) {
            this.rxBytes = rxBytes;
            this.txBytes = txBytes;
        }

        /** Add Tx/Rx bytes. */
        public void add(ForwardedStats other) {
            rxBytes += other.rxBytes;
Loading