diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt index 7a0142a6f4..b8dfd3db70 100644 --- a/framework/api/module-lib-current.txt +++ b/framework/api/module-lib-current.txt @@ -41,6 +41,7 @@ package android.net { field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8 field public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; // 0x0 field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1 + field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2; // 0x2 } public static class ConnectivityManager.NetworkCallback { diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java index ba8aabb646..9735252745 100644 --- a/framework/src/android/net/ConnectivityManager.java +++ b/framework/src/android/net/ConnectivityManager.java @@ -1078,7 +1078,8 @@ public class ConnectivityManager { } /** - * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}. + * Preference for {@link ProfileNetworkPreference#setPreference(int)}. + * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)} * Specify that the traffic for this user should by follow the default rules. * @hide */ @@ -1086,7 +1087,8 @@ public class ConnectivityManager { public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0; /** - * Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}. + * Preference for {@link ProfileNetworkPreference#setPreference(int)}. + * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)} * Specify that the traffic for this user should by default go on a network with * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network * if no such network is available. @@ -1095,11 +1097,23 @@ public class ConnectivityManager { @SystemApi(client = MODULE_LIBRARIES) public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; + /** + * Preference for {@link ProfileNetworkPreference#setPreference(int)}. + * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)} + * Specify that the traffic for this user should by default go on a network with + * {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE} and if no such network is available + * should not go on the system default network + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK = 2; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { PROFILE_NETWORK_PREFERENCE_DEFAULT, - PROFILE_NETWORK_PREFERENCE_ENTERPRISE + PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK }) public @interface ProfileNetworkPreferencePolicy { } diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java index d580209dec..2ce1698cf0 100644 --- a/framework/src/android/net/ProfileNetworkPreference.java +++ b/framework/src/android/net/ProfileNetworkPreference.java @@ -63,7 +63,7 @@ public final class ProfileNetworkPreference implements Parcelable { @Override public int hashCode() { - return (mPreference); + return mPreference; } /** @@ -91,6 +91,7 @@ public final class ProfileNetworkPreference implements Parcelable { mPreference = preference; return this; } + /** * Returns an instance of {@link ProfileNetworkPreference} created from the * fields set on this builder. diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index e4faf85c37..a90653719b 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -5674,7 +5674,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mPermissionMonitor.onUserRemoved(user); // If there was a network preference for this user, remove it. handleSetProfileNetworkPreference( - List.of(new ProfileNetworkPreferenceList.Preference(user, null)), + List.of(new ProfileNetworkPreferenceList.Preference(user, null, true)), null /* listener */); if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) { handleSetOemNetworkPreference(mOemNetworkPreferences, null); @@ -10139,12 +10139,16 @@ public class ConnectivityService extends IConnectivityManager.Stub final List preferenceList = new ArrayList(); + boolean allowFallback = true; for (final ProfileNetworkPreference preference : preferences) { final NetworkCapabilities nc; switch (preference.getPreference()) { case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT: nc = null; break; + case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK: + allowFallback = false; + // continue to process the enterprise preference. case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE: final UidRange uids = UidRange.createForUser(profile); nc = createDefaultNetworkCapabilitiesForUidRange(uids); @@ -10155,8 +10159,8 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new IllegalArgumentException( "Invalid preference in setProfileNetworkPreferences"); } - preferenceList.add( - new ProfileNetworkPreferenceList.Preference(profile, nc)); + preferenceList.add(new ProfileNetworkPreferenceList.Preference( + profile, nc, allowFallback)); } mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE, new Pair<>(preferenceList, listener))); @@ -10172,17 +10176,17 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull final ProfileNetworkPreferenceList prefs) { final ArraySet result = new ArraySet<>(); for (final ProfileNetworkPreferenceList.Preference pref : prefs.preferences) { - // The NRI for a user should be comprised of two layers: - // - The request for the capabilities - // - The request for the default network, for fallback. Create an image of it to - // have the correct UIDs in it (also a request can only be part of one NRI, because - // of lookups in 1:1 associations like mNetworkRequests). - // Note that denying a fallback can be implemented simply by not adding the second - // request. + // The NRI for a user should contain the request for capabilities. + // If fallback to default network is needed then NRI should include + // the request for the default network. Create an image of it to + // have the correct UIDs in it (also a request can only be part of one NRI, because + // of lookups in 1:1 associations like mNetworkRequests). final ArrayList nrs = new ArrayList<>(); nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities)); - nrs.add(createDefaultInternetRequestForTransport( - TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT)); + if (pref.allowFallback) { + nrs.add(createDefaultInternetRequestForTransport( + TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT)); + } setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids())); final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs, PREFERENCE_ORDER_PROFILE); diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java index b345ede3e4..71f342d78c 100644 --- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java +++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java @@ -38,16 +38,22 @@ public class ProfileNetworkPreferenceList { @NonNull public final UserHandle user; // Capabilities are only null when sending an object to remove the setting for a user @Nullable public final NetworkCapabilities capabilities; + public final boolean allowFallback; public Preference(@NonNull final UserHandle user, - @Nullable final NetworkCapabilities capabilities) { + @Nullable final NetworkCapabilities capabilities, + final boolean allowFallback) { this.user = user; this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities); + this.allowFallback = allowFallback; } /** toString */ public String toString() { - return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]"; + return "[ProfileNetworkPreference user=" + user + + " caps=" + capabilities + + " allowFallback=" + allowFallback + + "]"; } } diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 8d9155282e..abb34dcce7 100644 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -51,6 +51,7 @@ import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; +import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; @@ -250,6 +251,7 @@ import android.net.NetworkStateSnapshot; import android.net.NetworkTestResultParcelable; import android.net.OemNetworkPreferences; import android.net.PacProxyManager; +import android.net.ProfileNetworkPreference; import android.net.Proxy; import android.net.ProxyInfo; import android.net.QosCallbackException; @@ -13755,12 +13757,12 @@ public class ConnectivityServiceTest { } /** - * Make sure per-profile networking preference behaves as expected when the enterprise network - * goes up and down while the preference is active. Make sure they behave as expected whether - * there is a general default network or not. + * Make sure per profile network preferences behave as expected for a given + * profile network preference. */ - @Test - public void testPreferenceForUserNetworkUpDown() throws Exception { + public void testPreferenceForUserNetworkUpDownForGivenPreference( + ProfileNetworkPreference profileNetworkPreference, + boolean connectWorkProfileAgentAhead) throws Exception { final InOrder inOrder = inOrder(mMockNetd); final UserHandle testHandle = setupEnterpriseNetwork(); registerDefaultNetworkCallbacks(); @@ -13774,29 +13776,45 @@ public class ConnectivityServiceTest { inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); + if (connectWorkProfileAgentAhead) { + workAgent.connect(false); + } final TestOnCompleteListener listener = new TestOnCompleteListener(); - mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, + mCm.setProfileNetworkPreferences(testHandle, List.of(profileNetworkPreference), r -> r.run(), listener); listener.expectOnComplete(); - - // Setting a network preference for this user will create a new set of routing rules for - // the UID range that corresponds to this user, so as to define the default network - // for these apps separately. This is true because the multi-layer request relevant to - // this UID range contains a TRACK_DEFAULT, so the range will be moved through UID-specific - // rules to the correct network – in this case the system default network. The case where - // the default network for the profile happens to be the same as the system default - // is not handled specially, the rules are always active as long as a preference is set. - inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( - mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), - PREFERENCE_ORDER_PROFILE)); + boolean allowFallback = true; + if (profileNetworkPreference.getPreference() + == PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) { + allowFallback = false; + } + if (allowFallback) { + // Setting a network preference for this user will create a new set of routing rules for + // the UID range that corresponds to this user, inorder to define the default network + // for these apps separately. This is true because the multi-layer request relevant to + // this UID range contains a TRACK_DEFAULT, so the range will be moved through + // UID-specific rules to the correct network – in this case the system default network. + // The case where the default network for the profile happens to be the same as the + // system default is not handled specially, the rules are always active as long as + // a preference is set. + inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), + PREFERENCE_ORDER_PROFILE)); + } // The enterprise network is not ready yet. - assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, - mProfileDefaultNetworkCallback); + assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); + if (allowFallback) { + assertNoCallbacks(mProfileDefaultNetworkCallback); + } else if (!connectWorkProfileAgentAhead) { + mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + } - final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent(); - workAgent.connect(false); + if (!connectWorkProfileAgentAhead) { + workAgent.connect(false); + } mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent); mSystemDefaultNetworkCallback.assertNoCallback(); @@ -13805,9 +13823,12 @@ public class ConnectivityServiceTest { nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( workAgent.getNetwork().netId, uidRangeFor(testHandle), PREFERENCE_ORDER_PROFILE)); - inOrder.verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig( - mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), - PREFERENCE_ORDER_PROFILE)); + + if (allowFallback) { + inOrder.verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), + PREFERENCE_ORDER_PROFILE)); + } // Make sure changes to the work agent send callbacks to the app in the work profile, but // not to the other apps. @@ -13853,17 +13874,23 @@ public class ConnectivityServiceTest { // default network. workAgent.disconnect(); mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent); - mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + if (allowFallback) { + mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + } assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( - mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), - PREFERENCE_ORDER_PROFILE)); + if (allowFallback) { + inOrder.verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig( + mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle), + PREFERENCE_ORDER_PROFILE)); + } inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId); mCellNetworkAgent.disconnect(); mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + if (allowFallback) { + mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + } // Waiting for the handler to be idle before checking for networkDestroy is necessary // here because ConnectivityService calls onLost before the network is fully torn down. @@ -13891,7 +13918,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); inOrder.verify(mMockNetd, never()).networkAddUidRangesParcel(any()); - // When the agent disconnects, test that the app on the work profile falls back to the + // When the agent disconnects, test that the app on the work profile fall back to the // default network. workAgent2.disconnect(); mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2); @@ -13904,6 +13931,52 @@ public class ConnectivityServiceTest { // Callbacks will be unregistered by tearDown() } + /** + * Make sure per-profile networking preference behaves as expected when the enterprise network + * goes up and down while the preference is active. Make sure they behave as expected whether + * there is a general default network or not. + */ + @Test + public void testPreferenceForUserNetworkUpDown() throws Exception { + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE); + testPreferenceForUserNetworkUpDownForGivenPreference( + profileNetworkPreferenceBuilder.build(), false); + } + + /** + * Make sure per-profile networking preference behaves as expected when the enterprise network + * goes up and down while the preference is active. Make sure they behave as expected whether + * there is a general default network or not when configured to not fallback to default network. + */ + @Test + public void testPreferenceForUserNetworkUpDownWithNoFallback() throws Exception { + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK); + testPreferenceForUserNetworkUpDownForGivenPreference( + profileNetworkPreferenceBuilder.build(), false); + } + + /** + * Make sure per-profile networking preference behaves as expected when the enterprise network + * goes up and down while the preference is active. Make sure they behave as expected whether + * there is a general default network or not when configured to not fallback to default network + * along with already connected enterprise work agent + */ + @Test + public void testPreferenceForUserNetworkUpDownWithNoFallbackWithAlreadyConnectedWorkAgent() + throws Exception { + ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder = + new ProfileNetworkPreference.Builder(); + profileNetworkPreferenceBuilder.setPreference( + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK); + testPreferenceForUserNetworkUpDownForGivenPreference( + profileNetworkPreferenceBuilder.build(), true); + } + /** * Test that, in a given networking context, calling setPreferenceForUser to set per-profile * defaults on then off works as expected. @@ -14057,7 +14130,8 @@ public class ConnectivityServiceTest { assertThrows("Should not be able to set an illegal preference", IllegalArgumentException.class, () -> mCm.setProfileNetworkPreference(testHandle, - PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null)); + PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK + 1, + null, null)); } /**