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:
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user