[automerger skipped] Revert^2 "Change Ethernet API to use OutcomeReceiver" am: 2cef8f1314 -s ours
am skip reason: Merged-In I4c204a8489c0be006c00581d833f2bb46ae0e71d with SHA-1 ea7e6d56f4 is already in history
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2031105
Change-Id: Ia4d50250dffc07f2cc075a80937f4171a39bc9d8
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index 4fefa0a..1b47481 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -188,10 +188,12 @@
public final class NsdManager {
method public void discoverServices(String, int, android.net.nsd.NsdManager.DiscoveryListener);
- method public void discoverServices(@NonNull String, int, @Nullable android.net.Network, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
+ method public void discoverServices(@NonNull String, int, @Nullable android.net.Network, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void discoverServices(@NonNull String, int, @NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.DiscoveryListener);
method public void registerService(android.net.nsd.NsdServiceInfo, int, android.net.nsd.NsdManager.RegistrationListener);
+ method public void registerService(@NonNull android.net.nsd.NsdServiceInfo, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.RegistrationListener);
method public void resolveService(android.net.nsd.NsdServiceInfo, android.net.nsd.NsdManager.ResolveListener);
+ method public void resolveService(@NonNull android.net.nsd.NsdServiceInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.nsd.NsdManager.ResolveListener);
method public void stopServiceDiscovery(android.net.nsd.NsdManager.DiscoveryListener);
method public void unregisterService(android.net.nsd.NsdManager.RegistrationListener);
field public static final String ACTION_NSD_STATE_CHANGED = "android.net.nsd.STATE_CHANGED";
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index e110155..0149115 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -13,10 +13,10 @@
package android.net {
public class EthernetManager {
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void connectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable android.os.OutcomeReceiver<java.lang.String,android.net.EthernetNetworkManagementException>);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disconnectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable android.os.OutcomeReceiver<java.lang.String,android.net.EthernetNetworkManagementException>);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void connectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disconnectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration(@NonNull String, @NonNull android.net.EthernetNetworkUpdateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.os.OutcomeReceiver<java.lang.String,android.net.EthernetNetworkManagementException>);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration(@NonNull String, @NonNull android.net.EthernetNetworkUpdateRequest, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
}
public static interface EthernetManager.TetheredInterfaceCallback {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index bdefed1..53d485d 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -361,6 +361,7 @@
}
public class NetworkReleasedException extends java.lang.Exception {
+ ctor public NetworkReleasedException();
}
public class NetworkRequest implements android.os.Parcelable {
@@ -425,6 +426,8 @@
}
public final class QosCallbackException extends java.lang.Exception {
+ ctor public QosCallbackException(@NonNull String);
+ ctor public QosCallbackException(@NonNull Throwable);
}
public abstract class QosFilter {
@@ -470,9 +473,11 @@
}
public class SocketLocalAddressChangedException extends java.lang.Exception {
+ ctor public SocketLocalAddressChangedException();
}
public class SocketNotBoundException extends java.lang.Exception {
+ ctor public SocketNotBoundException();
}
public final class StaticIpConfiguration implements android.os.Parcelable {
diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl
index 2a863ad..847f14e 100644
--- a/framework/src/android/net/ITestNetworkManager.aidl
+++ b/framework/src/android/net/ITestNetworkManager.aidl
@@ -29,8 +29,7 @@
*/
interface ITestNetworkManager
{
- TestNetworkInterface createTunInterface(in LinkAddress[] linkAddrs);
- TestNetworkInterface createTapInterface();
+ TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs);
void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
in int[] administratorUids, in IBinder binder);
diff --git a/framework/src/android/net/NetworkReleasedException.java b/framework/src/android/net/NetworkReleasedException.java
index 0629b75..cdfb6a1 100644
--- a/framework/src/android/net/NetworkReleasedException.java
+++ b/framework/src/android/net/NetworkReleasedException.java
@@ -18,6 +18,8 @@
import android.annotation.SystemApi;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Indicates that the {@link Network} was released and is no longer available.
*
@@ -25,7 +27,7 @@
*/
@SystemApi
public class NetworkReleasedException extends Exception {
- /** @hide */
+ @VisibleForTesting
public NetworkReleasedException() {
super("The network was released and is no longer available");
}
diff --git a/framework/src/android/net/QosCallbackException.java b/framework/src/android/net/QosCallbackException.java
index 7fd9a52..ed6eb15 100644
--- a/framework/src/android/net/QosCallbackException.java
+++ b/framework/src/android/net/QosCallbackException.java
@@ -21,6 +21,8 @@
import android.annotation.SystemApi;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -94,16 +96,12 @@
}
}
- /**
- * @hide
- */
+ @VisibleForTesting
public QosCallbackException(@NonNull final String message) {
super(message);
}
- /**
- * @hide
- */
+ @VisibleForTesting
public QosCallbackException(@NonNull final Throwable cause) {
super(cause);
}
diff --git a/framework/src/android/net/SocketLocalAddressChangedException.java b/framework/src/android/net/SocketLocalAddressChangedException.java
index 9daad83..7be3793 100644
--- a/framework/src/android/net/SocketLocalAddressChangedException.java
+++ b/framework/src/android/net/SocketLocalAddressChangedException.java
@@ -18,6 +18,8 @@
import android.annotation.SystemApi;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Thrown when the local address of the socket has changed.
*
@@ -25,7 +27,7 @@
*/
@SystemApi
public class SocketLocalAddressChangedException extends Exception {
- /** @hide */
+ @VisibleForTesting
public SocketLocalAddressChangedException() {
super("The local address of the socket changed");
}
diff --git a/framework/src/android/net/SocketNotBoundException.java b/framework/src/android/net/SocketNotBoundException.java
index b1d7026..59f34a3 100644
--- a/framework/src/android/net/SocketNotBoundException.java
+++ b/framework/src/android/net/SocketNotBoundException.java
@@ -18,6 +18,8 @@
import android.annotation.SystemApi;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Thrown when a previously bound socket becomes unbound.
*
@@ -25,7 +27,7 @@
*/
@SystemApi
public class SocketNotBoundException extends Exception {
- /** @hide */
+ @VisibleForTesting
public SocketNotBoundException() {
super("The socket is unbound");
}
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 9ddd2f5..280e497 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -49,6 +49,11 @@
@NonNull private final ITestNetworkManager mService;
+ private static final boolean TAP = false;
+ private static final boolean TUN = true;
+ private static final boolean BRING_UP = true;
+ private static final LinkAddress[] NO_ADDRS = new LinkAddress[0];
+
/** @hide */
public TestNetworkManager(@NonNull ITestNetworkManager service) {
mService = Objects.requireNonNull(service, "missing ITestNetworkManager");
@@ -155,7 +160,7 @@
public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
try {
final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
- return mService.createTunInterface(linkAddrs.toArray(arr));
+ return mService.createInterface(TUN, BRING_UP, linkAddrs.toArray(arr));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -173,10 +178,28 @@
@NonNull
public TestNetworkInterface createTapInterface() {
try {
- return mService.createTapInterface();
+ return mService.createInterface(TAP, BRING_UP, NO_ADDRS);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * Create a tap interface for testing purposes
+ *
+ * @param bringUp whether to bring up the interface before returning it.
+ *
+ * @return A ParcelFileDescriptor of the underlying TAP interface. Close this to tear down the
+ * TAP interface.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+ @NonNull
+ public TestNetworkInterface createTapInterface(boolean bringUp) {
+ try {
+ return mService.createInterface(TAP, bringUp, NO_ADDRS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index faa9998..1af00c7 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -168,4 +168,15 @@
<!-- Regex of wired ethernet ifaces -->
<string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
+
+ <!-- Ignores Wi-Fi validation failures after roam.
+ If validation fails on a Wi-Fi network after a roam to a new BSSID,
+ assume that the roam temporarily disrupted network connectivity, and
+ ignore all failures until this time has passed.
+ NetworkMonitor will continue to attempt validation, and if it fails after this time has passed,
+ the network will be marked unvalidated.
+
+ Only supported up to S. On T+, the Wi-Fi code should use destroyAndAwaitReplacement in order
+ to ensure that apps see the network disconnect and reconnect. -->
+ <integer translatable="false" name="config_validationFailureAfterRoamIgnoreTimeMillis">-1</integer>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 9fa6a30..b92dd08 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -40,6 +40,7 @@
<item type="string" name="config_ethernet_tcp_buffers"/>
<item type="array" name="config_ethernet_interfaces"/>
<item type="string" name="config_ethernet_iface_regex"/>
+ <item type="integer" name="config_validationFailureAfterRoamIgnoreTimeMillis" />
</policy>
</overlayable>
</resources>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 221b65d..e58160a 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -199,6 +199,7 @@
import android.net.resolv.aidl.PrivateDnsValidationEventParcel;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.wifi.WifiInfo;
import android.os.BatteryStatsManager;
import android.os.Binder;
import android.os.Build;
@@ -348,6 +349,9 @@
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
+ // The maximum value for the blocking validation result, in milliseconds.
+ public static final int MAX_VALIDATION_FAILURE_BLOCKING_TIME_MS = 10000;
+
// The maximum number of network request allowed per uid before an exception is thrown.
@VisibleForTesting
static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@@ -2249,7 +2253,10 @@
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
- newNc.setAdministratorUids(new int[0]);
+ if (!checkAnyPermissionOf(callerPid, callerUid, android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)) {
+ newNc.setAdministratorUids(new int[0]);
+ }
if (!checkAnyPermissionOf(
callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
newNc.setAllowedUids(new ArraySet<>());
@@ -3543,6 +3550,7 @@
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
final NetworkCapabilities networkCapabilities = new NetworkCapabilities(
(NetworkCapabilities) arg.second);
+ maybeUpdateWifiRoamTimestamp(nai, networkCapabilities);
processCapabilitiesFromAgent(nai, networkCapabilities);
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
@@ -3790,15 +3798,22 @@
private void handleNetworkTested(
@NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
+ final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+ if (!valid && shouldIgnoreValidationFailureAfterRoam(nai)) {
+ // Assume the validation failure is due to a temporary failure after roaming
+ // and ignore it. NetworkMonitor will continue to retry validation. If it
+ // continues to fail after the block timeout expires, the network will be
+ // marked unvalidated. If it succeeds, then validation state will not change.
+ return;
+ }
+
+ final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
final boolean wasPartial = nai.partialConnectivity;
nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
final boolean partialConnectivityChanged =
(wasPartial != nai.partialConnectivity);
- final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
- final boolean wasValidated = nai.lastValidated;
- final boolean wasDefault = isDefaultNetwork(nai);
-
if (DBG) {
final String logMsg = !TextUtils.isEmpty(redirectUrl)
? " with redirect to " + redirectUrl
@@ -4197,6 +4212,23 @@
return nai.created && !nai.destroyed;
}
+ private boolean shouldIgnoreValidationFailureAfterRoam(NetworkAgentInfo nai) {
+ // T+ devices should use destroyAndAwaitReplacement.
+ if (SdkLevel.isAtLeastT()) return false;
+ final long blockTimeOut = Long.valueOf(mResources.get().getInteger(
+ R.integer.config_validationFailureAfterRoamIgnoreTimeMillis));
+ if (blockTimeOut <= MAX_VALIDATION_FAILURE_BLOCKING_TIME_MS
+ && blockTimeOut >= 0) {
+ final long currentTimeMs = SystemClock.elapsedRealtime();
+ long timeSinceLastRoam = currentTimeMs - nai.lastRoamTimestamp;
+ if (timeSinceLastRoam <= blockTimeOut) {
+ log ("blocked because only " + timeSinceLastRoam + "ms after roam");
+ return true;
+ }
+ }
+ return false;
+ }
+
private void handleNetworkAgentDisconnected(Message msg) {
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
disconnectAndDestroyNetwork(nai);
@@ -9613,6 +9645,18 @@
return ((VpnTransportInfo) ti).getType();
}
+ private void maybeUpdateWifiRoamTimestamp(NetworkAgentInfo nai, NetworkCapabilities nc) {
+ if (nai == null) return;
+ final TransportInfo prevInfo = nai.networkCapabilities.getTransportInfo();
+ final TransportInfo newInfo = nc.getTransportInfo();
+ if (!(prevInfo instanceof WifiInfo) || !(newInfo instanceof WifiInfo)) {
+ return;
+ }
+ if (!TextUtils.equals(((WifiInfo)prevInfo).getBSSID(), ((WifiInfo)newInfo).getBSSID())) {
+ nai.lastRoamTimestamp = SystemClock.elapsedRealtime();
+ }
+ }
+
/**
* @param connectionInfo the connection to resolve.
* @return {@code uid} if the connection is found and the app has permission to observe it
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index fffd2be..a0bfb4a 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -99,12 +99,14 @@
}
/**
- * Create a TUN or TAP interface with the given interface name and link addresses
+ * Create a TUN or TAP interface with the specified parameters.
*
* <p>This method will return the FileDescriptor to the interface. Close it to tear down the
* interface.
*/
- private TestNetworkInterface createInterface(boolean isTun, LinkAddress[] linkAddrs) {
+ @Override
+ public TestNetworkInterface createInterface(boolean isTun, boolean bringUp,
+ LinkAddress[] linkAddrs) {
enforceTestNetworkPermissions(mContext);
Objects.requireNonNull(linkAddrs, "missing linkAddrs");
@@ -122,7 +124,9 @@
addr.getPrefixLength());
}
- NetdUtils.setInterfaceUp(mNetd, iface);
+ if (bringUp) {
+ NetdUtils.setInterfaceUp(mNetd, iface);
+ }
return new TestNetworkInterface(tunIntf, iface);
} catch (RemoteException e) {
@@ -132,28 +136,6 @@
}
}
- /**
- * Create a TUN interface with the given interface name and link addresses
- *
- * <p>This method will return the FileDescriptor to the TUN interface. Close it to tear down the
- * TUN interface.
- */
- @Override
- public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) {
- return createInterface(true, linkAddrs);
- }
-
- /**
- * Create a TAP interface with the given interface name
- *
- * <p>This method will return the FileDescriptor to the TAP interface. Close it to tear down the
- * TAP interface.
- */
- @Override
- public TestNetworkInterface createTapInterface() {
- return createInterface(false, new LinkAddress[0]);
- }
-
// Tracker for TestNetworkAgents
@GuardedBy("mTestNetworkTracker")
@NonNull
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index c1a8195..2e26ae4 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.os.ParcelFileDescriptor;
@@ -36,8 +37,11 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;
+import java.util.Objects;
/**
* This coordinator is responsible for providing clat relevant functionality.
@@ -66,23 +70,13 @@
private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8");
private static final int INVALID_IFINDEX = 0;
- private static final int INVALID_PID = 0;
- private static final long INVALID_COOKIE = 0;
@NonNull
private final INetd mNetd;
@NonNull
private final Dependencies mDeps;
@Nullable
- private String mIface = null;
- @Nullable
- private String mNat64Prefix = null;
- @Nullable
- private String mXlatLocalAddress4 = null;
- @Nullable
- private String mXlatLocalAddress6 = null;
- private int mPid = INVALID_PID;
- private long mCookie = INVALID_COOKIE;
+ private ClatdTracker mClatdTracker = null;
@VisibleForTesting
abstract static class Dependencies {
@@ -204,6 +198,53 @@
}
@VisibleForTesting
+ static class ClatdTracker {
+ @NonNull
+ public final String iface;
+ public final int ifIndex;
+ @NonNull
+ public final String v4iface;
+ public final int v4ifIndex;
+ @NonNull
+ public final Inet4Address v4;
+ @NonNull
+ public final Inet6Address v6;
+ @NonNull
+ public final Inet6Address pfx96;
+ public final int pid;
+ public final long cookie;
+
+ ClatdTracker(@NonNull String iface, int ifIndex, @NonNull String v4iface,
+ int v4ifIndex, @NonNull Inet4Address v4, @NonNull Inet6Address v6,
+ @NonNull Inet6Address pfx96, int pid, long cookie) {
+ this.iface = iface;
+ this.ifIndex = ifIndex;
+ this.v4iface = v4iface;
+ this.v4ifIndex = v4ifIndex;
+ this.v4 = v4;
+ this.v6 = v6;
+ this.pfx96 = pfx96;
+ this.pid = pid;
+ this.cookie = cookie;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ClatdTracker)) return false;
+ ClatdTracker that = (ClatdTracker) o;
+ return Objects.equals(this.iface, that.iface)
+ && this.ifIndex == that.ifIndex
+ && Objects.equals(this.v4iface, that.v4iface)
+ && this.v4ifIndex == that.v4ifIndex
+ && Objects.equals(this.v4, that.v4)
+ && Objects.equals(this.v6, that.v6)
+ && Objects.equals(this.pfx96, that.pfx96)
+ && this.pid == that.pid
+ && this.cookie == that.cookie;
+ }
+ };
+
+ @VisibleForTesting
static int getFwmark(int netId) {
// See union Fwmark in system/netd/include/Fwmark.h
return (netId & 0xffff)
@@ -235,30 +276,46 @@
public String clatStart(final String iface, final int netId,
@NonNull final IpPrefix nat64Prefix)
throws IOException {
- if (mIface != null || mPid != INVALID_PID) {
- throw new IOException("Clatd is already running on " + mIface + " (pid " + mPid + ")");
+ if (mClatdTracker != null) {
+ throw new IOException("Clatd is already running on " + mClatdTracker.iface
+ + " (pid " + mClatdTracker.pid + ")");
}
if (nat64Prefix.getPrefixLength() != 96) {
throw new IOException("Prefix must be 96 bits long: " + nat64Prefix);
}
// [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 ..
- final String v4;
+ final String v4Str;
try {
- v4 = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN);
+ v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN);
} catch (IOException e) {
throw new IOException("no IPv4 addresses were available for clat: " + e);
}
- // [2] Generate a checksum-neutral IID.
- final String pfx96 = nat64Prefix.getAddress().getHostAddress();
- final String v6;
+ final Inet4Address v4;
try {
- v6 = mDeps.generateIpv6Address(iface, v4, pfx96);
+ v4 = (Inet4Address) InetAddresses.parseNumericAddress(v4Str);
+ } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
+ throw new IOException("Invalid IPv4 address " + v4Str);
+ }
+
+ // [2] Generate a checksum-neutral IID.
+ final String pfx96Str = nat64Prefix.getAddress().getHostAddress();
+ final String v6Str;
+ try {
+ v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str);
} catch (IOException e) {
throw new IOException("no IPv6 addresses were available for clat: " + e);
}
+ final Inet6Address pfx96 = (Inet6Address) nat64Prefix.getAddress();
+ final Inet6Address v6;
+ try {
+ v6 = (Inet6Address) InetAddresses.parseNumericAddress(v6Str);
+ } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
+ throw new IOException("Invalid IPv6 address " + v6Str);
+ }
+
// [3] Open, configure and bring up the tun interface.
// Create the v4-... tun interface.
final String tunIface = CLAT_PREFIX + iface;
@@ -269,6 +326,12 @@
throw new IOException("Create tun interface " + tunIface + " failed: " + e);
}
+ final int tunIfIndex = mDeps.getInterfaceIndex(tunIface);
+ if (tunIfIndex == INVALID_IFINDEX) {
+ tunFd.close();
+ throw new IOException("Fail to get interface index for interface " + tunIface);
+ }
+
// disable IPv6 on it - failing to do so is not a critical error
try {
mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */);
@@ -279,7 +342,7 @@
// Detect ipv4 mtu.
final Integer fwmark = getFwmark(netId);
- final int detectedMtu = mDeps.detectMtu(pfx96,
+ final int detectedMtu = mDeps.detectMtu(pfx96Str,
ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark);
final int mtu = adjustMtu(detectedMtu);
Log.i(TAG, "ipv4 mtu is " + mtu);
@@ -295,7 +358,7 @@
}
final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
ifConfig.ifName = tunIface;
- ifConfig.ipv4Addr = v4;
+ ifConfig.ipv4Addr = v4Str;
ifConfig.prefixLength = 32;
ifConfig.hwAddr = "";
ifConfig.flags = new String[] {IF_STATE_UP};
@@ -333,8 +396,8 @@
throw new IOException("Open raw socket failed: " + e);
}
- final int ifaceIndex = mDeps.getInterfaceIndex(iface);
- if (ifaceIndex == INVALID_IFINDEX) {
+ final int ifIndex = mDeps.getInterfaceIndex(iface);
+ if (ifIndex == INVALID_IFINDEX) {
tunFd.close();
readSock6.close();
writeSock6.close();
@@ -343,7 +406,7 @@
// Start translating packets to the new prefix.
try {
- mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6, ifaceIndex);
+ mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6Str, ifIndex);
} catch (IOException e) {
tunFd.close();
readSock6.close();
@@ -352,7 +415,7 @@
}
// Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting.
- long cookie;
+ final long cookie;
try {
cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor());
} catch (IOException e) {
@@ -364,7 +427,7 @@
// Update our packet socket filter to reflect the new 464xlat IP address.
try {
- mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6, ifaceIndex);
+ mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex);
} catch (IOException e) {
tunFd.close();
readSock6.close();
@@ -373,15 +436,12 @@
}
// [5] Start clatd.
+ final int pid;
try {
- mPid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
- writeSock6.getFileDescriptor(), iface, pfx96, v4, v6);
- mIface = iface;
- mNat64Prefix = pfx96;
- mXlatLocalAddress4 = v4;
- mXlatLocalAddress6 = v6;
- mCookie = cookie;
+ pid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(),
+ writeSock6.getFileDescriptor(), iface, pfx96Str, v4Str, v6Str);
} catch (IOException e) {
+ // TODO: probably refactor to handle the exception of #untagSocket if any.
mDeps.untagSocket(cookie);
throw new IOException("Error start clatd on " + iface + ": " + e);
} finally {
@@ -390,29 +450,38 @@
writeSock6.close();
}
- return v6;
+ // [6] Initialize and store clatd tracker object.
+ mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
+ pid, cookie);
+
+ return v6Str;
}
/**
* Stop clatd
*/
public void clatStop() throws IOException {
- if (mPid == INVALID_PID) {
+ if (mClatdTracker == null) {
throw new IOException("Clatd has not started");
}
- Log.i(TAG, "Stopping clatd pid=" + mPid + " on " + mIface);
+ Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
- mDeps.stopClatd(mIface, mNat64Prefix, mXlatLocalAddress4, mXlatLocalAddress6, mPid);
- mDeps.untagSocket(mCookie);
+ mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
+ mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
+ mClatdTracker.pid);
+ mDeps.untagSocket(mClatdTracker.cookie);
- Log.i(TAG, "clatd on " + mIface + " stopped");
+ Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped");
+ mClatdTracker = null;
+ }
- mIface = null;
- mNat64Prefix = null;
- mXlatLocalAddress4 = null;
- mXlatLocalAddress6 = null;
- mPid = INVALID_PID;
- mCookie = INVALID_COOKIE;
+ /**
+ * Get clatd tracker. For test only.
+ */
+ @VisibleForTesting
+ @Nullable
+ ClatdTracker getClatdTrackerForTesting() {
+ return mClatdTracker;
}
private static native String native_selectIpv4Address(String v4addr, int prefixlen)
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index cbfc4f7..b73e2cc 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -192,6 +192,8 @@
public boolean everConnected;
// Whether this network has been destroyed and is being kept temporarily until it is replaced.
public boolean destroyed;
+ // To check how long it has been since last roam.
+ public long lastRoamTimestamp;
// Set to true if this Network successfully passed validation or if it did not satisfy the
// default NetworkRequest in which case validation will not be attempted.
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index ac46054..2885ba7 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -52,6 +52,7 @@
import android.net.Uri;
import android.net.util.SharedLog;
import android.os.Build;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemConfigManager;
@@ -66,7 +67,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
+import com.android.networkstack.apishim.ProcessShimImpl;
+import com.android.networkstack.apishim.common.ProcessShim;
import com.android.server.BpfNetMaps;
import java.util.ArrayList;
@@ -95,6 +99,8 @@
private final Context mContext;
private final BpfNetMaps mBpfNetMaps;
+ private static final ProcessShim sProcessShim = ProcessShimImpl.newInstance();
+
@GuardedBy("this")
private final Set<UserHandle> mUsers = new HashSet<>();
@@ -235,6 +241,10 @@
}
}
+ private static boolean hasSdkSandbox(final int uid) {
+ return SdkLevel.isAtLeastT() && Process.isApplicationUid(uid);
+ }
+
// Return the network permission for the passed list of apps. Note that this depends on the
// current settings of the device (See isUidAllowedOnRestrictedNetworks).
private SparseIntArray makeUidsNetworkPerm(final List<PackageInfo> apps) {
@@ -247,6 +257,10 @@
final int permission = getPackageNetdNetworkPermission(app);
if (isHigherNetworkPermission(permission, uidsPerm.get(uid, PERMISSION_NONE))) {
uidsPerm.put(uid, permission);
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ uidsPerm.put(sdkSandboxUid, permission);
+ }
}
}
return uidsPerm;
@@ -262,7 +276,11 @@
}
final int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
app.requestedPermissionsFlags);
- appIdsPerm.put(appId, appIdsPerm.get(appId) | otherNetdPerms);
+ final int permission = appIdsPerm.get(appId) | otherNetdPerms;
+ appIdsPerm.put(appId, permission);
+ if (hasSdkSandbox(appId)) {
+ appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
+ }
}
return appIdsPerm;
}
@@ -288,11 +306,19 @@
final SparseIntArray appIdsPerm = new SparseIntArray();
for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
final int appId = UserHandle.getAppId(uid);
- appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_INTERNET);
+ final int permission = appIdsPerm.get(appId) | PERMISSION_INTERNET;
+ appIdsPerm.put(appId, permission);
+ if (hasSdkSandbox(appId)) {
+ appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
+ }
}
for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
final int appId = UserHandle.getAppId(uid);
- appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS);
+ final int permission = appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS;
+ appIdsPerm.put(appId, permission);
+ if (hasSdkSandbox(appId)) {
+ appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
+ }
}
return appIdsPerm;
}
@@ -592,6 +618,12 @@
SparseIntArray apps = new SparseIntArray();
apps.put(uid, permission);
+
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ mUidToNetworkPerm.put(sdkSandboxUid, permission);
+ apps.put(sdkSandboxUid, permission);
+ }
sendUidsNetworkPermission(apps, true /* add */);
}
@@ -654,13 +686,25 @@
+ ", tPerm=" + permissionToString(trafficPerm));
if (permission != currentPermission) {
final SparseIntArray apps = new SparseIntArray();
+ int sdkSandboxUid = -1;
+ if (hasSdkSandbox(uid)) {
+ sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ }
if (permission == PERMISSION_NONE) {
mUidToNetworkPerm.delete(uid);
apps.put(uid, PERMISSION_NETWORK); // doesn't matter which permission we pick here
+ if (sdkSandboxUid != -1) {
+ mUidToNetworkPerm.delete(sdkSandboxUid);
+ apps.put(sdkSandboxUid, PERMISSION_NETWORK);
+ }
sendUidsNetworkPermission(apps, false);
} else {
mUidToNetworkPerm.put(uid, permission);
apps.put(uid, permission);
+ if (sdkSandboxUid != -1) {
+ mUidToNetworkPerm.put(sdkSandboxUid, permission);
+ apps.put(sdkSandboxUid, permission);
+ }
sendUidsNetworkPermission(apps, true);
}
}
@@ -828,6 +872,10 @@
void sendPackagePermissionsForAppId(int appId, int permissions) {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(appId, permissions);
+ if (hasSdkSandbox(appId)) {
+ int sdkSandboxAppId = sProcessShim.toSdkSandboxUid(appId);
+ netdPermissionsAppIds.put(sdkSandboxAppId, permissions);
+ }
sendAppIdsTrafficPermission(netdPermissionsAppIds);
}
@@ -925,9 +973,19 @@
// Doesn't matter which permission is set here.
removedUids.put(uid, PERMISSION_NETWORK);
mUidToNetworkPerm.delete(uid);
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ removedUids.put(sdkSandboxUid, PERMISSION_NETWORK);
+ mUidToNetworkPerm.delete(sdkSandboxUid);
+ }
} else {
updatedUids.put(uid, permission);
mUidToNetworkPerm.put(uid, permission);
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ updatedUids.put(sdkSandboxUid, permission);
+ mUidToNetworkPerm.put(sdkSandboxUid, permission);
+ }
}
}
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index 8dfa455..d782008 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,3 +1,4 @@
-# Bug component: 31808
+# Bug template url: http://b/new?component=31808
+# Bug component: 685852 = per-file **IpSec*
set noparent
file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
deleted file mode 100644
index df5569e..0000000
--- a/tests/cts/net/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 31808
-# Inherits parent owners
-per-file src/android/net/cts/NetworkWatchlistTest.java=alanstokes@google.com
-
-# Bug component: 685852 = per-file *IpSec*
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index a378aa7..0344604 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -77,7 +77,7 @@
// Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
private const val WIFI_CONNECT_TIMEOUT_MS = 40_000L
-private const val TEST_TIMEOUT_MS = 10_000L
+private const val TEST_TIMEOUT_MS = 20_000L
private const val TAG = "CaptivePortalTest"
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
new file mode 100644
index 0000000..0a32f09
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.cts
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.Manifest.permission.NETWORK_SETTINGS
+import android.net.IpConfiguration
+import android.net.TestNetworkInterface
+import android.net.TestNetworkManager
+import android.platform.test.annotations.AppModeFull
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.net.module.util.ArrayTrackRecord
+import com.android.net.module.util.TrackRecord
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.SC_V2
+import com.android.testutils.runAsShell
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertNull
+import kotlin.test.fail
+import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.Looper
+import com.android.networkstack.apishim.common.EthernetManagerShim.InterfaceStateListener
+import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_ABSENT
+import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_DOWN
+import com.android.networkstack.apishim.common.EthernetManagerShim.STATE_LINK_UP
+import com.android.networkstack.apishim.common.EthernetManagerShim.ROLE_CLIENT
+import com.android.networkstack.apishim.common.EthernetManagerShim.ROLE_NONE
+import com.android.networkstack.apishim.EthernetManagerShimImpl
+import java.util.concurrent.Executor
+import kotlin.test.assertEquals
+
+private const val TIMEOUT_MS = 1000L
+private const val NO_CALLBACK_TIMEOUT_MS = 200L
+private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP,
+ IpConfiguration.ProxySettings.NONE, null, null)
+
+@AppModeFull(reason = "Instant apps can't access EthernetManager")
+@RunWith(AndroidJUnit4::class)
+class EthernetManagerTest {
+ // EthernetManager is not updatable before T, so tests do not need to be backwards compatible
+ @get:Rule
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+
+ private val context by lazy { InstrumentationRegistry.getInstrumentation().context }
+ private val em by lazy { EthernetManagerShimImpl.newInstance(context) }
+
+ private val createdIfaces = ArrayList<TestNetworkInterface>()
+ private val addedListeners = ArrayList<InterfaceStateListener>()
+
+ private open class EthernetStateListener private constructor(
+ private val history: ArrayTrackRecord<CallbackEntry>
+ ) : InterfaceStateListener,
+ TrackRecord<EthernetStateListener.CallbackEntry> by history {
+ constructor() : this(ArrayTrackRecord())
+
+ val events = history.newReadHead()
+
+ sealed class CallbackEntry {
+ data class InterfaceStateChanged(
+ val iface: String,
+ val state: Int,
+ val role: Int,
+ val configuration: IpConfiguration?
+ ) : CallbackEntry()
+ }
+
+ override fun onInterfaceStateChanged(
+ iface: String,
+ state: Int,
+ role: Int,
+ cfg: IpConfiguration?
+ ) {
+ add(InterfaceStateChanged(iface, state, role, cfg))
+ }
+
+ fun <T : CallbackEntry> expectCallback(expected: T): T {
+ val event = pollForNextCallback()
+ assertEquals(expected, event)
+ return event as T
+ }
+
+ fun expectCallback(iface: TestNetworkInterface, state: Int, role: Int) {
+ expectCallback(InterfaceStateChanged(iface.interfaceName, state, role,
+ if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null))
+ }
+
+ fun pollForNextCallback(): CallbackEntry {
+ return events.poll(TIMEOUT_MS) ?: fail("Did not receive callback after ${TIMEOUT_MS}ms")
+ }
+
+ fun assertNoCallback() {
+ val cb = events.poll(NO_CALLBACK_TIMEOUT_MS)
+ assertNull(cb, "Expected no callback but got $cb")
+ }
+ }
+
+ @Test
+ public fun testCallbacks() {
+ val executor = HandlerExecutor(Handler(Looper.getMainLooper()))
+
+ // If an interface exists when the callback is registered, it is reported on registration.
+ val iface = runAsShell(MANAGE_TEST_NETWORKS) {
+ createInterface()
+ }
+ val listener = EthernetStateListener()
+ addInterfaceStateListener(executor, listener)
+ listener.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+
+ // If an interface appears, existing callbacks see it.
+ // TODO: fix the up/up/down/up callbacks and only send down/up.
+ val iface2 = runAsShell(MANAGE_TEST_NETWORKS) {
+ createInterface()
+ }
+ listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
+ listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
+ listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT)
+ listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
+
+ // Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE.
+ removeInterface(iface)
+ listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT)
+ listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE)
+
+ removeInterface(iface2)
+ listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT)
+ listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE)
+ listener.assertNoCallback()
+ }
+
+ @Before
+ fun setUp() {
+ runAsShell(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS) {
+ em.setIncludeTestInterfaces(true)
+ }
+ }
+
+ @After
+ fun tearDown() {
+ runAsShell(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS) {
+ em.setIncludeTestInterfaces(false)
+ for (iface in createdIfaces) {
+ if (iface.fileDescriptor.fileDescriptor.valid()) iface.fileDescriptor.close()
+ }
+ for (listener in addedListeners) {
+ em.removeInterfaceStateListener(listener)
+ }
+ }
+ }
+
+ private fun addInterfaceStateListener(executor: Executor, listener: InterfaceStateListener) {
+ em.addInterfaceStateListener(executor, listener)
+ addedListeners.add(listener)
+ }
+
+ private fun createInterface(): TestNetworkInterface {
+ val tnm = context.getSystemService(TestNetworkManager::class.java)
+ return tnm.createTapInterface(false /* bringUp */).also { createdIfaces.add(it) }
+ }
+
+ private fun removeInterface(iface: TestNetworkInterface) {
+ iface.fileDescriptor.close()
+ createdIfaces.remove(iface)
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 9506081..b139a9b 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -71,6 +71,7 @@
import java.net.ServerSocket
import java.nio.charset.StandardCharsets
import java.util.Random
+import java.util.concurrent.Executor
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
@@ -342,9 +343,12 @@
if (DBG) Log.d(TAG, "Port = $localPort")
val registrationRecord = NsdRegistrationRecord()
- val registeredInfo = registerService(registrationRecord, si)
+ // Test registering without an Executor
+ nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, registrationRecord)
+ val registeredInfo = registrationRecord.expectCallback<ServiceRegistered>().serviceInfo
val discoveryRecord = NsdDiscoveryRecord()
+ // Test discovering without an Executor
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
// Expect discovery started
@@ -353,7 +357,10 @@
// Expect a service record to be discovered
val foundInfo = discoveryRecord.waitForServiceDiscovered(registeredInfo.serviceName)
- val resolvedService = resolveService(foundInfo)
+ // Test resolving without an Executor
+ val resolveRecord = NsdResolveRecord()
+ nsdManager.resolveService(foundInfo, resolveRecord)
+ val resolvedService = resolveRecord.expectCallback<ServiceResolved>().serviceInfo
// Check Txt attributes
assertEquals(8, resolvedService.attributes.size)
@@ -408,7 +415,7 @@
@Test
fun testNsdManager_DiscoverOnNetwork() {
- // This tests requires shims supporting T+ APIs (discovering on specific network)
+ // This test requires shims supporting T+ APIs (discovering on specific network)
assumeTrue(ConstantsShim.VERSION > SC_V2)
val si = NsdServiceInfo()
@@ -422,7 +429,7 @@
tryTest {
val discoveryRecord = NsdDiscoveryRecord()
nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
- testNetwork1.network, discoveryRecord)
+ testNetwork1.network, Executor { it.run() }, discoveryRecord)
val foundInfo = discoveryRecord.waitForServiceDiscovered(
serviceName, testNetwork1.network)
@@ -442,7 +449,7 @@
@Test
fun testNsdManager_DiscoverWithNetworkRequest() {
- // This tests requires shims supporting T+ APIs (discovering on network request)
+ // This test requires shims supporting T+ APIs (discovering on network request)
assumeTrue(ConstantsShim.VERSION > SC_V2)
val si = NsdServiceInfo()
@@ -462,7 +469,7 @@
.addTransportType(TRANSPORT_TEST)
.setNetworkSpecifier(specifier)
.build(),
- discoveryRecord)
+ Executor { it.run() }, discoveryRecord)
val discoveryStarted = discoveryRecord.expectCallback<DiscoveryStarted>()
assertEquals(SERVICE_TYPE, discoveryStarted.serviceType)
@@ -507,7 +514,7 @@
@Test
fun testNsdManager_ResolveOnNetwork() {
- // This tests requires shims supporting T+ APIs (NsdServiceInfo.network)
+ // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
assumeTrue(ConstantsShim.VERSION > SC_V2)
val si = NsdServiceInfo()
@@ -532,7 +539,7 @@
serviceName, testNetwork2.network)
assertEquals(testNetwork2.network, nsdShim.getNetwork(foundInfo2))
- nsdManager.resolveService(foundInfo1, resolveRecord)
+ nsdShim.resolveService(nsdManager, foundInfo1, Executor { it.run() }, resolveRecord)
val cb = resolveRecord.expectCallback<ServiceResolved>()
cb.serviceInfo.let {
// Resolved service type has leading dot
@@ -553,7 +560,8 @@
* Register a service and return its registration record.
*/
private fun registerService(record: NsdRegistrationRecord, si: NsdServiceInfo): NsdServiceInfo {
- nsdManager.registerService(si, NsdManager.PROTOCOL_DNS_SD, record)
+ nsdShim.registerService(nsdManager, si, NsdManager.PROTOCOL_DNS_SD, Executor { it.run() },
+ record)
// We may not always get the name that we tried to register;
// This events tells us the name that was registered.
val cb = record.expectCallback<ServiceRegistered>()
@@ -562,7 +570,7 @@
private fun resolveService(discoveredInfo: NsdServiceInfo): NsdServiceInfo {
val record = NsdResolveRecord()
- nsdManager.resolveService(discoveredInfo, record)
+ nsdShim.resolveService(nsdManager, discoveredInfo, Executor { it.run() }, record)
val resolvedCb = record.expectCallback<ServiceResolved>()
assertEquals(discoveredInfo.serviceName, resolvedCb.serviceInfo.serviceName)
diff --git a/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
new file mode 100644
index 0000000..cd43a34
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/QosCallbackExceptionTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.NetworkReleasedException;
+import android.net.QosCallbackException;
+import android.net.SocketLocalAddressChangedException;
+import android.net.SocketNotBoundException;
+import android.os.Build;
+
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@IgnoreUpTo(Build.VERSION_CODES.R)
+public class QosCallbackExceptionTest {
+ private static final String ERROR_MESSAGE = "Test Error Message";
+ private static final String ERROR_MSG_SOCK_NOT_BOUND = "The socket is unbound";
+ private static final String ERROR_MSG_NET_RELEASED =
+ "The network was released and is no longer available";
+ private static final String ERROR_MSG_SOCK_ADDR_CHANGED =
+ "The local address of the socket changed";
+
+
+ @Test
+ public void testQosCallbackException() throws Exception {
+ final Throwable testcause = new Throwable(ERROR_MESSAGE);
+ final QosCallbackException exception = new QosCallbackException(testcause);
+ assertEquals(testcause, exception.getCause());
+
+ final QosCallbackException exceptionMsg = new QosCallbackException(ERROR_MESSAGE);
+ assertEquals(ERROR_MESSAGE, exceptionMsg.getMessage());
+ }
+
+ @Test
+ public void testNetworkReleasedExceptions() throws Exception {
+ final Throwable netReleasedException = new NetworkReleasedException();
+ final QosCallbackException exception = new QosCallbackException(netReleasedException);
+
+ assertTrue(exception.getCause() instanceof NetworkReleasedException);
+ assertEquals(netReleasedException, exception.getCause());
+ assertTrue(exception.getMessage().contains(ERROR_MSG_NET_RELEASED));
+ assertThrowableMessageContains(exception, ERROR_MSG_NET_RELEASED);
+ }
+
+ @Test
+ public void testSocketNotBoundExceptions() throws Exception {
+ final Throwable sockNotBoundException = new SocketNotBoundException();
+ final QosCallbackException exception = new QosCallbackException(sockNotBoundException);
+
+ assertTrue(exception.getCause() instanceof SocketNotBoundException);
+ assertEquals(sockNotBoundException, exception.getCause());
+ assertTrue(exception.getMessage().contains(ERROR_MSG_SOCK_NOT_BOUND));
+ assertThrowableMessageContains(exception, ERROR_MSG_SOCK_NOT_BOUND);
+ }
+
+ @Test
+ public void testSocketLocalAddressChangedExceptions() throws Exception {
+ final Throwable localAddrChangedException = new SocketLocalAddressChangedException();
+ final QosCallbackException exception = new QosCallbackException(localAddrChangedException);
+
+ assertTrue(exception.getCause() instanceof SocketLocalAddressChangedException);
+ assertEquals(localAddrChangedException, exception.getCause());
+ assertTrue(exception.getMessage().contains(ERROR_MSG_SOCK_ADDR_CHANGED));
+ assertThrowableMessageContains(exception, ERROR_MSG_SOCK_ADDR_CHANGED);
+ }
+
+ private void assertThrowableMessageContains(QosCallbackException exception, String errorMsg)
+ throws Exception {
+ try {
+ triggerException(exception);
+ fail("Expect exception");
+ } catch (QosCallbackException e) {
+ assertTrue(e.getMessage().contains(errorMsg));
+ }
+ }
+
+ private void triggerException(QosCallbackException exception) throws Exception {
+ throw new QosCallbackException(exception.getCause());
+ }
+}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 6d802af..025b28c 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -160,6 +160,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -283,6 +284,7 @@
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.wifi.WifiInfo;
import android.os.BadParcelableException;
import android.os.BatteryStatsManager;
import android.os.Binder;
@@ -7080,6 +7082,36 @@
}
@Test
+ public void testAdminUidsRedacted() throws Exception {
+ final int[] adminUids = new int[] {Process.myUid() + 1};
+ final NetworkCapabilities ncTemplate = new NetworkCapabilities();
+ ncTemplate.setAdministratorUids(adminUids);
+ mCellNetworkAgent =
+ new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), ncTemplate);
+ mCellNetworkAgent.connect(false /* validated */);
+
+ // Verify case where caller has permission
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ callback.expectCapabilitiesThat(
+ mCellNetworkAgent, nc -> Arrays.equals(adminUids, nc.getAdministratorUids()));
+ mCm.unregisterNetworkCallback(callback);
+
+ // Verify case where caller does NOT have permission
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED);
+ mServiceContext.setPermission(NETWORK_STACK, PERMISSION_DENIED);
+ callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ callback.expectCapabilitiesThat(
+ mCellNetworkAgent, nc -> nc.getAdministratorUids().length == 0);
+ }
+
+ @Test
public void testNonVpnUnderlyingNetworks() throws Exception {
// Ensure wifi and cellular are not torn down.
for (int transport : new int[]{TRANSPORT_CELLULAR, TRANSPORT_WIFI}) {
@@ -15572,4 +15604,91 @@
assertNull(readHead.poll(TEST_CALLBACK_TIMEOUT_MS, it -> true));
}
+
+ @Test
+ public void testIgnoreValidationAfterRoamDisabled() throws Exception {
+ assumeFalse(SdkLevel.isAtLeastT());
+ // testIgnoreValidationAfterRoam off
+ doReturn(-1).when(mResources)
+ .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ NetworkCapabilities wifiNc1 = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build());
+ NetworkCapabilities wifiNc2 = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new WifiInfo.Builder().setBssid("BB:BB:BB:BB:BB:BB").build());
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc1);
+ mWiFiNetworkAgent.connect(true);
+
+ // The default network will be switching to Wi-Fi Network.
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ wifiNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+ registerDefaultNetworkCallbacks();
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+
+ // Wi-Fi roaming from wifiNc1 to wifiNc2.
+ mWiFiNetworkAgent.setNetworkCapabilities(wifiNc2, true);
+ mWiFiNetworkAgent.setNetworkInvalid(false);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ }
+
+ @Test
+ public void testIgnoreValidationAfterRoamEnabled() throws Exception {
+ assumeFalse(SdkLevel.isAtLeastT());
+ // testIgnoreValidationAfterRoam on
+ doReturn(5000).when(mResources)
+ .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ NetworkCapabilities wifiNc1 = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new WifiInfo.Builder().setBssid("AA:AA:AA:AA:AA:AA").build());
+ NetworkCapabilities wifiNc2 = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setTransportInfo(new WifiInfo.Builder().setBssid("BB:BB:BB:BB:BB:BB").build());
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc1);
+ mWiFiNetworkAgent.connect(true);
+
+ // The default network will be switching to Wi-Fi Network.
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ wifiNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+ registerDefaultNetworkCallbacks();
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
+
+ // Wi-Fi roaming from wifiNc1 to wifiNc2.
+ mWiFiNetworkAgent.setNetworkCapabilities(wifiNc2, true);
+ mWiFiNetworkAgent.setNetworkInvalid(false);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+
+ // Network validation failed, but the result will be ignored.
+ assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
+ NET_CAPABILITY_VALIDATED));
+ mWiFiNetworkAgent.setNetworkValid(false);
+
+ // Behavior of after config_validationFailureAfterRoamIgnoreTimeMillis
+ ConditionVariable waitForValidationBlock = new ConditionVariable();
+ doReturn(50).when(mResources)
+ .getInteger(R.integer.config_validationFailureAfterRoamIgnoreTimeMillis);
+ // Wi-Fi roaming from wifiNc2 to wifiNc1.
+ mWiFiNetworkAgent.setNetworkCapabilities(wifiNc1, true);
+ mWiFiNetworkAgent.setNetworkInvalid(false);
+ waitForValidationBlock.block(150);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+ mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 8a2cfc2..6c8b545 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -25,6 +25,7 @@
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.clearInvocations;
@@ -33,6 +34,7 @@
import android.annotation.NonNull;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.os.Build;
import android.os.ParcelFileDescriptor;
@@ -52,6 +54,8 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.util.Objects;
@RunWith(DevSdkIgnoreRunner.class)
@@ -61,9 +65,12 @@
private static final String BASE_IFACE = "test0";
private static final String STACKED_IFACE = "v4-test0";
private static final int BASE_IFINDEX = 1000;
+ private static final int STACKED_IFINDEX = 1001;
private static final IpPrefix NAT64_IP_PREFIX = new IpPrefix("64:ff9b::/96");
private static final String NAT64_PREFIX_STRING = "64:ff9b::";
+ private static final Inet6Address INET6_PFX96 = (Inet6Address)
+ InetAddresses.parseNumericAddress(NAT64_PREFIX_STRING);
private static final int GOOGLE_DNS_4 = 0x08080808; // 8.8.8.8
private static final int NETID = 42;
@@ -74,6 +81,10 @@
private static final String XLAT_LOCAL_IPV4ADDR_STRING = "192.0.0.46";
private static final String XLAT_LOCAL_IPV6ADDR_STRING = "2001:db8:0:b11::464";
+ private static final Inet4Address INET4_LOCAL4 = (Inet4Address)
+ InetAddresses.parseNumericAddress(XLAT_LOCAL_IPV4ADDR_STRING);
+ private static final Inet6Address INET6_LOCAL6 = (Inet6Address)
+ InetAddresses.parseNumericAddress(XLAT_LOCAL_IPV6ADDR_STRING);
private static final int CLATD_PID = 10483;
private static final int TUN_FD = 534;
@@ -129,6 +140,8 @@
public int getInterfaceIndex(String ifName) {
if (BASE_IFACE.equals(ifName)) {
return BASE_IFINDEX;
+ } else if (STACKED_IFACE.equals(ifName)) {
+ return STACKED_IFINDEX;
}
fail("unsupported arg: " + ifName);
return -1;
@@ -315,6 +328,11 @@
// [1] Start clatd.
final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
assertEquals(XLAT_LOCAL_IPV6ADDR_STRING, addr6For464xlat);
+ final ClatCoordinator.ClatdTracker expected = new ClatCoordinator.ClatdTracker(
+ BASE_IFACE, BASE_IFINDEX, STACKED_IFACE, STACKED_IFINDEX,
+ INET4_LOCAL4, INET6_LOCAL6, INET6_PFX96, CLATD_PID, RAW_SOCK_COOKIE);
+ final ClatCoordinator.ClatdTracker actual = coordinator.getClatdTrackerForTesting();
+ assertEquals(expected, actual);
// Pick an IPv4 address.
inOrder.verify(mDeps).selectIpv4Address(eq(INIT_V4ADDR_STRING),
@@ -327,6 +345,7 @@
// Open, configure and bring up the tun interface.
inOrder.verify(mDeps).createTunInterface(eq(STACKED_IFACE));
inOrder.verify(mDeps).adoptFd(eq(TUN_FD));
+ inOrder.verify(mDeps).getInterfaceIndex(eq(STACKED_IFACE));
inOrder.verify(mNetd).interfaceSetEnableIPv6(eq(STACKED_IFACE), eq(false /* enable */));
inOrder.verify(mDeps).detectMtu(eq(NAT64_PREFIX_STRING), eq(GOOGLE_DNS_4), eq(MARK));
inOrder.verify(mNetd).interfaceSetMtu(eq(STACKED_IFACE),
@@ -372,6 +391,7 @@
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
+ assertNull(coordinator.getClatdTrackerForTesting());
inOrder.verifyNoMoreInteractions();
// [4] Expect an IO exception while stopping a clatd that doesn't exist.
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 6590543..6b379e8 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -77,6 +77,7 @@
import android.net.UidRange;
import android.net.Uri;
import android.os.Build;
+import android.os.Process;
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -88,7 +89,10 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
+import com.android.networkstack.apishim.ProcessShimImpl;
+import com.android.networkstack.apishim.common.ProcessShim;
import com.android.server.BpfNetMaps;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -153,6 +157,8 @@
private NetdMonitor mNetdMonitor;
private BpfMapMonitor mBpfMapMonitor;
+ private ProcessShim mProcessShim = ProcessShimImpl.newInstance();
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -197,6 +203,10 @@
return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo);
}
+ private boolean hasSdkSandbox(final int uid) {
+ return SdkLevel.isAtLeastT() && Process.isApplicationUid(uid);
+ }
+
private static PackageInfo systemPackageInfoWithPermissions(String... permissions) {
return packageInfoWithPermissions(
REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
@@ -493,6 +503,11 @@
String... permissions) throws Exception {
addPackage(name, uid, permissions);
assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid));
+ if (hasSdkSandbox(uid)) {
+ final int sdkSandboxUid = mProcessShim.toSdkSandboxUid(uid);
+ assertEquals(hasPermission,
+ mPermissionMonitor.hasUseBackgroundNetworksPermission(sdkSandboxUid));
+ }
}
@Test
@@ -531,7 +546,7 @@
}).when(mockBpfmap).setNetPermForUids(anyInt(), any(int[].class));
}
- public void expectTrafficPerm(int permission, int... appIds) {
+ public void expectTrafficPerm(int permission, Integer... appIds) {
for (final int appId : appIds) {
if (mAppIdsTrafficPermission.get(appId, DOES_NOT_EXIST) == DOES_NOT_EXIST) {
fail("appId " + appId + " does not exist.");
@@ -540,6 +555,17 @@
fail("appId " + appId + " has wrong permission: "
+ mAppIdsTrafficPermission.get(appId));
}
+ if (hasSdkSandbox(appId)) {
+ int sdkSandboxAppId = mProcessShim.toSdkSandboxUid(appId);
+ if (mAppIdsTrafficPermission.get(sdkSandboxAppId, DOES_NOT_EXIST)
+ == DOES_NOT_EXIST) {
+ fail("SDK sandbox appId " + sdkSandboxAppId + " does not exist.");
+ }
+ if (mAppIdsTrafficPermission.get(sdkSandboxAppId) != permission) {
+ fail("SDK sandbox appId " + sdkSandboxAppId + " has wrong permission: "
+ + mAppIdsTrafficPermission.get(sdkSandboxAppId));
+ }
+ }
}
}
}
@@ -589,6 +615,17 @@
if (mUidsNetworkPermission.get(uid) != permission) {
fail("uid " + uid + " has wrong permission: " + permission);
}
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = mProcessShim.toSdkSandboxUid(uid);
+ if (mUidsNetworkPermission.get(sdkSandboxUid, DOES_NOT_EXIST)
+ == DOES_NOT_EXIST) {
+ fail("SDK sandbox uid " + uid + " does not exist.");
+ }
+ if (mUidsNetworkPermission.get(sdkSandboxUid) != permission) {
+ fail("SDK sandbox uid " + uid + " has wrong permission: "
+ + permission);
+ }
+ }
}
}
}
@@ -600,6 +637,14 @@
if (mUidsNetworkPermission.get(uid, DOES_NOT_EXIST) != DOES_NOT_EXIST) {
fail("uid " + uid + " has listed permissions, expected none.");
}
+ if (hasSdkSandbox(uid)) {
+ int sdkSandboxUid = mProcessShim.toSdkSandboxUid(uid);
+ if (mUidsNetworkPermission.get(sdkSandboxUid, DOES_NOT_EXIST)
+ != DOES_NOT_EXIST) {
+ fail("SDK sandbox uid " + sdkSandboxUid
+ + " has listed permissions, expected none.");
+ }
+ }
}
}
}
@@ -785,9 +830,18 @@
// MOCK_APPID2: MOCK_PACKAGE2 does not have any permission.
// SYSTEM_APPID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission
// SYSTEM_APPID2: SYSTEM_PACKAGE2 has only update device stats permission.
+ // The SDK sandbox APPIDs must have permissions mirroring the app
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
netdPermissionsAppIds.put(MOCK_APPID1, PERMISSION_INTERNET);
+ if (hasSdkSandbox(MOCK_APPID1)) {
+ netdPermissionsAppIds.put(mProcessShim.toSdkSandboxUid(MOCK_APPID1),
+ PERMISSION_INTERNET);
+ }
netdPermissionsAppIds.put(MOCK_APPID2, PERMISSION_NONE);
+ if (hasSdkSandbox(MOCK_APPID2)) {
+ netdPermissionsAppIds.put(mProcessShim.toSdkSandboxUid(MOCK_APPID2),
+ PERMISSION_NONE);
+ }
netdPermissionsAppIds.put(SYSTEM_APPID1, PERMISSION_TRAFFIC_ALL);
netdPermissionsAppIds.put(SYSTEM_APPID2, PERMISSION_UPDATE_DEVICE_STATS);