Merge "Remove ServiceManager usage from IpSecTransform"
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java
index 6f09383..f42c4b7 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ b/core/java/android/net/InternalNetworkUpdateRequest.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,7 +26,7 @@
 public final class InternalNetworkUpdateRequest implements Parcelable {
     @NonNull
     private final StaticIpConfiguration mIpConfig;
-    @Nullable
+    @NonNull
     private final NetworkCapabilities mNetworkCapabilities;
 
     @NonNull
@@ -37,20 +36,16 @@
 
     @NonNull
     public NetworkCapabilities getNetworkCapabilities() {
-        return mNetworkCapabilities == null
-                ? null : new NetworkCapabilities(mNetworkCapabilities);
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     /** @hide */
     public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
-            @Nullable final NetworkCapabilities networkCapabilities) {
+            @NonNull final NetworkCapabilities networkCapabilities) {
         Objects.requireNonNull(ipConfig);
+        Objects.requireNonNull(networkCapabilities);
         mIpConfig = new StaticIpConfiguration(ipConfig);
-        if (null == networkCapabilities) {
-            mNetworkCapabilities = null;
-        } else {
-            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
-        }
+        mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
     }
 
     private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index 0bda923..90bb76a 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -25,15 +25,21 @@
     name: "framework-connectivity-netstats-internal-sources",
     srcs: [
         "src/android/app/usage/*.java",
-        "src/android/net/DataUsage*.*",
-        "src/android/net/INetworkStats*.*",
-        "src/android/net/NetworkIdentity*.java",
+        "src/android/net/DataUsageRequest.*",
+        "src/android/net/INetworkStatsService.aidl",
+        "src/android/net/INetworkStatsSession.aidl",
+        "src/android/net/NetworkIdentity.java",
+        "src/android/net/NetworkIdentitySet.java",
         "src/android/net/NetworkStateSnapshot.*",
-        "src/android/net/NetworkStats*.*",
+        "src/android/net/NetworkStats.*",
+        "src/android/net/NetworkStatsAccess.*",
+        "src/android/net/NetworkStatsCollection.*",
+        "src/android/net/NetworkStatsHistory.*",
         "src/android/net/NetworkTemplate.*",
         "src/android/net/TrafficStats.java",
         "src/android/net/UnderlyingNetworkInfo.*",
         "src/android/net/netstats/**/*.*",
+        "src/com/android/server/NetworkManagementSocketTagger.java",
     ],
     path: "src",
     visibility: [
diff --git a/framework-t/src/android/app/usage/NetworkStats.java b/framework-t/src/android/app/usage/NetworkStats.java
index 216a4a0..d33666d 100644
--- a/framework-t/src/android/app/usage/NetworkStats.java
+++ b/framework-t/src/android/app/usage/NetworkStats.java
@@ -24,13 +24,15 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.RemoteException;
-import android.util.IntArray;
 import android.util.Log;
 
+import com.android.net.module.util.CollectionUtils;
+
 import dalvik.system.CloseGuard;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 
 /**
  * Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
@@ -543,6 +545,15 @@
     }
 
     /**
+     * Collects tagged summary results and sets summary enumeration mode.
+     * @throws RemoteException
+     */
+    void startTaggedSummaryEnumeration() throws RemoteException {
+        mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp);
+        mEnumerationIndex = 0;
+    }
+
+    /**
      * Collects history results for uid and resets history enumeration index.
      */
     void startHistoryEnumeration(int uid, int tag, int state) {
@@ -568,7 +579,7 @@
         //       the filtering logic below can be removed.
         int[] uids = mSession.getRelevantUids();
         // Filtering of uids with empty history.
-        IntArray filteredUids = new IntArray(uids.length);
+        final ArrayList<Integer> filteredUids = new ArrayList<>();
         for (int uid : uids) {
             try {
                 NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -581,7 +592,7 @@
                 Log.w(TAG, "Error while getting history of uid " + uid, e);
             }
         }
-        mUids = filteredUids.toArray();
+        mUids = CollectionUtils.toIntArray(filteredUids);
         mUidOrUidIndex = -1;
         stepHistory();
     }
diff --git a/framework-t/src/android/app/usage/NetworkStatsManager.java b/framework-t/src/android/app/usage/NetworkStatsManager.java
index ca83309..d00de36 100644
--- a/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -18,6 +18,7 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -47,7 +48,6 @@
 import android.os.RemoteException;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.DataUnit;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +55,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Provides access to network usage history and statistics. Usage data is collected in
@@ -119,7 +120,7 @@
      * is reached.
      * @hide
      */
-    public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+    public static final long MIN_THRESHOLD_BYTES = 2 * 1_048_576L; // 2MiB
 
     private final Context mContext;
     private final INetworkStatsService mService;
@@ -139,9 +140,24 @@
         mContext = context;
         mService = service;
         setPollOnOpen(true);
+        setAugmentWithSubscriptionPlan(true);
     }
 
-    /** @hide */
+    /**
+     * Set poll on open flag to indicate the poll is needed before service gets statistics
+     * result. This is default enabled. However, for any non-privileged caller, the poll might
+     * be omitted in case of rate limiting.
+     *
+     * @param pollOnOpen true if poll is needed.
+     * @hide
+     */
+    // The system will ignore any non-default values for non-privileged
+    // processes, so processes that don't hold the appropriate permissions
+    // can make no use of this API.
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
     public void setPollOnOpen(boolean pollOnOpen) {
         if (pollOnOpen) {
             mFlags |= FLAG_POLL_ON_OPEN;
@@ -170,16 +186,44 @@
         }
     }
 
-    /** @hide */
-    public Bucket querySummaryForDevice(NetworkTemplate template,
-            long startTime, long endTime) throws SecurityException, RemoteException {
-        Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
-                mService);
-        bucket = stats.getDeviceSummaryForNetwork();
-
-        stats.close();
-        return bucket;
+    /**
+     * Query network usage statistics summaries.
+     *
+     * Result is summarised data usage for the whole
+     * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
+     * roaming. This means the bucket's start and end timestamp will be the same as the
+     * 'startTime' and 'endTime' arguments. State is going to be
+     * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
+     * tag {@link NetworkStats.Bucket#TAG_NONE},
+     * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered {@link NetworkStats.Bucket#METERED_ALL},
+     * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Bucket Summarised data usage.
+     *
+     * @hide
+     */
+    @NonNull
+    @WorkerThread
+    // @SystemApi(client = MODULE_LIBRARIES)
+    public Bucket querySummaryForDevice(@NonNull NetworkTemplate template,
+            long startTime, long endTime) {
+        try {
+            NetworkStats stats =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            Bucket bucket = stats.getDeviceSummaryForNetwork();
+            stats.close();
+            return bucket;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null; // To make the compiler happy.
     }
 
     /**
@@ -323,14 +367,70 @@
         return querySummary(template, startTime, endTime);
     }
 
-    /** @hide */
-    public NetworkStats querySummary(NetworkTemplate template, long startTime,
-            long endTime) throws SecurityException, RemoteException {
-        NetworkStats result;
-        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
-        result.startSummaryEnumeration();
+    /**
+     * Query network usage statistics summaries.
+     *
+     * The results will only include traffic made by UIDs belonging to the calling user profile.
+     * The results are aggregated over time, so that all buckets will have the same start and
+     * end timestamps as the passed arguments. Not aggregated over state, uid, default network,
+     * metered, or roaming.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    // @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime,
+            long endTime) throws SecurityException {
+        try {
+            NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startSummaryEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null; // To make the compiler happy.
+    }
 
-        return result;
+    /**
+     * Query tagged network usage statistics summaries.
+     *
+     * The results will only include tagged traffic made by UIDs belonging to the calling user
+     * profile. The results are aggregated over time, so that all buckets will have the same
+     * start and end timestamps as the passed arguments. Not aggregated over state, uid,
+     * default network, metered, or roaming.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    // @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryTaggedSummary(@NonNull NetworkTemplate template, long startTime,
+            long endTime) throws SecurityException {
+        try {
+            NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startTaggedSummaryEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null; // To make the compiler happy.
     }
 
     /**
@@ -484,6 +584,31 @@
         return result;
     }
 
+    /**
+     * Query realtime network usage statistics details with interfaces constrains.
+     * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING},
+     * video calling data usage and count of network operations that set by
+     * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any
+     * statistics that is reported by {@link NetworkStatsProvider}.
+     *
+     * @param requiredIfaces A list of interfaces the stats should be restricted to, or
+     *               {@link NetworkStats#INTERFACES_ALL}.
+     *
+     * @hide
+     */
+    //@SystemApi
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    @NonNull public android.net.NetworkStats getDetailedUidStats(
+                @NonNull Set<String> requiredIfaces) {
+        Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null");
+        try {
+            return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0]));
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public void registerUsageCallback(NetworkTemplate template, int networkType,
             long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
@@ -753,4 +878,83 @@
             return msg.getData().getParcelable(key);
         }
     }
+
+    /**
+     * Mark given UID as being in foreground for stats purposes.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setUidForeground(int uid, boolean uidForeground) {
+        try {
+            mService.setUidForeground(uid, uidForeground);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set default value of global alert bytes, the value will be clamped to [128kB, 2MB].
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            Manifest.permission.NETWORK_STACK})
+    public void setDefaultGlobalAlert(long alertBytes) {
+        try {
+            // TODO: Sync internal naming with the API surface.
+            mService.advisePersistThreshold(alertBytes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force update of statistics.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void forceUpdate() {
+        try {
+            mService.forceUpdate();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     *
+     * Asynchronicity notes : because traffic may be happening on the device at the same time, it
+     * doesn't make sense to wait for the warning and limit to be set – a caller still wouldn't
+     * know when exactly it was effective. All that can matter is that it's done quickly. Also,
+     * this method can't fail, so there is no status to return. All providers will see the new
+     * values soon.
+     * As such, this method returns immediately and sends the warning and limit to all providers
+     * as soon as possible through a one-way binder call.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+            long limit) {
+        try {
+            mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/framework-t/src/android/net/EthernetNetworkSpecifier.java b/framework-t/src/android/net/EthernetNetworkSpecifier.java
index 62c5761..925d12b 100644
--- a/framework-t/src/android/net/EthernetNetworkSpecifier.java
+++ b/framework-t/src/android/net/EthernetNetworkSpecifier.java
@@ -23,8 +23,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Objects;
 
 /**
@@ -47,7 +45,9 @@
      * @param interfaceName Name of the ethernet interface the specifier refers to.
      */
     public EthernetNetworkSpecifier(@NonNull String interfaceName) {
-        Preconditions.checkStringNotEmpty(interfaceName);
+        if (TextUtils.isEmpty(interfaceName)) {
+            throw new IllegalArgumentException();
+        }
         mInterfaceName = interfaceName;
     }
 
diff --git a/framework-t/src/android/net/INetworkStatsService.aidl b/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..a4babb5 100644
--- a/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/framework-t/src/android/net/INetworkStatsService.aidl
@@ -94,4 +94,16 @@
     /** Registers a network stats provider */
     INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
             in INetworkStatsProvider provider);
+
+    /** Mark given UID as being in foreground for stats purposes. */
+    void setUidForeground(int uid, boolean uidForeground);
+
+    /** Advise persistence threshold; may be overridden internally. */
+    void advisePersistThreshold(long thresholdBytes);
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+     void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
 }
diff --git a/framework-t/src/android/net/INetworkStatsSession.aidl b/framework-t/src/android/net/INetworkStatsSession.aidl
index dfedf66..babe0bf 100644
--- a/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -46,6 +46,10 @@
      */
     @UnsupportedAppUsage
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
+
+    /** Return network layer usage summary per UID for tagged traffic that matches template. */
+    NetworkStats getTaggedSummaryForAllUid(in NetworkTemplate template, long start, long end);
+
     /** Return historical network layer stats for specific UID traffic that matches template. */
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
diff --git a/framework-t/src/android/net/IpSecManager.java b/framework-t/src/android/net/IpSecManager.java
index 819ac6b..a423783 100644
--- a/framework-t/src/android/net/IpSecManager.java
+++ b/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -25,7 +26,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -42,6 +42,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -89,6 +91,11 @@
     @SystemApi(client = MODULE_LIBRARIES)
     public static final int DIRECTION_FWD = 2;
 
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PolicyDirection {}
+
     /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
diff --git a/framework-t/src/android/net/NetworkIdentity.java b/framework-t/src/android/net/NetworkIdentity.java
index eb8f43e..8f1115e 100644
--- a/framework-t/src/android/net/NetworkIdentity.java
+++ b/framework-t/src/android/net/NetworkIdentity.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.service.NetworkIdentityProto;
 import android.telephony.Annotation.NetworkType;
 import android.util.proto.ProtoOutputStream;
@@ -228,11 +227,11 @@
         final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
 
         if (legacyType == TYPE_WIFI) {
-            networkId = snapshot.getNetworkCapabilities().getSsid();
-            if (networkId == null) {
-                final WifiManager wifi = context.getSystemService(WifiManager.class);
-                final WifiInfo info = wifi.getConnectionInfo();
-                networkId = info != null ? info.getSSID() : null;
+            final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+                    .getTransportInfo();
+            if (transportInfo instanceof WifiInfo) {
+                final WifiInfo info = (WifiInfo) transportInfo;
+                networkId = info != null ? info.getCurrentNetworkKey() : null;
             }
         }
 
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index 1986b83..9d532e7 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -31,7 +31,7 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.CollectionUtils;
 
 import libcore.util.EmptyArray;
 
@@ -83,10 +83,7 @@
      */
     // TODO: Rename TAG_ALL to TAG_ANY.
     public static final int TAG_ALL = -1;
-    /**
-     * {@link #set} value for all sets combined, not including debug sets.
-     * @hide
-     */
+    /** {@link #set} value for all sets combined, not including debug sets. */
     public static final int SET_ALL = -1;
     /** {@link #set} value where background data is accounted. */
     public static final int SET_DEFAULT = 0;
@@ -114,9 +111,6 @@
             SET_ALL,
             SET_DEFAULT,
             SET_FOREGROUND,
-            SET_DEBUG_START,
-            SET_DBG_VPN_IN,
-            SET_DBG_VPN_OUT
     })
     public @interface State {
     }
@@ -131,10 +125,7 @@
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
 
-    /**
-     * {@link #metered} value to account for all metered states.
-     * @hide
-     */
+    /** {@link #metered} value to account for all metered states. */
     public static final int METERED_ALL = -1;
     /** {@link #metered} value where native, unmetered data is accounted. */
     public static final int METERED_NO = 0;
@@ -152,10 +143,7 @@
     }
 
 
-    /**
-     * {@link #roaming} value to account for all roaming states.
-     * @hide
-     */
+    /** {@link #roaming} value to account for all roaming states. */
     public static final int ROAMING_ALL = -1;
     /** {@link #roaming} value where native, non-roaming data is accounted. */
     public static final int ROAMING_NO = 0;
@@ -172,10 +160,7 @@
     public @interface Roaming {
     }
 
-    /**
-     * {@link #onDefaultNetwork} value to account for all default network states.
-     * @hide
-     */
+    /** {@link #onDefaultNetwork} value to account for all default network states. */
     public static final int DEFAULT_NETWORK_ALL = -1;
     /** {@link #onDefaultNetwork} value to account for usage while not the default network. */
     public static final int DEFAULT_NETWORK_NO = 0;
@@ -398,6 +383,95 @@
             this.operations += another.operations;
         }
 
+        /**
+         * @return interface name of this entry.
+         * @hide
+         */
+        @Nullable public String getIface() {
+            return iface;
+        }
+
+        /**
+         * @return the uid of this entry.
+         */
+        public int getUid() {
+            return uid;
+        }
+
+        /**
+         * @return the set state of this entry. Should be one of the following
+         * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+         */
+        @State public int getSet() {
+            return set;
+        }
+
+        /**
+         * @return the tag value of this entry.
+         */
+        public int getTag() {
+            return tag;
+        }
+
+        /**
+         * @return the metered state. Should be one of the following
+         * values: {link #METERED_YES}, {link #METERED_NO}.
+         */
+        @Meteredness public int getMetered() {
+            return metered;
+        }
+
+        /**
+         * @return the roaming state. Should be one of the following
+         * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+         */
+        @Roaming public int getRoaming() {
+            return roaming;
+        }
+
+        /**
+         * @return the default network state. Should be one of the following
+         * values: {link #DEFAULT_NETWORK_YES}, {link #DEFAULT_NETWORK_NO}.
+         */
+        @DefaultNetwork public int getDefaultNetwork() {
+            return defaultNetwork;
+        }
+
+        /**
+         * @return the number of received bytes.
+         */
+        public long getRxBytes() {
+            return rxBytes;
+        }
+
+        /**
+         * @return the number of received packets.
+         */
+        public long getRxPackets() {
+            return rxPackets;
+        }
+
+        /**
+         * @return the number of transmitted bytes.
+         */
+        public long getTxBytes() {
+            return txBytes;
+        }
+
+        /**
+         * @return the number of transmitted packets.
+         */
+        public long getTxPackets() {
+            return txPackets;
+        }
+
+        /**
+         * @return the count of network operations performed for this entry.
+         */
+        public long getOperations() {
+            return operations;
+        }
+
         @Override
         public String toString() {
             final StringBuilder builder = new StringBuilder();
@@ -608,7 +682,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public Entry getValues(int i, Entry recycle) {
+    public Entry getValues(int i, @Nullable Entry recycle) {
         final Entry entry = recycle != null ? recycle : new Entry();
         entry.iface = iface[i];
         entry.uid = uid[i];
@@ -1185,7 +1259,7 @@
      * @hide
      */
     public void removeUids(int[] uids) {
-        filter(e -> !ArrayUtils.contains(uids, e.uid));
+        filter(e -> !CollectionUtils.contains(uids, e.uid));
     }
 
     /**
@@ -1218,7 +1292,7 @@
         filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
                 && (limitTag == TAG_ALL || limitTag == e.tag)
                 && (limitIfaces == INTERFACES_ALL
-                    || ArrayUtils.contains(limitIfaces, e.iface)));
+                    || CollectionUtils.contains(limitIfaces, e.iface)));
     }
 
     /**
diff --git a/framework-t/src/android/net/NetworkStatsAccess.java b/framework-t/src/android/net/NetworkStatsAccess.java
index 591605d..b64fbdb 100644
--- a/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.net.TrafficStats.UID_TETHERING;
@@ -106,7 +107,7 @@
 
     /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
     public static @NetworkStatsAccess.Level int checkAccessLevel(
-            Context context, int callingUid, String callingPackage) {
+            Context context, int callingPid, int callingUid, String callingPackage) {
         final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
         final TelephonyManager tm = (TelephonyManager)
                 context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -123,8 +124,12 @@
         final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
         final int appId = UserHandle.getAppId(callingUid);
 
+        final boolean isNetworkStack = context.checkPermission(
+                android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
+                == PERMISSION_GRANTED;
+
         if (hasCarrierPrivileges || isDeviceOwner
-                || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+                || appId == Process.SYSTEM_UID || isNetworkStack) {
             // Carrier-privileged apps and device owners, and the system (including the
             // network stack) can access data usage for all apps on the device.
             return NetworkStatsAccess.Level.DEVICE;
@@ -155,6 +160,8 @@
      */
     public static boolean isAccessibleToUser(int uid, int callerUid,
             @NetworkStatsAccess.Level int accessLevel) {
+        final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+        final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
         switch (accessLevel) {
             case NetworkStatsAccess.Level.DEVICE:
                 // Device-level access - can access usage for any uid.
@@ -165,13 +172,13 @@
                 // anonymized uids
                 return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                         || uid == UID_TETHERING || uid == UID_ALL
-                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+                        || userId == callerUserId;
             case NetworkStatsAccess.Level.USER:
                 // User-level access - can access usage for any app running in the same user, along
                 // with some special uids (system, removed, or tethering).
                 return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
                         || uid == UID_TETHERING
-                        || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+                        || userId == callerUserId;
             case NetworkStatsAccess.Level.DEFAULT:
             default:
                 // Default access level - can only access one's own usage.
@@ -185,8 +192,8 @@
             AppOpsManager appOps = (AppOpsManager) context.getSystemService(
                     Context.APP_OPS_SERVICE);
 
-            final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
-                    callingUid, callingPackage);
+            final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
+                    callingUid, callingPackage, null /* attributionTag */, null /* message */);
             if (mode == AppOpsManager.MODE_DEFAULT) {
                 // The default behavior here is to check if PackageManager has given the app
                 // permission.
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index 8d1347e..9f9d73f 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -40,21 +40,15 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
-import android.util.IntArray;
-import android.util.MathUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
 import android.util.Range;
-import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
 import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
 
 import libcore.io.IoUtils;
 
@@ -62,6 +56,7 @@
 import java.io.DataInput;
 import java.io.DataInputStream;
 import java.io.DataOutput;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -87,9 +82,6 @@
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
-    /** Default buffer size from BufferedInputStream */
-    private static final int BUFFER_SIZE = 8192;
-
     private static final int VERSION_NETWORK_INIT = 1;
 
     private static final int VERSION_UID_INIT = 1;
@@ -196,11 +188,11 @@
 
     public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
                 final int callerUid) {
-        IntArray uids = new IntArray();
+        final ArrayList<Integer> uids = new ArrayList<>();
         for (int i = 0; i < mStats.size(); i++) {
             final Key key = mStats.keyAt(i);
             if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
-                int j = uids.binarySearch(key.uid);
+                int j = Collections.binarySearch(uids, new Integer(key.uid));
 
                 if (j < 0) {
                     j = ~j;
@@ -208,7 +200,7 @@
                 }
             }
         }
-        return uids.toArray();
+        return CollectionUtils.toIntArray(uids);
     }
 
     /**
@@ -225,7 +217,8 @@
 
         // 180 days of history should be enough for anyone; if we end up needing
         // more, we'll dynamically grow the history object.
-        final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
+        final int bucketEstimate = (int) NetworkStatsUtils.constrain(
+                ((end - start) / mBucketDuration), 0,
                 (180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
         final NetworkStatsHistory combined = new NetworkStatsHistory(
                 mBucketDuration, bucketEstimate, fields);
@@ -316,7 +309,7 @@
 
             final long deltaTotal = combined.getTotalBytes() - beforeTotal;
             if (deltaTotal != 0) {
-                Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
+                Log.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
             }
 
             // Finally we can slice data as originally requested
@@ -442,8 +435,7 @@
 
     @Override
     public void read(InputStream in) throws IOException {
-        final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
-        read(dataIn);
+        read((DataInput) new DataInputStream(in));
     }
 
     private void read(DataInput in) throws IOException {
@@ -482,18 +474,17 @@
 
     @Override
     public void write(OutputStream out) throws IOException {
-        final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
-        write(dataOut);
-        dataOut.flush();
+        write((DataOutput) new DataOutputStream(out));
+        out.flush();
     }
 
     private void write(DataOutput out) throws IOException {
         // cluster key lists grouped by ident
-        final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
+        final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = new HashMap<>();
         for (Key key : mStats.keySet()) {
             ArrayList<Key> keys = keysByIdent.get(key.ident);
             if (keys == null) {
-                keys = Lists.newArrayList();
+                keys = new ArrayList<>();
                 keysByIdent.put(key.ident, keys);
             }
             keys.add(key);
@@ -640,12 +631,12 @@
      * {@link TrafficStats#UID_REMOVED}.
      */
     public void removeUids(int[] uids) {
-        final ArrayList<Key> knownKeys = Lists.newArrayList();
+        final ArrayList<Key> knownKeys = new ArrayList<>();
         knownKeys.addAll(mStats.keySet());
 
         // migrate all UID stats into special "removed" bucket
         for (Key key : knownKeys) {
-            if (ArrayUtils.contains(uids, key.uid)) {
+            if (CollectionUtils.contains(uids, key.uid)) {
                 // only migrate combined TAG_NONE history
                 if (key.tag == TAG_NONE) {
                     final NetworkStatsHistory uidHistory = mStats.get(key);
@@ -672,7 +663,7 @@
     }
 
     private ArrayList<Key> getSortedKeys() {
-        final ArrayList<Key> keys = Lists.newArrayList();
+        final ArrayList<Key> keys = new ArrayList<>();
         keys.addAll(mStats.keySet());
         Collections.sort(keys);
         return keys;
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 3eef4ee..428bc6d 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -28,7 +28,6 @@
 import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
-import static com.android.internal.util.ArrayUtils.total;
 import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
 
 import android.compat.annotation.UnsupportedAppUsage;
@@ -37,10 +36,11 @@
 import android.os.Parcelable;
 import android.service.NetworkStatsHistoryBucketProto;
 import android.service.NetworkStatsHistoryProto;
-import android.util.MathUtils;
+import android.util.IndentingPrintWriter;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
 
 import libcore.util.EmptyArray;
 
@@ -174,7 +174,7 @@
                 txPackets = new long[bucketStart.length];
                 operations = new long[bucketStart.length];
                 bucketCount = bucketStart.length;
-                totalBytes = total(rxBytes) + total(txBytes);
+                totalBytes = CollectionUtils.total(rxBytes) + CollectionUtils.total(txBytes);
                 break;
             }
             case VERSION_ADD_PACKETS:
@@ -189,7 +189,7 @@
                 txPackets = readVarLongArray(in);
                 operations = readVarLongArray(in);
                 bucketCount = bucketStart.length;
-                totalBytes = total(rxBytes) + total(txBytes);
+                totalBytes = CollectionUtils.total(rxBytes) + CollectionUtils.total(txBytes);
                 break;
             }
             default: {
@@ -267,7 +267,7 @@
         } else {
             index -= 1;
         }
-        return MathUtils.constrain(index, 0, bucketCount - 1);
+        return NetworkStatsUtils.constrain(index, 0, bucketCount - 1);
     }
 
     /**
@@ -281,7 +281,7 @@
         } else {
             index += 1;
         }
-        return MathUtils.constrain(index, 0, bucketCount - 1);
+        return NetworkStatsUtils.constrain(index, 0, bucketCount - 1);
     }
 
     /**
@@ -349,6 +349,9 @@
 
         // create any buckets needed by this range
         ensureBuckets(start, end);
+        // Return fast if there is still no entry. This would typically happen when the start,
+        // end or duration are not valid values, e.g. start > end, negative duration value, etc.
+        if (bucketCount == 0) return;
 
         // distribute data usage into buckets
         long duration = end - start;
@@ -560,6 +563,9 @@
         entry.txPackets = txPackets != null ? 0 : UNKNOWN;
         entry.operations = operations != null ? 0 : UNKNOWN;
 
+        // Return fast if there is no entry.
+        if (bucketCount == 0) return entry;
+
         final int startIndex = getIndexAfter(end);
         for (int i = startIndex; i >= 0; i--) {
             final long curStart = bucketStart[i];
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index 5da8e25..e9084b0 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -35,12 +36,13 @@
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
-import static android.net.wifi.WifiInfo.sanitizeSsid;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.wifi.WifiInfo;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -49,7 +51,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
-import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 import com.android.net.module.util.NetworkStatsUtils;
 
@@ -57,6 +59,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -70,7 +73,7 @@
  *
  * @hide
  */
-// @SystemApi(client = MODULE_LIBRARIES)
+@SystemApi(client = MODULE_LIBRARIES)
 public final class NetworkTemplate implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -213,11 +216,14 @@
     public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
             @NetworkType int ratType, int metered) {
         if (TextUtils.isEmpty(subscriberId)) {
-            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
-                    metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
+                    null /* matchSubscriberIds */,
+                    new String[0] /* matchWifiNetworkKeys */, metered, ROAMING_ALL,
+                    DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                     NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
         }
-        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
+        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[] { subscriberId },
+                new String[0] /* matchWifiNetworkKeys */,
                 metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
@@ -235,7 +241,7 @@
 
     /**
      * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
-     * regardless of SSID.
+     * regardless of key of the wifi network.
      *
      * @hide
      */
@@ -255,33 +261,40 @@
 
     /**
      * Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
-     * given SSID.
+     * given key of the wifi network.
      *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
      * @hide
      */
-    public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
-        Objects.requireNonNull(networkId);
+    public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
+        Objects.requireNonNull(wifiNetworkKey);
         return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
                 new String[] { null } /* matchSubscriberIds */,
-                networkId, METERED_ALL, ROAMING_ALL,
+                new String[] { wifiNetworkKey }, METERED_ALL, ROAMING_ALL,
                 DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL);
     }
 
     /**
-     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
-     * and IMSI.
+     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
+     * key of the wifi network and IMSI.
      *
-     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code networkId} to get result regardless
-     * of SSID.
+     * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
+     * of key of the wifi network.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
+     * @param subscriberId the IMSI associated to this wifi network.
      *
      * @hide
      */
-    public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
+    public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
             @Nullable String subscriberId) {
         return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
-                networkId, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+                wifiNetworkKey != null
+                        ? new String[] { wifiNetworkKey } : new String[0],
+                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
@@ -324,7 +337,9 @@
     public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
         Objects.requireNonNull(subscriberId);
         return new NetworkTemplate(MATCH_CARRIER, subscriberId,
-                new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
+                new String[] { subscriberId },
+                new String[0] /* matchWifiNetworkKeys */,
+                METERED_YES, ROAMING_ALL,
                 DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
@@ -342,8 +357,8 @@
      */
     private final String[] mMatchSubscriberIds;
 
-    // TODO: Change variable name to match the Api surface.
-    private final String mNetworkId;
+    @NonNull
+    private final String[] mMatchWifiNetworkKeys;
 
     // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
     private final int mMetered;
@@ -377,18 +392,19 @@
     /** @hide */
     // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
     @UnsupportedAppUsage
-    public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
-        this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
+    public NetworkTemplate(int matchRule, String subscriberId, String wifiNetworkKey) {
+        this(matchRule, subscriberId, new String[] { subscriberId }, wifiNetworkKey);
     }
 
     /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId) {
+            String wifiNetworkKey) {
         // Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
         // to metered networks. It is now possible to match mobile with any meteredness, but
         // in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
         //constructor passes METERED_YES for these types.
-        this(matchRule, subscriberId, matchSubscriberIds, networkId,
+        this(matchRule, subscriberId, matchSubscriberIds,
+                wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
                 (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
                 : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
                 OEM_MANAGED_ALL, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
@@ -397,23 +413,25 @@
     /** @hide */
     // TODO: Remove it after updating all of the caller.
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId, int metered, int roaming, int defaultNetwork, int subType,
+            String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
             int oemManaged) {
-        this(matchRule, subscriberId, matchSubscriberIds, networkId, metered, roaming,
-                defaultNetwork, subType, oemManaged,
+        this(matchRule, subscriberId, matchSubscriberIds,
+                wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
+                metered, roaming, defaultNetwork, subType, oemManaged,
                 NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     /** @hide */
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId, int metered, int roaming, int defaultNetwork, int subType,
-            int oemManaged, int subscriberIdMatchRule) {
+            String[] matchWifiNetworkKeys, int metered, int roaming,
+            int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+        Objects.requireNonNull(matchWifiNetworkKeys);
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         // TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
         // mSubscriberId is null
         mMatchSubscriberIds = matchSubscriberIds;
-        mNetworkId = networkId;
+        mMatchWifiNetworkKeys = matchWifiNetworkKeys;
         mMetered = metered;
         mRoaming = roaming;
         mDefaultNetwork = defaultNetwork;
@@ -431,7 +449,7 @@
         mMatchRule = in.readInt();
         mSubscriberId = in.readString();
         mMatchSubscriberIds = in.createStringArray();
-        mNetworkId = in.readString();
+        mMatchWifiNetworkKeys = in.createStringArray();
         mMetered = in.readInt();
         mRoaming = in.readInt();
         mDefaultNetwork = in.readInt();
@@ -445,7 +463,7 @@
         dest.writeInt(mMatchRule);
         dest.writeString(mSubscriberId);
         dest.writeStringArray(mMatchSubscriberIds);
-        dest.writeString(mNetworkId);
+        dest.writeStringArray(mMatchWifiNetworkKeys);
         dest.writeInt(mMetered);
         dest.writeInt(mRoaming);
         dest.writeInt(mDefaultNetwork);
@@ -471,9 +489,7 @@
             builder.append(", matchSubscriberIds=").append(
                     Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
         }
-        if (mNetworkId != null) {
-            builder.append(", networkId=").append(mNetworkId);
-        }
+        builder.append(", matchWifiNetworkKeys=").append(Arrays.toString(mMatchWifiNetworkKeys));
         if (mMetered != METERED_ALL) {
             builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
         }
@@ -497,8 +513,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
-                mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+        return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
+                mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
     }
 
     @Override
@@ -507,13 +523,13 @@
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
                     && Objects.equals(mSubscriberId, other.mSubscriberId)
-                    && Objects.equals(mNetworkId, other.mNetworkId)
                     && mMetered == other.mMetered
                     && mRoaming == other.mRoaming
                     && mDefaultNetwork == other.mDefaultNetwork
                     && mSubType == other.mSubType
                     && mOemManaged == other.mOemManaged
-                    && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule;
+                    && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
+                    && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
         }
         return false;
     }
@@ -559,6 +575,7 @@
 
     /**
      * Get subscriber Id of the template.
+     * @hide
      */
     @Nullable
     @UnsupportedAppUsage
@@ -575,18 +592,19 @@
     }
 
     /**
-     * Get Wifi Network Key of the template. See {@link WifiInfo#getCurrentNetworkKey()}.
+     * Get the set of Wifi Network Keys of the template.
+     * See {@link WifiInfo#getCurrentNetworkKey()}.
      */
-    @Nullable
-    public String getWifiNetworkKey() {
-        return mNetworkId;
+    @NonNull
+    public Set<String> getWifiNetworkKeys() {
+        return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
     }
 
     /** @hide */
-    // TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
+    // TODO: Remove this and replace all callers with {@link #getWifiNetworkKeys()}.
     @Nullable
     public String getNetworkId() {
-        return mNetworkId;
+        return getWifiNetworkKeys().isEmpty() ? null : getWifiNetworkKeys().iterator().next();
     }
 
     /**
@@ -703,28 +721,33 @@
      */
     public boolean matchesSubscriberId(@Nullable String subscriberId) {
         return mSubscriberIdMatchRule == NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
-                || ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
+                || CollectionUtils.contains(mMatchSubscriberIds, subscriberId);
     }
 
     /**
-     * Check if network with matching SSID. Returns true when the SSID matches, or when
-     * {@code mNetworkId} is {@code WIFI_NETWORK_KEY_ALL}.
+     * Check if network matches key of the wifi network.
+     * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
+     * empty.
+     *
+     * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+     *                  to know details about the key.
      */
-    private boolean matchesWifiNetworkId(@Nullable String networkId) {
-        return Objects.equals(mNetworkId, WIFI_NETWORK_KEY_ALL)
-                || Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
+    private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
+        Objects.requireNonNull(wifiNetworkKey);
+        return CollectionUtils.isEmpty(mMatchWifiNetworkKeys)
+                || CollectionUtils.contains(mMatchWifiNetworkKeys, wifiNetworkKey);
     }
 
     /**
-     * Check if mobile network with matching IMSI.
+     * Check if mobile network matches IMSI.
      */
     private boolean matchesMobile(NetworkIdentity ident) {
         if (ident.mType == TYPE_WIMAX) {
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
-            return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
-                    && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
+            return ident.mType == TYPE_MOBILE && !CollectionUtils.isEmpty(mMatchSubscriberIds)
+                    && CollectionUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
                     && matchesCollapsedRatType(ident);
         }
     }
@@ -814,7 +837,7 @@
         switch (ident.mType) {
             case TYPE_WIFI:
                 return matchesSubscriberId(ident.mSubscriberId)
-                        && matchesWifiNetworkId(ident.mNetworkId);
+                        && matchesWifiNetworkKey(ident.mNetworkId);
             default:
                 return false;
         }
@@ -835,8 +858,8 @@
      */
     private boolean matchesCarrier(NetworkIdentity ident) {
         return ident.mSubscriberId != null
-                && !ArrayUtils.isEmpty(mMatchSubscriberIds)
-                && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+                && !CollectionUtils.isEmpty(mMatchSubscriberIds)
+                && CollectionUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
     }
 
     private boolean matchesMobileWildcard(NetworkIdentity ident) {
@@ -953,11 +976,13 @@
         if (template.mSubscriberId == null) return template;
 
         for (String[] merged : mergedList) {
-            if (ArrayUtils.contains(merged, template.mSubscriberId)) {
+            if (CollectionUtils.contains(merged, template.mSubscriberId)) {
                 // Requested template subscriber is part of the merge group; return
                 // a template that matches all merged subscribers.
+                final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
                 return new NetworkTemplate(template.mMatchRule, merged[0], merged,
-                        template.mNetworkId);
+                        CollectionUtils.isEmpty(matchWifiNetworkKeys)
+                                ? null : matchWifiNetworkKeys[0]);
             }
         }
 
@@ -984,9 +1009,10 @@
         private final int mMatchRule;
         // Use a SortedSet to provide a deterministic order when fetching the first one.
         @NonNull
-        private final SortedSet<String> mMatchSubscriberIds = new TreeSet<>();
-        @Nullable
-        private String mWifiNetworkKey;
+        private final SortedSet<String> mMatchSubscriberIds =
+                new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder()));
+        @NonNull
+        private final SortedSet<String> mMatchWifiNetworkKeys = new TreeSet<>();
 
         // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
         private int mMetered;
@@ -1006,7 +1032,6 @@
             assertRequestableMatchRule(matchRule);
             // Initialize members with default values.
             mMatchRule = matchRule;
-            mWifiNetworkKey = WIFI_NETWORK_KEY_ALL;
             mMetered = METERED_ALL;
             mRoaming = ROAMING_ALL;
             mDefaultNetwork = DEFAULT_NETWORK_ALL;
@@ -1030,15 +1055,28 @@
         }
 
         /**
-         * Set the Wifi Network Key.
+         * Set the Wifi Network Keys. Calling this function with an empty set represents
+         * the intention of matching any Wifi Network Key.
          *
-         * @param wifiNetworkKey the Wifi Network Key, see {@link WifiInfo#getCurrentNetworkKey()}.
-         *                       Or null to match all networks.
+         * @param wifiNetworkKeys the list of Wifi Network Key,
+         *                        see {@link WifiInfo#getCurrentNetworkKey()}.
+         *                        Or an empty list to match all networks.
+         *                        Note that {@code getCurrentNetworkKey()} might get null key
+         *                        when wifi disconnects. However, the caller should never invoke
+         *                        this function with a null Wifi Network Key since such statistics
+         *                        never exists.
          * @return this builder.
          */
         @NonNull
-        public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
-            mWifiNetworkKey = wifiNetworkKey;
+        public Builder setWifiNetworkKeys(@NonNull Set<String> wifiNetworkKeys) {
+            Objects.requireNonNull(wifiNetworkKeys);
+            for (String key : wifiNetworkKeys) {
+                if (key == null) {
+                    throw new IllegalArgumentException("Null is not a valid key");
+                }
+            }
+            mMatchWifiNetworkKeys.clear();
+            mMatchWifiNetworkKeys.addAll(wifiNetworkKeys);
             return this;
         }
 
@@ -1122,9 +1160,17 @@
         }
 
         private void assertRequestableParameters() {
+            validateWifiNetworkKeys();
             // TODO: Check all the input are legitimate.
         }
 
+        private void validateWifiNetworkKeys() {
+            if (mMatchRule != MATCH_WIFI && !mMatchWifiNetworkKeys.isEmpty()) {
+                throw new IllegalArgumentException("Trying to build non wifi match rule: "
+                        + mMatchRule + " with wifi network keys");
+            }
+        }
+
         /**
          * For backward compatibility, deduce match rule to a wildcard match rule
          * if the Subscriber Ids are empty.
@@ -1133,7 +1179,7 @@
             if (mMatchRule == MATCH_MOBILE && mMatchSubscriberIds.isEmpty()) {
                 return MATCH_MOBILE_WILDCARD;
             } else if (mMatchRule == MATCH_WIFI && mMatchSubscriberIds.isEmpty()
-                    && mWifiNetworkKey == WIFI_NETWORK_KEY_ALL) {
+                    && mMatchWifiNetworkKeys.isEmpty()) {
                 return MATCH_WIFI_WILDCARD;
             }
             return mMatchRule;
@@ -1153,8 +1199,8 @@
             return new NetworkTemplate(getWildcardDeducedMatchRule(),
                     mMatchSubscriberIds.isEmpty() ? null : mMatchSubscriberIds.iterator().next(),
                     mMatchSubscriberIds.toArray(new String[0]),
-                    mWifiNetworkKey, mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged,
-                    subscriberIdMatchRule);
+                    mMatchWifiNetworkKeys.toArray(new String[0]), mMetered, mRoaming,
+                    mDefaultNetwork, mRatType, mOemManaged, subscriberIdMatchRule);
         }
     }
 }
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index fa65061..1af32bf 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -27,9 +28,8 @@
 import android.content.Context;
 import android.media.MediaPlayer;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.DataUnit;
 
 import com.android.server.NetworkManagementSocketTagger;
 
@@ -37,6 +37,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.DatagramSocket;
 import java.net.Socket;
 import java.net.SocketException;
@@ -54,24 +56,25 @@
  * use {@link NetworkStatsManager} instead.
  */
 public class TrafficStats {
+    private static final String TAG = TrafficStats.class.getSimpleName();
     /**
      * The return value to indicate that the device does not support the statistic.
      */
     public final static int UNSUPPORTED = -1;
 
-    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
     @Deprecated
     public static final long KB_IN_BYTES = 1024;
-    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
     @Deprecated
     public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
-    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
     @Deprecated
     public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
-    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
     @Deprecated
     public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
-    /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+    /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
     @Deprecated
     public static final long PB_IN_BYTES = TB_IN_BYTES * 1024;
 
@@ -174,12 +177,25 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
     private synchronized static INetworkStatsService getStatsService() {
         if (sStatsService == null) {
-            sStatsService = INetworkStatsService.Stub.asInterface(
-                    ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            sStatsService = getStatsBinder();
         }
         return sStatsService;
     }
 
+    @Nullable
+    private static INetworkStatsService getStatsBinder() {
+        try {
+            final Method getServiceMethod = Class.forName("android.os.ServiceManager")
+                    .getDeclaredMethod("getService", new Class[]{String.class});
+            final IBinder binder = (IBinder) getServiceMethod.invoke(
+                    null, Context.NETWORK_STATS_SERVICE);
+            return INetworkStatsService.Stub.asInterface(binder);
+        } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
+                | InvocationTargetException e) {
+            throw new NullPointerException("Cannot get INetworkStatsService: " + e);
+        }
+    }
+
     /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
      * session started, or {@code null} if no session active.
@@ -266,6 +282,18 @@
     }
 
     /**
+     * Set active tag to use when accounting {@link Socket} traffic originating
+     * from the current thread. The tag used internally is well-defined to
+     * distinguish all download provider traffic.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void setThreadStatsTagDownload() {
+        setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
+    }
+
+    /**
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
diff --git a/service-t/src/com/android/server/IpSecService.java b/service-t/src/com/android/server/IpSecService.java
index d1e432e..179d945 100644
--- a/service-t/src/com/android/server/IpSecService.java
+++ b/service-t/src/com/android/server/IpSecService.java
@@ -1236,37 +1236,53 @@
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
         final int resourceId = mNextResourceId++;
-        FileDescriptor sockFd = null;
+
+        ParcelFileDescriptor pFd = null;
         try {
             if (!userRecord.mSocketQuotaTracker.isAvailable()) {
                 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
             }
 
-            sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-            mUidFdTagger.tag(sockFd, callingUid);
+            FileDescriptor sockFd = null;
+            try {
+                sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+                pFd = ParcelFileDescriptor.dup(sockFd);
+            } finally {
+                IoUtils.closeQuietly(sockFd);
+            }
 
+            mUidFdTagger.tag(pFd.getFileDescriptor(), callingUid);
             // This code is common to both the unspecified and specified port cases
             Os.setsockoptInt(
-                    sockFd,
+                    pFd.getFileDescriptor(),
                     OsConstants.IPPROTO_UDP,
                     OsConstants.UDP_ENCAP,
                     OsConstants.UDP_ENCAP_ESPINUDP);
 
-            mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
+            mNetd.ipSecSetEncapSocketOwner(pFd, callingUid);
             if (port != 0) {
                 Log.v(TAG, "Binding to port " + port);
-                Os.bind(sockFd, INADDR_ANY, port);
+                Os.bind(pFd.getFileDescriptor(), INADDR_ANY, port);
             } else {
-                port = bindToRandomPort(sockFd);
+                port = bindToRandomPort(pFd.getFileDescriptor());
             }
 
             userRecord.mEncapSocketRecords.put(
                     resourceId,
                     new RefcountedResource<EncapSocketRecord>(
-                            new EncapSocketRecord(resourceId, sockFd, port), binder));
-            return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
+                            new EncapSocketRecord(resourceId, pFd.getFileDescriptor(), port),
+                            binder));
+            return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port,
+                    pFd.getFileDescriptor());
         } catch (IOException | ErrnoException e) {
-            IoUtils.closeQuietly(sockFd);
+            try {
+                if (pFd != null) {
+                    pFd.close();
+                }
+            } catch (IOException ex) {
+                // Nothing can be done at this point
+                Log.e(TAG, "Failed to close pFd.");
+            }
         }
         // If we make it to here, then something has gone wrong and we couldn't open a socket.
         // The only reasonable condition that would cause that is resource unavailable.
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index e6433db..bb123a3 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -24,19 +24,19 @@
 
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.INetd;
 import android.net.NetworkStats;
 import android.net.UnderlyingNetworkInfo;
-import android.net.util.NetdService;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.SystemClock;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ProcFileReader;
+import com.android.net.module.util.CollectionUtils;
 
 import libcore.io.IoUtils;
 
@@ -70,7 +70,7 @@
 
     private final boolean mUseBpfStats;
 
-    private INetd mNetdService;
+    private final INetd mNetd;
 
     /**
      * Guards persistent data access in this class
@@ -158,12 +158,12 @@
         NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
     }
 
-    public NetworkStatsFactory() {
-        this(new File("/proc/"), true);
+    public NetworkStatsFactory(@NonNull INetd netd) {
+        this(new File("/proc/"), true, netd);
     }
 
     @VisibleForTesting
-    public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
+    public NetworkStatsFactory(File procRoot, boolean useBpfStats, @NonNull INetd netd) {
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
         mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
@@ -172,6 +172,7 @@
             mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
             mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
         }
+        mNetd = netd;
     }
 
     public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -298,10 +299,7 @@
         // Ask netd to do a active map stats swap. When the binder call successfully returns,
         // the system server should be able to safely read and clean the inactive map
         // without race problem.
-        if (mNetdService == null) {
-            mNetdService = NetdService.getInstance();
-        }
-        mNetdService.trafficSwapActiveStatsMap();
+        mNetd.trafficSwapActiveStatsMap();
     }
 
     /**
@@ -434,7 +432,7 @@
                 entry.txBytes = reader.nextLong();
                 entry.txPackets = reader.nextLong();
 
-                if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
+                if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
                         && (limitUid == UID_ALL || limitUid == entry.uid)
                         && (limitTag == TAG_ALL || limitTag == entry.tag)) {
                     stats.insertEntry(entry);
diff --git a/service-t/src/com/android/server/net/NetworkStatsObservers.java b/service-t/src/com/android/server/net/NetworkStatsObservers.java
index 1a0866d..b57a4f9 100644
--- a/service-t/src/com/android/server/net/NetworkStatsObservers.java
+++ b/service-t/src/com/android/server/net/NetworkStatsObservers.java
@@ -18,8 +18,6 @@
 
 import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
 
-import static com.android.internal.util.Preconditions.checkArgument;
-
 import android.app.usage.NetworkStatsManager;
 import android.net.DataUsageRequest;
 import android.net.NetworkIdentitySet;
@@ -38,7 +36,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +81,7 @@
         RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
                 accessLevel);
 
-        if (LOGV) Slog.v(TAG, "Registering observer for " + request);
+        if (LOGV) Log.v(TAG, "Registering observer for " + request);
         getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
         return request;
     }
@@ -116,7 +114,7 @@
         if (mHandler == null) {
             synchronized (this) {
                 if (mHandler == null) {
-                    if (LOGV) Slog.v(TAG, "Creating handler");
+                    if (LOGV) Log.v(TAG, "Creating handler");
                     mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
                 }
             }
@@ -172,15 +170,15 @@
         RequestInfo requestInfo;
         requestInfo = mDataUsageRequests.get(request.requestId);
         if (requestInfo == null) {
-            if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
+            if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
             return;
         }
         if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
-            Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
+            Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
             return;
         }
 
-        if (LOGV) Slog.v(TAG, "Unregistering " + request);
+        if (LOGV) Log.v(TAG, "Unregistering " + request);
         mDataUsageRequests.remove(request.requestId);
         requestInfo.unlinkDeathRecipient();
         requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -201,7 +199,7 @@
         // Cap the minimum threshold to a safe default to avoid too many callbacks
         long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
         if (thresholdInBytes < request.thresholdInBytes) {
-            Slog.w(TAG, "Threshold was too low for " + request
+            Log.w(TAG, "Threshold was too low for " + request
                     + ". Overriding to a safer default of " + thresholdInBytes + " bytes");
         }
         return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
@@ -216,7 +214,10 @@
                     accessLevel);
         } else {
             // Safety check in case a new access level is added and we forgot to update this
-            checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY);
+            if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
+                throw new IllegalArgumentException(
+                        "accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
+            }
             return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
                     accessLevel);
         }
@@ -255,8 +256,9 @@
 
         @Override
         public void binderDied() {
-            if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
-                    + mRequest + ", " + mBinder + ")");
+            if (LOGV) {
+                Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
+            }
             mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
             callCallback(NetworkStatsManager.CALLBACK_RELEASED);
         }
@@ -299,13 +301,13 @@
             msg.setData(bundle);
             try {
                 if (LOGV) {
-                    Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+                    Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
                             + " for " + mRequest);
                 }
                 mMessenger.send(msg);
             } catch (RemoteException e) {
                 // May occur naturally in the race of binder death.
-                Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
+                Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
             }
         }
 
@@ -341,7 +343,7 @@
         protected boolean checkStats() {
             long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
             if (LOGV) {
-                Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
+                Log.v(TAG, bytesSoFar + " bytes so far since notification for "
                         + mRequest.template);
             }
             if (bytesSoFar > mRequest.thresholdInBytes) {
@@ -416,7 +418,7 @@
                 return history.getTotalBytes();
             } catch (SecurityException e) {
                 if (LOGV) {
-                    Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+                    Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
                             + uid);
                 }
                 return 0;
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index 5e27c77..c371f08 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -32,15 +32,12 @@
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.service.NetworkStatsRecorderProto;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Sets;
+import com.android.net.module.util.NetworkStatsUtils;
 
 import libcore.io.IoUtils;
 
@@ -132,8 +129,8 @@
     }
 
     public void setPersistThreshold(long thresholdBytes) {
-        if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
-        mPersistThresholdBytes = MathUtils.constrain(
+        if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
+        mPersistThresholdBytes = NetworkStatsUtils.constrain(
                 thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
     }
 
@@ -185,7 +182,7 @@
     }
 
     private NetworkStatsCollection loadLocked(long start, long end) {
-        if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
+        if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
         final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
         try {
             mRotator.readMatching(res, start, end);
@@ -207,7 +204,7 @@
      */
     public void recordSnapshotLocked(NetworkStats snapshot,
             Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
-        final HashSet<String> unknownIfaces = Sets.newHashSet();
+        final HashSet<String> unknownIfaces = new HashSet<>();
 
         // skip recording when snapshot missing
         if (snapshot == null) return;
@@ -272,7 +269,7 @@
         mLastSnapshot = snapshot;
 
         if (LOGV && unknownIfaces.size() > 0) {
-            Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+            Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
         }
     }
 
@@ -296,7 +293,7 @@
     public void forcePersistLocked(long currentTimeMillis) {
         Objects.requireNonNull(mRotator, "missing FileRotator");
         if (mPending.isDirty()) {
-            if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+            if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
             try {
                 mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
                 mRotator.maybeRotate(currentTimeMillis);
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 2beca73..0abc523 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -25,7 +25,6 @@
 import static android.content.Intent.EXTRA_UID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
-import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.IFACE_VT;
@@ -43,9 +42,9 @@
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
-import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.net.TrafficStats.UNSUPPORTED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
@@ -72,7 +71,7 @@
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
@@ -90,13 +89,14 @@
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.DataUsageRequest;
-import android.net.INetworkManagementEventObserver;
+import android.net.INetd;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
@@ -107,13 +107,14 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
+import android.net.TetheringManager;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
 import android.net.Uri;
 import android.net.netstats.provider.INetworkStatsProvider;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.net.netstats.provider.NetworkStatsProvider;
-import android.os.BestClock;
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.os.Environment;
@@ -121,12 +122,12 @@
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -141,28 +142,29 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BestClock;
 import com.android.net.module.util.BinderUtils;
-import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
+import com.android.net.module.util.PermissionUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.time.Clock;
 import java.time.ZoneOffset;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -206,8 +208,15 @@
 
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+      */
+    private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+    private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
     private final Context mContext;
-    private final INetworkManagementService mNetworkManager;
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
     private final Clock mClock;
@@ -222,6 +231,9 @@
     private final ContentObserver mContentObserver;
     private final ContentResolver mContentResolver;
 
+    protected INetd mNetd;
+    private final AlertObserver mAlertObserver = new AlertObserver();
+
     @VisibleForTesting
     public static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
@@ -404,19 +416,20 @@
         }
     }
 
-    public static NetworkStatsService create(Context context,
-                INetworkManagementService networkManager) {
+    /** Creates a new NetworkStatsService */
+    public static NetworkStatsService create(Context context) {
         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
-        final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+        final INetd netd = INetd.Stub.asInterface(
+                (IBinder) context.getSystemService(Context.NETD_SERVICE));
+        final NetworkStatsService service = new NetworkStatsService(context,
+                INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
                 alarmManager, wakeLock, getDefaultClock(),
-                new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
+                new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
-        service.registerLocalService();
 
         return service;
     }
@@ -424,14 +437,12 @@
     // This must not be called outside of tests, even within the same package, as this constructor
     // does not register the local service. Use the create() helper above.
     @VisibleForTesting
-    NetworkStatsService(Context context, INetworkManagementService networkManager,
-            AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
-            NetworkStatsSettings settings, NetworkStatsFactory factory,
-            NetworkStatsObservers statsObservers, File systemDir, File baseDir,
-            @NonNull Dependencies deps) {
+    NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager,
+            PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings,
+            NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+            File baseDir, @NonNull Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing Context");
-        mNetworkManager = Objects.requireNonNull(networkManager,
-                "missing INetworkManagementService");
+        mNetd = Objects.requireNonNull(netd, "missing Netd");
         mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
         mClock = Objects.requireNonNull(clock, "missing Clock");
         mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
@@ -446,7 +457,7 @@
         handlerThread.start();
         mHandler = new NetworkStatsHandler(handlerThread.getLooper());
         mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
-                mHandler.getLooper(), new HandlerExecutor(mHandler), this);
+                new HandlerExecutor(mHandler), this);
         mContentResolver = mContext.getContentResolver();
         mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
                 mNetworkStatsSubscriptionsMonitor);
@@ -472,11 +483,10 @@
          */
         @NonNull
         public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
-                @NonNull Looper looper, @NonNull Executor executor,
-                @NonNull NetworkStatsService service) {
+                @NonNull Executor executor, @NonNull NetworkStatsService service) {
             // TODO: Update RatType passively in NSS, instead of querying into the monitor
             //  when notifyNetworkStatus.
-            return new NetworkStatsSubscriptionsMonitor(context, looper, executor,
+            return new NetworkStatsSubscriptionsMonitor(context, executor,
                     (subscriberId, type) -> service.handleOnCollapsedRatTypeChanged());
         }
 
@@ -500,9 +510,24 @@
         }
     }
 
-    private void registerLocalService() {
-        LocalServices.addService(NetworkStatsManagerInternal.class,
-                new NetworkStatsManagerInternalImpl());
+    /**
+     * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
+     */
+    @VisibleForTesting
+    public class AlertObserver extends BaseNetdUnsolicitedEventListener {
+        @Override
+        public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) {
+            PermissionUtils.enforceNetworkStackPermission(mContext);
+
+            if (LIMIT_GLOBAL_ALERT.equals(alertName)) {
+                // kick off background poll to collect network stats unless there is already
+                // such a call pending; UID stats are handled during normal polling interval.
+                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+                            mSettings.getPollDelay());
+                }
+            }
+        }
     }
 
     public void systemReady() {
@@ -529,8 +554,9 @@
         }
 
         // watch for tethering changes
-        final IntentFilter tetherFilter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
-        mContext.registerReceiver(mTetherReceiver, tetherFilter, null, mHandler);
+        final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+        tetheringManager.registerTetheringEventCallback(
+                new HandlerExecutor(mHandler), mTetherListener);
 
         // listen for periodic polling events
         final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -549,9 +575,9 @@
         mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
 
         try {
-            mNetworkManager.registerObserver(mAlertObserver);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
+            mNetd.registerUnsolicitedEventListener(mAlertObserver);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.wtf(TAG, "Error registering event listener :", e);
         }
 
         //  schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
@@ -586,7 +612,8 @@
 
     @GuardedBy("mStatsLock")
     private void shutdownLocked() {
-        mContext.unregisterReceiver(mTetherReceiver);
+        final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
+        tetheringManager.unregisterTetheringEventCallback(mTetherListener);
         mContext.unregisterReceiver(mPollReceiver);
         mContext.unregisterReceiver(mRemovedReceiver);
         mContext.unregisterReceiver(mUserReceiver);
@@ -638,15 +665,15 @@
     }
 
     /**
-     * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+     * Register for a global alert that is delivered through {@link AlertObserver}
      * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
      * been transferred.
      */
     private void registerGlobalAlert() {
         try {
-            mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
+            mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
-            Slog.w(TAG, "problem registering for global alert: " + e);
+            Log.w(TAG, "problem registering for global alert: " + e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
@@ -655,8 +682,6 @@
 
     @Override
     public INetworkStatsSession openSession() {
-        // NOTE: if callers want to get non-augmented data, they should go
-        // through the public API
         return openSessionInternal(NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, null);
     }
 
@@ -682,12 +707,25 @@
         return now - lastCallTime < POLL_RATE_LIMIT_MS;
     }
 
-    private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+    private int restrictFlagsForCaller(int flags) {
+        // All non-privileged callers are not allowed to turn off POLL_ON_OPEN.
+        final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                android.Manifest.permission.NETWORK_STACK);
+        if (!isPrivileged) {
+            flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
+        }
+        // Non-system uids are rate limited for POLL_ON_OPEN.
         final int callingUid = Binder.getCallingUid();
-        final int usedFlags = isRateLimitedForPoll(callingUid)
+        flags = isRateLimitedForPoll(callingUid)
                 ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
                 : flags;
-        if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+        return flags;
+    }
+
+    private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+        final int restrictedFlags = restrictFlagsForCaller(flags);
+        if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
                 | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -701,7 +739,7 @@
         // for its lifetime; when caller closes only weak references remain.
 
         return new INetworkStatsSession.Stub() {
-            private final int mCallingUid = callingUid;
+            private final int mCallingUid = Binder.getCallingUid();
             private final String mCallingPackage = callingPackage;
             private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel(
                     callingPackage);
@@ -735,21 +773,21 @@
             @Override
             public NetworkStats getDeviceSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
-                        mCallingUid);
+                return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+                        mAccessLevel, mCallingUid);
             }
 
             @Override
             public NetworkStats getSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
-                        mCallingUid);
+                return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+                        mAccessLevel, mCallingUid);
             }
 
             @Override
             public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
-                return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel,
-                        mCallingUid);
+                return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+                        mAccessLevel, mCallingUid);
             }
 
             @Override
@@ -765,8 +803,18 @@
                     }
                     return stats;
                 } catch (NullPointerException e) {
-                    // TODO: Track down and fix the cause of this crash and remove this catch block.
-                    Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    throw e;
+                }
+            }
+
+            @Override
+            public NetworkStats getTaggedSummaryForAllUid(
+                    NetworkTemplate template, long start, long end) {
+                try {
+                    final NetworkStats tagStats = getUidTagComplete()
+                            .getSummary(template, start, end, mAccessLevel, mCallingUid);
+                    return tagStats;
+                } catch (NullPointerException e) {
                     throw e;
                 }
             }
@@ -811,7 +859,7 @@
 
     private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
         return NetworkStatsAccess.checkAccessLevel(
-                mContext, Binder.getCallingUid(), callingPackage);
+                mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
     }
 
     /**
@@ -823,15 +871,15 @@
         SubscriptionPlan plan = null;
         if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
                 && mSettings.getAugmentEnabled()) {
-            if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
+            if (LOGD) Log.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
-                plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+                plan = mContext.getSystemService(NetworkPolicyManager.class)
                         .getSubscriptionPlan(template);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
-            if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
+            if (LOGD) Log.d(TAG, "Resolved to plan " + plan);
         }
         return plan;
     }
@@ -876,8 +924,6 @@
     private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
         assertSystemReady();
 
-        // NOTE: if callers want to get non-augmented data, they should go
-        // through the public API
         return internalGetSummaryForNetwork(template,
                 NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, start, end,
                 NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotalBytes();
@@ -923,6 +969,7 @@
 
     @Override
     public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         try {
             final String[] ifacesToQuery =
                     mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
@@ -936,7 +983,7 @@
     @Override
     public String[] getMobileIfaces() {
         // TODO (b/192758557): Remove debug log.
-        if (ArrayUtils.contains(mMobileIfaces, null)) {
+        if (CollectionUtils.contains(mMobileIfaces, null)) {
             throw new NullPointerException(
                     "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
         }
@@ -966,7 +1013,8 @@
     }
 
     @VisibleForTesting
-    void setUidForeground(int uid, boolean uidForeground) {
+    public void setUidForeground(int uid, boolean uidForeground) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
             final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -985,7 +1033,7 @@
             @NonNull NetworkStateSnapshot[] networkStates,
             @Nullable String activeIface,
             @NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) {
-        checkNetworkStackPermission(mContext);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1002,7 +1050,7 @@
 
     @Override
     public void forceUpdate() {
-        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1012,11 +1060,14 @@
         }
     }
 
-    private void advisePersistThreshold(long thresholdBytes) {
+    /** Advise persistence threshold; may be overridden internally. */
+    public void advisePersistThreshold(long thresholdBytes) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         // clamp threshold into safe range
-        mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
+        mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
+                128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
         if (LOGV) {
-            Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+            Log.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
                     + mPersistThreshold);
         }
 
@@ -1153,14 +1204,15 @@
     }
 
     /**
-     * Receiver that watches for {@link Tethering} to claim interface pairs.
+     * Listener that watches for {@link TetheringManager} to claim interface pairs.
      */
-    private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            performPoll(FLAG_PERSIST_NETWORK);
-        }
-    };
+    private final TetheringManager.TetheringEventCallback mTetherListener =
+            new TetheringManager.TetheringEventCallback() {
+                @Override
+                public void onUpstreamChanged(@Nullable Network network) {
+                    performPoll(FLAG_PERSIST_NETWORK);
+                }
+            };
 
     private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
         @Override
@@ -1200,13 +1252,13 @@
             // On background handler thread, and USER_REMOVED is protected
             // broadcast.
 
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            if (userId == -1) return;
+            final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+            if (userHandle == null) return;
 
             synchronized (mStatsLock) {
                 mWakeLock.acquire();
                 try {
-                    removeUserLocked(userId);
+                    removeUserLocked(userHandle);
                 } finally {
                     mWakeLock.release();
                 }
@@ -1225,26 +1277,6 @@
     };
 
     /**
-     * Observer that watches for {@link INetworkManagementService} alerts.
-     */
-    private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
-        @Override
-        public void limitReached(String limitName, String iface) {
-            // only someone like NMS should be calling us
-            NetworkStack.checkNetworkStackPermission(mContext);
-
-            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
-                // kick off background poll to collect network stats unless there is already
-                // such a call pending; UID stats are handled during normal polling interval.
-                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
-                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
-                            mSettings.getPollDelay());
-                }
-            }
-        }
-    };
-
-    /**
      * Handle collapsed RAT type changed event.
      */
     @VisibleForTesting
@@ -1279,7 +1311,7 @@
     private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
             @NonNull NetworkStateSnapshot[] snapshots) {
         if (!mSystemReady) return;
-        if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
+        if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
 
         // take one last stats snapshot before updating iface mapping. this
         // isn't perfect, since the kernel may already be counting traffic from
@@ -1303,7 +1335,8 @@
             final int displayTransport =
                     getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
             final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
-            final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
+            final boolean isDefault = CollectionUtils.contains(
+                    mDefaultNetworks, snapshot.getNetwork());
             final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
                     : getSubTypeForStateSnapshot(snapshot);
             final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -1386,7 +1419,7 @@
 
         mMobileIfaces = mobileIfaces.toArray(new String[0]);
         // TODO (b/192758557): Remove debug log.
-        if (ArrayUtils.contains(mMobileIfaces, null)) {
+        if (CollectionUtils.contains(mMobileIfaces, null)) {
             throw new NullPointerException(
                     "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
         }
@@ -1401,7 +1434,7 @@
         if (spec instanceof TelephonyNetworkSpecifier) {
              return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
         } else {
-            Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+            Log.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
             return INVALID_SUBSCRIPTION_ID;
         }
     }
@@ -1485,7 +1518,7 @@
         try {
             recordSnapshotLocked(currentTime);
         } catch (IllegalStateException e) {
-            Slog.w(TAG, "problem reading network stats: " + e);
+            Log.w(TAG, "problem reading network stats: " + e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
@@ -1510,7 +1543,7 @@
     @GuardedBy("mStatsLock")
     private void performPollLocked(int flags) {
         if (!mSystemReady) return;
-        if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
+        if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
         Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
 
         final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
@@ -1608,7 +1641,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsMobileSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1620,7 +1653,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsWifiSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1632,7 +1665,7 @@
      */
     @GuardedBy("mStatsLock")
     private void removeUidsLocked(int... uids) {
-        if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
+        if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
 
         // Perform one last poll before removing
         performPollLocked(FLAG_PERSIST_ALL);
@@ -1650,73 +1683,40 @@
      * Clean up {@link #mUidRecorder} after user is removed.
      */
     @GuardedBy("mStatsLock")
-    private void removeUserLocked(int userId) {
-        if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
+    private void removeUserLocked(@NonNull UserHandle userHandle) {
+        if (LOGV) Log.v(TAG, "removeUserLocked() for UserHandle=" + userHandle);
 
         // Build list of UIDs that we should clean up
-        int[] uids = new int[0];
+        final ArrayList<Integer> uids = new ArrayList<>();
         final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
                 PackageManager.MATCH_ANY_USER
                 | PackageManager.MATCH_DISABLED_COMPONENTS);
         for (ApplicationInfo app : apps) {
-            final int uid = UserHandle.getUid(userId, app.uid);
-            uids = ArrayUtils.appendInt(uids, uid);
+            final int uid = userHandle.getUid(app.uid);
+            uids.add(uid);
         }
 
-        removeUidsLocked(uids);
+        removeUidsLocked(CollectionUtils.toIntArray(uids));
     }
 
-    private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
-        @Override
-        public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
-            try {
-                return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+    public void setStatsProviderWarningAndLimitAsync(
+            @NonNull String iface, long warning, long limit) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (LOGV) {
+            Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                    + iface + "," + warning + "," + limit + ")");
         }
-
-        @Override
-        public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
-            try {
-                return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
-        }
-
-        @Override
-        public void setUidForeground(int uid, boolean uidForeground) {
-            NetworkStatsService.this.setUidForeground(uid, uidForeground);
-        }
-
-        @Override
-        public void advisePersistThreshold(long thresholdBytes) {
-            NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
-        }
-
-        @Override
-        public void forceUpdate() {
-            NetworkStatsService.this.forceUpdate();
-        }
-
-        @Override
-        public void setStatsProviderWarningAndLimitAsync(
-                @NonNull String iface, long warning, long limit) {
-            if (LOGV) {
-                Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
-                        + iface + "," + warning + "," + limit + ")");
-            }
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
-                    warning, limit));
-        }
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+                warning, limit));
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
+        if (!PermissionUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
 
         long duration = DateUtils.DAY_IN_MILLIS;
         final HashSet<String> argSet = new HashSet<String>();
@@ -1777,15 +1777,15 @@
 
             pw.println("Configs:");
             pw.increaseIndent();
-            pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
+            pw.print(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
             pw.println();
             pw.decreaseIndent();
 
             pw.println("Active interfaces:");
             pw.increaseIndent();
             for (int i = 0; i < mActiveIfaces.size(); i++) {
-                pw.printPair("iface", mActiveIfaces.keyAt(i));
-                pw.printPair("ident", mActiveIfaces.valueAt(i));
+                pw.print("iface", mActiveIfaces.keyAt(i));
+                pw.print("ident", mActiveIfaces.valueAt(i));
                 pw.println();
             }
             pw.decreaseIndent();
@@ -1793,8 +1793,8 @@
             pw.println("Active UID interfaces:");
             pw.increaseIndent();
             for (int i = 0; i < mActiveUidIfaces.size(); i++) {
-                pw.printPair("iface", mActiveUidIfaces.keyAt(i));
-                pw.printPair("ident", mActiveUidIfaces.valueAt(i));
+                pw.print("iface", mActiveUidIfaces.keyAt(i));
+                pw.print("ident", mActiveUidIfaces.valueAt(i));
                 pw.println();
             }
             pw.decreaseIndent();
@@ -1867,7 +1867,7 @@
 
     @GuardedBy("mStatsLock")
     private void dumpProtoLocked(FileDescriptor fd) {
-        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));
 
         // TODO Right now it writes all history.  Should it limit to the "since-boot" log?
 
@@ -1954,13 +1954,29 @@
      */
     // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
     //  tethering stats.
-    private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+    private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+         // We only need to return per-UID stats. Per-device stats are already counted by
+        // interface counters.
+        if (how != STATS_PER_UID) {
+            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
+        }
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
         try {
-            return mNetworkManager.getNetworkStatsTethering(how);
+            final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats();
+            for (TetherStatsParcel tetherStats : tetherStatsParcels) {
+                try {
+                    stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
+                            SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+                            tetherStats.txBytes, tetherStats.txPackets, 0L));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    throw new IllegalStateException("invalid tethering stats " + e);
+                }
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem reading network stats", e);
-            return new NetworkStats(0L, 10);
         }
+        return stats;
     }
 
     // TODO: It is copied from ConnectivityService, consider refactor these check permission
@@ -1999,10 +2015,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
+        final NetworkPolicyManager netPolicyManager = mContext
+                .getSystemService(NetworkPolicyManager.class);
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
                     tag, provider, mStatsProviderSem, mAlertObserver,
-                    mStatsProviderCbList);
+                    mStatsProviderCbList, netPolicyManager);
             mStatsProviderCbList.add(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
@@ -2042,8 +2060,9 @@
 
         @NonNull final INetworkStatsProvider mProvider;
         @NonNull private final Semaphore mSemaphore;
-        @NonNull final INetworkManagementEventObserver mAlertObserver;
+        @NonNull final AlertObserver mAlertObserver;
         @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+        @NonNull final NetworkPolicyManager mNetworkPolicyManager;
 
         @NonNull private final Object mProviderStatsLock = new Object();
 
@@ -2056,8 +2075,9 @@
         NetworkStatsProviderCallbackImpl(
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
                 @NonNull Semaphore semaphore,
-                @NonNull INetworkManagementEventObserver alertObserver,
-                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+                @NonNull AlertObserver alertObserver,
+                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+                @NonNull NetworkPolicyManager networkPolicyManager)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
@@ -2065,6 +2085,7 @@
             mSemaphore = semaphore;
             mAlertObserver = alertObserver;
             mStatsProviderCbList = cbList;
+            mNetworkPolicyManager = networkPolicyManager;
         }
 
         @NonNull
@@ -2104,15 +2125,14 @@
             // This binder object can only have been obtained by a process that holds
             // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
             BinderUtils.withCleanCallingIdentity(() ->
-                    mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
+                    mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
         }
 
         @Override
         public void notifyWarningOrLimitReached() {
             Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    LocalServices.getService(NetworkPolicyManagerInternal.class)
-                            .onStatsProviderWarningOrLimitReached(mTag));
+                    mNetworkPolicyManager.notifyStatsProviderWarningOrLimitReached());
         }
 
         @Override
diff --git a/service-t/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/service-t/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 5646c75..4875f1c 100644
--- a/service-t/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/service-t/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -18,22 +18,23 @@
 
 import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA;
 import static android.net.NetworkTemplate.getCollapsedRatType;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
+import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.os.Looper;
 import android.telephony.Annotation;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -64,7 +65,7 @@
     private final Delegate mDelegate;
 
     /**
-     * Receivers that watches for {@link ServiceState} changes for each subscription, to
+     * Receivers that watches for {@link TelephonyDisplayInfo} changes for each subscription, to
      * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
      */
     @NonNull
@@ -79,9 +80,9 @@
     @NonNull
     private final Executor mExecutor;
 
-    NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper,
+    NetworkStatsSubscriptionsMonitor(@NonNull Context context,
             @NonNull Executor executor, @NonNull Delegate delegate) {
-        super(looper);
+        super();
         mSubscriptionManager = (SubscriptionManager) context.getSystemService(
                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
         mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -99,35 +100,36 @@
         // prevent binder call to telephony when querying RAT. Keep listener registration with empty
         // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
         // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
-        final List<Pair<Integer, String>> filteredNewSubs =
-                CollectionUtils.mapNotNull(newSubs, subId -> {
-                    final String subscriberId = mTeleManager.getSubscriberId(subId);
-                    return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
-                });
+        final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
+        for (final int subId : newSubs) {
+            final String subscriberId =
+                    mTeleManager.createForSubscriptionId(subId).getSubscriberId();
+            if (!TextUtils.isEmpty(subscriberId)) {
+                filteredNewSubs.add(new Pair(subId, subscriberId));
+            }
+        }
 
         for (final Pair<Integer, String> sub : filteredNewSubs) {
             // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
             // suddenly change regardless of subId, such as switch IMSI feature in modem side.
             // If that happens, register new listener with new IMSI and remove old one later.
-            if (CollectionUtils.find(mRatListeners,
-                    it -> it.equalsKey(sub.first, sub.second)) != null) {
+            if (CollectionUtils.any(mRatListeners, it -> it.equalsKey(sub.first, sub.second))) {
                 continue;
             }
 
-            final RatTypeListener listener =
-                    new RatTypeListener(mExecutor, this, sub.first, sub.second);
+            final RatTypeListener listener = new RatTypeListener(this, sub.first, sub.second);
             mRatListeners.add(listener);
 
             // Register listener to the telephony manager that associated with specific sub.
             mTeleManager.createForSubscriptionId(sub.first)
-                    .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+                    .registerTelephonyCallback(mExecutor, listener);
             Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
         }
 
         for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
             // If there is no subId and IMSI matched the listener, removes it.
-            if (CollectionUtils.find(filteredNewSubs,
-                    it -> listener.equalsKey(it.first, it.second)) == null) {
+            if (!CollectionUtils.any(filteredNewSubs,
+                    it -> listener.equalsKey(it.first, it.second))) {
                 handleRemoveRatTypeListener(listener);
             }
         }
@@ -148,9 +150,10 @@
      * @return collapsed RatType for the given subscriberId
      */
     public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
-        final RatTypeListener match = CollectionUtils.find(mRatListeners,
+        final int index = CollectionUtils.indexOf(mRatListeners,
                 it -> TextUtils.equals(subscriberId, it.mSubscriberId));
-        return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        return index != -1 ? mRatListeners.get(index).mLastCollapsedRatType
+                : TelephonyManager.NETWORK_TYPE_UNKNOWN;
     }
 
     /**
@@ -173,7 +176,7 @@
 
     private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
         mTeleManager.createForSubscriptionId(listener.mSubId)
-                .listen(listener, PhoneStateListener.LISTEN_NONE);
+                .unregisterTelephonyCallback(listener);
         Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
         mRatListeners.remove(listener);
 
@@ -183,7 +186,8 @@
                 listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
     }
 
-    static class RatTypeListener extends PhoneStateListener {
+    static class RatTypeListener extends TelephonyCallback
+            implements TelephonyCallback.DisplayInfoListener {
         // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
         @NonNull
         private final int mSubId;
@@ -197,29 +201,27 @@
         @NonNull
         private final NetworkStatsSubscriptionsMonitor mMonitor;
 
-        RatTypeListener(@NonNull Executor executor,
-                @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+        RatTypeListener(@NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
                 @NonNull String subscriberId) {
-            super(executor);
             mSubId = subId;
             mSubscriberId = subscriberId;
             mMonitor = monitor;
         }
 
         @Override
-        public void onServiceStateChanged(@NonNull ServiceState ss) {
+        public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
             // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
             // would report RAT = 5G_NR.
             // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
             // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
             // NR state as connected. In such case, attributes the data usage to NR.
             // See b/160727498.
-            final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
-                    || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
-                    && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+            final boolean is5GNsa = displayInfo.getNetworkType() == NETWORK_TYPE_LTE
+                    && (displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_NSA
+                    || displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
 
             final int networkType =
-                    (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType());
+                    (is5GNsa ? NETWORK_TYPE_5G_NSA : displayInfo.getNetworkType());
             final int collapsedRatType = getCollapsedRatType(networkType);
             if (collapsedRatType == mLastCollapsedRatType) return;