Ability to specify which all applications fall under enterprise slice.
Bug: 194332512
Test: unit test
Change-Id: I94549a41aaa717add22b0a3e5035beacf6f1b8f2
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index d625d1b..6c27c4a 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -262,6 +262,7 @@
import com.android.server.connectivity.ProfileNetworkPreferenceList;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
+import com.android.server.connectivity.UidRangeUtils;
import libcore.io.IoUtils;
@@ -1489,16 +1490,17 @@
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
- return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
+ return createDefaultNetworkCapabilitiesForUidRangeSet(Collections.singleton(
+ new UidRange(uid, uid)));
}
- private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
- @NonNull final UidRange uids) {
+ private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRangeSet(
+ @NonNull final Set<UidRange> uidRangeSet) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
- netCap.setUids(UidRange.toIntRanges(Collections.singleton(uids)));
+ netCap.setUids(UidRange.toIntRanges(uidRangeSet));
return netCap;
}
@@ -10150,8 +10152,14 @@
allowFallback = false;
// continue to process the enterprise preference.
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
- final UidRange uids = UidRange.createForUser(profile);
- nc = createDefaultNetworkCapabilitiesForUidRange(uids);
+ final Set<UidRange> uidRangeSet =
+ getUidListToBeAppliedForNetworkPreference(profile, preference);
+ if (!isRangeAlreadyInPreferenceList(preferenceList, uidRangeSet)) {
+ nc = createDefaultNetworkCapabilitiesForUidRangeSet(uidRangeSet);
+ } else {
+ throw new IllegalArgumentException(
+ "Overlapping uid range in setProfileNetworkPreferences");
+ }
nc.addCapability(NET_CAPABILITY_ENTERPRISE);
nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
break;
@@ -10166,6 +10174,35 @@
new Pair<>(preferenceList, listener)));
}
+ private Set<UidRange> getUidListToBeAppliedForNetworkPreference(
+ @NonNull final UserHandle profile,
+ @NonNull final ProfileNetworkPreference profileNetworkPreference) {
+ final UidRange profileUids = UidRange.createForUser(profile);
+ Set<UidRange> uidRangeSet = UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getIncludedUids());
+ if (uidRangeSet.size() > 0) {
+ if (!UidRangeUtils.isRangeSetInUidRange(profileUids, uidRangeSet)) {
+ throw new IllegalArgumentException(
+ "Allow uid range is outside the uid range of profile.");
+ }
+ } else {
+ ArraySet<UidRange> disallowUidRangeSet = UidRangeUtils.convertListToUidRange(
+ profileNetworkPreference.getExcludedUids());
+ if (disallowUidRangeSet.size() > 0) {
+ if (!UidRangeUtils.isRangeSetInUidRange(profileUids, disallowUidRangeSet)) {
+ throw new IllegalArgumentException(
+ "disallow uid range is outside the uid range of profile.");
+ }
+ uidRangeSet = UidRangeUtils.removeRangeSetFromUidRange(profileUids,
+ disallowUidRangeSet);
+ } else {
+ uidRangeSet = new ArraySet<UidRange>();
+ uidRangeSet.add(profileUids);
+ }
+ }
+ return uidRangeSet;
+ }
+
private void validateNetworkCapabilitiesOfProfileNetworkPreference(
@Nullable final NetworkCapabilities nc) {
if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
@@ -10187,6 +10224,11 @@
nrs.add(createDefaultInternetRequestForTransport(
TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
}
+ if (VDBG) {
+ loge("pref.capabilities.getUids():" + UidRange.fromIntRanges(
+ pref.capabilities.getUids()));
+ }
+
setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids()));
final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs,
PREFERENCE_ORDER_PROFILE);
@@ -10195,6 +10237,25 @@
return result;
}
+ /**
+ * Compare if the given UID range sets have the same UIDs.
+ *
+ */
+ private boolean isRangeAlreadyInPreferenceList(
+ @NonNull List<ProfileNetworkPreferenceList.Preference> preferenceList,
+ @NonNull Set<UidRange> uidRangeSet) {
+ if (uidRangeSet.size() == 0 || preferenceList.size() == 0) {
+ return false;
+ }
+ for (ProfileNetworkPreferenceList.Preference pref : preferenceList) {
+ if (UidRangeUtils.doesRangeSetOverlap(
+ UidRange.fromIntRanges(pref.capabilities.getUids()), uidRangeSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void handleSetProfileNetworkPreference(
@NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
@Nullable final IOnCompleteListener listener) {
diff --git a/service/src/com/android/server/connectivity/UidRangeUtils.java b/service/src/com/android/server/connectivity/UidRangeUtils.java
new file mode 100644
index 0000000..7318296
--- /dev/null
+++ b/service/src/com/android/server/connectivity/UidRangeUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.net.UidRange;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility class for UidRange
+ *
+ * @hide
+ */
+public final class UidRangeUtils {
+ /**
+ * Check if given uid range set is within the uid range
+ * @param uids uid range in which uidRangeSet is checked to be in range.
+ * @param uidRangeSet uid range set to be be checked if it is in range of uids
+ * @return true uidRangeSet is in the range of uids
+ * @hide
+ */
+ public static boolean isRangeSetInUidRange(@NonNull UidRange uids,
+ @NonNull Set<UidRange> uidRangeSet) {
+ Objects.requireNonNull(uids);
+ Objects.requireNonNull(uidRangeSet);
+ if (uidRangeSet.size() == 0) {
+ return true;
+ }
+ for (UidRange range : uidRangeSet) {
+ if (!uids.contains(range.start) || !uids.contains(range.stop)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove given uid ranges set from a uid range
+ * @param uids uid range from which uidRangeSet will be removed
+ * @param uidRangeSet uid range set to be removed from uids.
+ * WARNING : This function requires the UidRanges in uidRangeSet to be disjoint
+ * WARNING : This function requires the arrayset to be iterated in increasing order of the
+ * ranges. Today this is provided by the iteration order stability of
+ * ArraySet, and the fact that the code creating this ArraySet always
+ * creates it in increasing order.
+ * Note : if any of the above is not satisfied this function throws IllegalArgumentException
+ * TODO : remove these limitations
+ * @hide
+ */
+ public static ArraySet<UidRange> removeRangeSetFromUidRange(@NonNull UidRange uids,
+ @NonNull ArraySet<UidRange> uidRangeSet) {
+ Objects.requireNonNull(uids);
+ Objects.requireNonNull(uidRangeSet);
+ final ArraySet<UidRange> filteredRangeSet = new ArraySet<UidRange>();
+ if (uidRangeSet.size() == 0) {
+ filteredRangeSet.add(uids);
+ return filteredRangeSet;
+ }
+
+ int start = uids.start;
+ UidRange previousRange = null;
+ for (UidRange uidRange : uidRangeSet) {
+ if (previousRange != null) {
+ if (previousRange.stop > uidRange.start) {
+ throw new IllegalArgumentException("UID ranges are not increasing order");
+ }
+ }
+ if (uidRange.start > start) {
+ filteredRangeSet.add(new UidRange(start, uidRange.start - 1));
+ start = uidRange.stop + 1;
+ } else if (uidRange.start == start) {
+ start = uidRange.stop + 1;
+ }
+ previousRange = uidRange;
+ }
+ if (start < uids.stop) {
+ filteredRangeSet.add(new UidRange(start, uids.stop));
+ }
+ return filteredRangeSet;
+ }
+
+ /**
+ * Compare if the given UID range sets have overlapping uids
+ * @param uidRangeSet1 first uid range set to check for overlap
+ * @param uidRangeSet2 second uid range set to check for overlap
+ * @hide
+ */
+ public static boolean doesRangeSetOverlap(@NonNull Set<UidRange> uidRangeSet1,
+ @NonNull Set<UidRange> uidRangeSet2) {
+ Objects.requireNonNull(uidRangeSet1);
+ Objects.requireNonNull(uidRangeSet2);
+
+ if (uidRangeSet1.size() == 0 || uidRangeSet2.size() == 0) {
+ return false;
+ }
+ for (UidRange range1 : uidRangeSet1) {
+ for (UidRange range2 : uidRangeSet2) {
+ if (range1.contains(range2.start) || range1.contains(range2.stop)
+ || range2.contains(range1.start) || range2.contains(range1.stop)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Convert a list of uid to set of UidRanges.
+ * @param uids list of uids
+ * @return set of UidRanges
+ * @hide
+ */
+ public static ArraySet<UidRange> convertListToUidRange(@NonNull List<Integer> uids) {
+ Objects.requireNonNull(uids);
+ final ArraySet<UidRange> uidRangeSet = new ArraySet<UidRange>();
+ if (uids.size() == 0) {
+ return uidRangeSet;
+ }
+ List<Integer> uidsNew = new ArrayList<>(uids);
+ Collections.sort(uidsNew);
+ int start = uidsNew.get(0);
+ int stop = start;
+
+ for (Integer i : uidsNew) {
+ if (i <= stop + 1) {
+ stop = i;
+ } else {
+ uidRangeSet.add(new UidRange(start, stop));
+ start = i;
+ stop = i;
+ }
+ }
+ uidRangeSet.add(new UidRange(start, stop));
+ return uidRangeSet;
+ }
+}