Merge changes I335e82e2,I84ba363d,I8f18083b,I854a952d,I00e23441

* changes:
  Remove per-user preference when the user is removed
  Expose the enterprise per-profile networking API.
  Implement setNetworkPreferenceForUser.
  Public API for per-profile network preference.
  Add tests for setNetworkPreferenceForUser
This commit is contained in:
Chalard Jean
2021-03-17 05:48:18 +00:00
committed by Gerrit Code Review
9 changed files with 827 additions and 64 deletions

View File

@@ -11,6 +11,7 @@ package android.net {
method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
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);
field public static final String PRIVATE_DNS_MODE_OFF = "off";
field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";

View File

@@ -56,7 +56,7 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -67,6 +67,8 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
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 TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
@@ -78,10 +80,6 @@ package android.net {
field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
method public void onComplete();
}
@Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
method @Deprecated public void onTetheringFailed();

View File

@@ -64,6 +64,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -970,6 +971,33 @@ public class ConnectivityManager {
}
}
/**
* Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, Executor, Runnable)}.
* Specify that the traffic for this user should by follow the default rules.
* @hide
*/
@SystemApi
public static final int PROFILE_NETWORK_PREFERENCE_DEFAULT = 0;
/**
* Preference for {@link #setNetworkPreferenceForUser(UserHandle, int, 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.
* @hide
*/
@SystemApi
public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
PROFILE_NETWORK_PREFERENCE_DEFAULT,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE
})
public @interface ProfileNetworkPreference {
}
/**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
@@ -5066,19 +5094,6 @@ public class ConnectivityManager {
TYPE_NONE, new CallbackHandler(handler));
}
/**
* Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor,
* OnSetOemNetworkPreferenceListener)}.
* @hide
*/
@SystemApi
public interface OnSetOemNetworkPreferenceListener {
/**
* Called when setOemNetworkPreference() successfully completes.
*/
void onComplete();
}
/**
* Used by automotive devices to set the network preferences used to direct traffic at an
* application level as per the given OemNetworkPreferences. An example use-case would be an
@@ -5101,16 +5116,16 @@ public class ConnectivityManager {
@RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE)
public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference,
@Nullable @CallbackExecutor final Executor executor,
@Nullable final OnSetOemNetworkPreferenceListener listener) {
@Nullable final Runnable listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
if (null != listener) {
Objects.requireNonNull(executor, "Executor must be non-null");
}
final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null :
new IOnSetOemNetworkPreferenceListener.Stub() {
final IOnCompleteListener listenerInternal = listener == null ? null :
new IOnCompleteListener.Stub() {
@Override
public void onComplete() {
executor.execute(listener::onComplete);
executor.execute(listener::run);
}
};
@@ -5122,6 +5137,52 @@ public class ConnectivityManager {
}
}
/**
* Request that a user profile is put by default on a network matching a given preference.
*
* 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.
* @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
*/
// This function is for establishing per-profile default networking and can only be called by
// the device policy manager, running as the system server. It would make no sense to call it
// on a context for a user because it does not establish a setting on behalf of a user, rather
// it establishes a setting for a user on behalf of the DPM.
@SuppressLint({"UserHandle"})
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void setProfileNetworkPreference(@NonNull final UserHandle profile,
@ProfileNetworkPreference final int preference,
@Nullable @CallbackExecutor final Executor executor,
@Nullable final Runnable listener) {
if (null != listener) {
Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
}
final IOnCompleteListener proxy;
if (null == listener) {
proxy = null;
} else {
proxy = new IOnCompleteListener.Stub() {
@Override
public void onComplete() {
executor.execute(listener::run);
}
};
}
try {
mService.setProfileNetworkPreference(profile, preference, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// The first network ID of IPSec tunnel interface.
private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512
// The network ID range of IPSec tunnel interface.

View File

@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IOnCompleteListener;
import android.net.INetworkActivityListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
@@ -43,6 +43,7 @@ import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.UserHandle;
import com.android.connectivity.aidl.INetworkAgent;
@@ -215,5 +216,8 @@ interface IConnectivityManager
void unregisterQosCallback(in IQosCallback callback);
void setOemNetworkPreference(in OemNetworkPreferences preference,
in IOnSetOemNetworkPreferenceListener listener);
in IOnCompleteListener listener);
void setProfileNetworkPreference(in UserHandle profile, int preference,
in IOnCompleteListener listener);
}

View File

@@ -1,23 +0,0 @@
/**
*
* 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;
/** @hide */
oneway interface IOnSetOemNetworkPreferenceListener {
void onComplete();
}

View File

@@ -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.

View File

@@ -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;
@@ -98,7 +99,7 @@ import android.net.INetworkActivityListener;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
@@ -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;
}
@@ -4533,12 +4546,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
break;
case EVENT_SET_OEM_NETWORK_PREFERENCE: {
final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg =
(Pair<OemNetworkPreferences,
IOnSetOemNetworkPreferenceListener>) msg.obj;
final Pair<OemNetworkPreferences, IOnCompleteListener> arg =
(Pair<OemNetworkPreferences, IOnCompleteListener>) msg.obj;
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;
@@ -5109,6 +5127,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onUserRemoved(UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
// If there was a network preference for this user, remove it.
handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
null /* listener */);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
@@ -5907,10 +5928,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.
@@ -9111,6 +9138,143 @@ public class ConnectivityService extends IConnectivityManager.Stub
mQosCallbackTracker.unregisterCallback(callback);
}
// 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");
}
/**
* Request that a user profile is put by default on a network matching a given preference.
*
* 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 listener an optional listener to listen for completion of the operation.
*/
@Override
public void setProfileNetworkPreference(@NonNull final UserHandle profile,
@ConnectivityManager.ProfileNetworkPreference final int preference,
@Nullable final IOnCompleteListener listener) {
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()) {
// This may happen on a device with an OEM preference set when a user is removed.
// In this case, it's safe to ignore. In particular this happens in the tests.
loge("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);
@@ -9129,17 +9293,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
* Calling this will overwrite the existing preference.
*
* @param preference {@link OemNetworkPreferences} The application network preference to be set.
* @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used
* @param listener {@link ConnectivityManager.OnCompleteListener} Listener used
* to communicate completion of setOemNetworkPreference();
*/
@Override
public void setOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
@Nullable final IOnSetOemNetworkPreferenceListener listener) {
@Nullable final IOnCompleteListener listener) {
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,
@@ -9158,11 +9331,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleSetOemNetworkPreference(
@NonNull final OemNetworkPreferences preference,
@Nullable final IOnSetOemNetworkPreferenceListener listener) {
@Nullable final IOnCompleteListener listener) {
Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null");
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);

View File

@@ -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();
}
}

View File

@@ -35,6 +35,8 @@ import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -183,7 +185,7 @@ import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.IOnSetOemNetworkPreferenceListener;
import android.net.IOnCompleteListener;
import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -378,6 +380,11 @@ public class ConnectivityServiceTest {
// Set a non-zero value to verify the flow to set tcp init rwnd value.
private static final int TEST_TCP_INIT_RWND = 60;
// Used for testing the per-work-profile default network.
private static final int TEST_APP_ID = 103;
private static final int TEST_WORK_PROFILE_USER_ID = 2;
private static final int TEST_WORK_PROFILE_APP_UID =
UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID);
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -421,6 +428,7 @@ public class ConnectivityServiceTest {
private VpnManagerService mVpnManagerService;
private TestNetworkCallback mDefaultNetworkCallback;
private TestNetworkCallback mSystemDefaultNetworkCallback;
private TestNetworkCallback mProfileDefaultNetworkCallback;
// State variables required to emulate NetworkPolicyManagerService behaviour.
private int mUidRules = RULE_NONE;
@@ -542,13 +550,26 @@ public class ConnectivityServiceTest {
return super.getSystemService(name);
}
final HashMap<UserHandle, UserManager> mUserManagers = new HashMap<>();
@Override
public Context createContextAsUser(UserHandle user, int flags) {
final Context asUser = mock(Context.class, AdditionalAnswers.delegatesTo(this));
doReturn(user).when(asUser).getUser();
doAnswer((inv) -> {
final UserManager um = mUserManagers.computeIfAbsent(user,
u -> mock(UserManager.class, AdditionalAnswers.delegatesTo(mUserManager)));
return um;
}).when(asUser).getSystemService(Context.USER_SERVICE);
return asUser;
}
public void setWorkProfile(@NonNull final UserHandle userHandle, boolean value) {
// This relies on all contexts for a given user returning the same UM mock
final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
.getSystemService(UserManager.class);
doReturn(value).when(umMock).isManagedProfile();
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
@@ -1408,17 +1429,36 @@ public class ConnectivityServiceTest {
fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
int uid) {
private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
when(mDeps.getCallingUid()).thenReturn(uid);
try {
mCm.registerNetworkCallback(request, callback);
waitForIdle();
return what.get();
} finally {
returnRealCallingUid();
}
}
private void doAsUid(final int uid, @NonNull final Runnable what) {
doAsUid(uid, () -> {
what.run(); return Void.TYPE;
});
}
private void registerNetworkCallbackAsUid(NetworkRequest request, NetworkCallback callback,
int uid) {
doAsUid(uid, () -> {
mCm.registerNetworkCallback(request, callback);
});
}
private void registerDefaultNetworkCallbackAsUid(@NonNull final NetworkCallback callback,
final int uid) {
doAsUid(uid, () -> {
mCm.registerDefaultNetworkCallback(callback);
waitForIdle();
});
}
private static final int PRIMARY_USER = 0;
private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100);
private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101);
@@ -1578,6 +1618,7 @@ public class ConnectivityServiceTest {
@After
public void tearDown() throws Exception {
unregisterDefaultNetworkCallbacks();
maybeTearDownEnterpriseNetwork();
setAlwaysOnNetworks(false);
if (mCellNetworkAgent != null) {
mCellNetworkAgent.disconnect();
@@ -10118,9 +10159,12 @@ public class ConnectivityServiceTest {
Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
mSystemDefaultNetworkCallback = new TestNetworkCallback();
mDefaultNetworkCallback = new TestNetworkCallback();
mProfileDefaultNetworkCallback = new TestNetworkCallback();
mCm.registerSystemDefaultNetworkCallback(mSystemDefaultNetworkCallback,
new Handler(ConnectivityThread.getInstanceLooper()));
mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback);
registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback,
TEST_WORK_PROFILE_APP_UID);
mServiceContext.setPermission(
Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
}
@@ -10132,6 +10176,9 @@ public class ConnectivityServiceTest {
if (null != mSystemDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mSystemDefaultNetworkCallback);
}
if (null != mProfileDefaultNetworkCallback) {
mCm.unregisterNetworkCallback(mProfileDefaultNetworkCallback);
}
}
private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
@@ -10187,7 +10234,7 @@ public class ConnectivityServiceTest {
oemPrefListener.expectOnComplete();
}
private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener {
private static class TestOemListenerCallback implements IOnCompleteListener {
final CompletableFuture<Object> mDone = new CompletableFuture<>();
@Override
@@ -11220,4 +11267,400 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
private UidRangeParcel[] uidRangeFor(final UserHandle handle) {
UidRange range = UidRange.createForUser(handle);
return new UidRangeParcel[] { new UidRangeParcel(range.start, range.stop) };
}
private static class TestOnCompleteListener implements Runnable {
final class OnComplete {}
final ArrayTrackRecord<OnComplete>.ReadHead mHistory =
new ArrayTrackRecord<OnComplete>().newReadHead();
@Override
public void run() {
mHistory.add(new OnComplete());
}
public void expectOnComplete() {
assertNotNull(mHistory.poll(TIMEOUT_MS, it -> true));
}
}
private TestNetworkAgentWrapper makeEnterpriseNetworkAgent() throws Exception {
final NetworkCapabilities workNc = new NetworkCapabilities();
workNc.addCapability(NET_CAPABILITY_ENTERPRISE);
workNc.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
return new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), workNc);
}
private TestNetworkCallback mEnterpriseCallback;
private UserHandle setupEnterpriseNetwork() {
final UserHandle userHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(userHandle, true);
// File a request to avoid the enterprise network being disconnected as soon as the default
// request goes away it would make impossible to test that networkRemoveUidRanges
// is called, as the network would disconnect first for lack of a request.
mEnterpriseCallback = new TestNetworkCallback();
final NetworkRequest keepUpRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_ENTERPRISE)
.build();
mCm.requestNetwork(keepUpRequest, mEnterpriseCallback);
return userHandle;
}
private void maybeTearDownEnterpriseNetwork() {
if (null != mEnterpriseCallback) {
mCm.unregisterNetworkCallback(mEnterpriseCallback);
}
}
/**
* 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 {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
registerDefaultNetworkCallbacks();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
INetd.PERMISSION_NONE);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
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).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
// The enterprise network is not ready yet.
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
mProfileDefaultNetworkCallback);
final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
workAgent.connect(false);
mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent);
mSystemDefaultNetworkCallback.assertNoCallback();
mDefaultNetworkCallback.assertNoCallback();
inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
INetd.PERMISSION_SYSTEM);
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle));
inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
// Make sure changes to the work agent send callbacks to the app in the work profile, but
// not to the other apps.
workAgent.setNetworkValid(true /* isStrictMode */);
workAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent,
nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)
&& nc.hasCapability(NET_CAPABILITY_ENTERPRISE));
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
workAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent, nc ->
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
// Conversely, change a capability on the system-wide default network and make sure
// that only the apps outside of the work profile receive the callbacks.
mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
mSystemDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
mDefaultNetworkCallback.expectCapabilitiesThat(mCellNetworkAgent, nc ->
nc.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
mProfileDefaultNetworkCallback.assertNoCallback();
// Disconnect and reconnect the system-wide default network and make sure that the
// apps on this network see the appropriate callbacks, and the app on the work profile
// doesn't because it continues to use the enterprise network.
mCellNetworkAgent.disconnect();
mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mProfileDefaultNetworkCallback.assertNoCallback();
waitForIdle();
inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.assertNoCallback();
inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
INetd.PERMISSION_NONE);
// When the agent disconnects, test that the app on the work profile falls back to the
// default network.
workAgent.disconnect();
mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent);
mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
waitForIdle();
inOrder.verify(mMockNetd).networkDestroy(workAgent.getNetwork().netId);
mCellNetworkAgent.disconnect();
mSystemDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
inOrder.verify(mMockNetd).networkDestroy(mCellNetworkAgent.getNetwork().netId);
// If the control comes here, callbacks seem to behave correctly in the presence of
// a default network when the enterprise network goes up and down. Now, make sure they
// also behave correctly in the absence of a system-wide default network.
final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent();
workAgent2.connect(false);
mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId,
INetd.PERMISSION_SYSTEM);
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId,
uidRangeFor(testHandle));
workAgent2.setNetworkValid(true /* isStrictMode */);
workAgent2.mNetworkMonitor.forceReevaluation(Process.myUid());
mProfileDefaultNetworkCallback.expectCapabilitiesThat(workAgent2,
nc -> nc.hasCapability(NET_CAPABILITY_ENTERPRISE)
&& !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd, never()).networkAddUidRanges(anyInt(), any());
// When the agent disconnects, test that the app on the work profile falls back to the
// default network.
workAgent2.disconnect();
mProfileDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, workAgent2);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
waitForIdle();
inOrder.verify(mMockNetd).networkDestroy(workAgent2.getNetwork().netId);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
mProfileDefaultNetworkCallback);
// 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.
*/
@Test
public void testSetPreferenceForUserOnOff() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
// Connect both a regular cell agent and an enterprise network first.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
workAgent.connect(true);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
INetd.PERMISSION_NONE);
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle));
registerDefaultNetworkCallbacks();
mSystemDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_DEFAULT,
r -> r.run(), listener);
listener.expectOnComplete();
mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle));
workAgent.disconnect();
mCellNetworkAgent.disconnect();
// Callbacks will be unregistered by tearDown()
}
/**
* Test per-profile default networks for two different profiles concurrently.
*/
@Test
public void testSetPreferenceForTwoProfiles() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle2 = setupEnterpriseNetwork();
final UserHandle testHandle4 = UserHandle.of(TEST_WORK_PROFILE_USER_ID + 2);
mServiceContext.setWorkProfile(testHandle4, true);
registerDefaultNetworkCallbacks();
final TestNetworkCallback app4Cb = new TestNetworkCallback();
final int testWorkProfileAppUid4 =
UserHandle.getUid(testHandle4.getIdentifier(), TEST_APP_ID);
registerDefaultNetworkCallbackAsUid(app4Cb, testWorkProfileAppUid4);
// Connect both a regular cell agent and an enterprise network first.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
final TestNetworkAgentWrapper workAgent = makeEnterpriseNetworkAgent();
workAgent.connect(true);
mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
INetd.PERMISSION_NONE);
inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId,
INetd.PERMISSION_SYSTEM);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle2));
mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(workAgent);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
app4Cb);
mCm.setProfileNetworkPreference(testHandle4, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle4));
app4Cb.expectAvailableCallbacksValidated(workAgent);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
mProfileDefaultNetworkCallback);
mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_DEFAULT,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).networkRemoveUidRanges(workAgent.getNetwork().netId,
uidRangeFor(testHandle2));
mProfileDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback,
app4Cb);
workAgent.disconnect();
mCellNetworkAgent.disconnect();
mCm.unregisterNetworkCallback(app4Cb);
// Other callbacks will be unregistered by tearDown()
}
@Test
public void testProfilePreferenceRemovedUponUserRemoved() throws Exception {
final InOrder inOrder = inOrder(mMockNetd);
final UserHandle testHandle = setupEnterpriseNetwork();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId,
INetd.PERMISSION_NONE);
inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER, testHandle);
processBroadcast(removedIntent);
inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId,
uidRangeFor(testHandle));
}
/**
* Make sure that OEM preference and per-profile preference can't be used at the same
* time and throw ISE if tried
*/
@Test
public void testOemPreferenceAndProfilePreferenceExclusive() throws Exception {
final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(testHandle, true);
final TestOnCompleteListener listener = new TestOnCompleteListener();
setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(
OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY);
assertThrows("Should not be able to set per-profile pref while OEM prefs present",
IllegalStateException.class, () ->
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener));
// Empty the OEM prefs
final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
final OemNetworkPreferences emptyOemPref = new OemNetworkPreferences.Builder().build();
mService.setOemNetworkPreference(emptyOemPref, oemPrefListener);
oemPrefListener.expectOnComplete();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
r -> r.run(), listener);
listener.expectOnComplete();
assertThrows("Should not be able to set OEM prefs while per-profile pref is on",
IllegalStateException.class , () ->
mService.setOemNetworkPreference(emptyOemPref, oemPrefListener));
}
/**
* Make sure wrong preferences for per-profile default networking are rejected.
*/
@Test
public void testProfileNetworkPrefWrongPreference() throws Exception {
final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(testHandle, true);
assertThrows("Should not be able to set an illegal preference",
IllegalArgumentException.class,
() -> mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE + 1, null, null));
}
/**
* Make sure requests for per-profile default networking for a non-work profile are
* rejected
*/
@Test
public void testProfileNetworkPrefWrongProfile() throws Exception {
final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(testHandle, false);
assertThrows("Should not be able to set a user pref for a non-work profile",
IllegalArgumentException.class , () ->
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
}
}