diff --git a/framework/aidl-export/android/net/ProfileNetworkPreference.aidl b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl new file mode 100644 index 0000000000..d7f2402d34 --- /dev/null +++ b/framework/aidl-export/android/net/ProfileNetworkPreference.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 android.net; + +parcelable ProfileNetworkPreference; diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt index 81a1e5dd62..7a0142a6f4 100644 --- a/framework/api/module-lib-current.txt +++ b/framework/api/module-lib-current.txt @@ -19,7 +19,8 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network); method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable); + method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreferences(@NonNull android.os.UserHandle, @NonNull java.util.List, @Nullable java.util.concurrent.Executor, @Nullable Runnable); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection>); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network); @@ -149,6 +150,19 @@ package android.net { method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set>); } + public final class ProfileNetworkPreference implements android.os.Parcelable { + method public int describeContents(); + method public int getPreference(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class ProfileNetworkPreference.Builder { + ctor public ProfileNetworkPreference.Builder(); + method @NonNull public android.net.ProfileNetworkPreference build(); + method @NonNull public android.net.ProfileNetworkPreference.Builder setPreference(int); + } + public final class TestNetworkInterface implements android.os.Parcelable { ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); method public int describeContents(); diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java index c21bcfa18a..ba8aabb646 100644 --- a/framework/src/android/net/ConnectivityManager.java +++ b/framework/src/android/net/ConnectivityManager.java @@ -1101,7 +1101,7 @@ public class ConnectivityManager { PROFILE_NETWORK_PREFERENCE_DEFAULT, PROFILE_NETWORK_PREFERENCE_ENTERPRISE }) - public @interface ProfileNetworkPreference { + public @interface ProfileNetworkPreferencePolicy { } /** @@ -5461,6 +5461,8 @@ public class ConnectivityManager { * @param listener an optional listener to listen for completion of the operation. * @throws IllegalArgumentException if {@code profile} is not a valid user profile. * @throws SecurityException if missing the appropriate permissions. + * @deprecated Use {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)} + * instead as it provides a more flexible API with more options. * @hide */ // This function is for establishing per-profile default networking and can only be called by @@ -5470,8 +5472,45 @@ public class ConnectivityManager { @SuppressLint({"UserHandle"}) @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + @Deprecated public void setProfileNetworkPreference(@NonNull final UserHandle profile, - @ProfileNetworkPreference final int preference, + @ProfileNetworkPreferencePolicy final int preference, + @Nullable @CallbackExecutor final Executor executor, + @Nullable final Runnable listener) { + + ProfileNetworkPreference.Builder preferenceBuilder = + new ProfileNetworkPreference.Builder(); + preferenceBuilder.setPreference(preference); + setProfileNetworkPreferences(profile, + List.of(preferenceBuilder.build()), executor, listener); + } + + /** + * Set a list of default network selection policies for a user profile. + * + * Calling this API with a user handle defines the entire policy for that user handle. + * It will overwrite any setting previously set for the same user profile, + * and not affect previously set settings for other handles. + * + * Call this API with an empty list to remove settings for this user profile. + * + * See {@link ProfileNetworkPreference} for more details on each preference + * parameter. + * + * @param profile the user profile for which the preference is being set. + * @param profileNetworkPreferences the list of profile network preferences for the + * provided profile. + * @param executor an executor to execute the listener on. Optional if listener is null. + * @param listener an optional listener to listen for completion of the operation. + * @throws IllegalArgumentException if {@code profile} is not a valid user profile. + * @throws SecurityException if missing the appropriate permissions. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + public void setProfileNetworkPreferences( + @NonNull final UserHandle profile, + @NonNull List profileNetworkPreferences, @Nullable @CallbackExecutor final Executor executor, @Nullable final Runnable listener) { if (null != listener) { @@ -5489,7 +5528,7 @@ public class ConnectivityManager { }; } try { - mService.setProfileNetworkPreference(profile, preference, proxy); + mService.setProfileNetworkPreferences(profile, profileNetworkPreferences, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl index 50ec78120f..7c39bec277 100644 --- a/framework/src/android/net/IConnectivityManager.aidl +++ b/framework/src/android/net/IConnectivityManager.aidl @@ -36,6 +36,7 @@ import android.net.NetworkScore; import android.net.NetworkState; import android.net.NetworkStateSnapshot; import android.net.OemNetworkPreferences; +import android.net.ProfileNetworkPreference; import android.net.ProxyInfo; import android.net.UidRange; import android.net.QosSocketInfo; @@ -218,7 +219,8 @@ interface IConnectivityManager void setOemNetworkPreference(in OemNetworkPreferences preference, in IOnCompleteListener listener); - void setProfileNetworkPreference(in UserHandle profile, int preference, + void setProfileNetworkPreferences(in UserHandle profile, + in List preferences, in IOnCompleteListener listener); int getRestrictBackgroundStatusByCaller(); diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java new file mode 100644 index 0000000000..d580209dec --- /dev/null +++ b/framework/src/android/net/ProfileNetworkPreference.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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 android.net; + +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.ConnectivityManager.ProfileNetworkPreferencePolicy; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Network preferences to be set for the user profile + * {@link ProfileNetworkPreferencePolicy}. + * @hide + */ +@SystemApi(client = MODULE_LIBRARIES) +public final class ProfileNetworkPreference implements Parcelable { + private final @ProfileNetworkPreferencePolicy int mPreference; + + private ProfileNetworkPreference(int preference) { + mPreference = preference; + } + + private ProfileNetworkPreference(Parcel in) { + mPreference = in.readInt(); + } + + public int getPreference() { + return mPreference; + } + + @Override + public String toString() { + return "ProfileNetworkPreference{" + + "mPreference=" + getPreference() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ProfileNetworkPreference that = (ProfileNetworkPreference) o; + return mPreference == that.mPreference; + } + + @Override + public int hashCode() { + return (mPreference); + } + + /** + * Builder used to create {@link ProfileNetworkPreference} objects. + * Specify the preferred Network preference + */ + public static final class Builder { + private @ProfileNetworkPreferencePolicy int mPreference = + PROFILE_NETWORK_PREFERENCE_DEFAULT; + + /** + * Constructs an empty Builder with PROFILE_NETWORK_PREFERENCE_DEFAULT profile preference + */ + public Builder() {} + + /** + * Set the profile network preference + * See the documentation for the individual preferences for a description of the supported + * behaviors. Default value is PROFILE_NETWORK_PREFERENCE_DEFAULT. + * @param preference the desired network preference to use + * @return The builder to facilitate chaining. + */ + @NonNull + public Builder setPreference(@ProfileNetworkPreferencePolicy int preference) { + mPreference = preference; + return this; + } + /** + * Returns an instance of {@link ProfileNetworkPreference} created from the + * fields set on this builder. + */ + @NonNull + public ProfileNetworkPreference build() { + return new ProfileNetworkPreference(mPreference); + } + } + + @Override + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + dest.writeInt(mPreference); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public ProfileNetworkPreference[] newArray(int size) { + return new ProfileNetworkPreference[size]; + } + + @Override + public ProfileNetworkPreference createFromParcel( + @NonNull android.os.Parcel in) { + return new ProfileNetworkPreference(in); + } + }; +} diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index a59b5d6178..17474508b4 100644 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -166,6 +166,7 @@ import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; import android.net.OemNetworkPreferences; import android.net.PrivateDnsConfigParcel; +import android.net.ProfileNetworkPreference; import android.net.ProxyInfo; import android.net.QosCallbackException; import android.net.QosFilter; @@ -258,7 +259,7 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy import com.android.server.connectivity.NetworkOffer; import com.android.server.connectivity.NetworkRanker; import com.android.server.connectivity.PermissionMonitor; -import com.android.server.connectivity.ProfileNetworkPreferences; +import com.android.server.connectivity.ProfileNetworkPreferenceList; import com.android.server.connectivity.ProxyTracker; import com.android.server.connectivity.QosCallbackTracker; @@ -5046,9 +5047,10 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case EVENT_SET_PROFILE_NETWORK_PREFERENCE: { - final Pair arg = - (Pair) - msg.obj; + final Pair, + IOnCompleteListener> arg = + (Pair, + IOnCompleteListener>) msg.obj; handleSetProfileNetworkPreference(arg.first, arg.second); break; } @@ -5671,7 +5673,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserRemoved(@NonNull final UserHandle user) { mPermissionMonitor.onUserRemoved(user); // If there was a network preference for this user, remove it. - handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null), + handleSetProfileNetworkPreference( + List.of(new ProfileNetworkPreferenceList.Preference(user, null)), null /* listener */); if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) { handleSetOemNetworkPreference(mOemNetworkPreferences, null); @@ -6605,7 +6608,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Current per-profile network preferences. This object follows the same threading rules as // the OEM network preferences above. @NonNull - private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences(); + private ProfileNetworkPreferenceList mProfileNetworkPreferences = + new ProfileNetworkPreferenceList(); // A set of UIDs that should use mobile data preferentially if available. This object follows // the same threading rules as the OEM network preferences above. @@ -10103,19 +10107,26 @@ public class ConnectivityService extends IConnectivityManager.Stub * See the documentation for the individual preferences for a description of the supported * behaviors. * - * @param profile the profile concerned. - * @param preference the preference for this profile, as one of the PROFILE_NETWORK_PREFERENCE_* - * constants. + * @param profile the user profile for whih the preference is being set. + * @param preferences the list of profile network preferences for the + * provided profile. * @param listener an optional listener to listen for completion of the operation. */ @Override - public void setProfileNetworkPreference(@NonNull final UserHandle profile, - @ConnectivityManager.ProfileNetworkPreference final int preference, + public void setProfileNetworkPreferences( + @NonNull final UserHandle profile, + @NonNull List preferences, @Nullable final IOnCompleteListener listener) { + Objects.requireNonNull(preferences); Objects.requireNonNull(profile); + + if (preferences.size() == 0) { + preferences.add((new ProfileNetworkPreference.Builder()).build()); + } + PermissionUtils.enforceNetworkStackPermission(mContext); if (DBG) { - log("setProfileNetworkPreference " + profile + " to " + preference); + log("setProfileNetworkPreferences " + profile + " to " + preferences); } if (profile.getIdentifier() < 0) { throw new IllegalArgumentException("Must explicitly specify a user handle (" @@ -10126,23 +10137,29 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new IllegalArgumentException("Profile must be a managed profile"); } - final NetworkCapabilities nc; - switch (preference) { - case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT: - nc = null; - break; - case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE: - final UidRange uids = UidRange.createForUser(profile); - nc = createDefaultNetworkCapabilitiesForUidRange(uids); - nc.addCapability(NET_CAPABILITY_ENTERPRISE); - nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); - break; - default: - throw new IllegalArgumentException( - "Invalid preference in setProfileNetworkPreference"); + final List preferenceList = + new ArrayList(); + 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: + final UidRange uids = UidRange.createForUser(profile); + nc = createDefaultNetworkCapabilitiesForUidRange(uids); + nc.addCapability(NET_CAPABILITY_ENTERPRISE); + nc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + break; + default: + throw new IllegalArgumentException( + "Invalid preference in setProfileNetworkPreferences"); + } + preferenceList.add( + new ProfileNetworkPreferenceList.Preference(profile, nc)); } mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE, - new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener))); + new Pair<>(preferenceList, listener))); } private void validateNetworkCapabilitiesOfProfileNetworkPreference( @@ -10152,9 +10169,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } private ArraySet createNrisFromProfileNetworkPreferences( - @NonNull final ProfileNetworkPreferences prefs) { + @NonNull final ProfileNetworkPreferenceList prefs) { final ArraySet result = new ArraySet<>(); - for (final ProfileNetworkPreferences.Preference pref : prefs.preferences) { + 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 @@ -10175,11 +10192,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void handleSetProfileNetworkPreference( - @NonNull final ProfileNetworkPreferences.Preference preference, + @NonNull final List preferenceList, @Nullable final IOnCompleteListener listener) { - validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities); - - mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference); + 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/ProfileNetworkPreferences.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java similarity index 89% rename from service/src/com/android/server/connectivity/ProfileNetworkPreferences.java rename to service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java index dd2815d9e2..b345ede3e4 100644 --- a/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java +++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java @@ -30,7 +30,7 @@ import java.util.List; * * A given profile can only have one preference. */ -public class ProfileNetworkPreferences { +public class ProfileNetworkPreferenceList { /** * A single preference, as it applies to a given user profile. */ @@ -53,11 +53,11 @@ public class ProfileNetworkPreferences { @NonNull public final List preferences; - public ProfileNetworkPreferences() { + public ProfileNetworkPreferenceList() { preferences = Collections.EMPTY_LIST; } - private ProfileNetworkPreferences(@NonNull final List list) { + private ProfileNetworkPreferenceList(@NonNull final List list) { preferences = Collections.unmodifiableList(list); } @@ -68,7 +68,7 @@ public class ProfileNetworkPreferences { * preference. Passing a Preference object containing a null capabilities object is equivalent * to (and indeed, implemented as) removing the preference for this user. */ - public ProfileNetworkPreferences plus(@NonNull final Preference pref) { + public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) { final ArrayList newPrefs = new ArrayList<>(); for (final Preference existingPref : preferences) { if (!existingPref.user.equals(pref.user)) { @@ -78,7 +78,7 @@ public class ProfileNetworkPreferences { if (null != pref.capabilities) { newPrefs.add(pref); } - return new ProfileNetworkPreferences(newPrefs); + return new ProfileNetworkPreferenceList(newPrefs); } public boolean isEmpty() {