Refactored whitelist restrict background uids.

On Android N, the list of UIDs whitelisted was kept in a separate array
and XML tag, while in reality it should be a new UID
policy (POLICY_ALLOW_METERED_BACKGROUND).

This change refactors NetworkPolicyManagerService to use the new UID
policy, although without removing any of its existing methods (but
marking them as deprecated).

Test: m -j32 FrameworksServicesTests && adb install -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"

Test: cts-tradefed run commandAndExit cts --skip-device-info --skip-system-status-check com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker --skip-preconditions -m CtsHostsideNetworkTests --abi armeabi-v7a -t com.android.cts.net.HostsideRestrictBackgroundNetworkTests

BUG: 28791717

Change-Id: I39869efda554ca0b736dd2380e439474f91dfbe6
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 60fa4df..72f465c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -46,6 +46,7 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -357,12 +358,6 @@
     private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
 
     /**
-     * UIDs that have been white-listed to avoid restricted background.
-     */
-    @GuardedBy("mUidRulesFirstLock")
-    private final SparseBooleanArray mRestrictBackgroundWhitelistUids = new SparseBooleanArray();
-
-    /**
      * UIDs that have been initially white-listed by system to avoid restricted background.
      */
     @GuardedBy("mUidRulesFirstLock")
@@ -486,7 +481,7 @@
      * Whitelists pre-defined apps for restrict background, but only if the user didn't already
      * revoke the whitelist.
      *
-     * @return whether any uid has been added to {@link #mRestrictBackgroundWhitelistUids}.
+     * @return whether any uid has been whitelisted.
      */
     boolean addDefaultRestrictBackgroundWhitelistUidsUL() {
         final List<UserInfo> users = mUserManager.getUsers();
@@ -531,7 +526,7 @@
             if (!mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
                 Slog.i(TAG, "adding default package " + pkg + " (uid " + uid + " for user "
                         + userId + ") to restrict background whitelist");
-                mRestrictBackgroundWhitelistUids.append(uid, true);
+                setUidPolicyUncheckedUL(uid, POLICY_ALLOW_METERED_BACKGROUND, false);
                 changed = true;
             }
         }
@@ -1460,6 +1455,10 @@
             final XmlPullParser in = Xml.newPullParser();
             in.setInput(fis, StandardCharsets.UTF_8.name());
 
+             // Must save the <restrict-background> tags and convert them to <uid-policy> later,
+             // to skip UIDs that were explicitly blacklisted.
+            final SparseBooleanArray whitelistedRestrictBackground = new SparseBooleanArray();
+
             int type;
             int version = VERSION_INIT;
             boolean insideWhitelist = false;
@@ -1568,7 +1567,7 @@
                         insideWhitelist = true;
                     } else if (TAG_RESTRICT_BACKGROUND.equals(tag) && insideWhitelist) {
                         final int uid = readIntAttribute(in, ATTR_UID);
-                        mRestrictBackgroundWhitelistUids.put(uid, true);
+                        whitelistedRestrictBackground.append(uid, true);
                     } else if (TAG_REVOKED_RESTRICT_BACKGROUND.equals(tag) && insideWhitelist) {
                         final int uid = readIntAttribute(in, ATTR_UID);
                         mRestrictBackgroundWhitelistRevokedUids.put(uid, true);
@@ -1581,6 +1580,25 @@
                 }
             }
 
+            final int size = whitelistedRestrictBackground.size();
+            for (int i = 0; i < size; i++) {
+                final int uid = whitelistedRestrictBackground.keyAt(i);
+                final int policy = mUidPolicy.get(uid, POLICY_NONE);
+                if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
+                    Slog.w(TAG, "ignoring restrict-background-whitelist for " + uid
+                            + " because its policy is " + uidPoliciesToString(policy));
+                    continue;
+                }
+                if (UserHandle.isApp(uid)) {
+                    final int newPolicy = policy | POLICY_ALLOW_METERED_BACKGROUND;
+                    if (LOGV)
+                        Log.v(TAG, "new policy for " + uid + ": " + uidPoliciesToString(newPolicy));
+                    setUidPolicyUncheckedUL(uid, newPolicy, false);
+                } else {
+                    Slog.w(TAG, "unable to update policy on UID " + uid);
+                }
+            }
+
         } catch (FileNotFoundException e) {
             // missing policy is okay, probably first boot
             upgradeLegacyBackgroundDataUL();
@@ -1670,17 +1688,8 @@
             // write all whitelists
             out.startTag(null, TAG_WHITELIST);
 
-            // restrict background whitelist
-            int size = mRestrictBackgroundWhitelistUids.size();
-            for (int i = 0; i < size; i++) {
-                final int uid = mRestrictBackgroundWhitelistUids.keyAt(i);
-                out.startTag(null, TAG_RESTRICT_BACKGROUND);
-                writeIntAttribute(out, ATTR_UID, uid);
-                out.endTag(null, TAG_RESTRICT_BACKGROUND);
-            }
-
             // revoked restrict background whitelist
-            size = mRestrictBackgroundWhitelistRevokedUids.size();
+            int size = mRestrictBackgroundWhitelistRevokedUids.size();
             for (int i = 0; i < size; i++) {
                 final int uid = mRestrictBackgroundWhitelistRevokedUids.keyAt(i);
                 out.startTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
@@ -1817,22 +1826,6 @@
         if (LOGV) Slog.v(TAG, "removeUserStateUL()");
         boolean changed = false;
 
-        // Remove entries from restricted background UID whitelist
-        int[] wlUids = new int[0];
-        for (int i = 0; i < mRestrictBackgroundWhitelistUids.size(); i++) {
-            final int uid = mRestrictBackgroundWhitelistUids.keyAt(i);
-            if (UserHandle.getUserId(uid) == userId) {
-                wlUids = appendInt(wlUids, uid);
-            }
-        }
-
-        if (wlUids.length > 0) {
-            for (int uid : wlUids) {
-                removeRestrictBackgroundWhitelistedUidUL(uid, false, false);
-            }
-            changed = true;
-        }
-
         // Remove entries from revoked default restricted background UID whitelist
         for (int i = mRestrictBackgroundWhitelistRevokedUids.size() - 1; i >= 0; i--) {
             final int uid = mRestrictBackgroundWhitelistRevokedUids.keyAt(i);
@@ -2068,21 +2061,26 @@
         }
     }
 
+    /**
+     * @deprecated - should use {@link #setUidPolicy(int, int)} directly.
+     */
     @Override
+    @Deprecated
     public void addRestrictBackgroundWhitelistedUid(int uid) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
         final boolean oldStatus;
         final boolean needFirewallRules;
         int changed;
         synchronized (mUidRulesFirstLock) {
-            oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+            final int oldUidPolicy = mUidPolicy.get(uid, POLICY_NONE);
+            oldStatus = (oldUidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
             if (oldStatus) {
                 if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
                 return;
             }
             needFirewallRules = isUidValidForWhitelistRules(uid);
             Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
-            mRestrictBackgroundWhitelistUids.append(uid, true);
+            setUidPolicyUncheckedUL(uid, oldUidPolicy, POLICY_ALLOW_METERED_BACKGROUND, false);
             if (mDefaultRestrictBackgroundWhitelistUids.get(uid)
                     && mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
                 if (LOGD) Slog.d(TAG, "Removing uid " + uid
@@ -2103,7 +2101,11 @@
                 Boolean.TRUE).sendToTarget();
     }
 
+    /**
+     * @deprecated - should use {@link #setUidPolicy(int, int)} directly.
+     */
     @Override
+    @Deprecated
     public void removeRestrictBackgroundWhitelistedUid(int uid) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
         final boolean changed;
@@ -2120,7 +2122,8 @@
      */
     private boolean removeRestrictBackgroundWhitelistedUidUL(int uid, boolean uidDeleted,
             boolean updateNow) {
-        final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+        final boolean oldStatus =
+                (mUidPolicy.get(uid, POLICY_NONE) & POLICY_ALLOW_METERED_BACKGROUND) != 0;
         if (!oldStatus && !uidDeleted) {
             if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
             return false;
@@ -2128,7 +2131,7 @@
         final boolean needFirewallRules = uidDeleted || isUidValidForWhitelistRules(uid);
         if (oldStatus) {
             Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
-            mRestrictBackgroundWhitelistUids.delete(uid);
+            mUidPolicy.delete(uid);
         }
         if (mDefaultRestrictBackgroundWhitelistUids.get(uid)
                 && !mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
@@ -2151,21 +2154,41 @@
         return mRestrictBackground && needFirewallRules;
     }
 
+    /**
+     * @deprecated - should use {@link #getUidsWithPolicy(int)} instead, but first need to change
+     * that method to use logical OR (|).
+     */
     @Override
+    @Deprecated
     public int[] getRestrictBackgroundWhitelistedUids() {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+        int[] whitelist = null;
         synchronized (mUidRulesFirstLock) {
-            final int size = mRestrictBackgroundWhitelistUids.size();
-            final int[] whitelist = new int[size];
-            for (int i = 0; i < size; i++) {
-                whitelist[i] = mRestrictBackgroundWhitelistUids.keyAt(i);
+            // First calculate size
+            int size = 0;
+            int policySize = mUidPolicy.size();
+            for (int i = 0; i < policySize; i++) {
+                if ((mUidPolicy.valueAt(i) & POLICY_ALLOW_METERED_BACKGROUND) != 0) {
+                    size ++;
+                }
             }
-            if (LOGV) {
-                Slog.v(TAG, "getRestrictBackgroundWhitelistedUids(): "
-                        + mRestrictBackgroundWhitelistUids);
+            whitelist = new int[size];
+            // Then populate it.
+            if (size > 0) {
+                int index = 0;
+                for (int i = 0; i < policySize; i++) {
+                    final int uid = mUidPolicy.keyAt(i);
+                    if ((mUidPolicy.valueAt(i) & POLICY_ALLOW_METERED_BACKGROUND) != 0) {
+                        whitelist[index++] = uid;
+                    }
+                }
             }
-            return whitelist;
         }
+        if (LOGV) {
+            Slog.v(TAG, "getRestrictBackgroundWhitelistedUids(): "
+                    + Arrays.toString(whitelist));
+        }
+        return whitelist;
     }
 
     @Override
@@ -2189,7 +2212,7 @@
             if (!mRestrictBackground) {
                 return RESTRICT_BACKGROUND_STATUS_DISABLED;
             }
-            return mRestrictBackgroundWhitelistUids.get(uid)
+            return (mUidPolicy.get(uid) & POLICY_ALLOW_METERED_BACKGROUND) != 0
                     ? RESTRICT_BACKGROUND_STATUS_WHITELISTED
                     : RESTRICT_BACKGROUND_STATUS_ENABLED;
         }
@@ -2385,13 +2408,15 @@
                     fout.decreaseIndent();
                 }
 
-                size = mRestrictBackgroundWhitelistUids.size();
+                final int[] restrictBackgroundWhitelistUids =
+                        getRestrictBackgroundWhitelistedUids();
+                size = restrictBackgroundWhitelistUids.length;
                 if (size > 0) {
                     fout.println("Restrict background whitelist uids:");
                     fout.increaseIndent();
                     for (int i = 0; i < size; i++) {
                         fout.print("UID=");
-                        fout.print(mRestrictBackgroundWhitelistUids.keyAt(i));
+                        fout.print(restrictBackgroundWhitelistUids[i]);
                         fout.println();
                     }
                     fout.decreaseIndent();
@@ -2915,7 +2940,7 @@
         final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
 
         final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
-        final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid);
+        final boolean isWhitelisted = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
         final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
         int newRule = RULE_NONE;