Merge changes from topic "sp06-offloadcontroller" am: 01d30a4ce0 am: b61116705a am: c6a841b77b

Change-Id: I4e7924ebb2d37ecffb8facce0ec51643afb12264
diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml
index 5a71eb2..c71d0d7 100644
--- a/Tethering/AndroidManifest.xml
+++ b/Tethering/AndroidManifest.xml
@@ -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
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 6ac467e..4306cf0 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -26,7 +26,6 @@
 
 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 @@
 
     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 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;
@@ -882,12 +878,6 @@
             // 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) {
                 mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index ce7c2a6..cc36f4a 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -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 @@
     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 @@
     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 @@
                         // and we need to synchronize stats and limits between
                         // software and hardware forwarding.
                         updateStatsForAllUpstreams();
-                        forceTetherStatsPoll();
+                        mStatsProvider.pushTetherStats();
                     }
 
                     @Override
@@ -186,7 +198,7 @@
                         // 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 @@
                         // 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 @@
         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 @@
                 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 @@
         }
     }
 
-    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;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 4a8ef1f..90b9d3f 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -29,6 +29,8 @@
 import android.os.RemoteException;
 import android.system.OsConstants;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 
 
@@ -91,6 +93,12 @@
             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;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 038d7ae..b301d3d 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -53,6 +53,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothProfile;
@@ -65,7 +66,6 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -176,7 +176,6 @@
     private final Context mContext;
     private final ArrayMap<String, TetherState> mTetherStates;
     private final BroadcastReceiver mStateReceiver;
-    private final INetworkStatsService mStatsService;
     private final INetworkPolicyManager mPolicyManager;
     private final Looper mLooper;
     private final StateMachine mTetherMasterSM;
@@ -212,7 +211,6 @@
         mLog.mark("Tethering.constructed");
         mDeps = deps;
         mContext = mDeps.getContext();
-        mStatsService = mDeps.getINetworkStatsService();
         mPolicyManager = mDeps.getINetworkPolicyManager();
         mNetd = mDeps.getINetd(mContext);
         mLooper = mDeps.getTetheringLooper();
@@ -224,10 +222,12 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
+        final NetworkStatsManager statsManager =
+                (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
         mHandler = mTetherMasterSM.getHandler();
         mOffloadController = new OffloadController(mHandler,
                 mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
-                mDeps.getINetworkManagementService(), mLog);
+                statsManager, mLog);
         mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
                 TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
@@ -264,7 +264,7 @@
         }
 
         final UserManager userManager = (UserManager) mContext.getSystemService(
-                    Context.USER_SERVICE);
+                Context.USER_SERVICE);
         mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
         final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
@@ -2054,7 +2054,7 @@
 
         mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
         final TetherState tetherState = new TetherState(
-                new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService,
+                new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
                              mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index b16b329..79e99e8 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.NetworkRequest;
 import android.net.ip.IpServer;
 import android.net.util.SharedLog;
@@ -107,15 +106,6 @@
     }
 
     /**
-     *  Get a reference to INetworkStatsService to force update tethering usage.
-     *  Note: This should be removed in R development cycle.
-     */
-    public INetworkStatsService getINetworkStatsService() {
-        return INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-    }
-
-    /**
      * Get a reference to INetworkPolicyManager to be used by tethering.
      */
     public INetworkPolicyManager getINetworkPolicyManager() {
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index e4e4a09..99130d2 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -42,7 +42,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -363,7 +362,7 @@
                     IBinder connector;
                     try {
                         final long before = System.currentTimeMillis();
-                        while ((connector = ServiceManager.getService(
+                        while ((connector = (IBinder) mContext.getSystemService(
                                 Context.NETWORK_STACK_SERVICE)) == null) {
                             if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
                                 Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 65a0ac1..1f50b6b 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -52,7 +52,6 @@
 import static org.mockito.Mockito.when;
 
 import android.net.INetd;
-import android.net.INetworkStatsService;
 import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -99,7 +98,6 @@
     private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
 
     @Mock private INetd mNetd;
-    @Mock private INetworkStatsService mStatsService;
     @Mock private IpServer.Callback mCallback;
     @Mock private SharedLog mSharedLog;
     @Mock private IDhcpServer mDhcpServer;
@@ -139,13 +137,13 @@
             mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
         }
         mIpServer = new IpServer(
-                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
+                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
                 mCallback, usingLegacyDhcp, mDependencies);
         mIpServer.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
-        reset(mNetd, mStatsService, mCallback);
+        reset(mNetd, mCallback);
 
         when(mRaDaemon.start()).thenReturn(true);
     }
@@ -162,7 +160,7 @@
         if (upstreamIface != null) {
             dispatchTetherConnectionChanged(upstreamIface);
         }
-        reset(mNetd, mStatsService, mCallback);
+        reset(mNetd, mCallback);
     }
 
     @Before public void setUp() throws Exception {
@@ -173,13 +171,13 @@
     @Test
     public void startsOutAvailable() {
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
-                mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
+                mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
+        verifyNoMoreInteractions(mCallback, mNetd);
     }
 
     @Test
@@ -198,7 +196,7 @@
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
-            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mCallback);
         }
     }
 
@@ -210,7 +208,7 @@
         verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
         verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -228,7 +226,7 @@
                 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -236,7 +234,7 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNetd, mCallback);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
         inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
@@ -245,7 +243,7 @@
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -265,7 +263,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -285,7 +283,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -298,7 +296,7 @@
         InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -306,13 +304,12 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -322,12 +319,10 @@
         doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
@@ -340,13 +335,11 @@
                 IFACE_NAME, UPSTREAM_IFACE2);
 
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
-        InOrder inOrder = inOrder(mNetd, mStatsService);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
-        inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
     }
@@ -356,8 +349,7 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
-        inOrder.verify(mStatsService).forceUpdate();
+        InOrder inOrder = inOrder(mNetd, mCallback);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
@@ -368,7 +360,7 @@
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
     }
 
     @Test
@@ -435,11 +427,11 @@
     public void ignoresDuplicateUpstreamNotifications() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback);
 
         for (int i = 0; i < 5; i++) {
             dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-            verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+            verifyNoMoreInteractions(mNetd, mCallback);
         }
     }
 
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7886ca6..7e62e5a 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -16,21 +16,26 @@
 
 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_IFACE;
-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.NetworkStats.UID_TETHERING;
 import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID;
 import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
 import static com.android.testutils.MiscAssertsKt.assertContainsAll;
 import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
@@ -39,11 +44,14 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.net.ITetheringStatsProvider;
@@ -51,10 +59,12 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
 import android.net.RouteInfo;
+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.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
@@ -97,11 +107,13 @@
     @Mock private OffloadHardwareInterface mHardware;
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
-    @Mock private INetworkManagementService mNMService;
+    @Mock private NetworkStatsManager mStatsManager;
+    @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
-    private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
-            ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
+    private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+            mTetherStatsProviderCaptor =
+            ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
     private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
             ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
     private MockContentResolver mContentResolver;
@@ -114,6 +126,8 @@
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         FakeSettingsProvider.clearSettingsProvider();
+        when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
+                .thenReturn(mTetherStatsProviderCb);
     }
 
     @After public void tearDown() throws Exception {
@@ -139,9 +153,9 @@
 
     private OffloadController makeOffloadController() throws Exception {
         OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
-                mHardware, mContentResolver, mNMService, new SharedLog("test"));
-        verify(mNMService).registerTetheringStatsProvider(
-                mTetherStatsProviderCaptor.capture(), anyString());
+                mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+        verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+                mTetherStatsProviderCaptor.capture());
         return offload;
     }
 
@@ -384,12 +398,11 @@
         inOrder.verifyNoMoreInteractions();
     }
 
-    private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) {
-        assertEquals(iface, entry.iface);
-        assertEquals(stats.rxBytes, entry.rxBytes);
-        assertEquals(stats.txBytes, entry.txBytes);
-        assertEquals(SET_DEFAULT, entry.set);
-        assertEquals(TAG_NONE, entry.tag);
+    private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how,
+            @NonNull String iface, long rxBytes, long txBytes) {
+        return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT,
+                TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L,
+                txBytes, 0L, 0L);
     }
 
     @Test
@@ -400,19 +413,16 @@
         final OffloadController offload = makeOffloadController();
         offload.start();
 
+        final OffloadController.OffloadTetheringStatsProvider provider =
+                mTetherStatsProviderCaptor.getValue();
+
         final String ethernetIface = "eth1";
         final String mobileIface = "rmnet_data0";
 
-        ForwardedStats ethernetStats = new ForwardedStats();
-        ethernetStats.rxBytes = 12345;
-        ethernetStats.txBytes = 54321;
-
-        ForwardedStats mobileStats = new ForwardedStats();
-        mobileStats.rxBytes = 999;
-        mobileStats.txBytes = 99999;
-
-        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
-        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(12345, 54321));
+        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(
+                new ForwardedStats(999, 99999));
 
         InOrder inOrder = inOrder(mHardware);
 
@@ -432,10 +442,35 @@
         // Expect that we fetch stats from the previous upstream.
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
 
-        ethernetStats = new ForwardedStats();
-        ethernetStats.rxBytes = 100000;
-        ethernetStats.txBytes = 100000;
-        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
+        // Verify that the fetched stats are stored.
+        final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+
+        final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+
+        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
+        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
+
+        final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
+                NetworkStats.class);
+        final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
+                NetworkStats.class);
+
+        // Force pushing stats update to verify the stats reported.
+        provider.pushTetherStats();
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
+        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
+
+
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(100000, 100000));
         offload.setUpstreamLinkProperties(null);
         // Expect that we first clear the HAL's upstream parameters.
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
@@ -443,37 +478,38 @@
         // Expect that we fetch stats from the previous upstream.
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
 
-        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
-        NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
-        NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
-        waitForIdle();
         // There is no current upstream, so no stats are fetched.
         inOrder.verify(mHardware, never()).getForwardedStats(any());
         inOrder.verifyNoMoreInteractions();
 
-        assertEquals(2, stats.size());
-        assertEquals(2, perUidStats.size());
+        // Verify that the stored stats is accumulated.
+        final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
 
-        NetworkStats.Entry entry = null;
-        for (int i = 0; i < stats.size(); i++) {
-            assertEquals(UID_ALL, stats.getValues(i, entry).uid);
-            assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid);
-        }
+        final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
 
-        int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
-        int mobilePosition = 1 - ethernetPosition;
+        assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
+        assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
 
-        entry = stats.getValues(mobilePosition, entry);
-        assertNetworkStats(mobileIface, mobileStats, entry);
-        entry = perUidStats.getValues(mobilePosition, entry);
-        assertNetworkStats(mobileIface, mobileStats, entry);
+        // Verify that only diff of stats is reported.
+        reset(mTetherStatsProviderCb);
+        provider.pushTetherStats();
+        final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
 
-        ethernetStats.rxBytes = 12345 + 100000;
-        ethernetStats.txBytes = 54321 + 100000;
-        entry = stats.getValues(ethernetPosition, entry);
-        assertNetworkStats(ethernetIface, ethernetStats, entry);
-        entry = perUidStats.getValues(ethernetPosition, entry);
-        assertNetworkStats(ethernetIface, ethernetStats, entry);
+        final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+        assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
+        assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
     }
 
     @Test
@@ -493,19 +529,19 @@
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
 
-        ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+        AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
         final InOrder inOrder = inOrder(mHardware);
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
 
         // Applying an interface quota to the current upstream immediately sends it to the hardware.
-        provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+        provider.setLimit(ethernetIface, ethernetLimit);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
         inOrder.verifyNoMoreInteractions();
 
         // Applying an interface quota to another upstream does not take any immediate action.
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -518,7 +554,7 @@
 
         // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
         // to Long.MAX_VALUE.
-        provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+        provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
 
@@ -526,7 +562,7 @@
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -535,7 +571,7 @@
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
         lp.setInterfaceName(mobileIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setInterfaceQuota(mobileIface, mobileLimit);
+        provider.setLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware).getForwardedStats(ethernetIface);
         inOrder.verify(mHardware).stopOffloadControl();
@@ -551,7 +587,7 @@
 
         OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
         callback.onStoppedLimitReached();
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
     }
 
     @Test
@@ -654,9 +690,10 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+        // TODO: verify the exact stats reported.
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verifyNoMoreInteractions(mTetherStatsProviderCb);
         verifyNoMoreInteractions(mHardware);
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
-        verifyNoMoreInteractions(mNMService);
     }
 
     @Test
@@ -719,8 +756,8 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
-        verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
-        verifyNoMoreInteractions(mNMService);
+        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verifyNoMoreInteractions(mTetherStatsProviderCb);
 
         // TODO: verify local prefixes and downstreams are also pushed to the HAL.
         verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 900c671..7c2c75b 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -60,6 +60,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.NetworkStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -70,7 +71,6 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
@@ -152,7 +152,7 @@
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
-    @Mock private INetworkStatsService mStatsService;
+    @Mock private NetworkStatsManager mStatsManager;
     @Mock private INetworkPolicyManager mPolicyManager;
     @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
     @Mock private Resources mResources;
@@ -217,6 +217,7 @@
             if (Context.USB_SERVICE.equals(name)) return mUsbManager;
             if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
+            if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
             return super.getSystemService(name);
         }
 
@@ -339,11 +340,6 @@
         }
 
         @Override
-        public INetworkStatsService getINetworkStatsService() {
-            return mStatsService;
-        }
-
-        @Override
         public INetworkPolicyManager getINetworkPolicyManager() {
             return mPolicyManager;
         }
@@ -457,7 +453,7 @@
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ACTION_TETHER_STATE_CHANGED));
         mTethering = makeTethering();
-        verify(mNMService).registerTetheringStatsProvider(any(), anyString());
+        verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
         verify(mNetd).registerUnsolicitedEventListener(any());
         final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
                 ArgumentCaptor.forClass(PhoneStateListener.class);