diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 078fff0bfb..7de7c270ea 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -10659,7 +10659,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Objects.requireNonNull(profile); if (preferences.size() == 0) { - preferences.add((new ProfileNetworkPreference.Builder()).build()); + final ProfileNetworkPreference pref = new ProfileNetworkPreference.Builder() + .setPreference(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT) + .build(); + preferences.add(pref); } PermissionUtils.enforceNetworkStackPermission(mContext); @@ -10677,12 +10680,14 @@ public class ConnectivityService extends IConnectivityManager.Stub final List preferenceList = new ArrayList(); - boolean allowFallback = true; + boolean hasDefaultPreference = false; for (final ProfileNetworkPreference preference : preferences) { final NetworkCapabilities nc; + boolean allowFallback = true; switch (preference.getPreference()) { case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT: nc = null; + hasDefaultPreference = true; if (preference.getPreferenceEnterpriseId() != 0) { throw new IllegalArgumentException( "Invalid enterprise identifier in setProfileNetworkPreferences"); @@ -10692,6 +10697,14 @@ public class ConnectivityService extends IConnectivityManager.Stub allowFallback = false; // continue to process the enterprise preference. case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE: + // This code is needed even though there is a check later on, + // because isRangeAlreadyInPreferenceList assumes that every preference + // has a UID list. + if (hasDefaultPreference) { + throw new IllegalArgumentException( + "Default profile preference should not be set along with other " + + "preference"); + } if (!isEnterpriseIdentifierValid(preference.getPreferenceEnterpriseId())) { throw new IllegalArgumentException( "Invalid enterprise identifier in setProfileNetworkPreferences"); @@ -10715,6 +10728,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } preferenceList.add(new ProfileNetworkPreferenceList.Preference( profile, nc, allowFallback)); + if (hasDefaultPreference && preferenceList.size() > 1) { + throw new IllegalArgumentException( + "Default profile preference should not be set along with other preference"); + } } mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE, new Pair<>(preferenceList, listener))); @@ -10759,12 +10776,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } - private void validateNetworkCapabilitiesOfProfileNetworkPreference( - @Nullable final NetworkCapabilities nc) { - if (null == nc) return; // Null caps are always allowed. It means to remove the setting. - ensureRequestableCapabilities(nc); - } - private ArraySet createNrisFromProfileNetworkPreferences( @NonNull final ProfileNetworkPreferenceList prefs) { final ArraySet result = new ArraySet<>(); @@ -10822,10 +10833,9 @@ public class ConnectivityService extends IConnectivityManager.Stub * Clear all the existing preferences for the user before applying new preferences. * */ - mProfileNetworkPreferences = mProfileNetworkPreferences.clearUser( + mProfileNetworkPreferences = mProfileNetworkPreferences.withoutUser( preferenceList.get(0).user); for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) { - validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities); mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference); } diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java index 473a115a64..5bafef9868 100644 --- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java +++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java @@ -87,7 +87,7 @@ public class ProfileNetworkPreferenceList { /** * Remove all preferences corresponding to a user. */ - public ProfileNetworkPreferenceList clearUser(UserHandle user) { + public ProfileNetworkPreferenceList withoutUser(UserHandle user) { final ArrayList newPrefs = new ArrayList<>(); for (final Preference existingPref : preferences) { if (!existingPref.user.equals(user)) { diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 39a82bd39a..32160c7d21 100644 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -105,6 +105,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_2; +import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_3; +import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_4; +import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5; import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; @@ -471,6 +474,8 @@ public class ConnectivityServiceTest { private static final int TEST_WORK_PROFILE_APP_UID_2 = UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID_2); private static final int TEST_APP_ID_3 = 105; + private static final int TEST_APP_ID_4 = 106; + private static final int TEST_APP_ID_5 = 107; private static final String CLAT_PREFIX = "v4-"; private static final String MOBILE_IFNAME = "test_rmnet_data0"; @@ -14652,6 +14657,40 @@ public class ConnectivityServiceTest { IllegalStateException.class, () -> profileNetworkPreferenceBuilder.build()); } + /** + * Make sure per-profile networking preference throws exception when default preference + * is set along with enterprise preference. + */ + @Test + public void testPreferenceWithInvalidPreferenceDefaultAndEnterpriseTogether() + throws Exception { + final UserHandle testHandle = setupEnterpriseNetwork(); + mServiceContext.setWorkProfile(testHandle, true); + + final int testWorkProfileAppUid1 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID); + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1); + profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1}); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT); + final TestOnCompleteListener listener = new TestOnCompleteListener(); + Assert.assertThrows(IllegalArgumentException.class, + () -> mCm.setProfileNetworkPreferences( + testHandle, List.of(profileNetworkPreferenceBuilder1.build(), + profileNetworkPreferenceBuilder2.build()), + r -> r.run(), listener)); + Assert.assertThrows(IllegalArgumentException.class, + () -> mCm.setProfileNetworkPreferences( + testHandle, List.of(profileNetworkPreferenceBuilder2.build(), + profileNetworkPreferenceBuilder1.build()), + r -> r.run(), listener)); + } + /** * Make sure per profile network preferences behave as expected when two slices with * two different apps within same user profile is configured @@ -14692,6 +14731,7 @@ public class ConnectivityServiceTest { mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb1.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); appCb2.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); appCb3.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); @@ -14792,6 +14832,218 @@ public class ConnectivityServiceTest { // Other callbacks will be unregistered by tearDown() } + /** + * Make sure per profile network preferences behave as expected when multiple slices with + * multiple different apps within same user profile is configured. + */ + @Test + public void testSetPreferenceWithMultiplePreferences() + throws Exception { + final InOrder inOrder = inOrder(mMockNetd); + + final UserHandle testHandle = setupEnterpriseNetwork(); + mServiceContext.setWorkProfile(testHandle, true); + registerDefaultNetworkCallbacks(); + + final TestNetworkCallback appCb1 = new TestNetworkCallback(); + final TestNetworkCallback appCb2 = new TestNetworkCallback(); + final TestNetworkCallback appCb3 = new TestNetworkCallback(); + final TestNetworkCallback appCb4 = new TestNetworkCallback(); + final TestNetworkCallback appCb5 = new TestNetworkCallback(); + + final int testWorkProfileAppUid1 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID); + final int testWorkProfileAppUid2 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_2); + final int testWorkProfileAppUid3 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_3); + final int testWorkProfileAppUid4 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_4); + final int testWorkProfileAppUid5 = + UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_5); + + registerDefaultNetworkCallbackAsUid(appCb1, testWorkProfileAppUid1); + registerDefaultNetworkCallbackAsUid(appCb2, testWorkProfileAppUid2); + registerDefaultNetworkCallbackAsUid(appCb3, testWorkProfileAppUid3); + registerDefaultNetworkCallbackAsUid(appCb4, testWorkProfileAppUid4); + registerDefaultNetworkCallbackAsUid(appCb5, testWorkProfileAppUid5); + + // Connect both a regular cell agent and an enterprise network first. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + final TestNetworkAgentWrapper workAgent1 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_1); + final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_2); + final TestNetworkAgentWrapper workAgent3 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_3); + final TestNetworkAgentWrapper workAgent4 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_4); + final TestNetworkAgentWrapper workAgent5 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_5); + + workAgent1.connect(true); + workAgent2.connect(true); + workAgent3.connect(true); + workAgent4.connect(true); + workAgent5.connect(true); + + mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb1.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb2.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb3.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb4.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + appCb5.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent1.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent3.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent4.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent5.getNetwork().netId, INetd.PERMISSION_SYSTEM)); + + final TestOnCompleteListener listener = new TestOnCompleteListener(); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1); + profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1}); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder2.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK); + profileNetworkPreferenceBuilder2.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_2); + profileNetworkPreferenceBuilder2.setIncludedUids(new int[]{testWorkProfileAppUid2}); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder3 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder3.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + profileNetworkPreferenceBuilder3.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_3); + profileNetworkPreferenceBuilder3.setIncludedUids(new int[]{testWorkProfileAppUid3}); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder4 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder4.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK); + profileNetworkPreferenceBuilder4.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_4); + profileNetworkPreferenceBuilder4.setIncludedUids(new int[]{testWorkProfileAppUid4}); + + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder5 = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder5.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + profileNetworkPreferenceBuilder5.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_5); + profileNetworkPreferenceBuilder5.setIncludedUids(new int[]{testWorkProfileAppUid5}); + + mCm.setProfileNetworkPreferences(testHandle, + List.of(profileNetworkPreferenceBuilder1.build(), + profileNetworkPreferenceBuilder2.build(), + profileNetworkPreferenceBuilder3.build(), + profileNetworkPreferenceBuilder4.build(), + profileNetworkPreferenceBuilder5.build()), + r -> r.run(), listener); + + listener.expectOnComplete(); + + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + workAgent1.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + workAgent2.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + workAgent3.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + workAgent4.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder4.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + workAgent5.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder5.build()), + PREFERENCE_ORDER_PROFILE)); + + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + appCb1.expectAvailableCallbacksValidated(workAgent1); + appCb2.expectAvailableCallbacksValidated(workAgent2); + appCb3.expectAvailableCallbacksValidated(workAgent3); + appCb4.expectAvailableCallbacksValidated(workAgent4); + appCb5.expectAvailableCallbacksValidated(workAgent5); + + workAgent1.disconnect(); + workAgent2.disconnect(); + workAgent3.disconnect(); + workAgent4.disconnect(); + workAgent5.disconnect(); + + appCb1.expectCallback(CallbackEntry.LOST, workAgent1); + appCb2.expectCallback(CallbackEntry.LOST, workAgent2); + appCb3.expectCallback(CallbackEntry.LOST, workAgent3); + appCb4.expectCallback(CallbackEntry.LOST, workAgent4); + appCb5.expectCallback(CallbackEntry.LOST, workAgent5); + + appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent); + appCb2.assertNoCallback(); + appCb3.expectAvailableCallbacksValidated(mCellNetworkAgent); + appCb4.assertNoCallback(); + appCb5.expectAvailableCallbacksValidated(mCellNetworkAgent); + + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd, never()).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd, never()).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder4.build()), + PREFERENCE_ORDER_PROFILE)); + verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, + uidRangeFor(testHandle, profileNetworkPreferenceBuilder5.build()), + PREFERENCE_ORDER_PROFILE)); + + mSystemDefaultNetworkCallback.assertNoCallback(); + mDefaultNetworkCallback.assertNoCallback(); + + // Set the preferences for testHandle to default. + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT); + + mCm.setProfileNetworkPreferences(testHandle, + List.of(profileNetworkPreferenceBuilder.build()), + r -> r.run(), listener); + listener.expectOnComplete(); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1, appCb3, + appCb5); + appCb2.expectAvailableCallbacksValidated(mCellNetworkAgent); + appCb4.expectAvailableCallbacksValidated(mCellNetworkAgent); + mCellNetworkAgent.disconnect(); + + mCm.unregisterNetworkCallback(appCb1); + mCm.unregisterNetworkCallback(appCb2); + mCm.unregisterNetworkCallback(appCb3); + mCm.unregisterNetworkCallback(appCb4); + mCm.unregisterNetworkCallback(appCb5); + // Other callbacks will be unregistered by tearDown() + } + /** * Test that, in a given networking context, calling setPreferenceForUser to set per-profile * defaults on then off works as expected.