Merge "Do not remove profile network preference for different uids"
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
index fb271e3..fdcab02 100644
--- a/framework/src/android/net/ProfileNetworkPreference.java
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -120,8 +120,8 @@
     public String toString() {
         return "ProfileNetworkPreference{"
                 + "mPreference=" + getPreference()
-                + "mIncludedUids=" + mIncludedUids.toString()
-                + "mExcludedUids=" + mExcludedUids.toString()
+                + "mIncludedUids=" + Arrays.toString(mIncludedUids)
+                + "mExcludedUids=" + Arrays.toString(mExcludedUids)
                 + "mPreferenceEnterpriseId=" + mPreferenceEnterpriseId
                 + '}';
     }
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 1bd5a1d..fccb2a6 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -10833,10 +10833,20 @@
     private void handleSetProfileNetworkPreference(
             @NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
             @Nullable final IOnCompleteListener listener) {
+        /*
+         * handleSetProfileNetworkPreference is always called for single user.
+         * preferenceList only contains preferences for different uids within the same user
+         * (enforced by getUidListToBeAppliedForNetworkPreference).
+         * Clear all the existing preferences for the user before applying new preferences.
+         *
+         */
+        mProfileNetworkPreferences = mProfileNetworkPreferences.clearUser(
+                preferenceList.get(0).user);
         for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) {
             validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
             mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
         }
+
         removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
         addPerAppDefaultNetworkRequests(
                 createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
index 71f342d..473a115 100644
--- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
+++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
@@ -70,23 +70,33 @@
     /**
      * Returns a new object consisting of this object plus the passed preference.
      *
-     * If a preference already exists for the same user, it will be replaced by the passed
-     * preference. Passing a Preference object containing a null capabilities object is equivalent
-     * to (and indeed, implemented as) removing the preference for this user.
+     * It is not expected that unwanted preference already exists for the same user.
+     * All preferences for the user that were previously configured should be cleared before
+     * adding a new preference.
+     * Passing a Preference object containing a null capabilities object is equivalent
+     * to removing the preference for this user.
      */
     public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) {
-        final ArrayList<Preference> newPrefs = new ArrayList<>();
-        for (final Preference existingPref : preferences) {
-            if (!existingPref.user.equals(pref.user)) {
-                newPrefs.add(existingPref);
-            }
-        }
+        final ArrayList<Preference> newPrefs = new ArrayList<>(preferences);
         if (null != pref.capabilities) {
             newPrefs.add(pref);
         }
         return new ProfileNetworkPreferenceList(newPrefs);
     }
 
+    /**
+     * Remove all preferences corresponding to a user.
+     */
+    public ProfileNetworkPreferenceList clearUser(UserHandle user) {
+        final ArrayList<Preference> newPrefs = new ArrayList<>();
+        for (final Preference existingPref : preferences) {
+            if (!existingPref.user.equals(user)) {
+                newPrefs.add(existingPref);
+            }
+        }
+        return new ProfileNetworkPreferenceList(newPrefs);
+    }
+
     public boolean isEmpty() {
         return preferences.isEmpty();
     }
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 34c8ff5..8c6e3a1 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -14630,7 +14630,7 @@
         profileNetworkPreferenceBuilder.setPreference(
                 PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
         profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(
-                NetworkCapabilities.NET_ENTERPRISE_ID_2);
+                NET_ENTERPRISE_ID_2);
         registerDefaultNetworkCallbacks();
         testPreferenceForUserNetworkUpDownForGivenPreference(
                 profileNetworkPreferenceBuilder.build(), true,
@@ -14730,14 +14730,13 @@
                 workAgent2.getNetwork().netId,
                 uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
                 PREFERENCE_ORDER_PROFILE));
-        // BUG: the second preference silently replaces the first because they are
-        // both for testHandle.
-        verify(mMockNetd, never()).networkAddUidRangesParcel(new NativeUidRangeConfig(
+        verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
                 workAgent1.getNetwork().netId,
                 uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
                 PREFERENCE_ORDER_PROFILE));
 
-        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+        appCb1.expectAvailableCallbacksValidated(workAgent1);
         appCb2.expectAvailableCallbacksValidated(workAgent2);
 
         // Set preferences for testHandle to map testWorkProfileAppUid3 to
@@ -14760,16 +14759,15 @@
                 workAgent2.getNetwork().netId,
                 uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
                 PREFERENCE_ORDER_PROFILE));
-        // BUG: the second preference should also get removed here unless it was silently
-        // discarded
-        verify(mMockNetd, never()).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+        verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
                 workAgent1.getNetwork().netId,
                 uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
                 PREFERENCE_ORDER_PROFILE));
 
-        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1);
+        assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
         appCb3.expectAvailableCallbacksValidated(workAgent1);
         appCb2.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent);
 
         // Set the preferences for testHandle to default.
         ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =