Merge "Return not supported if no necessary socket option"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 68ac46c..63ba00b 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -678,11 +678,20 @@
@Deprecated
public static final int TYPE_VPN = 17;
- /** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_VPN;
+ /**
+ * A network that is exclusively meant to be used for testing
+ *
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ public static final int MAX_RADIO_TYPE = TYPE_TEST;
+
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_TEST;
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
@@ -3659,7 +3668,8 @@
/**
* Registers to receive notifications about all networks which satisfy the given
* {@link NetworkRequest}. The callbacks will continue to be called until
- * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called.
+ * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+ * called.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
@@ -3675,7 +3685,8 @@
/**
* Registers to receive notifications about all networks which satisfy the given
* {@link NetworkRequest}. The callbacks will continue to be called until
- * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called.
+ * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is
+ * called.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
@@ -3911,15 +3922,16 @@
*
* <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
* corresponding permission.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Extras to include in the app start intent.
* @hide
*/
@SystemApi
@TestApi
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
- public void startCaptivePortalApp(Bundle appExtras) {
+ public void startCaptivePortalApp(Network network, Bundle appExtras) {
try {
- mService.startCaptivePortalAppInternal(appExtras);
+ mService.startCaptivePortalAppInternal(network, appExtras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4265,6 +4277,8 @@
* @return {@code uid} if the connection is found and the app has permission to observe it
* (e.g., if it is associated with the calling VPN app's tunnel) or
* {@link android.os.Process#INVALID_UID} if the connection is not found.
+ * Throws {@link SecurityException} if the caller is not the active VPN for the current user.
+ * Throws {@link IllegalArgumentException} if an unsupported protocol is requested.
*/
public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
@NonNull InetSocketAddress remote) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 92a5839..84036ca 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -122,8 +122,6 @@
LegacyVpnInfo getLegacyVpnInfo(int userId);
- VpnInfo[] getAllVpnInfo();
-
boolean updateLockdownVpn();
boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown,
@@ -168,7 +166,7 @@
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
- void startCaptivePortalAppInternal(in Bundle appExtras);
+ void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/InetAddresses.java b/core/java/android/net/InetAddresses.java
index 8e6c69a..01b795e 100644
--- a/core/java/android/net/InetAddresses.java
+++ b/core/java/android/net/InetAddresses.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.NonNull;
+
import libcore.net.InetAddressUtils;
import java.net.InetAddress;
@@ -40,7 +42,7 @@
* @param address the address to parse.
* @return true if the supplied address is numeric, false otherwise.
*/
- public static boolean isNumericAddress(String address) {
+ public static boolean isNumericAddress(@NonNull String address) {
return InetAddressUtils.isNumericAddress(address);
}
@@ -57,7 +59,7 @@
* @return an {@link InetAddress} instance corresponding to the address.
* @throws IllegalArgumentException if {@code address} is not a numeric address.
*/
- public static InetAddress parseNumericAddress(String address) {
+ public static @NonNull InetAddress parseNumericAddress(@NonNull String address) {
return InetAddressUtils.parseNumericAddress(address);
}
}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 8d779aa..8b01960 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -25,6 +25,7 @@
import static android.system.OsConstants.RT_SCOPE_SITE;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -177,7 +178,7 @@
*/
@SystemApi
@TestApi
- public LinkAddress(InetAddress address, int prefixLength) {
+ public LinkAddress(@NonNull InetAddress address, int prefixLength) {
this(address, prefixLength, 0, 0);
this.scope = scopeForUnicastAddress(address);
}
@@ -196,12 +197,12 @@
/**
* Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
* "2001:db8::1/64". The flags are set to zero and the scope is determined from the address.
- * @param string The string to parse.
+ * @param address The string to parse.
* @hide
*/
@SystemApi
@TestApi
- public LinkAddress(String address) {
+ public LinkAddress(@NonNull String address) {
this(address, 0, 0);
this.scope = scopeForUnicastAddress(this.address);
}
@@ -209,7 +210,7 @@
/**
* Constructs a new {@code LinkAddress} from a string such as "192.0.2.5/24" or
* "2001:db8::1/64", with the specified flags and scope.
- * @param string The string to parse.
+ * @param address The string to parse.
* @param flags The address flags.
* @param scope The address scope.
* @hide
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda1..1d2d81d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@
TRANSPORT_VPN,
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
+ TRANSPORT_TEST,
})
public @interface Transport { }
@@ -635,10 +636,18 @@
*/
public static final int TRANSPORT_LOWPAN = 6;
+ /**
+ * Indicates this network uses a Test-only virtual interface as a transport.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int TRANSPORT_TEST = 7;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ public static final int MAX_TRANSPORT = TRANSPORT_TEST;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@
"ETHERNET",
"VPN",
"WIFI_AWARE",
- "LOWPAN"
+ "LOWPAN",
+ "TEST"
};
/**
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index daa2640..c0487b5 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -46,7 +46,7 @@
/**
* Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
* appropriate value based on previous user choice.
*/
public boolean acceptUnvalidated;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9b4cd23..cc3e6fb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -69,6 +71,8 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
import android.net.MatchAllNetworkSpecifier;
@@ -85,6 +89,7 @@
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
@@ -916,7 +921,8 @@
mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
- //set up the listener for user state for creating user VPNs
+ // Set up the listener for user state for creating user VPNs.
+ // Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -924,7 +930,11 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
@@ -935,7 +945,11 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
try {
mNMS.registerObserver(mTethering);
@@ -1728,6 +1742,12 @@
}
}
}
+
+ @Override
+ public void onNat64PrefixEvent(int netId, boolean added,
+ String prefixString, int prefixLength) {
+ mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength));
+ }
};
@VisibleForTesting
@@ -2361,6 +2381,11 @@
pw.decreaseIndent();
}
+
+ pw.println();
+ pw.println("NetworkStackClient logs:");
+ pw.increaseIndent();
+ NetworkStackClient.getInstance().dump(pw);
}
private void dumpNetworks(IndentingPrintWriter pw) {
@@ -2689,11 +2714,6 @@
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
-
- @Override
- public void logCaptivePortalLoginEvent(int eventId, String packageName) {
- new MetricsLogger().action(eventId, packageName);
- }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -2755,6 +2775,29 @@
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
+ private void handleNat64PrefixEvent(int netId, boolean added, String prefixString,
+ int prefixLength) {
+ NetworkAgentInfo nai = mNetworkForNetId.get(netId);
+ if (nai == null) return;
+
+ log(String.format("NAT64 prefix %s on netId %d: %s/%d",
+ (added ? "added" : "removed"), netId, prefixString, prefixLength));
+
+ IpPrefix prefix = null;
+ if (added) {
+ try {
+ prefix = new IpPrefix(InetAddresses.parseNumericAddress(prefixString),
+ prefixLength);
+ } catch (IllegalArgumentException e) {
+ loge("Invalid NAT64 prefix " + prefixString + "/" + prefixLength);
+ return;
+ }
+ }
+
+ nai.clatd.setNat64Prefix(prefix);
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+ }
+
private void updateLingerState(NetworkAgentInfo nai, long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
@@ -2870,7 +2913,7 @@
e.rethrowFromSystemServer();
}
mNetworkAgentInfos.remove(nai.messenger);
- nai.maybeStopClat();
+ nai.clatd.update();
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
@@ -3239,22 +3282,63 @@
/**
* NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
* endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+ * @param network Network on which the captive portal was detected.
* @param appExtras Bundle to use as intent extras for the captive portal application.
* Must be treated as opaque to avoid preventing the captive portal app to
* update its arguments.
*/
@Override
- public void startCaptivePortalAppInternal(Bundle appExtras) {
+ public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
appIntent.putExtras(appExtras);
+ appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+ new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
Binder.withCleanCallingIdentity(() ->
mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
}
+ private class CaptivePortalImpl extends ICaptivePortal.Stub {
+ private final Network mNetwork;
+
+ private CaptivePortalImpl(Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public void appResponse(final int response) throws RemoteException {
+ if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+ enforceSettingsPermission();
+ }
+
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+ if (nai == null) return;
+
+ // nai.networkMonitor() is thread-safe
+ final INetworkMonitor nm = nai.networkMonitor();
+ if (nm == null) return;
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ nm.notifyCaptivePortalAppFinished(response);
+ } finally {
+ // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void logEvent(int eventId, String packageName) {
+ enforceSettingsPermission();
+
+ new MetricsLogger().action(eventId, packageName);
+ }
+ }
+
public boolean avoidBadWifi() {
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
@@ -4012,12 +4096,14 @@
}
/**
- * Return the information of all ongoing VPNs. This method is used by NetworkStatsService
- * and not available in ConnectivityManager.
+ * Return the information of all ongoing VPNs.
+ *
+ * <p>This method is used to update NetworkStatsService.
+ *
+ * <p>Must be called on the handler thread.
*/
- @Override
- public VpnInfo[] getAllVpnInfo() {
- enforceConnectivityInternalPermission();
+ private VpnInfo[] getAllVpnInfo() {
+ ensureRunningOnConnectivityServiceThread();
synchronized (mVpns) {
if (mLockdownEnabled) {
return new VpnInfo[0];
@@ -4089,17 +4175,27 @@
* handler thread through their agent, this is asynchronous. When the capabilities objects
* are computed they will be up-to-date as they are computed synchronously from here and
* this is running on the ConnectivityService thread.
- * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
*/
private void updateAllVpnsCapabilities() {
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
- vpn.updateCapabilities();
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
+ private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+ ensureRunningOnConnectivityServiceThread();
+ NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+ if (vpnNai == null || nc == null) {
+ return;
+ }
+ updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+ }
+
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4440,22 +4536,28 @@
private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
@@ -4524,6 +4626,7 @@
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5028,6 +5131,19 @@
return getNetworkForRequest(mDefaultRequest.requestId);
}
+ @Nullable
+ private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+ return nai != null ? nai.network : null;
+ }
+
+ private void ensureRunningOnConnectivityServiceThread() {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on ConnectivityService thread: "
+ + Thread.currentThread().getName());
+ }
+ }
+
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
return nai == getDefaultNetwork();
}
@@ -5057,7 +5173,7 @@
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+ getNetworkStack().makeNetworkMonitor(
toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5069,6 +5185,11 @@
return nai.network.netId;
}
+ @VisibleForTesting
+ protected NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
@@ -5102,11 +5223,10 @@
LinkProperties oldLp) {
int netId = networkAgent.network.netId;
- // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
- // we do anything else, make sure its LinkProperties are accurate.
- if (networkAgent.clatd != null) {
- networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- }
+ // The NetworkAgentInfo does not know whether clatd is running on its network or not, or
+ // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties
+ // are accurate.
+ networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
updateMtu(newLp, oldLp);
@@ -5136,8 +5256,8 @@
synchronized (networkAgent) {
networkAgent.linkProperties = newLp;
}
- // Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNMS);
+ // Start or stop DNS64 detection and 464xlat according to network state.
+ networkAgent.clatd.update();
notifyIfacesChangedForNetworkStats();
if (networkAgent.everConnected) {
try {
@@ -5445,6 +5565,8 @@
}
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+ ensureRunningOnConnectivityServiceThread();
+
if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
// Ignore updates for disconnected networks
return;
@@ -5621,6 +5743,8 @@
updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
+ // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+ updateAllVpnsCapabilities();
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6060,6 +6184,10 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ if (networkAgent.isVPN()) {
+ updateAllVpnsCapabilities();
+ }
+
// Consider network even though it is not yet validated.
final long now = SystemClock.elapsedRealtime();
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6273,6 +6401,7 @@
* Must be called on the handler thread.
*/
private Network[] getDefaultNetworks() {
+ ensureRunningOnConnectivityServiceThread();
ArrayList<Network> defaultNetworks = new ArrayList<>();
NetworkAgentInfo defaultNetwork = getDefaultNetwork();
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
@@ -6288,8 +6417,15 @@
* properties tracked by NetworkStatsService on an active iface has changed.
*/
private void notifyIfacesChangedForNetworkStats() {
+ ensureRunningOnConnectivityServiceThread();
+ String activeIface = null;
+ LinkProperties activeLinkProperties = getActiveLinkProperties();
+ if (activeLinkProperties != null) {
+ activeIface = activeLinkProperties.getInterfaceName();
+ }
try {
- mStatsService.forceUpdateIfaces(getDefaultNetworks());
+ mStatsService.forceUpdateIfaces(
+ getDefaultNetworks(), getAllVpnInfo(), getAllNetworkState(), activeIface);
} catch (Exception ignored) {
}
}
@@ -6321,7 +6457,11 @@
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+ mHandler.post(() -> {
+ // Update VPN's capabilities based on updated underlying network set.
+ updateAllVpnsCapabilities();
+ notifyIfacesChangedForNetworkStats();
+ });
}
return success;
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 9d9b1cf..2646d76 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -18,19 +18,24 @@
import android.net.ConnectivityManager;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.RouteInfo;
import android.os.INetworkManagementService;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.net.BaseNetworkObserver;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.util.Objects;
/**
@@ -67,15 +72,16 @@
private final NetworkAgentInfo mNetwork;
private enum State {
- IDLE, // start() not called. Base iface and stacked iface names are null.
- STARTING, // start() called. Base iface and stacked iface names are known.
- RUNNING, // start() called, and the stacked iface is known to be up.
- STOPPING; // stop() called, this Nat464Xlat is still registered as a network observer for
- // the stacked interface.
+ IDLE, // start() not called. Base iface and stacked iface names are null.
+ DISCOVERING, // same as IDLE, except prefix discovery in progress.
+ STARTING, // start() called. Base iface and stacked iface names are known.
+ RUNNING, // start() called, and the stacked iface is known to be up.
}
+ private IpPrefix mNat64Prefix;
private String mBaseIface;
private String mIface;
+ private Inet6Address mIPv6Address;
private State mState = State.IDLE;
public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
@@ -85,20 +91,51 @@
}
/**
- * Determines whether a network requires clat.
+ * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is
+ * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
+ * enable NAT64 prefix discovery.
+ *
* @param network the NetworkAgentInfo corresponding to the network.
* @return true if the network requires clat, false otherwise.
*/
- public static boolean requiresClat(NetworkAgentInfo nai) {
+ @VisibleForTesting
+ protected static boolean requiresClat(NetworkAgentInfo nai) {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState());
- // We only run clat on networks that don't have a native IPv4 address.
- final boolean hasIPv4Address =
- (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address();
- final boolean skip464xlat =
- (nai.netMisc() != null) && nai.netMisc().skip464xlat;
- return supported && connected && !hasIPv4Address && !skip464xlat;
+
+ // Only run clat on networks that have a global IPv6 address and don't have a native IPv4
+ // address.
+ LinkProperties lp = nai.linkProperties;
+ final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIPv6Address()
+ && !lp.hasIPv4Address();
+
+ // If the network tells us it doesn't use clat, respect that.
+ final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+
+ return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
+ }
+
+ /**
+ * Whether the clat demon should be started on this network now. This is true if requiresClat is
+ * true and a NAT64 prefix has been discovered.
+ *
+ * @param nai the NetworkAgentInfo corresponding to the network.
+ * @return true if the network should start clat, false otherwise.
+ */
+ @VisibleForTesting
+ protected static boolean shouldStartClat(NetworkAgentInfo nai) {
+ LinkProperties lp = nai.linkProperties;
+ return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null;
+ }
+
+ /**
+ * @return true if we have started prefix discovery and not yet stopped it (regardless of
+ * whether it is still running or has succeeded).
+ * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING.
+ */
+ public boolean isPrefixDiscoveryStarted() {
+ return mState == State.DISCOVERING || isStarted();
}
/**
@@ -106,7 +143,7 @@
* A true result corresponds to internal states STARTING and RUNNING.
*/
public boolean isStarted() {
- return mState != State.IDLE;
+ return (mState == State.STARTING || mState == State.RUNNING);
}
/**
@@ -124,32 +161,31 @@
}
/**
- * @return true if clatd has been stopped.
- */
- public boolean isStopping() {
- return mState == State.STOPPING;
- }
-
- /**
* Start clatd, register this Nat464Xlat as a network observer for the stacked interface,
* and set internal state.
*/
private void enterStartingState(String baseIface) {
try {
mNMService.registerObserver(this);
- } catch(RemoteException e) {
- Slog.e(TAG,
- "startClat: Can't register interface observer for clat on " + mNetwork.name());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
return;
}
+
+ String addrStr = null;
try {
- mNetd.clatdStart(baseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error starting clatd on " + baseIface, e);
+ addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
}
mIface = CLAT_PREFIX + baseIface;
mBaseIface = baseIface;
mState = State.STARTING;
+ try {
+ mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
+ } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
+ Slog.e(TAG, "Invalid IPv6 address " + addrStr);
+ }
}
/**
@@ -161,37 +197,27 @@
}
/**
- * Stop clatd, and turn ND offload on if it had been turned off.
- */
- private void enterStoppingState() {
- try {
- mNetd.clatdStop(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
- }
-
- mState = State.STOPPING;
- }
-
- /**
* Unregister as a base observer for the stacked interface, and clear internal state.
*/
- private void enterIdleState() {
+ private void leaveStartedState() {
try {
mNMService.unregisterObserver(this);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e);
+ } catch (RemoteException | IllegalStateException e) {
+ Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
}
-
mIface = null;
mBaseIface = null;
mState = State.IDLE;
+ if (requiresClat(mNetwork)) {
+ mState = State.DISCOVERING;
+ } else {
+ stopPrefixDiscovery();
+ mState = State.IDLE;
+ }
}
- /**
- * Starts the clat daemon.
- */
- public void start() {
+ @VisibleForTesting
+ protected void start() {
if (isStarted()) {
Slog.e(TAG, "startClat: already started");
return;
@@ -212,20 +238,82 @@
enterStartingState(baseIface);
}
- /**
- * Stops the clat daemon.
- */
- public void stop() {
+ @VisibleForTesting
+ protected void stop() {
if (!isStarted()) {
+ Slog.e(TAG, "stopClat: already stopped");
return;
}
- Slog.i(TAG, "Stopping clatd on " + mBaseIface);
- boolean wasStarting = isStarting();
- enterStoppingState();
- if (wasStarting) {
- enterIdleState();
+ Slog.i(TAG, "Stopping clatd on " + mBaseIface);
+ try {
+ mNetd.clatdStop(mBaseIface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
}
+
+ String iface = mIface;
+ boolean wasRunning = isRunning();
+
+ // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling
+ // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties
+ // would wrongly inform ConnectivityService that there is still a stacked interface.
+ leaveStartedState();
+
+ if (wasRunning) {
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.removeStackedLink(iface);
+ mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
+ }
+ }
+
+ private void startPrefixDiscovery() {
+ try {
+ mNetd.resolverStartPrefix64Discovery(getNetId());
+ mState = State.DISCOVERING;
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
+ }
+ }
+
+ private void stopPrefixDiscovery() {
+ try {
+ mNetd.resolverStopPrefix64Discovery(getNetId());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
+ }
+ }
+
+ /**
+ * Starts/stops NAT64 prefix discovery and clatd as necessary.
+ */
+ public void update() {
+ // TODO: turn this class into a proper StateMachine. // http://b/126113090
+ if (requiresClat(mNetwork)) {
+ if (!isPrefixDiscoveryStarted()) {
+ startPrefixDiscovery();
+ } else if (shouldStartClat(mNetwork)) {
+ // NAT64 prefix detected. Start clatd.
+ // TODO: support the NAT64 prefix changing after it's been discovered. There is no
+ // need to support this at the moment because it cannot happen without changes to
+ // the Dns64Configuration code in netd.
+ start();
+ } else {
+ // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
+ stop();
+ }
+ } else {
+ // Network no longer requires clat. Stop clat and prefix discovery.
+ if (isStarted()) {
+ stop();
+ } else if (isPrefixDiscoveryStarted()) {
+ leaveStartedState();
+ }
+ }
+ }
+
+ public void setNat64Prefix(IpPrefix nat64Prefix) {
+ mNat64Prefix = nat64Prefix;
}
/**
@@ -234,6 +322,8 @@
* has no idea that 464xlat is running on top of it.
*/
public void fixupLinkProperties(LinkProperties oldLp, LinkProperties lp) {
+ lp.setNat64Prefix(mNat64Prefix);
+
if (!isRunning()) {
return;
}
@@ -272,7 +362,7 @@
try {
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
return config.getLinkAddress();
- } catch(RemoteException|IllegalStateException e) {
+ } catch (RemoteException | IllegalStateException e) {
Slog.e(TAG, "Error getting link properties: " + e);
return null;
}
@@ -282,6 +372,20 @@
* Adds stacked link on base link and transitions to RUNNING state.
*/
private void handleInterfaceLinkStateChanged(String iface, boolean up) {
+ // TODO: if we call start(), then stop(), then start() again, and the
+ // interfaceLinkStateChanged notification for the first start is delayed past the first
+ // stop, then the code becomes out of sync with system state and will behave incorrectly.
+ //
+ // This is not trivial to fix because:
+ // 1. It is not guaranteed that start() will eventually result in the interface coming up,
+ // because there could be an error starting clat (e.g., if the interface goes down before
+ // the packet socket can be bound).
+ // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged
+ // notification that says which start() call the interface was created by.
+ //
+ // Once this code is converted to StateMachine, it will be possible to use deferMessage to
+ // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires,
+ // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1.
if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
return;
}
@@ -307,20 +411,16 @@
if (!Objects.equals(mIface, iface)) {
return;
}
- if (!isRunning() && !isStopping()) {
+ if (!isRunning()) {
return;
}
Slog.i(TAG, "interface " + iface + " removed");
- if (!isStopping()) {
- // Ensure clatd is stopped if stop() has not been called: this likely means that clatd
- // has crashed.
- enterStoppingState();
- }
- enterIdleState();
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.removeStackedLink(iface);
- mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
+ // If we're running, and the interface was removed, then we didn't call stop(), and it's
+ // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling
+ // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update
+ // will cause ConnectivityService to call start() again.
+ stop();
}
@Override
@@ -337,4 +437,9 @@
public String toString() {
return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
}
+
+ @VisibleForTesting
+ protected int getNetId() {
+ return mNetwork.network.netId;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d0cff25..f11a0de 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -236,7 +236,7 @@
public final AsyncChannel asyncChannel;
// Used by ConnectivityService to keep track of 464xlat.
- public Nat464Xlat clatd;
+ public final Nat464Xlat clatd;
// Set after asynchronous creation of the NetworkMonitor.
private volatile INetworkMonitor mNetworkMonitor;
@@ -244,8 +244,6 @@
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
private final ConnectivityService mConnService;
- private final INetd mNetd;
- private final INetworkManagementService mNMS;
private final Context mContext;
private final Handler mHandler;
@@ -260,9 +258,8 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
+ clatd = new Nat464Xlat(this, netd, nms);
mConnService = connService;
- mNetd = netd;
- mNMS = nms;
mContext = context;
mHandler = handler;
networkMisc = misc;
@@ -595,32 +592,6 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
- public void updateClat(INetworkManagementService netd) {
- if (Nat464Xlat.requiresClat(this)) {
- maybeStartClat();
- } else {
- maybeStopClat();
- }
- }
-
- /** Ensure clat has started for this network. */
- public void maybeStartClat() {
- if (clatd != null && clatd.isStarted()) {
- return;
- }
- clatd = new Nat464Xlat(this, mNetd, mNMS);
- clatd.start();
- }
-
- /** Ensure clat has stopped for this network. */
- public void maybeStopClat() {
- if (clatd == null) {
- return;
- }
- clatd.stop();
- clatd = null;
- }
-
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} "
+ "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} "
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 420b23e..d84a4d2 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,10 +19,11 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.INTERNET;
import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
-import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -32,23 +33,31 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
-import android.net.Uri;
+import android.net.INetd;
+import android.net.util.NetdService;
import android.os.Build;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map.Entry;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
/**
@@ -75,6 +84,59 @@
// Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
private final Map<Integer, Boolean> mApps = new HashMap<>();
+ // Keys are App packageNames, Values are app uids. . We need to keep track of this information
+ // because PackageListObserver#onPackageRemoved does not pass the UID.
+ @GuardedBy("mPackageNameUidMap")
+ private final Map<String, Integer> mPackageNameUidMap = new HashMap<>();
+
+ private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
+ @Override
+ public void onPackageAdded(String packageName) {
+ final PackageInfo app = getPackageInfo(packageName);
+ if (app == null) {
+ Slog.wtf(TAG, "Failed to get information of installed package: " + packageName);
+ return;
+ }
+ int uid = (app.applicationInfo != null) ? app.applicationInfo.uid : INVALID_UID;
+ if (uid == INVALID_UID) {
+ Slog.wtf(TAG, "Failed to get the uid of installed package: " + packageName
+ + "uid: " + uid);
+ return;
+ }
+ if (app.requestedPermissions == null) {
+ return;
+ }
+ sendPackagePermissionsForUid(uid,
+ filterPermission(Arrays.asList(app.requestedPermissions)));
+ synchronized (mPackageNameUidMap) {
+ mPackageNameUidMap.put(packageName, uid);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName) {
+ int uid;
+ synchronized (mPackageNameUidMap) {
+ if (!mPackageNameUidMap.containsKey(packageName)) {
+ return;
+ }
+ uid = mPackageNameUidMap.get(packageName);
+ mPackageNameUidMap.remove(packageName);
+ }
+ int permission = 0;
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages != null && packages.length > 0) {
+ for (String name : packages) {
+ final PackageInfo app = getPackageInfo(name);
+ if (app != null && app.requestedPermissions != null) {
+ permission |= filterPermission(Arrays.asList(app.requestedPermissions));
+ }
+ }
+ }
+ sendPackagePermissionsForUid(uid, permission);
+ }
+ }
+
public PermissionMonitor(Context context, INetworkManagementService netd) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -87,12 +149,21 @@
public synchronized void startMonitoring() {
log("Monitoring");
- List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ if (pmi != null) {
+ pmi.getPackageList(new PackageListObserver());
+ } else {
+ loge("failed to get the PackageManagerInternal service");
+ }
+ List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
+ | MATCH_ANY_USER);
if (apps == null) {
loge("No apps");
return;
}
+ SparseIntArray netdPermsUids = new SparseIntArray();
+
for (PackageInfo app : apps) {
int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
if (uid < 0) {
@@ -110,6 +181,17 @@
mApps.put(uid, hasRestrictedPermission);
}
}
+
+ //TODO: unify the management of the permissions into one codepath.
+ if (app.requestedPermissions != null) {
+ int otherNetdPerms = filterPermission(Arrays.asList(app.requestedPermissions));
+ if (otherNetdPerms != 0) {
+ netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
+ synchronized (mPackageNameUidMap) {
+ mPackageNameUidMap.put(app.applicationInfo.packageName, uid);
+ }
+ }
+ }
}
List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
@@ -121,6 +203,7 @@
log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
update(mUsers, mApps, true);
+ sendPackagePermissionsToNetd(netdPermsUids);
}
@VisibleForTesting
@@ -339,6 +422,107 @@
}
}
+ private static int filterPermission(List<String> requestedPermissions) {
+ int permissions = 0;
+ if (requestedPermissions.contains(INTERNET)) {
+ permissions |= INetd.PERMISSION_INTERNET;
+ }
+ if (requestedPermissions.contains(UPDATE_DEVICE_STATS)) {
+ permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
+ }
+ return permissions;
+ }
+
+ private PackageInfo getPackageInfo(String packageName) {
+ try {
+ PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
+ | MATCH_ANY_USER);
+ return app;
+ } catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + packageName);
+ return null;
+ }
+ }
+
+ /**
+ * Called by PackageListObserver when a package is installed/uninstalled. Send the updated
+ * permission information to netd.
+ *
+ * @param uid the app uid of the package installed
+ * @param permissions the permissions the app requested and netd cares about.
+ *
+ * @hide
+ */
+ private void sendPackagePermissionsForUid(int uid, int permissions) {
+ SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+ netdPermissionsAppIds.put(uid, permissions);
+ sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ }
+
+ /**
+ * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
+ * and/or UPDATE_DEVICE_STATS permission of the uids in array.
+ *
+ * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
+ * permission is 0, revoke all permissions of that uid.
+ *
+ * @hide
+ */
+ private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+ INetd netdService = NetdService.getInstance();
+ if (netdService == null) {
+ Log.e(TAG, "Failed to get the netd service");
+ return;
+ }
+ ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+ ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+ for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
+ int permissions = netdPermissionsAppIds.valueAt(i);
+ switch(permissions) {
+ case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
+ allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_INTERNET:
+ internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.PERMISSION_UPDATE_DEVICE_STATS:
+ updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ case INetd.NO_PERMISSIONS:
+ uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+ break;
+ default:
+ Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+ + netdPermissionsAppIds.keyAt(i));
+ }
+ }
+ try {
+ // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
+ if (allPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(allPermissionAppIds));
+ }
+ if (internetPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+ ArrayUtils.convertToIntArray(internetPermissionAppIds));
+ }
+ if (updateStatsPermissionAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
+ }
+ if (uninstalledAppIds.size() != 0) {
+ netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+ ArrayUtils.convertToIntArray(uninstalledAppIds));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Pass appId list of special permission failed." + e);
+ }
+ }
+
private static void log(String s) {
if (DBG) {
Log.d(TAG, s);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c83ab84..b460c90 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+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;
@@ -123,7 +124,8 @@
import android.net.NetworkParcelable;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
+import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
@@ -155,6 +157,7 @@
import android.util.Log;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -245,7 +248,7 @@
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock INetd mMockNetd;
- @Mock NetworkStack mNetworkStack;
+ @Mock NetworkStackClient mNetworkStack;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -885,11 +888,14 @@
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
}
@Override
public int getNetId() {
+ if (mMockNetworkAgent == null) {
+ return NETID_UNSET;
+ }
return mMockNetworkAgent.getNetwork().netId;
}
@@ -911,12 +917,13 @@
}
@Override
- public void updateCapabilities() {
- if (!mConnected) return;
- super.updateCapabilities();
- // Because super.updateCapabilities will update the capabilities of the agent but not
- // the mock agent, the mock agent needs to know about them.
+ public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+ if (!mConnected) return null;
+ super.updateCapabilities(defaultNetwork);
+ // Because super.updateCapabilities will update the capabilities of the agent but
+ // not the mock agent, the mock agent needs to know about them.
copyCapabilitiesToNetworkAgent();
+ return new NetworkCapabilities(mNetworkCapabilities);
}
private void copyCapabilitiesToNetworkAgent() {
@@ -1061,6 +1068,11 @@
}
@Override
+ protected NetworkStackClient getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
public WakeupMessage makeWakeupMessage(
Context context, Handler handler, String cmdName, int cmd, Object obj) {
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -1788,6 +1800,12 @@
fn.test((NetworkCapabilities) cbi.arg));
}
+ void expectLinkPropertiesLike(Predicate<LinkProperties> fn, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.LINK_PROPERTIES, agent);
+ assertTrue("Received LinkProperties don't match expectations : " + cbi.arg,
+ fn.test((LinkProperties) cbi.arg));
+ }
+
void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
boolean actualBlocked = (boolean) cbi.arg;
@@ -4257,48 +4275,91 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
- Network[] onlyCell = new Network[]{mCellNetworkAgent.getNetwork()};
- Network[] onlyWifi = new Network[]{mWiFiNetworkAgent.getNetwork()};
+ Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+ Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+
+ LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
// Simple connection should have updated ifaces
mCellNetworkAgent.connect(false);
+ mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
// Default network switch should update ifaces.
mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyWifi);
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyWifi),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(WIFI_IFNAME));
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
// Captive portal change shouldn't update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
waitForIdle();
- verify(mStatsService, never()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, never())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce()).forceUpdateIfaces(onlyCell);
+ verify(mStatsService, atLeastOnce())
+ .forceUpdateIfaces(
+ eq(onlyCell),
+ eq(new VpnInfo[0]),
+ any(NetworkState[].class),
+ eq(MOBILE_IFNAME));
reset(mStatsService);
}
@@ -4664,6 +4725,7 @@
vpnNetworkAgent.connect(false);
mMockVpn.connect();
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4696,6 +4758,7 @@
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
+ vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4729,12 +4792,11 @@
}
@Test
- public void testVpnWithAndWithoutInternet() {
+ public void testVpnWithoutInternet() {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
@@ -4756,11 +4818,30 @@
vpnNetworkAgent.disconnect();
defaultCallback.assertNoCallback();
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
+ @Test
+ public void testVpnWithInternet() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+ MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
mMockVpn.connect();
+
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -4768,14 +4849,6 @@
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
- ranges.clear();
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
- mMockVpn.connect();
- defaultCallback.assertNoCallback();
-
mCm.unregisterNetworkCallback(defaultCallback);
}
@@ -4878,6 +4951,70 @@
}
@Test
+ public void testNullUnderlyingNetworks() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ NetworkCapabilities nc;
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+ vpnNetworkCallback.assertNoCallback();
+
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+ mMockVpn.setUids(ranges);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ // By default, VPN is set to track default network (i.e. its underlying networks is null).
+ // In case of no default network, VPN is considered metered.
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Connect to Cell; Cell is the default network.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Connect to WiFi; WiFi is the new default.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+ // the capabilities.
+ mCellNetworkAgent.disconnect();
+
+ // Disconnect wifi too. Now we have no default network.
+ mWiFiNetworkAgent.disconnect();
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ mMockVpn.disconnect();
+ }
+
+ @Test
public void testNetworkBlockedStatus() {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
@@ -5001,6 +5138,9 @@
public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
+ final String kNat64PrefixString = "2001:db8:64:64:64:64::";
+ final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96);
+
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
@@ -5008,8 +5148,9 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mCm.registerNetworkCallback(networkRequest, networkCallback);
- // Prepare ipv6 only link properties and connect.
+ // Prepare ipv6 only link properties.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
cellLp.addLinkAddress(myIpv6);
@@ -5019,15 +5160,44 @@
when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfig(myIpv4));
- // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
- // properties properly.
+ // Connect with ipv6 link properties. Expect prefix discovery to be started.
mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME);
- Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+ verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
- // Clat iface up, expect stack link updated.
+ // Switching default network updates TCP buffer sizes.
+ verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
+
+ // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that
+ // the NAT64 prefix was removed because one was never discovered.
+ cellLp.addLinkAddress(myIpv4);
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+
+ verifyNoMoreInteractions(mMockNetd);
+ reset(mMockNetd);
+
+ // Remove IPv4 address. Expect prefix discovery to be started again.
+ cellLp.removeLinkAddress(myIpv4);
+ cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+
+ // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
+ Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+ assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix());
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+ kNat64PrefixString, 96);
+ LinkProperties lpBeforeClat = (LinkProperties) networkCallback.expectCallback(
+ CallbackState.LINK_PROPERTIES, mCellNetworkAgent).arg;
+ assertEquals(0, lpBeforeClat.getStackedLinks().size());
+ assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
+ verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+
+ // Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
@@ -5044,20 +5214,66 @@
assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
- // Add ipv4 address, expect stacked linkproperties be cleaned up
+ // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
+ // linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
- // Clat iface removed, expect linkproperties revert to original one
- clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+ // As soon as stop is called, the linkproperties lose the stacked interface.
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
- assertEquals(cellLp, actualLpAfterIpv4);
+ LinkProperties expected = new LinkProperties(cellLp);
+ expected.setNat64Prefix(kNat64Prefix);
+ assertEquals(expected, actualLpAfterIpv4);
+ assertEquals(0, actualLpAfterIpv4.getStackedLinks().size());
- // Clean up
+ // The interface removed callback happens but has no effect after stop is called.
+ clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+ networkCallback.assertNoCallback();
+
+ verifyNoMoreInteractions(mMockNetd);
+ reset(mMockNetd);
+
+ // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
+ kNat64PrefixString, 96);
+ networkCallback.expectLinkPropertiesLike((lp) -> lp.getNat64Prefix() == null,
+ mCellNetworkAgent);
+
+ // Remove IPv4 address and expect prefix discovery and clatd to be started again.
+ cellLp.removeLinkAddress(myIpv4);
+ cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+ kNat64PrefixString, 96);
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+
+
+ // Clat iface comes up. Expect stacked link to be added.
+ clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ networkCallback.expectLinkPropertiesLike(
+ (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null,
+ mCellNetworkAgent);
+
+ // NAT64 prefix is removed. Expect that clat is stopped.
+ mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
+ kNat64PrefixString, 96);
+ networkCallback.expectLinkPropertiesLike(
+ (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null,
+ mCellNetworkAgent);
+ verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ networkCallback.expectLinkPropertiesLike((lp) -> lp.getStackedLinks().size() == 0,
+ mCellNetworkAgent);
+
+ // Clean up.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
@@ -5138,30 +5354,34 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6";
-
- private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception {
+ private void verifyTcpBufferSizeChange(String tcpBufferSizes) {
String[] values = tcpBufferSizes.split(",");
String rmemValues = String.join(" ", values[0], values[1], values[2]);
String wmemValues = String.join(" ", values[3], values[4], values[5]);
waitForIdle();
- verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
+ try {
+ verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues);
+ } catch (RemoteException e) {
+ fail("mMockNetd should never throw RemoteException");
+ }
reset(mMockNetd);
}
@Test
- public void testTcpBufferReset() throws Exception {
+ public void testTcpBufferReset() {
+ final String testTcpBufferSizes = "1,2,3,4,5,6";
+
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
reset(mMockNetd);
- // Simple connection should have updated tcp buffer size.
+ // Switching default network updates TCP buffer sizes.
mCellNetworkAgent.connect(false);
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
// Change link Properties should have updated tcp buffer size.
LinkProperties lp = new LinkProperties();
- lp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES);
+ lp.setTcpBufferSizes(testTcpBufferSizes);
mCellNetworkAgent.sendLinkProperties(lp);
- verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
+ verifyTcpBufferSizeChange(testTcpBufferSizes);
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 9578ded..aa6cbda 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -37,7 +37,6 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
-import android.net.NetworkStack;
import android.os.INetworkManagementService;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,16 +73,12 @@
@Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
- @Mock NetworkStack mNetworkStack;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
- when(mCtx.getSystemServiceName(NetworkStack.class))
- .thenReturn(Context.NETWORK_STACK_SERVICE);
- when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 07b1d05..37c0df8 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -16,9 +16,11 @@
package com.android.server.connectivity;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -27,6 +29,7 @@
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.InterfaceConfiguration;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkInfo;
@@ -43,6 +46,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -53,6 +57,8 @@
static final String BASE_IFACE = "test0";
static final String STACKED_IFACE = "v4-test0";
static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
+ static final String NAT64_PREFIX = "64:ff9b::/96";
+ static final int NETID = 42;
@Mock ConnectivityService mConnectivity;
@Mock NetworkMisc mMisc;
@@ -65,7 +71,11 @@
Handler mHandler;
Nat464Xlat makeNat464Xlat() {
- return new Nat464Xlat(mNai, mNetd, mNms);
+ return new Nat464Xlat(mNai, mNetd, mNms) {
+ @Override protected int getNetId() {
+ return NETID;
+ }
+ };
}
@Before
@@ -87,6 +97,24 @@
when(mConfig.getLinkAddress()).thenReturn(ADDR);
}
+ private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) {
+ String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
+ + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
+ nai.networkInfo.getDetailedState(),
+ mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ nai.linkProperties.getLinkAddresses());
+ assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
+ }
+
+ private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) {
+ String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+ + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
+ nai.networkInfo.getDetailedState(),
+ mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ nai.linkProperties.getLinkAddresses());
+ assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
+ }
+
@Test
public void testRequiresClat() throws Exception {
final int[] supportedTypes = {
@@ -102,20 +130,45 @@
NetworkInfo.DetailedState.SUSPENDED,
};
+ LinkProperties oldLp = new LinkProperties(mNai.linkProperties);
for (int type : supportedTypes) {
mNai.networkInfo.setType(type);
for (NetworkInfo.DetailedState state : supportedDetailedStates) {
mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
- String msg = String.format("requiresClat expected for type=%d state=%s",
- type, state);
+
+ mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ assertRequiresClat(false, mNai);
+ assertShouldStartClat(false, mNai);
+
+ mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64"));
+ assertRequiresClat(false, mNai);
+ assertShouldStartClat(false, mNai);
+
+ mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ assertRequiresClat(true, mNai);
+ assertShouldStartClat(true, mNai);
mMisc.skip464xlat = true;
- String errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
- assertFalse(errorMsg, Nat464Xlat.requiresClat(mNai));
+ assertRequiresClat(false, mNai);
+ assertShouldStartClat(false, mNai);
mMisc.skip464xlat = false;
- errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat);
- assertTrue(errorMsg, Nat464Xlat.requiresClat(mNai));
+ assertRequiresClat(true, mNai);
+ assertShouldStartClat(true, mNai);
+
+ mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24"));
+ assertRequiresClat(false, mNai);
+ assertShouldStartClat(false, mNai);
+
+ mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24"));
+ assertRequiresClat(true, mNai);
+ assertShouldStartClat(true, mNai);
+
+ mNai.linkProperties.setNat64Prefix(null);
+ assertRequiresClat(true, mNai);
+ assertShouldStartClat(false, mNai);
+
+ mNai.linkProperties = new LinkProperties(oldLp);
}
}
}
@@ -125,11 +178,13 @@
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
- // ConnectivityService starts clat.
+ nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+
+ // Start clat.
nat.start();
verify(mNms).registerObserver(eq(nat));
- verify(mNetd).clatdStart(eq(BASE_IFACE));
+ verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// Stacked interface up notification arrives.
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -141,22 +196,109 @@
assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
assertRunning(nat);
- // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
+ // Stop clat (Network disconnects, IPv4 addr appears, ...).
nat.stop();
verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ verify(mNms).unregisterObserver(eq(nat));
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+ assertIdle(nat);
- // Stacked interface removed notification arrives.
+ // Stacked interface removed notification arrives and is ignored.
nat.interfaceRemoved(STACKED_IFACE);
mLooper.dispatchNext();
- verify(mNms).unregisterObserver(eq(nat));
- verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ }
+
+ private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+ ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
+ InOrder inOrder = inOrder(mNetd, mConnectivity);
+
+ nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+
+ nat.start();
+
+ inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
+ nat.stop();
+
+ inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+
+ inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
assertIdle(nat);
- verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
+ if (interfaceRemovedFirst) {
+ // Stacked interface removed notification arrives and is ignored.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+ nat.interfaceLinkStateChanged(STACKED_IFACE, false);
+ mLooper.dispatchNext();
+ }
+
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+ inOrder.verifyNoMoreInteractions();
+
+ nat.start();
+
+ inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+
+ if (!interfaceRemovedFirst) {
+ // Stacked interface removed notification arrives and is ignored.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+ nat.interfaceLinkStateChanged(STACKED_IFACE, false);
+ mLooper.dispatchNext();
+ }
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // ConnectivityService stops clat again.
+ nat.stop();
+
+ inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+
+ inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testStartStopStart() throws Exception {
+ checkStartStopStart(true);
+ }
+
+ @Test
+ public void testStartStopStartBeforeInterfaceRemoved() throws Exception {
+ checkStartStopStart(false);
}
@Test
@@ -164,11 +306,12 @@
Nat464Xlat nat = makeNat464Xlat();
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
- // ConnectivityService starts clat.
+ nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+
nat.start();
verify(mNms).registerObserver(eq(nat));
- verify(mNetd).clatdStart(eq(BASE_IFACE));
+ verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// Stacked interface up notification arrives.
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -184,9 +327,10 @@
nat.interfaceRemoved(STACKED_IFACE);
mLooper.dispatchNext();
- verify(mNms).unregisterObserver(eq(nat));
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
assertIdle(nat);
@@ -201,24 +345,25 @@
public void testStopBeforeClatdStarts() throws Exception {
Nat464Xlat nat = makeNat464Xlat();
- // ConnectivityService starts clat.
+ nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+
nat.start();
verify(mNms).registerObserver(eq(nat));
- verify(mNetd).clatdStart(eq(BASE_IFACE));
+ verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
nat.stop();
- verify(mNms).unregisterObserver(eq(nat));
verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
assertIdle(nat);
// In-flight interface up notification arrives: no-op
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
mLooper.dispatchNext();
-
// Interface removed notification arrives after stopClatd() takes effect: no-op.
nat.interfaceRemoved(STACKED_IFACE);
mLooper.dispatchNext();
@@ -232,17 +377,19 @@
public void testStopAndClatdNeverStarts() throws Exception {
Nat464Xlat nat = makeNat464Xlat();
- // ConnectivityService starts clat.
+ nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+
nat.start();
verify(mNms).registerObserver(eq(nat));
- verify(mNetd).clatdStart(eq(BASE_IFACE));
+ verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
nat.stop();
- verify(mNms).unregisterObserver(eq(nat));
verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
assertIdle(nat);
verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..b5d1ff9 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -28,6 +28,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.RouteInfo.RTN_UNREACHABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -89,6 +90,7 @@
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -566,7 +568,7 @@
final NetworkCapabilities caps = new NetworkCapabilities();
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +579,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile},
caps,
@@ -591,7 +593,7 @@
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +604,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +615,7 @@
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(
+ Vpn.applyUnderlyingCapabilities(
mConnectivityManager,
new Network[] {mobile, wifi},
caps,
@@ -775,6 +777,16 @@
// V4 does not, but V6 has sufficient coverage again
lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.clear();
+ // V4-unreachable route should not be treated as sufficient coverage
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
+
+ lp.clear();
+ // V6-unreachable route should not be treated as sufficient coverage
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ assertFalse(Vpn.providesRoutesToMostDestinations(lp));
}
@Test
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index f89f303..d91d3eb 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -70,7 +70,6 @@
import android.content.Context;
import android.content.Intent;
import android.net.DataUsageRequest;
-import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
@@ -163,7 +162,6 @@
private @Mock INetworkManagementService mNetManager;
private @Mock NetworkStatsSettings mSettings;
- private @Mock IConnectivityManager mConnManager;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
private HandlerThread mHandlerThread;
@@ -205,7 +203,6 @@
Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
mHandler = new Handler(mHandlerThread.getLooper(), callback);
mService.setHandler(mHandler, callback);
- mService.bindConnectivityManager(mConnManager);
mElapsedRealtime = 0L;
@@ -234,7 +231,6 @@
mNetManager = null;
mSettings = null;
- mConnManager = null;
mSession.close();
mService = null;
@@ -245,12 +241,12 @@
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -289,12 +285,12 @@
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -363,12 +359,12 @@
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// modify some number on wifi, and trigger poll event
@@ -405,12 +401,12 @@
public void testUidStatsAcrossNetworks() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1));
+ NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
// create some traffic on first network
@@ -437,7 +433,7 @@
// disappearing, to verify we don't count backwards.
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_2));
+ states = new NetworkState[] {buildMobile3gState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
@@ -446,7 +442,7 @@
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
forcePollAndWaitForIdle();
@@ -481,12 +477,12 @@
public void testUidRemovedIsMoved() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// create some traffic
@@ -540,12 +536,12 @@
public void testUid3g4gCombinedByTemplate() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1));
+ NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
// create some traffic
@@ -566,14 +562,14 @@
// now switch over to 4g network
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- expectNetworkState(buildMobile4gState(TEST_IFACE2));
+ states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
forcePollAndWaitForIdle();
@@ -598,12 +594,12 @@
public void testSummaryForAllUid() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// create some traffic for two apps
@@ -657,12 +653,12 @@
public void testDetailedUidStats() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -700,13 +696,13 @@
stackedProp.setInterfaceName(stackedIface);
final NetworkState wifiState = buildWifiState();
wifiState.linkProperties.addStackedLink(stackedProp);
- expectNetworkState(wifiState);
+ NetworkState[] states = new NetworkState[] {wifiState};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -745,12 +741,12 @@
public void testForegroundBackground() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// create some initial traffic
@@ -803,12 +799,12 @@
public void testMetered() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildWifiState(true /* isMetered */));
+ NetworkState[] states = new NetworkState[] {buildWifiState(true /* isMetered */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// create some initial traffic
@@ -843,12 +839,13 @@
public void testRoaming() throws Exception {
// pretend that network comes online
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1, true /* isRoaming */));
+ NetworkState[] states =
+ new NetworkState[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
// Create some traffic
@@ -882,12 +879,12 @@
public void testTethering() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1));
+ NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
// create some tethering traffic
@@ -925,12 +922,12 @@
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
expectDefaultSettings();
- expectNetworkState(buildWifiState());
+ NetworkState[] states = new NetworkState[] {buildWifiState()};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1077,11 +1074,11 @@
expectBandwidthControlCheck();
}
- private void expectNetworkState(NetworkState... state) throws Exception {
- when(mConnManager.getAllNetworkState()).thenReturn(state);
-
- final LinkProperties linkProp = state.length > 0 ? state[0].linkProperties : null;
- when(mConnManager.getActiveLinkProperties()).thenReturn(linkProp);
+ private String getActiveIface(NetworkState... states) throws Exception {
+ if (states == null || states.length == 0 || states[0].linkProperties == null) {
+ return null;
+ }
+ return states[0].linkProperties.getInterfaceName();
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
@@ -1090,8 +1087,6 @@
private void expectNetworkStatsSummary(NetworkStats summary, NetworkStats tetherStats)
throws Exception {
- when(mConnManager.getAllVpnInfo()).thenReturn(new VpnInfo[0]);
-
expectNetworkStatsTethering(STATS_PER_IFACE, tetherStats);
expectNetworkStatsSummaryDev(summary.clone());
expectNetworkStatsSummaryXt(summary.clone());