Allow applications to query for foreground/background data usage.

Currently the NetworkStatsManager APIs allow applications to
query for their own data usage by UID and tag, but do not allow
applications to query by foreground/background state.

This is causing popular apps to resort to parsing xt_qtaguid
stats files directly. Because this is no longer allowed for apps
targeting P and above, provide replacement functionality.

This API allows apps to query for data usage for a given state,
but not to receive data usage broken down by state. This is
consistent with how the current UID and tag APIs work. It is also
not an undue burden on apps: there are currently only two states
of interest (FOREGROUND and everything else), and even if we add
states in the future, unmodified apps can still obtain total
traffic using STATE_ALL.

Bug: 72977484
Test: New CTS test added in other change in this topic.
Change-Id: Ic8c9194569ffd599b49e4a8197c5c2ea0ec3f7f7
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index e315e91..7252f02 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -68,6 +68,11 @@
     private int mTag = android.net.NetworkStats.TAG_NONE;
 
     /**
+     * State in case it was not specified in the query.
+     */
+    private int mState = Bucket.STATE_ALL;
+
+    /**
      * The session while the query requires it, null if all the stats have been collected or close()
      * has been called.
      */
@@ -267,6 +272,15 @@
         private long mTxBytes;
         private long mTxPackets;
 
+        private static int convertSet(@State int state) {
+            switch (state) {
+                case STATE_ALL: return android.net.NetworkStats.SET_ALL;
+                case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
+                case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND;
+            }
+            return 0;
+        }
+
         private static @State int convertState(int networkStatsSet) {
             switch (networkStatsSet) {
                 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
@@ -527,20 +541,13 @@
     /**
      * Collects history results for uid and resets history enumeration index.
      */
-    void startHistoryEnumeration(int uid) {
-        startHistoryEnumeration(uid, android.net.NetworkStats.TAG_NONE);
-    }
-
-    /**
-     * Collects history results for uid and resets history enumeration index.
-     */
-    void startHistoryEnumeration(int uid, int tag) {
+    void startHistoryEnumeration(int uid, int tag, int state) {
         mHistory = null;
         try {
             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
-                    android.net.NetworkStats.SET_ALL, tag,
-                    NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
-            setSingleUidTag(uid, tag);
+                    Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL,
+                    mStartTimeStamp, mEndTimeStamp);
+            setSingleUidTagState(uid, tag, state);
         } catch (RemoteException e) {
             Log.w(TAG, e);
             // Leaving mHistory null
@@ -636,6 +643,7 @@
         fillBucketFromSummaryEntry(bucket);
         return bucket;
     }
+
     /**
      * Getting the next item in a history enumeration.
      * @param bucketOut Next item will be set here.
@@ -648,7 +656,7 @@
                         mRecycledHistoryEntry);
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
-                bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mState = mState;
                 bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
@@ -691,9 +699,10 @@
         return mUidOrUidIndex;
     }
 
-    private void setSingleUidTag(int uid, int tag) {
+    private void setSingleUidTagState(int uid, int tag, int state) {
         mUidOrUidIndex = uid;
         mTag = tag;
+        mState = state;
     }
 
     private void stepUid() {
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 2357637..b2fe958 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -263,20 +263,31 @@
     /**
      * Query network usage statistics details for a given uid.
      *
-     * #see queryDetailsForUidTag(int, String, long, long, int, int)
+     * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
     public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
-            long startTime, long endTime, int uid) throws SecurityException, RemoteException {
-        return queryDetailsForUidTag(networkType, subscriberId, startTime, endTime, uid,
-            NetworkStats.Bucket.TAG_NONE);
+            long startTime, long endTime, int uid) throws SecurityException {
+        return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
+            NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
     }
 
     /**
-     * Query network usage statistics details for a given uid and tag. Only usable for uids
-     * belonging to calling user. Result is aggregated over state but not aggregated over time.
-     * This means buckets' start and end timestamps are going to be between 'startTime' and
-     * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
-     * same as the 'uid' parameter and tag the same as 'tag' parameter.
+     * Query network usage statistics details for a given uid and tag.
+     *
+     * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+     */
+    public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
+            long startTime, long endTime, int uid, int tag) throws SecurityException {
+        return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
+            tag, NetworkStats.Bucket.STATE_ALL);
+    }
+
+    /**
+     * Query network usage statistics details for a given 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}.
@@ -297,17 +308,18 @@
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
-    public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
-            long startTime, long endTime, int uid, int tag) throws SecurityException {
+    public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
+            long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
         NetworkTemplate template;
         template = createTemplate(networkType, subscriberId);
 
         NetworkStats result;
         try {
             result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
-            result.startHistoryEnumeration(uid, tag);
+            result.startHistoryEnumeration(uid, tag, state);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
+            Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
+                    + " state=" + state, e);
             return null;
         }