Implement setNetworkPreferenceForUser.
Test: FrameworksNetTests Change-Id: I8f18083b5857289892fe8adea5f5ea3f5dbe0809
This commit is contained in:
@@ -72,6 +72,14 @@ public final class OemNetworkPreferences implements Parcelable {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private final Bundle mNetworkMappings;
|
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 currently built application package name to {@link OemNetworkPreference} mappings.
|
||||||
* @return the current network preferences map.
|
* @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_PARTIAL;
|
||||||
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
|
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_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_FOREGROUND;
|
||||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
|
||||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
|
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.NetworkNotificationManager.NotificationType;
|
||||||
import com.android.server.connectivity.NetworkRanker;
|
import com.android.server.connectivity.NetworkRanker;
|
||||||
import com.android.server.connectivity.PermissionMonitor;
|
import com.android.server.connectivity.PermissionMonitor;
|
||||||
|
import com.android.server.connectivity.ProfileNetworkPreferences;
|
||||||
import com.android.server.connectivity.ProxyTracker;
|
import com.android.server.connectivity.ProxyTracker;
|
||||||
import com.android.server.connectivity.QosCallbackTracker;
|
import com.android.server.connectivity.QosCallbackTracker;
|
||||||
import com.android.server.net.NetworkPolicyManagerInternal;
|
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;
|
private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* used internally when setting the default networks for OemNetworkPreferences.
|
* Used internally when setting the default networks for OemNetworkPreferences.
|
||||||
* obj = OemNetworkPreferences
|
* obj = Pair<OemNetworkPreferences, listener>
|
||||||
*/
|
*/
|
||||||
private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48;
|
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;
|
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
|
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
|
||||||
* should be shown.
|
* should be shown.
|
||||||
@@ -1290,11 +1298,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
|
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();
|
final NetworkCapabilities netCap = new NetworkCapabilities();
|
||||||
netCap.addCapability(NET_CAPABILITY_INTERNET);
|
netCap.addCapability(NET_CAPABILITY_INTERNET);
|
||||||
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
|
netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
|
||||||
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
|
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
|
||||||
netCap.setSingleUid(uid);
|
netCap.setUids(Collections.singleton(uids));
|
||||||
return netCap;
|
return netCap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4537,6 +4550,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
handleSetOemNetworkPreference(arg.first, arg.second);
|
handleSetOemNetworkPreference(arg.first, arg.second);
|
||||||
break;
|
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:
|
case EVENT_REPORT_NETWORK_ACTIVITY:
|
||||||
mNetworkActivityTracker.handleReportNetworkActivity();
|
mNetworkActivityTracker.handleReportNetworkActivity();
|
||||||
break;
|
break;
|
||||||
@@ -5897,10 +5916,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
@GuardedBy("mBlockedAppUids")
|
@GuardedBy("mBlockedAppUids")
|
||||||
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
|
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
|
@NonNull
|
||||||
private OemNetworkPreferences mOemNetworkPreferences =
|
private OemNetworkPreferences mOemNetworkPreferences =
|
||||||
new OemNetworkPreferences.Builder().build();
|
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
|
// The always-on request for an Internet-capable network that apps without a specific default
|
||||||
// fall back to.
|
// fall back to.
|
||||||
@@ -9101,13 +9126,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
mQosCallbackTracker.unregisterCallback(callback);
|
mQosCallbackTracker.unregisterCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enforceAutomotiveDevice() {
|
// Network preference per-profile and OEM network preferences can't be set at the same
|
||||||
final boolean isAutomotiveDevice =
|
// time, because it is unclear what should happen if both preferences are active for
|
||||||
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
// one given UID. To make it possible, the stack would have to clarify what would happen
|
||||||
if (!isAutomotiveDevice) {
|
// in case both are active at the same time. The implementation may have to be adjusted
|
||||||
throw new UnsupportedOperationException(
|
// to implement the resulting rules. For example, a priority could be defined between them,
|
||||||
"setOemNetworkPreference() is only available on automotive devices.");
|
// 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,
|
public void setProfileNetworkPreference(@NonNull final UserHandle profile,
|
||||||
@ConnectivityManager.ProfileNetworkPreference final int preference,
|
@ConnectivityManager.ProfileNetworkPreference final int preference,
|
||||||
@Nullable final IOnCompleteListener listener) {
|
@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();
|
enforceAutomotiveDevice();
|
||||||
enforceOemNetworkPreferencesPermission();
|
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");
|
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
|
||||||
validateOemNetworkPreferences(preference);
|
validateOemNetworkPreferences(preference);
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE,
|
||||||
@@ -9171,6 +9322,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||||||
if (DBG) {
|
if (DBG) {
|
||||||
log("set OEM network preferences :" + preference.toString());
|
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 =
|
final ArraySet<NetworkRequestInfo> nris =
|
||||||
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
|
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
|
||||||
replaceDefaultNetworkRequestsForPreference(nris);
|
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