Implement setNetworkPreferenceForUser.
Test: FrameworksNetTests Change-Id: I8f18083b5857289892fe8adea5f5ea3f5dbe0809
This commit is contained in:
@@ -72,6 +72,14 @@ public final class OemNetworkPreferences implements Parcelable {
|
||||
@NonNull
|
||||
private final Bundle mNetworkMappings;
|
||||
|
||||
/**
|
||||
* Return whether this object is empty.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return mNetworkMappings.keySet().size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the currently built application package name to {@link OemNetworkPreference} mappings.
|
||||
* @return the current network preferences map.
|
||||
|
||||
@@ -37,6 +37,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
|
||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
|
||||
@@ -215,6 +216,7 @@ import com.android.server.connectivity.NetworkNotificationManager;
|
||||
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
|
||||
import com.android.server.connectivity.NetworkRanker;
|
||||
import com.android.server.connectivity.PermissionMonitor;
|
||||
import com.android.server.connectivity.ProfileNetworkPreferences;
|
||||
import com.android.server.connectivity.ProxyTracker;
|
||||
import com.android.server.connectivity.QosCallbackTracker;
|
||||
import com.android.server.net.NetworkPolicyManagerInternal;
|
||||
@@ -559,8 +561,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
|
||||
|
||||
/**
|
||||
* used internally when setting the default networks for OemNetworkPreferences.
|
||||
* obj = OemNetworkPreferences
|
||||
* Used internally when setting the default networks for OemNetworkPreferences.
|
||||
* obj = Pair<OemNetworkPreferences, listener>
|
||||
*/
|
||||
private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
|
||||
|
||||
@@ -569,6 +571,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
*/
|
||||
private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49;
|
||||
|
||||
/**
|
||||
* Used internally when setting a network preference for a user profile.
|
||||
* obj = Pair<ProfileNetworkPreference, Listener>
|
||||
*/
|
||||
private static final int EVENT_SET_PROFILE_NETWORK_PREFERENCE = 50;
|
||||
|
||||
/**
|
||||
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
|
||||
* should be shown.
|
||||
@@ -1290,11 +1298,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
|
||||
return createDefaultNetworkCapabilitiesForUidRange(new UidRange(uid, uid));
|
||||
}
|
||||
|
||||
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUidRange(
|
||||
@NonNull final UidRange uids) {
|
||||
final NetworkCapabilities netCap = new NetworkCapabilities();
|
||||
netCap.addCapability(NET_CAPABILITY_INTERNET);
|
||||
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
|
||||
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
|
||||
netCap.setSingleUid(uid);
|
||||
netCap.setUids(Collections.singleton(uids));
|
||||
return netCap;
|
||||
}
|
||||
|
||||
@@ -4537,6 +4550,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
handleSetOemNetworkPreference(arg.first, arg.second);
|
||||
break;
|
||||
}
|
||||
case EVENT_SET_PROFILE_NETWORK_PREFERENCE: {
|
||||
final Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener> arg =
|
||||
(Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
|
||||
msg.obj;
|
||||
handleSetProfileNetworkPreference(arg.first, arg.second);
|
||||
}
|
||||
case EVENT_REPORT_NETWORK_ACTIVITY:
|
||||
mNetworkActivityTracker.handleReportNetworkActivity();
|
||||
break;
|
||||
@@ -5897,10 +5916,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
@GuardedBy("mBlockedAppUids")
|
||||
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
|
||||
|
||||
// Current OEM network preferences.
|
||||
// Current OEM network preferences. This object must only be written to on the handler thread.
|
||||
// Since it is immutable and always non-null, other threads may read it if they only care
|
||||
// about seeing a consistent object but not that it is current.
|
||||
@NonNull
|
||||
private OemNetworkPreferences mOemNetworkPreferences =
|
||||
new OemNetworkPreferences.Builder().build();
|
||||
// Current per-profile network preferences. This object follows the same threading rules as
|
||||
// the OEM network preferences above.
|
||||
@NonNull
|
||||
private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
|
||||
|
||||
// The always-on request for an Internet-capable network that apps without a specific default
|
||||
// fall back to.
|
||||
@@ -9101,13 +9126,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mQosCallbackTracker.unregisterCallback(callback);
|
||||
}
|
||||
|
||||
private void enforceAutomotiveDevice() {
|
||||
final boolean isAutomotiveDevice =
|
||||
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||
if (!isAutomotiveDevice) {
|
||||
throw new UnsupportedOperationException(
|
||||
"setOemNetworkPreference() is only available on automotive devices.");
|
||||
}
|
||||
// Network preference per-profile and OEM network preferences can't be set at the same
|
||||
// time, because it is unclear what should happen if both preferences are active for
|
||||
// one given UID. To make it possible, the stack would have to clarify what would happen
|
||||
// in case both are active at the same time. The implementation may have to be adjusted
|
||||
// to implement the resulting rules. For example, a priority could be defined between them,
|
||||
// where the OEM preference would be considered less or more important than the enterprise
|
||||
// preference ; this would entail implementing the priorities somehow, e.g. by doing
|
||||
// UID arithmetic with UID ranges or passing a priority to netd so that the routing rules
|
||||
// are set at the right level. Other solutions are possible, e.g. merging of the
|
||||
// preferences for the relevant UIDs.
|
||||
private static void throwConcurrentPreferenceException() {
|
||||
throw new IllegalStateException("Can't set NetworkPreferenceForUser and "
|
||||
+ "set OemNetworkPreference at the same time");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9125,7 +9156,118 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
public void setProfileNetworkPreference(@NonNull final UserHandle profile,
|
||||
@ConnectivityManager.ProfileNetworkPreference final int preference,
|
||||
@Nullable final IOnCompleteListener listener) {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
Objects.requireNonNull(profile);
|
||||
PermissionUtils.enforceNetworkStackPermission(mContext);
|
||||
if (DBG) {
|
||||
log("setProfileNetworkPreference " + profile + " to " + preference);
|
||||
}
|
||||
if (profile.getIdentifier() < 0) {
|
||||
throw new IllegalArgumentException("Must explicitly specify a user handle ("
|
||||
+ "UserHandle.CURRENT not supported)");
|
||||
}
|
||||
final UserManager um;
|
||||
try {
|
||||
um = mContext.createContextAsUser(profile, 0 /* flags */)
|
||||
.getSystemService(UserManager.class);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new IllegalArgumentException("Profile does not exist");
|
||||
}
|
||||
if (!um.isManagedProfile()) {
|
||||
throw new IllegalArgumentException("Profile must be a managed profile");
|
||||
}
|
||||
// Strictly speaking, mOemNetworkPreferences should only be touched on the
|
||||
// handler thread. However it is an immutable object, so reading the reference is
|
||||
// safe - it's just possible the value is slightly outdated. For the final check,
|
||||
// see #handleSetProfileNetworkPreference. But if this can be caught here it is a
|
||||
// lot easier to understand, so opportunistically check it.
|
||||
if (!mOemNetworkPreferences.isEmpty()) {
|
||||
throwConcurrentPreferenceException();
|
||||
}
|
||||
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");
|
||||
}
|
||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
|
||||
new Pair<>(new ProfileNetworkPreferences.Preference(profile, nc), listener)));
|
||||
}
|
||||
|
||||
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<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
|
||||
@NonNull final ProfileNetworkPreferences prefs) {
|
||||
final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
|
||||
for (final ProfileNetworkPreferences.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.
|
||||
final ArrayList<NetworkRequest> nrs = new ArrayList<>();
|
||||
nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities));
|
||||
nrs.add(createDefaultRequest());
|
||||
setNetworkRequestUids(nrs, pref.capabilities.getUids());
|
||||
final NetworkRequestInfo nri = new NetworkRequestInfo(nrs);
|
||||
result.add(nri);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void handleSetProfileNetworkPreference(
|
||||
@NonNull final ProfileNetworkPreferences.Preference preference,
|
||||
@Nullable final IOnCompleteListener listener) {
|
||||
// setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
|
||||
// particular because it's not clear what preference should win in case both apply
|
||||
// to the same app.
|
||||
// The binder call has already checked this, but as mOemNetworkPreferences is only
|
||||
// touched on the handler thread, it's theoretically not impossible that it has changed
|
||||
// since.
|
||||
if (!mOemNetworkPreferences.isEmpty()) {
|
||||
logwtf("handleSetProfileNetworkPreference, but OEM network preferences not empty");
|
||||
return;
|
||||
}
|
||||
|
||||
validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
|
||||
|
||||
mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
|
||||
final ArraySet<NetworkRequestInfo> nris =
|
||||
createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
|
||||
replaceDefaultNetworkRequestsForPreference(nris);
|
||||
// Finally, rematch.
|
||||
rematchAllNetworksAndRequests();
|
||||
|
||||
if (null != listener) {
|
||||
try {
|
||||
listener.onComplete();
|
||||
} catch (RemoteException e) {
|
||||
loge("Listener for setProfileNetworkPreference has died");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceAutomotiveDevice() {
|
||||
final boolean isAutomotiveDevice =
|
||||
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||
if (!isAutomotiveDevice) {
|
||||
throw new UnsupportedOperationException(
|
||||
"setOemNetworkPreference() is only available on automotive devices.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9148,6 +9290,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
enforceAutomotiveDevice();
|
||||
enforceOemNetworkPreferencesPermission();
|
||||
|
||||
if (!mProfileNetworkPreferences.isEmpty()) {
|
||||
// Strictly speaking, mProfileNetworkPreferences should only be touched on the
|
||||
// handler thread. However it is an immutable object, so reading the reference is
|
||||
// safe - it's just possible the value is slightly outdated. For the final check,
|
||||
// see #handleSetOemPreference. But if this can be caught here it is a
|
||||
// lot easier to understand, so opportunistically check it.
|
||||
throwConcurrentPreferenceException();
|
||||
}
|
||||
|
||||
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
|
||||
validateOemNetworkPreferences(preference);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
|
||||
@@ -9171,6 +9322,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
if (DBG) {
|
||||
log("set OEM network preferences :" + preference.toString());
|
||||
}
|
||||
// setProfileNetworkPreference and setOemNetworkPreference are mutually exclusive, in
|
||||
// particular because it's not clear what preference should win in case both apply
|
||||
// to the same app.
|
||||
// The binder call has already checked this, but as mOemNetworkPreferences is only
|
||||
// touched on the handler thread, it's theoretically not impossible that it has changed
|
||||
// since.
|
||||
if (!mProfileNetworkPreferences.isEmpty()) {
|
||||
logwtf("handleSetOemPreference, but per-profile network preferences not empty");
|
||||
return;
|
||||
}
|
||||
|
||||
final ArraySet<NetworkRequestInfo> nris =
|
||||
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
|
||||
replaceDefaultNetworkRequestsForPreference(nris);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 com.android.server.connectivity;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A data class containing all the per-profile network preferences.
|
||||
*
|
||||
* A given profile can only have one preference.
|
||||
*/
|
||||
public class ProfileNetworkPreferences {
|
||||
/**
|
||||
* A single preference, as it applies to a given user profile.
|
||||
*/
|
||||
public static class Preference {
|
||||
@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 Preference(@NonNull final UserHandle user,
|
||||
@Nullable final NetworkCapabilities capabilities) {
|
||||
this.user = user;
|
||||
this.capabilities = null == capabilities ? null : new NetworkCapabilities(capabilities);
|
||||
}
|
||||
|
||||
/** toString */
|
||||
public String toString() {
|
||||
return "[ProfileNetworkPreference user=" + user + " caps=" + capabilities + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull public final List<Preference> preferences;
|
||||
|
||||
public ProfileNetworkPreferences() {
|
||||
preferences = Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
private ProfileNetworkPreferences(@NonNull final List<Preference> list) {
|
||||
preferences = Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public ProfileNetworkPreferences 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);
|
||||
}
|
||||
}
|
||||
if (null != pref.capabilities) {
|
||||
newPrefs.add(pref);
|
||||
}
|
||||
return new ProfileNetworkPreferences(newPrefs);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return preferences.isEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user