Merge "Add enterpriseSpecifier"
diff --git a/framework/api/current.txt b/framework/api/current.txt
index ecb7024..827da6d 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -294,6 +294,7 @@
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
     method public int describeContents();
     method @NonNull public int[] getCapabilities();
+    method @NonNull public int[] getEnterpriseCapabilitySubLevels();
     method public int getLinkDownstreamBandwidthKbps();
     method public int getLinkUpstreamBandwidthKbps();
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 50dd2ad..81a1e5d 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -121,6 +121,11 @@
   public final class NetworkCapabilities implements android.os.Parcelable {
     method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
     method public boolean hasForbiddenCapability(int);
+    field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1 = 1; // 0x1
+    field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2 = 2; // 0x2
+    field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3 = 3; // 0x3
+    field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4 = 4; // 0x4
+    field public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5 = 5; // 0x5
     field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
     field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L
     field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 5337b38..8d084e6 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -294,9 +294,11 @@
     ctor public NetworkCapabilities.Builder();
     ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
     method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder addEnterpriseCapabilitySubLevel(int);
     method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
     method @NonNull public android.net.NetworkCapabilities build();
     method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder removeEnterpriseCapabilitySubLevel(int);
     method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
     method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 721a12d..84f7cbb 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.annotation.IntDef;
@@ -146,6 +148,70 @@
      */
     private String mRequestorPackageName;
 
+    /**
+     * enterprise capability sub level 1
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1 = 1;
+
+    /**
+     * enterprise capability sub level 2
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2 = 2;
+
+    /**
+     * enterprise capability sub level 3
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3 = 3;
+
+    /**
+     * enterprise capability sub level 4
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4 = 4;
+
+    /**
+     * enterprise capability sub level 5
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5 = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = {
+            NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+            NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+            NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3,
+            NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4,
+            NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5,
+    })
+
+    public @interface EnterpriseCapabilitySubLevel {
+    }
+
+    /**
+     * Bitfield representing the network's enterprise capability sublevel.  If any are specified
+     * they will be satisfied by any Network that matches all of them.
+     * {@see addEnterpriseCapabilitySubLevel} for details on how masks are added
+     */
+    private int mEnterpriseCapabilitySubLevel;
+
+    /**
+     * @return all the enteprise capabilities sub level set on this {@code NetworkCapability}
+     * instance.
+     *
+     */
+    public @NonNull @EnterpriseCapabilitySubLevel int[] getEnterpriseCapabilitySubLevels() {
+        return NetworkCapabilitiesUtils.unpackBits(mEnterpriseCapabilitySubLevel);
+    }
+
     public NetworkCapabilities() {
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -192,6 +258,7 @@
         mRequestorPackageName = null;
         mSubIds = new ArraySet<>();
         mUnderlyingNetworks = null;
+        mEnterpriseCapabilitySubLevel = 0;
     }
 
     /**
@@ -224,6 +291,7 @@
         // mUnderlyingNetworks is an unmodifiable list if non-null, so a defensive copy is not
         // necessary.
         mUnderlyingNetworks = nc.mUnderlyingNetworks;
+        mEnterpriseCapabilitySubLevel = nc.mEnterpriseCapabilitySubLevel;
     }
 
     /**
@@ -716,6 +784,38 @@
     }
 
     /**
+     * Adds the given enterprise capability sub level to this {@code NetworkCapability} instance.
+     * Note that when searching for a network to satisfy a request, all capabilities sub level
+     * requested must be satisfied.
+     *
+     * @param enterpriseCapabilitySubLevel the enterprise capability sub level to be added.
+     * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
+     */
+    private @NonNull NetworkCapabilities addEnterpriseCapabilitySubLevel(
+            @EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+        checkValidEnterpriseCapabilitySublevel(enterpriseCapabilitySubLevel);
+        mEnterpriseCapabilitySubLevel |= 1 << enterpriseCapabilitySubLevel;
+        return this;
+    }
+
+    /**
+     * Removes (if found) the given enterprise capability sublevel from this
+     * {@code NetworkCapability} instance that were added via addEnterpriseCapabilitySubLevel(int)
+     *
+     * @param enterpriseCapabilitySubLevel the enterprise capability sublevel to be removed.
+     * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
+     */
+    private @NonNull NetworkCapabilities removeEnterpriseCapabilitySubLevel(
+            @EnterpriseCapabilitySubLevel  int enterpriseCapabilitySubLevel) {
+        checkValidEnterpriseCapabilitySublevel(enterpriseCapabilitySubLevel);
+        final int mask = ~(1 << enterpriseCapabilitySubLevel);
+        mEnterpriseCapabilitySubLevel &= mask;
+        return this;
+    }
+
+    /**
      * Set the underlying networks of this network.
      *
      * @param networks The underlying networks of this network.
@@ -815,6 +915,22 @@
         return null;
     }
 
+    private boolean equalsEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
+        return nc.mEnterpriseCapabilitySubLevel == this.mEnterpriseCapabilitySubLevel;
+    }
+
+    private boolean satisfiedByEnterpriseCapabilitiesSubLevel(@NonNull NetworkCapabilities nc) {
+        final int requestedEnterpriseCapabilitiesSubLevel = mEnterpriseCapabilitySubLevel;
+        final int providedEnterpriseCapabailitiesSubLevel = nc.mEnterpriseCapabilitySubLevel;
+
+        if ((providedEnterpriseCapabailitiesSubLevel & requestedEnterpriseCapabilitiesSubLevel)
+                == requestedEnterpriseCapabilitiesSubLevel) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     private boolean satisfiedByNetCapabilities(@NonNull NetworkCapabilities nc,
             boolean onlyImmutable) {
         long requestedCapabilities = mNetworkCapabilities;
@@ -1720,6 +1836,7 @@
                 && satisfiedByTransportTypes(nc)
                 && (onlyImmutable || satisfiedByLinkBandwidths(nc))
                 && satisfiedBySpecifier(nc)
+                && satisfiedByEnterpriseCapabilitiesSubLevel(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
                 && (onlyImmutable || satisfiedBySSID(nc))
@@ -1822,7 +1939,8 @@
                 && equalsRequestor(that)
                 && equalsAdministratorUids(that)
                 && equalsSubscriptionIds(that)
-                && equalsUnderlyingNetworks(that);
+                && equalsUnderlyingNetworks(that)
+                && equalsEnterpriseCapabilitiesSubLevel(that);
     }
 
     @Override
@@ -1846,7 +1964,8 @@
                 + Objects.hashCode(mRequestorPackageName) * 59
                 + Arrays.hashCode(mAdministratorUids) * 61
                 + Objects.hashCode(mSubIds) * 67
-                + Objects.hashCode(mUnderlyingNetworks) * 71;
+                + Objects.hashCode(mUnderlyingNetworks) * 71
+                + mEnterpriseCapabilitySubLevel * 73;
     }
 
     @Override
@@ -1882,6 +2001,7 @@
         dest.writeString(mRequestorPackageName);
         dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
         dest.writeTypedList(mUnderlyingNetworks);
+        dest.writeInt(mEnterpriseCapabilitySubLevel);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1911,6 +2031,7 @@
                     netCap.mSubIds.add(subIdInts[i]);
                 }
                 netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
+                netCap.mEnterpriseCapabilitySubLevel = in.readInt();
                 return netCap;
             }
             @Override
@@ -2002,6 +2123,12 @@
             sb.append(" SubscriptionIds: ").append(mSubIds);
         }
 
+        if (0 != mEnterpriseCapabilitySubLevel) {
+            sb.append(" EnterpriseCapabilitySublevel: ");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, mEnterpriseCapabilitySubLevel,
+                    NetworkCapabilities::enterpriseCapabilitySublevelNameOf, "&");
+        }
+
         sb.append(" UnderlyingNetworks: ");
         if (mUnderlyingNetworks != null) {
             sb.append("[");
@@ -2101,6 +2228,11 @@
         }
     }
 
+    private static @NonNull String enterpriseCapabilitySublevelNameOf(
+            @NetCapability int capability) {
+        return Integer.toString(capability);
+    }
+
     /**
      * @hide
      */
@@ -2141,6 +2273,20 @@
         }
     }
 
+    private static boolean isValidEnterpriseCapabilitySubLevel(
+            @NetworkCapabilities.EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+        return enterpriseCapabilitySubLevel >= NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1
+                && enterpriseCapabilitySubLevel <= NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5;
+    }
+
+    private static void checkValidEnterpriseCapabilitySublevel(
+            @NetworkCapabilities.EnterpriseCapabilitySubLevel int enterpriseCapabilitySubLevel) {
+        if (!isValidEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel)) {
+            throw new IllegalArgumentException("enterprise capability sublevel "
+                    + enterpriseCapabilitySubLevel + " is out of range");
+        }
+    }
+
     /**
      * Check if this {@code NetworkCapability} instance is metered.
      *
@@ -2467,6 +2613,37 @@
         }
 
         /**
+         * Adds the given enterprise capability sub level.
+         * Note that when searching for a network to satisfy a request, all capabilities sub level
+         * requested must be satisfied. Enterprise capability sub-level is applicable only
+         * for NET_CAPABILITY_ENTERPRISE capability
+         *
+         * @param enterpriseCapabilitySubLevel enterprise capability sub-level.
+         *
+         * @return this builder
+         */
+        @NonNull
+        public Builder addEnterpriseCapabilitySubLevel(
+                @EnterpriseCapabilitySubLevel  int enterpriseCapabilitySubLevel) {
+            mCaps.addEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+            return this;
+        }
+
+        /**
+         * Removes the given enterprise capability sub level. Enterprise capability sub-level is
+         * applicable only for NET_CAPABILITY_ENTERPRISE capability
+         *
+         * @param enterpriseCapabilitySubLevel the enterprise capability subLevel
+         * @return this builder
+         */
+        @NonNull
+        public Builder removeEnterpriseCapabilitySubLevel(
+                @EnterpriseCapabilitySubLevel  int enterpriseCapabilitySubLevel) {
+            mCaps.removeEnterpriseCapabilitySubLevel(enterpriseCapabilitySubLevel);
+            return this;
+        }
+
+        /**
          * Sets the owner UID.
          *
          * The default value is {@link Process#INVALID_UID}. Pass this value to reset.
@@ -2724,6 +2901,12 @@
                             + " administrator UIDs.");
                 }
             }
+
+            if ((mCaps.getEnterpriseCapabilitySubLevels().length != 0)
+                    && !mCaps.hasCapability(NET_CAPABILITY_ENTERPRISE)) {
+                throw new IllegalStateException("Enterprise capability sublevel is applicable only"
+                        + " with ENTERPRISE capability.");
+            }
             return new NetworkCapabilities(mCaps);
         }
     }
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 32f00a3..2a4df7a 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -22,6 +22,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
@@ -347,7 +353,7 @@
         if (isAtLeastS()) {
             // When this test is run on S+, NetworkCapabilities is as recent as the test,
             // so this should be the most recent known number of fields.
-            assertParcelSane(cap, 17);
+            assertParcelSane(cap, 18);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -797,6 +803,87 @@
         } catch (IllegalStateException expected) { }
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.S)
+    public void testEnterpriseCapabilitySubLevel() {
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .build();
+        assertEquals(1, nc1.getEnterpriseCapabilitySubLevels().length);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+                nc1.getEnterpriseCapabilitySubLevels()[0]);
+        final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .build();
+        assertEquals(2, nc2.getEnterpriseCapabilitySubLevels().length);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+                nc2.getEnterpriseCapabilitySubLevels()[0]);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+                nc2.getEnterpriseCapabilitySubLevels()[1]);
+        final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5)
+                .build();
+        assertEquals(5, nc3.getEnterpriseCapabilitySubLevels().length);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1,
+                nc3.getEnterpriseCapabilitySubLevels()[0]);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2,
+                nc3.getEnterpriseCapabilitySubLevels()[1]);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_3,
+                nc3.getEnterpriseCapabilitySubLevels()[2]);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_4,
+                nc3.getEnterpriseCapabilitySubLevels()[3]);
+        assertEquals(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_5,
+                nc3.getEnterpriseCapabilitySubLevels()[4]);
+
+        final Class<IllegalArgumentException> illegalArgumentExceptionClass =
+                IllegalArgumentException.class;
+        assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
+                .addEnterpriseCapabilitySubLevel(6)
+                .build());
+        assertThrows(illegalArgumentExceptionClass, () -> new NetworkCapabilities.Builder()
+                .removeEnterpriseCapabilitySubLevel(6)
+                .build());
+
+        final Class<IllegalStateException> illegalStateException =
+                IllegalStateException.class;
+        assertThrows(illegalStateException, () -> new NetworkCapabilities.Builder()
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .build());
+
+        final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_ENTERPRISE)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .build();
+        assertEquals(0, nc4.getEnterpriseCapabilitySubLevels().length);
+
+        final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_CBS)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .addEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_1)
+                .removeEnterpriseCapabilitySubLevel(NET_CAPABILITY_ENTERPRISE_SUB_LEVEL_2)
+                .build();
+
+        assertTrue(nc4.satisfiedByNetworkCapabilities(nc1));
+        assertFalse(nc1.satisfiedByNetworkCapabilities(nc4));
+
+        assertFalse(nc3.satisfiedByNetworkCapabilities(nc2));
+        assertTrue(nc2.satisfiedByNetworkCapabilities(nc3));
+
+        assertFalse(nc1.satisfiedByNetworkCapabilities(nc5));
+        assertFalse(nc5.satisfiedByNetworkCapabilities(nc1));
+    }
+
     @Test
     public void testWifiAwareNetworkSpecifier() {
         final NetworkCapabilities nc = new NetworkCapabilities()