Merge "[DU05]Remove the android.provider.Settings.Global in NetworkStatsService"
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index 90bb76a..d3d8bba 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -156,8 +156,18 @@
         ":framework-connectivity-ethernet-sources",
         ":framework-connectivity-ipsec-sources",
         ":framework-connectivity-netstats-sources",
+    ],
+    visibility: ["//frameworks/base"],
+}
+
+filegroup {
+    name: "framework-connectivity-tiramisu-updatable-sources",
+    srcs: [
         ":framework-connectivity-nsd-sources",
         ":framework-connectivity-tiramisu-internal-sources",
     ],
-    visibility: ["//frameworks/base"],
+    visibility: [
+        "//frameworks/base",
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
 }
diff --git a/framework-t/src/android/app/usage/NetworkStats.java b/framework-t/src/android/app/usage/NetworkStats.java
index d33666d..2b6570a 100644
--- a/framework-t/src/android/app/usage/NetworkStats.java
+++ b/framework-t/src/android/app/usage/NetworkStats.java
@@ -556,7 +556,7 @@
     /**
      * Collects history results for uid and resets history enumeration index.
      */
-    void startHistoryEnumeration(int uid, int tag, int state) {
+    void startHistoryUidEnumeration(int uid, int tag, int state) {
         mHistory = null;
         try {
             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -571,6 +571,20 @@
     }
 
     /**
+     * Collects history results for network and resets history enumeration index.
+     */
+    void startHistoryDeviceEnumeration() {
+        try {
+            mHistory = mSession.getHistoryIntervalForNetwork(
+                    mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+            mHistory = null;
+        }
+        mEnumerationIndex = 0;
+    }
+
+    /**
      * Starts uid enumeration for current user.
      * @throws RemoteException
      */
diff --git a/framework-t/src/android/app/usage/NetworkStatsManager.java b/framework-t/src/android/app/usage/NetworkStatsManager.java
index f74edb1..4b906c9 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;
@@ -150,7 +151,13 @@
      * @param pollOnOpen true if poll is needed.
      * @hide
      */
-    // @SystemApi(client = MODULE_LIBRARIES)
+    // 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;
@@ -427,6 +434,42 @@
     }
 
     /**
+     * Query usage statistics details for networks matching a given {@link NetworkTemplate}.
+     *
+     * Result is not aggregated over time. This means buckets' start and
+     * end timestamps will be between 'startTime' and 'endTime' parameters.
+     * <p>Only includes buckets whose entire time period is included between
+     * startTime and endTime. Doesn't interpolate or return partial buckets.
+     * Since bucket length is in the order of hours, this
+     * method cannot be used to measure data usage on a fine grained time scale.
+     * 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 queryDetailsForDevice(@NonNull NetworkTemplate template,
+            long startTime, long endTime) {
+        try {
+            final NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startHistoryDeviceEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+
+        return null; // To make the compiler happy.
+    }
+
+    /**
      * Query network usage statistics details for a given uid.
      * This may take a long time, and apps should avoid calling this on their main thread.
      *
@@ -492,7 +535,8 @@
      * @param endTime End of period. Defined in terms of "Unix time", see
      *            {@link java.lang.System#currentTimeMillis}.
      * @param uid UID of app
-     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
+     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+     *            across all the tags.
      * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
      *            traffic from all states.
      * @return Statistics object or null if an error happened during statistics collection.
@@ -507,21 +551,51 @@
         return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
     }
 
-    /** @hide */
-    public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
+    /**
+     * Query network usage statistics details for a given template, uid, tag, and state.
+     *
+     * Only usable for uids belonging to calling user. Result is not aggregated over time.
+     * This means buckets' start and end timestamps are going to be between 'startTime' and
+     * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+     * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
+     * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+     * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
+     * interpolate across partial buckets. Since bucket length is in the order of hours, this
+     * method cannot be used to measure data usage on a fine grained time scale.
+     * 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}.
+     * @param uid UID of app
+     * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+     *            across all the tags.
+     * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
+     *            traffic from all states.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    // @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryDetailsForUidTagState(@NonNull NetworkTemplate template,
             long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
-
-        NetworkStats result;
         try {
-            result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
-            result.startHistoryEnumeration(uid, tag, state);
+            final NetworkStats result = new NetworkStats(
+                    mContext, template, mFlags, startTime, endTime, mService);
+            result.startHistoryUidEnumeration(uid, tag, state);
+            return result;
         } catch (RemoteException e) {
             Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
                     + " state=" + state, e);
-            return null;
+            e.rethrowFromSystemServer();
         }
 
-        return result;
+        return null; // To make the compiler happy.
     }
 
     /**
@@ -877,7 +951,7 @@
      *
      * @hide
      */
-    // @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
@@ -890,17 +964,18 @@
     }
 
     /**
-     * Advise persistence threshold; may be overridden internally.
+     * Set default value of global alert bytes, the value will be clamped to [128kB, 2MB].
      *
      * @hide
      */
-    // @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_STACK})
-    public void advisePersistThreshold(long thresholdBytes) {
+            Manifest.permission.NETWORK_STACK})
+    public void setDefaultGlobalAlert(long alertBytes) {
         try {
-            mService.advisePersistThreshold(thresholdBytes);
+            // TODO: Sync internal naming with the API surface.
+            mService.advisePersistThreshold(alertBytes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -911,7 +986,7 @@
      *
      * @hide
      */
-    // @SystemApi
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
@@ -927,9 +1002,17 @@
      * 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
+    @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(anyOf = {
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
             android.Manifest.permission.NETWORK_STACK})
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/INetworkStatsSession.aidl b/framework-t/src/android/net/INetworkStatsSession.aidl
index babe0bf..ab70be8 100644
--- a/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -32,6 +32,11 @@
     /** Return historical network layer stats for traffic that matches template. */
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
+    /**
+     * Return historical network layer stats for traffic that matches template, start and end
+     * timestamp.
+     */
+    NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end);
 
     /**
      * Return network layer usage summary per UID for traffic that matches template.
diff --git a/framework-t/src/android/net/IpSecManager.java b/framework-t/src/android/net/IpSecManager.java
index 49aa99b..a423783 100644
--- a/framework-t/src/android/net/IpSecManager.java
+++ b/framework-t/src/android/net/IpSecManager.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -988,6 +989,29 @@
     }
 
     /**
+     * @hide
+     */
+    public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder,
+            String callingPackage) {
+        try {
+            return mService.createTransform(config, binder, callingPackage);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void deleteTransform(int resourceId) {
+        try {
+            mService.deleteTransform(resourceId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Construct an instance of IpSecManager within an application context.
      *
      * @param context the application context for this manager
diff --git a/framework-t/src/android/net/IpSecTransform.java b/framework-t/src/android/net/IpSecTransform.java
index 36199a0..68ae5de 100644
--- a/framework-t/src/android/net/IpSecTransform.java
+++ b/framework-t/src/android/net/IpSecTransform.java
@@ -26,9 +26,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.util.Log;
 
@@ -93,16 +90,9 @@
         mResourceId = INVALID_RESOURCE_ID;
     }
 
-    private IIpSecService getIpSecService() {
-        IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
-        if (b == null) {
-            throw new RemoteException("Failed to connect to IpSecService")
-                    .rethrowAsRuntimeException();
-        }
-
-        return IIpSecService.Stub.asInterface(b);
+    private IpSecManager getIpSecManager(Context context) {
+        return context.getSystemService(IpSecManager.class);
     }
-
     /**
      * Checks the result status and throws an appropriate exception if the status is not Status.OK.
      */
@@ -130,8 +120,7 @@
                     IpSecManager.SpiUnavailableException {
         synchronized (this) {
             try {
-                IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result = svc.createTransform(
+                IpSecTransformResponse result = getIpSecManager(mContext).createTransform(
                         mConfig, new Binder(), mContext.getOpPackageName());
                 int status = result.status;
                 checkResultStatus(status);
@@ -140,8 +129,6 @@
                 mCloseGuard.open("build");
             } catch (ServiceSpecificException e) {
                 throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
             }
         }
 
@@ -177,10 +164,7 @@
             return;
         }
         try {
-            IIpSecService svc = getIpSecService();
-            svc.deleteTransform(mResourceId);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
+            getIpSecManager(mContext).deleteTransform(mResourceId);
         } catch (Exception e) {
             // On close we swallow all random exceptions since failure to close is not
             // actionable by the user.
diff --git a/framework-t/src/android/net/NetworkIdentity.java b/framework-t/src/android/net/NetworkIdentity.java
index 8f1115e..04d1d68 100644
--- a/framework-t/src/android/net/NetworkIdentity.java
+++ b/framework-t/src/android/net/NetworkIdentity.java
@@ -160,11 +160,6 @@
 
         // Not dumping mSubType, subtypes are no longer supported.
 
-        if (mSubscriberId != null) {
-            proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
-                    NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
-        }
-        proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
         proto.write(NetworkIdentityProto.METERED, mMetered);
         proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index 9d532e7..9175809 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -57,7 +58,7 @@
  */
 // @NotThreadSafe
 @SystemApi
-public final class NetworkStats implements Parcelable {
+public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Entry> {
     private static final String TAG = "NetworkStats";
 
     /**
@@ -678,6 +679,35 @@
     }
 
     /**
+     * Iterate over Entry objects.
+     *
+     * Return an iterator of this object that will iterate through all contained Entry objects.
+     *
+     * This iterator does not support concurrent modification and makes no guarantee of fail-fast
+     * behavior. If any method that can mutate the contents of this object is called while
+     * iteration is in progress, either inside the loop or in another thread, then behavior is
+     * undefined.
+     * The remove() method is not implemented and will throw UnsupportedOperationException.
+     * @hide
+     */
+    @SystemApi
+    @NonNull public Iterator<Entry> iterator() {
+        return new Iterator<Entry>() {
+            int mIndex = 0;
+
+            @Override
+            public boolean hasNext() {
+                return mIndex < size;
+            }
+
+            @Override
+            public Entry next() {
+                return getValues(mIndex++, null);
+            }
+        };
+    }
+
+    /**
      * Return specific stats entry.
      * @hide
      */
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index 032bc3f..1af32bf 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -282,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/Sources.bp b/service-t/Sources.bp
index 97dfb64..b261e16 100644
--- a/service-t/Sources.bp
+++ b/service-t/Sources.bp
@@ -82,8 +82,18 @@
         ":services.connectivity-ethernet-sources",
         ":services.connectivity-ipsec-sources",
         ":services.connectivity-netstats-sources",
-        ":services.connectivity-nsd-sources",
     ],
     path: "src",
     visibility: ["//frameworks/base/services/core"],
 }
+
+filegroup {
+    name: "services.connectivity-tiramisu-updatable-sources",
+    srcs: [
+        ":services.connectivity-nsd-sources",
+    ],
+    path: "src",
+    visibility: [
+        "//packages/modules/Connectivity:__subpackages__",
+    ],
+}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index a0ee1b6..d0db938 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -137,6 +137,7 @@
 import com.android.net.module.util.BestClock;
 import com.android.net.module.util.BinderUtils;
 import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.LocationPermissionChecker;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
 
@@ -352,6 +353,9 @@
     @NonNull
     private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
 
+    @NonNull
+    private final LocationPermissionChecker mLocationPermissionChecker;
+
     private static @NonNull File getDefaultSystemDir() {
         return new File(Environment.getDataDirectory(), "system");
     }
@@ -448,6 +452,7 @@
         mContentResolver = mContext.getContentResolver();
         mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
                 mNetworkStatsSubscriptionsMonitor);
+        mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
     }
 
     /**
@@ -495,6 +500,13 @@
                 }
             };
         }
+
+        /**
+         * @see LocationPermissionChecker
+         */
+        public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+            return new LocationPermissionChecker(context);
+        }
     }
 
     /**
@@ -694,12 +706,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 {
@@ -713,7 +738,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);
@@ -747,26 +772,41 @@
             @Override
             public NetworkStats getDeviceSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
-                return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
-                        mCallingUid);
+                enforceTemplatePermissions(template, callingPackage);
+                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);
+                enforceTemplatePermissions(template, callingPackage);
+                return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+                        mAccessLevel, mCallingUid);
+            }
+
+            // TODO: Remove this after all callers are removed.
+            @Override
+            public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
+                enforceTemplatePermissions(template, callingPackage);
+                return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+                        mAccessLevel, mCallingUid, Long.MIN_VALUE, Long.MAX_VALUE);
             }
 
             @Override
-            public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
-                return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel,
-                        mCallingUid);
+            public NetworkStatsHistory getHistoryIntervalForNetwork(NetworkTemplate template,
+                    int fields, long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
+                // TODO(b/200768422): Redact returned history if the template is location
+                //  sensitive but the caller is not privileged.
+                return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+                        mAccessLevel, mCallingUid, start, end);
             }
 
             @Override
             public NetworkStats getSummaryForAllUid(
                     NetworkTemplate template, long start, long end, boolean includeTags) {
+                enforceTemplatePermissions(template, callingPackage);
                 try {
                     final NetworkStats stats = getUidComplete()
                             .getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -784,6 +824,7 @@
             @Override
             public NetworkStats getTaggedSummaryForAllUid(
                     NetworkTemplate template, long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
                 try {
                     final NetworkStats tagStats = getUidTagComplete()
                             .getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -796,6 +837,7 @@
             @Override
             public NetworkStatsHistory getHistoryForUid(
                     NetworkTemplate template, int uid, int set, int tag, int fields) {
+                enforceTemplatePermissions(template, callingPackage);
                 // NOTE: We don't augment UID-level statistics
                 if (tag == TAG_NONE) {
                     return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -810,6 +852,9 @@
             public NetworkStatsHistory getHistoryIntervalForUid(
                     NetworkTemplate template, int uid, int set, int tag, int fields,
                     long start, long end) {
+                enforceTemplatePermissions(template, callingPackage);
+                // TODO(b/200768422): Redact returned history if the template is location
+                //  sensitive but the caller is not privileged.
                 // NOTE: We don't augment UID-level statistics
                 if (tag == TAG_NONE) {
                     return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -831,6 +876,26 @@
         };
     }
 
+    private void enforceTemplatePermissions(@NonNull NetworkTemplate template,
+            @NonNull String callingPackage) {
+        // For a template with wifi network keys, it is possible for a malicious
+        // client to track the user locations via querying data usage. Thus, enforce
+        // fine location permission check.
+        if (!template.getWifiNetworkKeys().isEmpty()) {
+            final boolean canAccessFineLocation = mLocationPermissionChecker
+                    .checkCallersLocationPermission(callingPackage,
+                    null /* featureId */,
+                            Binder.getCallingUid(),
+                            false /* coarseForTargetSdkLessThanQ */,
+                            null /* message */);
+            if (!canAccessFineLocation) {
+                throw new SecurityException("Access fine location is required when querying"
+                        + " with wifi network keys, make sure the app has the necessary"
+                        + "permissions and the location toggle is on.");
+            }
+        }
+    }
+
     private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
         return NetworkStatsAccess.checkAccessLevel(
                 mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
@@ -867,7 +932,7 @@
         // We've been using pure XT stats long enough that we no longer need to
         // splice DEV and XT together.
         final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
-                accessLevel, callingUid);
+                accessLevel, callingUid, start, end);
 
         final long now = System.currentTimeMillis();
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
@@ -884,14 +949,14 @@
      * appropriate.
      */
     private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template,
-            int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
+            int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid,
+            long start, long end) {
         // We've been using pure XT stats long enough that we no longer need to
         // splice DEV and XT together.
         final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
         synchronized (mStatsLock) {
             return mXtStatsCached.getHistory(template, augmentPlan,
-                    UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
-                    accessLevel, callingUid);
+                    UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid);
         }
     }
 
@@ -2106,7 +2171,7 @@
         public void notifyWarningOrLimitReached() {
             Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
+                    mNetworkPolicyManager.notifyStatsProviderWarningOrLimitReached());
         }
 
         @Override