Exclude PID for rate-limited

rate-limited would include PID in current design, which means that
an app can ignore the rate-limits by creating a process (a new PID)
every time. Thus, maintain mOpenSessionCallsPerUid for rate-limits
and OpenSessionKey for debugging.

Also, there's no rate-limit for SYSTEM_UID so if the uid is SYSTEM_UID,
the mLastStatsSessionPoll should not be updated. Otherwise, it may
cause an app to be rate-limited to do a query after a system user
polled.

Test: adb shell dumpsys netstats and check the output
Bug: 228081549
Change-Id: I69cc1f3990c9960347691256ed4cb500f9bb48b2
(cherry picked from commit 8d5907fc13bd6238a2eaa56c1d80488c42a2d564)
Merged-In: I69cc1f3990c9960347691256ed4cb500f9bb48b2
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 391356c..05e8fe6 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -377,8 +377,18 @@
 
     private long mLastStatsSessionPoll;
 
-    /** Map from key {@code OpenSessionKey} to count of opened sessions */
-    @GuardedBy("mOpenSessionCallsPerCaller")
+    private final Object mOpenSessionCallsLock = new Object();
+    /**
+     * Map from UID to number of opened sessions. This is used for rate-limt an app to open
+     * session frequently
+     */
+    @GuardedBy("mOpenSessionCallsLock")
+    private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
+    /**
+     * Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording
+     * the caller of open session and it is only for debugging.
+     */
+    @GuardedBy("mOpenSessionCallsLock")
     private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
 
     private final static int DUMP_STATS_SESSION_COUNT = 20;
@@ -415,23 +425,20 @@
      * the caller.
      */
     private static class OpenSessionKey {
-        public final int mPid;
-        public final int mUid;
-        public final String mPackage;
+        public final int uid;
+        public final String packageName;
 
-        OpenSessionKey(int pid, int uid, @NonNull String packageName) {
-            mPid = pid;
-            mUid = uid;
-            mPackage = packageName;
+        OpenSessionKey(int uid, @NonNull String packageName) {
+            this.uid = uid;
+            this.packageName = packageName;
         }
 
         @Override
         public String toString() {
             final StringBuilder sb = new StringBuilder();
             sb.append("{");
-            sb.append("pid=").append(mPid).append(",");
-            sb.append("uid=").append(mUid).append(",");
-            sb.append("package=").append(mPackage);
+            sb.append("uid=").append(uid).append(",");
+            sb.append("package=").append(packageName);
             sb.append("}");
             return sb.toString();
         }
@@ -442,13 +449,12 @@
             if (o.getClass() != getClass()) return false;
 
             final OpenSessionKey key = (OpenSessionKey) o;
-            return mPid == key.mPid && mUid == key.mUid
-                    && TextUtils.equals(mPackage, key.mPackage);
+            return this.uid == key.uid && TextUtils.equals(this.packageName, key.packageName);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mPid, mUid, mPackage);
+            return Objects.hash(uid, packageName);
         }
     }
 
@@ -843,21 +849,27 @@
         final long lastCallTime;
         final long now = SystemClock.elapsedRealtime();
 
-        synchronized (mOpenSessionCallsPerCaller) {
-            Integer calls = mOpenSessionCallsPerCaller.get(key);
-            if (calls == null) {
+        synchronized (mOpenSessionCallsLock) {
+            Integer callsPerCaller = mOpenSessionCallsPerCaller.get(key);
+            if (callsPerCaller == null) {
                 mOpenSessionCallsPerCaller.put((key), 1);
             } else {
-                mOpenSessionCallsPerCaller.put(key, Integer.sum(calls, 1));
+                mOpenSessionCallsPerCaller.put(key, Integer.sum(callsPerCaller, 1));
             }
+
+            int callsPerUid = mOpenSessionCallsPerUid.get(key.uid, 0);
+            mOpenSessionCallsPerUid.put(key.uid, callsPerUid + 1);
+
+            if (key.uid == android.os.Process.SYSTEM_UID) {
+                return false;
+            }
+
+            // To avoid a non-system user to be rate-limited after system users open sessions,
+            // so update mLastStatsSessionPoll after checked if the uid is SYSTEM_UID.
             lastCallTime = mLastStatsSessionPoll;
             mLastStatsSessionPoll = now;
         }
 
-        if (key.mUid == android.os.Process.SYSTEM_UID) {
-            return false;
-        }
-
         return now - lastCallTime < POLL_RATE_LIMIT_MS;
     }
 
@@ -870,9 +882,8 @@
             flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
         }
         // Non-system uids are rate limited for POLL_ON_OPEN.
-        final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        final OpenSessionKey key = new OpenSessionKey(callingPid, callingUid, callingPackage);
+        final OpenSessionKey key = new OpenSessionKey(callingUid, callingPackage);
         flags = isRateLimitedForPoll(key)
                 ? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
                 : flags;
@@ -1989,6 +2000,9 @@
         for (int uid : uids) {
             deleteKernelTagData(uid);
         }
+
+       // TODO: Remove the UID's entries from mOpenSessionCallsPerUid and
+       // mOpenSessionCallsPerCaller
     }
 
     /**
@@ -2113,7 +2127,7 @@
 
             // Get the top openSession callers
             final HashMap calls;
-            synchronized (mOpenSessionCallsPerCaller) {
+            synchronized (mOpenSessionCallsLock) {
                 calls = new HashMap<>(mOpenSessionCallsPerCaller);
             }
             final List<Map.Entry<OpenSessionKey, Integer>> list = new ArrayList<>(calls.entrySet());