Merge "Add ApfCapabilities common test cases"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0e10de8..a69ca99 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3449,6 +3449,10 @@
final NetworkCallback callback;
synchronized (sCallbacks) {
callback = sCallbacks.get(request);
+ if (message.what == CALLBACK_UNAVAIL) {
+ sCallbacks.remove(request);
+ callback.networkRequest = ALREADY_UNREGISTERED;
+ }
}
if (DBG) {
Log.d(TAG, getCallbackName(message.what) + " for network " + network);
@@ -3995,8 +3999,10 @@
synchronized (sCallbacks) {
Preconditions.checkArgument(networkCallback.networkRequest != null,
"NetworkCallback was not registered");
- Preconditions.checkArgument(networkCallback.networkRequest != ALREADY_UNREGISTERED,
- "NetworkCallback was already unregistered");
+ if (networkCallback.networkRequest == ALREADY_UNREGISTERED) {
+ Log.d(TAG, "NetworkCallback was already unregistered");
+ return;
+ }
for (Map.Entry<NetworkRequest, NetworkCallback> e : sCallbacks.entrySet()) {
if (e.getValue() == networkCallback) {
reqs.add(e.getKey());
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 9d91620..46eddde 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -43,6 +43,10 @@
* To stop an existing keepalive, call {@link SocketKeepalive#stop}. The system will call
* {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
* {@link SocketKeepalive.Callback#onError} if an error occurred.
+ *
+ * The device SHOULD support keepalive offload. If it does not, it MUST reply with
+ * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
+ * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport.
*/
public abstract class SocketKeepalive implements AutoCloseable {
static final String TAG = "SocketKeepalive";
diff --git a/core/java/android/net/util/KeepaliveUtils.java b/core/java/android/net/util/KeepaliveUtils.java
new file mode 100644
index 0000000..569fed1
--- /dev/null
+++ b/core/java/android/net/util/KeepaliveUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.text.TextUtils;
+import android.util.AndroidRuntimeException;
+
+import com.android.internal.R;
+
+/**
+ * Collection of utilities for socket keepalive offload.
+ *
+ * @hide
+ */
+public final class KeepaliveUtils {
+
+ public static final String TAG = "KeepaliveUtils";
+
+ // Minimum supported keepalive count per transport if the network supports keepalive.
+ public static final int MIN_SUPPORTED_KEEPALIVE_COUNT = 3;
+
+ public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException {
+ public KeepaliveDeviceConfigurationException(final String msg) {
+ super(msg);
+ }
+ }
+
+ /**
+ * Read supported keepalive count for each transport type from overlay resource. This should be
+ * used to create a local variable store of resource customization, and use it as the input for
+ * {@link getSupportedKeepalivesForNetworkCapabilities}.
+ *
+ * @param context The context to read resource from.
+ * @return An array of supported keepalive count for each transport type.
+ */
+ @NonNull
+ public static int[] getSupportedKeepalives(@NonNull Context context) {
+ String[] res = null;
+ try {
+ res = context.getResources().getStringArray(
+ R.array.config_networkSupportedKeepaliveCount);
+ } catch (Resources.NotFoundException unused) {
+ }
+ if (res == null) throw new KeepaliveDeviceConfigurationException("invalid resource");
+
+ final int[] ret = new int[NetworkCapabilities.MAX_TRANSPORT + 1];
+ for (final String row : res) {
+ if (TextUtils.isEmpty(row)) {
+ throw new KeepaliveDeviceConfigurationException("Empty string");
+ }
+ final String[] arr = row.split(",");
+ if (arr.length != 2) {
+ throw new KeepaliveDeviceConfigurationException("Invalid parameter length");
+ }
+
+ int transport;
+ int supported;
+ try {
+ transport = Integer.parseInt(arr[0]);
+ supported = Integer.parseInt(arr[1]);
+ } catch (NumberFormatException e) {
+ throw new KeepaliveDeviceConfigurationException("Invalid number format");
+ }
+
+ if (!NetworkCapabilities.isValidTransport(transport)) {
+ throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport);
+ }
+
+ // Customized values should be either 0 to indicate the network doesn't support
+ // keepalive offload, or a positive value that is at least
+ // MIN_SUPPORTED_KEEPALIVE_COUNT if supported.
+ if (supported != 0 && supported < MIN_SUPPORTED_KEEPALIVE_COUNT) {
+ throw new KeepaliveDeviceConfigurationException(
+ "Invalid supported count " + supported + " for "
+ + NetworkCapabilities.transportNameOf(transport));
+ }
+ ret[transport] = supported;
+ }
+ return ret;
+ }
+
+ /**
+ * Get supported keepalive count for the given {@link NetworkCapabilities}.
+ *
+ * @param supportedKeepalives An array of supported keepalive count for each transport type.
+ * @param nc The {@link NetworkCapabilities} of the network the socket keepalive is on.
+ *
+ * @return Supported keepalive count for the given {@link NetworkCapabilities}.
+ */
+ public static int getSupportedKeepalivesForNetworkCapabilities(
+ @NonNull int[] supportedKeepalives, @NonNull NetworkCapabilities nc) {
+ final int[] transports = nc.getTransportTypes();
+ if (transports.length == 0) return 0;
+ int supportedCount = supportedKeepalives[transports[0]];
+ // Iterate through transports and return minimum supported value.
+ for (final int transport : transports) {
+ if (supportedCount > supportedKeepalives[transport]) {
+ supportedCount = supportedKeepalives[transport];
+ }
+ }
+ return supportedCount;
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 63fd2fd..562199a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -27,7 +27,6 @@
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,7 +40,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
-import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
+import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -2825,8 +2824,8 @@
}
}
- private boolean networkRequiresValidation(NetworkAgentInfo nai) {
- return isValidationRequired(nai.networkCapabilities);
+ private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
+ return isPrivateDnsValidationRequired(nai.networkCapabilities);
}
private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
@@ -2844,7 +2843,7 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
handlePerNetworkPrivateDnsConfig(nai, cfg);
- if (networkRequiresValidation(nai)) {
+ if (networkRequiresPrivateDnsValidation(nai)) {
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
}
@@ -2853,7 +2852,7 @@
private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
// Private DNS only ever applies to networks that might provide
// Internet access and therefore also require validation.
- if (!networkRequiresValidation(nai)) return;
+ if (!networkRequiresPrivateDnsValidation(nai)) return;
// Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
// schedule DNS resolutions. If a DNS resolution is required the
@@ -6859,7 +6858,6 @@
enforceKeepalivePermission();
mKeepaliveTracker.startNattKeepalive(
getNetworkAgentInfoForNetwork(network), null /* fd */,
- INVALID_RESOURCE_ID /* Unused */,
intervalSeconds, cb,
srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index bde430c..526b4ff 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import static android.net.NattSocketKeepalive.NATT_PORT;
import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
@@ -30,6 +29,7 @@
import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
import static android.net.SocketKeepalive.NO_KEEPALIVE;
@@ -38,7 +38,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.net.IIpSecService;
import android.net.ISocketKeepaliveCallback;
import android.net.KeepalivePacketData;
import android.net.NattKeepalivePacketData;
@@ -48,18 +47,19 @@
import android.net.SocketKeepalive.InvalidSocketException;
import android.net.TcpKeepalivePacketData;
import android.net.util.IpUtils;
+import android.net.util.KeepaliveUtils;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.R;
import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
@@ -68,6 +68,7 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
/**
@@ -92,14 +93,30 @@
private final TcpKeepaliveController mTcpController;
@NonNull
private final Context mContext;
+
+ // Supported keepalive count for each transport type, can be configured through
+ // config_networkSupportedKeepaliveCount. For better error handling, use
+ // {@link getSupportedKeepalivesForNetworkCapabilities} instead of direct access.
@NonNull
- private final IIpSecService mIpSec;
+ private final int[] mSupportedKeepalives;
+
+ // Reserved privileged keepalive slots per transport. Caller's permission will be enforced if
+ // the number of remaining keepalive slots is less than or equal to the threshold.
+ private final int mReservedPrivilegedSlots;
+
+ // Allowed unprivileged keepalive slots per uid. Caller's permission will be enforced if
+ // the number of remaining keepalive slots is less than or equal to the threshold.
+ private final int mAllowedUnprivilegedSlotsForUid;
public KeepaliveTracker(Context context, Handler handler) {
mConnectivityServiceHandler = handler;
mTcpController = new TcpKeepaliveController(handler);
mContext = context;
- mIpSec = IIpSecService.Stub.asInterface(ServiceManager.getService(Context.IPSEC_SERVICE));
+ mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
+ mReservedPrivilegedSlots = mContext.getResources().getInteger(
+ R.integer.config_reservedPrivilegedKeepaliveSlots);
+ mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger(
+ R.integer.config_allowedUnprivilegedKeepalivePerUid);
}
/**
@@ -118,18 +135,9 @@
private final int mType;
private final FileDescriptor mFd;
- private final int mEncapSocketResourceId;
- // Stores the NATT keepalive resource ID returned by IpSecService.
- private int mNattIpsecResourceId = INVALID_RESOURCE_ID;
-
public static final int TYPE_NATT = 1;
public static final int TYPE_TCP = 2;
- // Max allowed unprivileged keepalive slots per network. Caller's permission will be
- // enforced if number of existing keepalives reach this limit.
- // TODO: consider making this limit configurable via resources.
- private static final int MAX_UNPRIVILEGED_SLOTS = 3;
-
// Keepalive slot. A small integer that identifies this keepalive among the ones handled
// by this network.
private int mSlot = NO_KEEPALIVE;
@@ -150,8 +158,7 @@
@NonNull KeepalivePacketData packet,
int interval,
int type,
- @Nullable FileDescriptor fd,
- int encapSocketResourceId) throws InvalidSocketException {
+ @Nullable FileDescriptor fd) throws InvalidSocketException {
mCallback = callback;
mPid = Binder.getCallingPid();
mUid = Binder.getCallingUid();
@@ -162,9 +169,6 @@
mInterval = interval;
mType = type;
- mEncapSocketResourceId = encapSocketResourceId;
- mNattIpsecResourceId = INVALID_RESOURCE_ID;
-
// For SocketKeepalive, a dup of fd is kept in mFd so the source port from which the
// keepalives are sent cannot be reused by another app even if the fd gets closed by
// the user. A null is acceptable here for backward compatibility of PacketKeepalive
@@ -172,7 +176,7 @@
try {
if (fd != null) {
mFd = Os.dup(fd);
- } else {
+ } else {
Log.d(TAG, toString() + " calls with null fd");
if (!mPrivileged) {
throw new SecurityException(
@@ -220,8 +224,6 @@
+ IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+ " interval=" + mInterval
+ " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
- + " nattIpsecRId=" + mNattIpsecResourceId
- + " encapSocketRId=" + mEncapSocketResourceId
+ " packetData=" + HexDump.toHexString(mPacket.getPacket())
+ " ]";
}
@@ -263,69 +265,54 @@
private int checkPermission() {
final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
- int unprivilegedCount = 0;
if (networkKeepalives == null) {
return ERROR_INVALID_NETWORK;
}
- for (KeepaliveInfo ki : networkKeepalives.values()) {
- if (!ki.mPrivileged) {
- unprivilegedCount++;
+
+ if (mPrivileged) return SUCCESS;
+
+ final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
+ mSupportedKeepalives, mNai.networkCapabilities);
+
+ int takenUnprivilegedSlots = 0;
+ for (final KeepaliveInfo ki : networkKeepalives.values()) {
+ if (!ki.mPrivileged) ++takenUnprivilegedSlots;
+ }
+ if (takenUnprivilegedSlots > supported - mReservedPrivilegedSlots) {
+ return ERROR_INSUFFICIENT_RESOURCES;
+ }
+
+ // Count unprivileged keepalives for the same uid across networks.
+ int unprivilegedCountSameUid = 0;
+ for (final HashMap<Integer, KeepaliveInfo> kaForNetwork : mKeepalives.values()) {
+ for (final KeepaliveInfo ki : kaForNetwork.values()) {
+ if (ki.mUid == mUid) {
+ unprivilegedCountSameUid++;
+ }
}
- if (unprivilegedCount >= MAX_UNPRIVILEGED_SLOTS) {
- return mPrivileged ? SUCCESS : ERROR_INSUFFICIENT_RESOURCES;
- }
+ }
+ if (unprivilegedCountSameUid > mAllowedUnprivilegedSlotsForUid) {
+ return ERROR_INSUFFICIENT_RESOURCES;
}
return SUCCESS;
}
- private int checkAndLockNattKeepaliveResource() {
- // Check that apps should be either privileged or fill in an ipsec encapsulated socket
- // resource id.
- if (mEncapSocketResourceId == INVALID_RESOURCE_ID) {
- if (mPrivileged) {
- return SUCCESS;
- } else {
- // Invalid access.
- return ERROR_INVALID_SOCKET;
- }
- }
-
- // Check if the ipsec encapsulated socket resource id is registered.
+ private int checkLimit() {
final HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(mNai);
if (networkKeepalives == null) {
return ERROR_INVALID_NETWORK;
}
- for (KeepaliveInfo ki : networkKeepalives.values()) {
- if (ki.mEncapSocketResourceId == mEncapSocketResourceId
- && ki.mNattIpsecResourceId != INVALID_RESOURCE_ID) {
- Log.d(TAG, "Registered resource found on keepalive " + mSlot
- + " when verify NATT socket with uid=" + mUid + " rid="
- + mEncapSocketResourceId);
- return ERROR_INVALID_SOCKET;
- }
- }
-
- // Ensure that the socket is created by IpSecService, and lock the resource that is
- // preserved by IpSecService. If succeed, a resource id is stored to keep tracking
- // the resource preserved by IpSecServce and must be released when stopping keepalive.
- try {
- mNattIpsecResourceId =
- mIpSec.lockEncapSocketForNattKeepalive(mEncapSocketResourceId, mUid);
- return SUCCESS;
- } catch (IllegalArgumentException e) {
- // The UID specified does not own the specified UDP encapsulation socket.
- Log.d(TAG, "Failed to verify NATT socket with uid=" + mUid + " rid="
- + mEncapSocketResourceId + ": " + e);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling lockEncapSocketForNattKeepalive with "
- + this.toString(), e);
- }
- return ERROR_INVALID_SOCKET;
+ final int supported = KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
+ mSupportedKeepalives, mNai.networkCapabilities);
+ if (supported == 0) return ERROR_UNSUPPORTED;
+ if (networkKeepalives.size() > supported) return ERROR_INSUFFICIENT_RESOURCES;
+ return SUCCESS;
}
private int isValid() {
synchronized (mNai) {
int error = checkInterval();
+ if (error == SUCCESS) error = checkLimit();
if (error == SUCCESS) error = checkPermission();
if (error == SUCCESS) error = checkNetworkConnected();
if (error == SUCCESS) error = checkSourceAddress();
@@ -336,17 +323,12 @@
void start(int slot) {
mSlot = slot;
int error = isValid();
-
- // Check and lock ipsec resource needed by natt kepalive. This should be only called
- // once per keepalive.
- if (error == SUCCESS && mType == TYPE_NATT) {
- error = checkAndLockNattKeepaliveResource();
- }
-
if (error == SUCCESS) {
Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
switch (mType) {
case TYPE_NATT:
+ mNai.asyncChannel.sendMessage(
+ CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
mNai.asyncChannel
.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
break;
@@ -357,9 +339,8 @@
handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
return;
}
- mNai.asyncChannel
- .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
- mPacket);
+ mNai.asyncChannel.sendMessage(
+ CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */, mPacket);
// TODO: check result from apf and notify of failure as needed.
mNai.asyncChannel
.sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
@@ -395,14 +376,17 @@
return;
default:
mStartedState = STOPPING;
- if (mType == TYPE_NATT) {
- mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
- } else if (mType == TYPE_TCP) {
- mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
- mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
- mTcpController.stopSocketMonitor(mSlot);
- } else {
- Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
+ switch (mType) {
+ case TYPE_TCP:
+ mTcpController.stopSocketMonitor(mSlot);
+ // fall through
+ case TYPE_NATT:
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
+ mSlot);
+ break;
+ default:
+ Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
}
}
@@ -418,20 +402,6 @@
}
}
- // Release the resource held by keepalive in IpSecService.
- if (mNattIpsecResourceId != INVALID_RESOURCE_ID) {
- if (mType != TYPE_NATT) {
- Log.wtf(TAG, "natt ipsec resource held by incorrect keepalive "
- + this.toString());
- }
- try {
- mIpSec.releaseNattKeepalive(mNattIpsecResourceId, mUid);
- } catch (RemoteException e) {
- Log.wtf(TAG, "error calling releaseNattKeepalive with " + this.toString(), e);
- }
- mNattIpsecResourceId = INVALID_RESOURCE_ID;
- }
-
if (reason == SUCCESS) {
try {
mCallback.onStopped();
@@ -496,10 +466,11 @@
if (networkKeepalives != null) {
for (KeepaliveInfo ki : networkKeepalives.values()) {
ki.stop(reason);
+ // Clean up keepalives since the network agent is disconnected and unable to pass
+ // back asynchronous result of stop().
+ cleanupStoppedKeepalive(nai, ki.mSlot);
}
}
- // Clean up keepalives will be done as a result of calling ki.stop() after the slots are
- // freed.
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
@@ -620,7 +591,6 @@
**/
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
@Nullable FileDescriptor fd,
- int encapSocketResourceId,
int intervalSeconds,
@NonNull ISocketKeepaliveCallback cb,
@NonNull String srcAddrString,
@@ -652,7 +622,7 @@
KeepaliveInfo ki = null;
try {
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
- KeepaliveInfo.TYPE_NATT, fd, encapSocketResourceId);
+ KeepaliveInfo.TYPE_NATT, fd);
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive", e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
@@ -692,7 +662,7 @@
KeepaliveInfo ki = null;
try {
ki = new KeepaliveInfo(cb, nai, packet, intervalSeconds,
- KeepaliveInfo.TYPE_TCP, fd, INVALID_RESOURCE_ID /* Unused */);
+ KeepaliveInfo.TYPE_TCP, fd);
} catch (InvalidSocketException | IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Fail to construct keepalive e=" + e);
notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
@@ -711,12 +681,17 @@
**/
public void startNattKeepalive(@Nullable NetworkAgentInfo nai,
@Nullable FileDescriptor fd,
- int encapSocketResourceId,
+ int resourceId,
int intervalSeconds,
@NonNull ISocketKeepaliveCallback cb,
@NonNull String srcAddrString,
@NonNull String dstAddrString,
int dstPort) {
+ // Ensure that the socket is created by IpSecService.
+ if (!isNattKeepaliveSocketValid(fd, resourceId)) {
+ notifyErrorCallback(cb, ERROR_INVALID_SOCKET);
+ }
+
// Get src port to adopt old API.
int srcPort = 0;
try {
@@ -727,11 +702,29 @@
}
// Forward request to old API.
- startNattKeepalive(nai, fd, encapSocketResourceId, intervalSeconds, cb, srcAddrString,
- srcPort, dstAddrString, dstPort);
+ startNattKeepalive(nai, fd, intervalSeconds, cb, srcAddrString, srcPort,
+ dstAddrString, dstPort);
+ }
+
+ /**
+ * Verify if the IPsec NAT-T file descriptor and resource Id hold for IPsec keepalive is valid.
+ **/
+ public static boolean isNattKeepaliveSocketValid(@Nullable FileDescriptor fd, int resourceId) {
+ // TODO: 1. confirm whether the fd is called from system api or created by IpSecService.
+ // 2. If the fd is created from the system api, check that it's bounded. And
+ // call dup to keep the fd open.
+ // 3. If the fd is created from IpSecService, check if the resource ID is valid. And
+ // hold the resource needed in IpSecService.
+ if (null == fd) {
+ return false;
+ }
+ return true;
}
public void dump(IndentingPrintWriter pw) {
+ pw.println("Supported Socket keepalives: " + Arrays.toString(mSupportedKeepalives));
+ pw.println("Reserved Privileged keepalives: " + mReservedPrivilegedSlots);
+ pw.println("Allowed Unprivileged keepalives per uid: " + mAllowedUnprivilegedSlotsForUid);
pw.println("Socket keepalives:");
pw.increaseIndent();
for (NetworkAgentInfo nai : mKeepalives.keySet()) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index cfa9131..34772d0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -483,11 +483,11 @@
// down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available.
if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
- return ConnectivityConstants.MAXIMUM_NETWORK_SCORE;
+ return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
int score = currentScore;
- if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) {
+ if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 709f5f6..e1c4238 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -46,28 +47,80 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LinkPropertiesTest {
- private static InetAddress ADDRV4 = NetworkUtils.numericToInetAddress("75.208.6.1");
- private static InetAddress ADDRV6 = NetworkUtils.numericToInetAddress(
+ private static final InetAddress ADDRV4 = InetAddresses.parseNumericAddress("75.208.6.1");
+ private static final InetAddress ADDRV6 = InetAddresses.parseNumericAddress(
"2001:0db8:85a3:0000:0000:8a2e:0370:7334");
- private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1");
- private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1");
- private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
- private static InetAddress PCSCFV6 = NetworkUtils.numericToInetAddress(
+ private static final InetAddress DNS1 = InetAddresses.parseNumericAddress("75.208.7.1");
+ private static final InetAddress DNS2 = InetAddresses.parseNumericAddress("69.78.7.1");
+ private static final InetAddress DNS6 = InetAddresses.parseNumericAddress(
+ "2001:4860:4860::8888");
+ private static final InetAddress PRIVDNS1 = InetAddresses.parseNumericAddress("1.1.1.1");
+ private static final InetAddress PRIVDNS2 = InetAddresses.parseNumericAddress("1.0.0.1");
+ private static final InetAddress PRIVDNS6 = InetAddresses.parseNumericAddress(
+ "2606:4700:4700::1111");
+ private static final InetAddress PCSCFV4 = InetAddresses.parseNumericAddress("10.77.25.37");
+ private static final InetAddress PCSCFV6 = InetAddresses.parseNumericAddress(
"2001:0db8:85a3:0000:0000:8a2e:0370:1");
- private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
- private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
- private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
- private static InetAddress GATEWAY62 = NetworkUtils.numericToInetAddress("fe80::6:2222");
- private static String NAME = "qmi0";
- private static int MTU = 1500;
-
- private static LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
- private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
- private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ private static final InetAddress GATEWAY1 = InetAddresses.parseNumericAddress("75.208.8.1");
+ private static final InetAddress GATEWAY2 = InetAddresses.parseNumericAddress("69.78.8.1");
+ private static final InetAddress GATEWAY61 = InetAddresses.parseNumericAddress(
+ "fe80::6:0000:613");
+ private static final InetAddress GATEWAY62 = InetAddresses.parseNumericAddress("fe80::6:2222");
+ private static final String NAME = "qmi0";
+ private static final String DOMAINS = "google.com";
+ private static final String PRIV_DNS_SERVER_NAME = "private.dns.com";
+ private static final String TCP_BUFFER_SIZES = "524288,1048576,2097152,262144,524288,1048576";
+ private static final int MTU = 1500;
+ private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
+ private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
+ private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
// TODO: replace all calls to NetworkUtils.numericToInetAddress with calls to this method.
private InetAddress Address(String addrString) {
- return NetworkUtils.numericToInetAddress(addrString);
+ return InetAddresses.parseNumericAddress(addrString);
+ }
+
+ private void checkEmpty(final LinkProperties lp) {
+ assertEquals(0, lp.getAllInterfaceNames().size());
+ assertEquals(0, lp.getAllAddresses().size());
+ assertEquals(0, lp.getDnsServers().size());
+ assertEquals(0, lp.getValidatedPrivateDnsServers().size());
+ assertEquals(0, lp.getPcscfServers().size());
+ assertEquals(0, lp.getAllRoutes().size());
+ assertEquals(0, lp.getAllLinkAddresses().size());
+ assertEquals(0, lp.getStackedLinks().size());
+ assertEquals(0, lp.getMtu());
+ assertNull(lp.getPrivateDnsServerName());
+ assertNull(lp.getDomains());
+ assertNull(lp.getHttpProxy());
+ assertNull(lp.getTcpBufferSizes());
+ assertNull(lp.getNat64Prefix());
+ assertFalse(lp.isProvisioned());
+ assertFalse(lp.isIpv4Provisioned());
+ assertFalse(lp.isIpv6Provisioned());
+ assertFalse(lp.isPrivateDnsActive());
+ }
+
+ private LinkProperties makeTestObject() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(NAME);
+ lp.addLinkAddress(LINKADDRV4);
+ lp.addLinkAddress(LINKADDRV6);
+ lp.addDnsServer(DNS1);
+ lp.addDnsServer(DNS2);
+ lp.addValidatedPrivateDnsServer(PRIVDNS1);
+ lp.addValidatedPrivateDnsServer(PRIVDNS2);
+ lp.setUsePrivateDns(true);
+ lp.setPrivateDnsServerName(PRIV_DNS_SERVER_NAME);
+ lp.addPcscfServer(PCSCFV6);
+ lp.setDomains(DOMAINS);
+ lp.addRoute(new RouteInfo(GATEWAY1));
+ lp.addRoute(new RouteInfo(GATEWAY2));
+ lp.setHttpProxy(ProxyInfo.buildDirectProxy("test", 8888));
+ lp.setMtu(MTU);
+ lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
+ lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ return lp;
}
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
@@ -170,8 +223,7 @@
target.clear();
target.setInterfaceName(NAME);
// change link addresses
- target.addLinkAddress(new LinkAddress(
- NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
+ target.addLinkAddress(new LinkAddress(Address("75.208.6.2"), 32));
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
@@ -186,7 +238,7 @@
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
// change dnses
- target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(Address("75.208.7.2"));
target.addDnsServer(DNS2);
target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
@@ -198,11 +250,10 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(Address("75.208.7.2"));
target.addDnsServer(DNS2);
// change pcscf
- target.addPcscfServer(NetworkUtils.numericToInetAddress(
- "2001::1"));
+ target.addPcscfServer(Address("2001::1"));
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -215,7 +266,7 @@
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
// change gateway
- target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
+ target.addRoute(new RouteInfo(Address("75.208.8.2")));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
assertFalse(source.equals(target));
@@ -285,10 +336,15 @@
}
}
+ private void assertAllRoutesNotHaveInterface(String iface, LinkProperties lp) {
+ for (RouteInfo r : lp.getRoutes()) {
+ assertNotEquals(iface, r.getInterface());
+ }
+ }
+
@Test
public void testRouteInterfaces() {
- LinkAddress prefix = new LinkAddress(
- NetworkUtils.numericToInetAddress("2001:db8::"), 32);
+ LinkAddress prefix = new LinkAddress(Address("2001:db8::"), 32);
InetAddress address = ADDRV6;
// Add a route with no interface to a LinkProperties with no interface. No errors.
@@ -312,6 +368,8 @@
// Change the interface name. All the routes should change their interface name too.
lp.setInterfaceName("rmnet0");
assertAllRoutesHaveInterface("rmnet0", lp);
+ assertAllRoutesNotHaveInterface(null, lp);
+ assertAllRoutesNotHaveInterface("wlan0", lp);
// Now add a route with the wrong interface. This causes an exception too.
try {
@@ -325,6 +383,7 @@
lp.addRoute(r);
assertEquals(2, lp.getRoutes().size());
assertAllRoutesHaveInterface("wlan0", lp);
+ assertAllRoutesNotHaveInterface("rmnet0", lp);
// Routes with null interfaces are converted to wlan0.
r = RouteInfo.makeHostRoute(ADDRV6, null);
@@ -334,14 +393,23 @@
// Check comparisons work.
LinkProperties lp2 = new LinkProperties(lp);
- assertAllRoutesHaveInterface("wlan0", lp);
+ assertAllRoutesHaveInterface("wlan0", lp2);
assertEquals(0, lp.compareAllRoutes(lp2).added.size());
assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
lp2.setInterfaceName("p2p0");
assertAllRoutesHaveInterface("p2p0", lp2);
+ assertAllRoutesNotHaveInterface("wlan0", lp2);
assertEquals(3, lp.compareAllRoutes(lp2).added.size());
assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+
+ // Check remove works
+ lp.removeRoute(new RouteInfo(prefix, address, null));
+ assertEquals(3, lp.getRoutes().size());
+ lp.removeRoute(new RouteInfo(prefix, address, "wlan0"));
+ assertEquals(2, lp.getRoutes().size());
+ assertAllRoutesHaveInterface("wlan0", lp);
+ assertAllRoutesNotHaveInterface("p2p0", lp);
}
@Test
@@ -488,18 +556,26 @@
}
@Test
- public void testSetLinkAddresses() {
- LinkProperties lp = new LinkProperties();
+ public void testLinkAddresses() {
+ final LinkProperties lp = new LinkProperties();
lp.addLinkAddress(LINKADDRV4);
lp.addLinkAddress(LINKADDRV6);
- LinkProperties lp2 = new LinkProperties();
+ final LinkProperties lp2 = new LinkProperties();
lp2.addLinkAddress(LINKADDRV6);
- assertFalse(lp.equals(lp2));
+ final LinkProperties lp3 = new LinkProperties();
+ final List<LinkAddress> linkAddresses = Arrays.asList(LINKADDRV4);
+ lp3.setLinkAddresses(linkAddresses);
- lp2.setLinkAddresses(lp.getLinkAddresses());
- assertTrue(lp.equals(lp));
+ assertFalse(lp.equals(lp2));
+ assertFalse(lp2.equals(lp3));
+
+ lp.removeLinkAddress(LINKADDRV4);
+ assertTrue(lp.equals(lp2));
+
+ lp2.setLinkAddresses(lp3.getLinkAddresses());
+ assertTrue(lp2.equals(lp3));
}
@Test
@@ -675,9 +751,9 @@
assertTrue(v4lp.isReachable(DNS2));
final LinkProperties v6lp = new LinkProperties();
- final InetAddress kLinkLocalDns = NetworkUtils.numericToInetAddress("fe80::6:1");
- final InetAddress kLinkLocalDnsWithScope = NetworkUtils.numericToInetAddress("fe80::6:2%43");
- final InetAddress kOnLinkDns = NetworkUtils.numericToInetAddress("2001:db8:85a3::53");
+ final InetAddress kLinkLocalDns = Address("fe80::6:1");
+ final InetAddress kLinkLocalDnsWithScope = Address("fe80::6:2%43");
+ final InetAddress kOnLinkDns = Address("2001:db8:85a3::53");
assertFalse(v6lp.isReachable(kLinkLocalDns));
assertFalse(v6lp.isReachable(kLinkLocalDnsWithScope));
assertFalse(v6lp.isReachable(kOnLinkDns));
@@ -686,8 +762,7 @@
// Add a link-local route, making the link-local DNS servers reachable. Because
// we assume the presence of an IPv6 link-local address, link-local DNS servers
// are considered reachable, but only those with a non-zero scope identifier.
- assertTrue(v6lp.addRoute(new RouteInfo(
- new IpPrefix(NetworkUtils.numericToInetAddress("fe80::"), 64))));
+ assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(Address("fe80::"), 64))));
assertFalse(v6lp.isReachable(kLinkLocalDns));
assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
assertFalse(v6lp.isReachable(kOnLinkDns));
@@ -703,8 +778,7 @@
// Add a global route on link, but no global address yet. DNS servers reachable
// via a route that doesn't require a gateway: give them the benefit of the
// doubt and hope the link-local source address suffices for communication.
- assertTrue(v6lp.addRoute(new RouteInfo(
- new IpPrefix(NetworkUtils.numericToInetAddress("2001:db8:85a3::"), 64))));
+ assertTrue(v6lp.addRoute(new RouteInfo(new IpPrefix(Address("2001:db8:85a3::"), 64))));
assertFalse(v6lp.isReachable(kLinkLocalDns));
assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
assertTrue(v6lp.isReachable(kOnLinkDns));
@@ -766,8 +840,8 @@
LinkProperties rmnet1 = new LinkProperties();
rmnet1.setInterfaceName("rmnet1");
rmnet1.addLinkAddress(new LinkAddress("10.0.0.3/8"));
- RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null,
- NetworkUtils.numericToInetAddress("10.0.0.1"), rmnet1.getInterfaceName());
+ RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null, Address("10.0.0.1"),
+ rmnet1.getInterfaceName());
RouteInfo directRoute1 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null,
rmnet1.getInterfaceName());
rmnet1.addRoute(defaultRoute1);
@@ -785,8 +859,8 @@
rmnet2.setInterfaceName("rmnet2");
rmnet2.addLinkAddress(new LinkAddress("fe80::cafe/64"));
rmnet2.addLinkAddress(new LinkAddress("2001:db8::2/64"));
- RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null,
- NetworkUtils.numericToInetAddress("2001:db8::1"), rmnet2.getInterfaceName());
+ RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null, Address("2001:db8::1"),
+ rmnet2.getInterfaceName());
RouteInfo directRoute2 = new RouteInfo(new IpPrefix("2001:db8::/64"), null,
rmnet2.getInterfaceName());
RouteInfo linkLocalRoute2 = new RouteInfo(new IpPrefix("fe80::/64"), null,
@@ -876,4 +950,111 @@
LinkProperties empty = new LinkProperties();
TestUtils.assertParcelingIsLossless(empty);
}
+
+ @Test
+ public void testConstructor() {
+ LinkProperties lp = new LinkProperties();
+ checkEmpty(lp);
+ assertLinkPropertiesEqual(lp, new LinkProperties(lp));
+ assertLinkPropertiesEqual(lp, new LinkProperties());
+
+ lp = makeTestObject();
+ assertLinkPropertiesEqual(lp, new LinkProperties(lp));
+ }
+
+ @Test
+ public void testDnsServers() {
+ final LinkProperties lp = new LinkProperties();
+ final List<InetAddress> dnsServers = Arrays.asList(DNS1, DNS2);
+ lp.setDnsServers(dnsServers);
+ assertEquals(2, lp.getDnsServers().size());
+ assertEquals(DNS1, lp.getDnsServers().get(0));
+ assertEquals(DNS2, lp.getDnsServers().get(1));
+
+ lp.removeDnsServer(DNS1);
+ assertEquals(1, lp.getDnsServers().size());
+ assertEquals(DNS2, lp.getDnsServers().get(0));
+
+ lp.addDnsServer(DNS6);
+ assertEquals(2, lp.getDnsServers().size());
+ assertEquals(DNS2, lp.getDnsServers().get(0));
+ assertEquals(DNS6, lp.getDnsServers().get(1));
+ }
+
+ @Test
+ public void testValidatedPrivateDnsServers() {
+ final LinkProperties lp = new LinkProperties();
+ final List<InetAddress> privDnsServers = Arrays.asList(PRIVDNS1, PRIVDNS2);
+ lp.setValidatedPrivateDnsServers(privDnsServers);
+ assertEquals(2, lp.getValidatedPrivateDnsServers().size());
+ assertEquals(PRIVDNS1, lp.getValidatedPrivateDnsServers().get(0));
+ assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(1));
+
+ lp.removeValidatedPrivateDnsServer(PRIVDNS1);
+ assertEquals(1, lp.getValidatedPrivateDnsServers().size());
+ assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0));
+
+ lp.addValidatedPrivateDnsServer(PRIVDNS6);
+ assertEquals(2, lp.getValidatedPrivateDnsServers().size());
+ assertEquals(PRIVDNS2, lp.getValidatedPrivateDnsServers().get(0));
+ assertEquals(PRIVDNS6, lp.getValidatedPrivateDnsServers().get(1));
+ }
+
+ @Test
+ public void testPcscfServers() {
+ final LinkProperties lp = new LinkProperties();
+ final List<InetAddress> pcscfServers = Arrays.asList(PCSCFV4);
+ lp.setPcscfServers(pcscfServers);
+ assertEquals(1, lp.getPcscfServers().size());
+ assertEquals(PCSCFV4, lp.getPcscfServers().get(0));
+
+ lp.removePcscfServer(PCSCFV4);
+ assertEquals(0, lp.getPcscfServers().size());
+
+ lp.addPcscfServer(PCSCFV6);
+ assertEquals(1, lp.getPcscfServers().size());
+ assertEquals(PCSCFV6, lp.getPcscfServers().get(0));
+ }
+
+ @Test
+ public void testTcpBufferSizes() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(TCP_BUFFER_SIZES, lp.getTcpBufferSizes());
+
+ lp.setTcpBufferSizes(null);
+ assertNull(lp.getTcpBufferSizes());
+ }
+
+ @Test
+ public void testHasIpv6DefaultRoute() {
+ final LinkProperties lp = makeTestObject();
+ assertFalse(lp.hasIPv6DefaultRoute());
+
+ lp.addRoute(new RouteInfo(GATEWAY61));
+ assertTrue(lp.hasIPv6DefaultRoute());
+ }
+
+ @Test
+ public void testHttpProxy() {
+ final LinkProperties lp = makeTestObject();
+ assertTrue(lp.getHttpProxy().equals(ProxyInfo.buildDirectProxy("test", 8888)));
+ }
+
+ @Test
+ public void testPrivateDnsServerName() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(PRIV_DNS_SERVER_NAME, lp.getPrivateDnsServerName());
+
+ lp.setPrivateDnsServerName(null);
+ assertNull(lp.getPrivateDnsServerName());
+ }
+
+ @Test
+ public void testUsePrivateDns() {
+ final LinkProperties lp = makeTestObject();
+ assertTrue(lp.isPrivateDnsActive());
+
+ lp.clear();
+ assertFalse(lp.isPrivateDnsActive());
+ }
}
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index 18c6768..6e69b34 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -16,10 +16,26 @@
package android.net;
-import static org.mockito.ArgumentMatchers.any;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+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 android.content.Context;
+import android.net.ipmemorystore.Blob;
+import android.net.ipmemorystore.IOnStatusListener;
+import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.Status;
+import android.os.RemoteException;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -27,28 +43,57 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpMemoryStoreTest {
+ private static final String TAG = IpMemoryStoreTest.class.getSimpleName();
+ private static final String TEST_CLIENT_ID = "testClientId";
+ private static final String TEST_DATA_NAME = "testData";
+ private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other";
+ private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12,
+ -128, 0, 89, 112, 91, -34 };
+ private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes(
+ "hint", 219);
+
@Mock
Context mMockContext;
@Mock
NetworkStackClient mNetworkStackClient;
@Mock
IIpMemoryStore mMockService;
+ @Mock
+ IOnStatusListener mIOnStatusListener;
IpMemoryStore mStore;
+ @Captor
+ ArgumentCaptor<IIpMemoryStoreCallbacks> mCbCaptor;
+ @Captor
+ ArgumentCaptor<NetworkAttributesParcelable> mNapCaptor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- doAnswer(invocation -> {
- ((IIpMemoryStoreCallbacks) invocation.getArgument(0))
- .onIpMemoryStoreFetched(mMockService);
- return null;
- }).when(mNetworkStackClient).fetchIpMemoryStore(any());
+ }
+
+ private void startIpMemoryStore(boolean supplyService) {
+ if (supplyService) {
+ doAnswer(invocation -> {
+ ((IIpMemoryStoreCallbacks) invocation.getArgument(0))
+ .onIpMemoryStoreFetched(mMockService);
+ return null;
+ }).when(mNetworkStackClient).fetchIpMemoryStore(any());
+ } else {
+ doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
+ }
mStore = new IpMemoryStore(mMockContext) {
@Override
protected NetworkStackClient getNetworkStackClient() {
@@ -57,24 +102,223 @@
};
}
- @Test
- public void testNetworkAttributes() {
- // TODO : implement this
+ private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) {
+ return new NetworkAttributes.Builder()
+ .setGroupHint(hint)
+ .setMtu(mtu)
+ .build();
}
@Test
- public void testPrivateData() {
- // TODO : implement this
+ public void testNetworkAttributes() throws Exception {
+ startIpMemoryStore(true /* supplyService */);
+ final String l2Key = "fakeKey";
+
+ mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key),
+ mNapCaptor.capture(), any());
+ assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
+
+ mStore.retrieveNetworkAttributes(l2Key,
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
+ });
+
+ verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any());
}
@Test
- public void testFindL2Key() {
- // TODO : implement this
+ public void testPrivateData() throws RemoteException {
+ startIpMemoryStore(true /* supplyService */);
+ final Blob b = new Blob();
+ b.data = TEST_BLOB_DATA;
+ final String l2Key = "fakeKey";
+
+ mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ status -> {
+ assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
+ });
+ verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
+ eq(b), any());
+
+ mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ assertTrue(Arrays.equals(b.data, data.data));
+ });
+ verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
+ eq(TEST_OTHER_DATA_NAME), any());
}
@Test
- public void testIsSameNetwork() {
- // TODO : implement this
+ public void testFindL2Key()
+ throws UnknownHostException, RemoteException, Exception {
+ startIpMemoryStore(true /* supplyService */);
+ final String l2Key = "fakeKey";
+
+ mStore.findL2Key(TEST_NETWORK_ATTRIBUTES,
+ (status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ });
+ verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any());
+ assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
}
+ @Test
+ public void testIsSameNetwork() throws UnknownHostException, RemoteException {
+ startIpMemoryStore(true /* supplyService */);
+ final String l2Key1 = "fakeKey1";
+ final String l2Key2 = "fakeKey2";
+
+ mStore.isSameNetwork(l2Key1, l2Key2,
+ (status, answer) -> {
+ assertFalse("Retrieve network sameness suspiciously successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ assertNull(answer);
+ });
+ verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any());
+ }
+
+ @Test
+ public void testEnqueuedIpMsRequests() throws Exception {
+ startIpMemoryStore(false /* supplyService */);
+
+ final Blob b = new Blob();
+ b.data = TEST_BLOB_DATA;
+ final String l2Key = "fakeKey";
+
+ // enqueue multiple ipms requests
+ mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ mStore.retrieveNetworkAttributes(l2Key,
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
+ });
+ mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ assertTrue(Arrays.equals(b.data, data.data));
+ });
+
+ // get ipms service ready
+ mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService);
+
+ InOrder inOrder = inOrder(mMockService);
+
+ inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any());
+ inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any());
+ inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
+ eq(b), any());
+ inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
+ eq(TEST_OTHER_DATA_NAME), any());
+ assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
+ }
+
+ @Test
+ public void testEnqueuedIpMsRequestsWithException() throws Exception {
+ startIpMemoryStore(true /* supplyService */);
+ doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any());
+
+ final Blob b = new Blob();
+ b.data = TEST_BLOB_DATA;
+ final String l2Key = "fakeKey";
+
+ // enqueue multiple ipms requests
+ mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ mStore.retrieveNetworkAttributes(l2Key,
+ (status, key, attr) -> {
+ assertTrue("Retrieve network attributes not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
+ });
+ mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ assertTrue(Arrays.equals(b.data, data.data));
+ });
+
+ // verify the rest of the queue is still processed in order even if the remote exception
+ // occurs when calling one or more requests
+ InOrder inOrder = inOrder(mMockService);
+
+ inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any());
+ inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
+ eq(b), any());
+ inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
+ eq(TEST_OTHER_DATA_NAME), any());
+ assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
+ }
+
+ @Test
+ public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception {
+ startIpMemoryStore(true /* supplyService */);
+
+ final Blob b = new Blob();
+ b.data = TEST_BLOB_DATA;
+ final String l2Key = "fakeKey";
+
+ // enqueue multiple ipms requests
+ mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ mStore.retrieveNetworkAttributes(l2Key,
+ (status, key, attr) -> {
+ throw new RuntimeException("retrieveNetworkAttributes test");
+ });
+ mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
+ status -> {
+ throw new RuntimeException("storeBlob test");
+ });
+ mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
+ (status, key, name, data) -> {
+ assertTrue("Retrieve blob status not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(l2Key, key);
+ assertEquals(name, TEST_DATA_NAME);
+ assertTrue(Arrays.equals(b.data, data.data));
+ });
+
+ // verify the rest of the queue is still processed in order even if when one or more
+ // callback throw the remote exception
+ InOrder inOrder = inOrder(mMockService);
+
+ inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(),
+ any());
+ inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any());
+ inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
+ eq(b), any());
+ inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
+ eq(TEST_OTHER_DATA_NAME), any());
+ assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
+ }
}
diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
new file mode 100644
index 0000000..814e06e
--- /dev/null
+++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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.util
+
+import android.content.Context
+import android.content.res.Resources
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.MAX_TRANSPORT
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_VPN
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+
+/**
+ * Tests for [KeepaliveUtils].
+ *
+ * Build, install and run with:
+ * atest android.net.util.KeepaliveUtilsTest
+ */
+@RunWith(JUnit4::class)
+@SmallTest
+class KeepaliveUtilsTest {
+
+ // Prepare mocked context with given resource strings.
+ private fun getMockedContextWithStringArrayRes(id: Int, res: Array<out String?>?): Context {
+ val mockRes = mock(Resources::class.java)
+ doReturn(res).`when`(mockRes).getStringArray(ArgumentMatchers.eq(id))
+
+ return mock(Context::class.java).apply {
+ doReturn(mockRes).`when`(this).getResources()
+ }
+ }
+
+ @Test
+ fun testGetSupportedKeepalives() {
+ fun assertRunWithException(res: Array<out String?>?) {
+ try {
+ val mockContext = getMockedContextWithStringArrayRes(
+ R.array.config_networkSupportedKeepaliveCount, res)
+ KeepaliveUtils.getSupportedKeepalives(mockContext)
+ fail("Expected KeepaliveDeviceConfigurationException")
+ } catch (expected: KeepaliveUtils.KeepaliveDeviceConfigurationException) {
+ }
+ }
+
+ // Check resource with various invalid format.
+ assertRunWithException(null)
+ assertRunWithException(arrayOf<String?>(null))
+ assertRunWithException(arrayOfNulls<String?>(10))
+ assertRunWithException(arrayOf(""))
+ assertRunWithException(arrayOf("3,ABC"))
+ assertRunWithException(arrayOf("6,3,3"))
+ assertRunWithException(arrayOf("5"))
+
+ // Check resource with invalid slots value.
+ assertRunWithException(arrayOf("2,2"))
+ assertRunWithException(arrayOf("3,-1"))
+
+ // Check resource with invalid transport type.
+ assertRunWithException(arrayOf("-1,3"))
+ assertRunWithException(arrayOf("10,3"))
+
+ // Check valid customization generates expected array.
+ val validRes = arrayOf("0,3", "1,0", "4,4")
+ val expectedValidRes = intArrayOf(3, 0, 0, 0, 4, 0, 0, 0)
+
+ val mockContext = getMockedContextWithStringArrayRes(
+ R.array.config_networkSupportedKeepaliveCount, validRes)
+ val actual = KeepaliveUtils.getSupportedKeepalives(mockContext)
+ assertArrayEquals(expectedValidRes, actual)
+ }
+
+ @Test
+ fun testGetSupportedKeepalivesForNetworkCapabilities() {
+ // Mock customized supported keepalives for each transport type, and assuming:
+ // 3 for cellular,
+ // 6 for wifi,
+ // 0 for others.
+ val cust = IntArray(MAX_TRANSPORT + 1).apply {
+ this[TRANSPORT_CELLULAR] = 3
+ this[TRANSPORT_WIFI] = 6
+ }
+
+ val nc = NetworkCapabilities()
+ // Check supported keepalives with single transport type.
+ nc.transportTypes = intArrayOf(TRANSPORT_CELLULAR)
+ assertEquals(3, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc))
+
+ // Check supported keepalives with multiple transport types.
+ nc.transportTypes = intArrayOf(TRANSPORT_WIFI, TRANSPORT_VPN)
+ assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc))
+
+ // Check supported keepalives with non-customized transport type.
+ nc.transportTypes = intArrayOf(TRANSPORT_ETHERNET)
+ assertEquals(0, KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc))
+
+ // Check supported keepalives with undefined transport type.
+ nc.transportTypes = intArrayOf(MAX_TRANSPORT + 1)
+ try {
+ KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(cust, nc)
+ fail("Expected ArrayIndexOutOfBoundsException")
+ } catch (expected: ArrayIndexOutOfBoundsException) {
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 37af461..23cfbd4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -28,6 +28,7 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
@@ -489,7 +490,7 @@
MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
- final String typeName = ConnectivityManager.getNetworkTypeName(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(transport);
@@ -619,6 +620,10 @@
mNetworkAgent.sendNetworkScore(mScore);
}
+ public int getScore() {
+ return mScore;
+ }
+
public void explicitlySelected(boolean acceptUnvalidated) {
mNetworkAgent.explicitlySelected(acceptUnvalidated);
}
@@ -1330,6 +1335,8 @@
return TYPE_WIFI;
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
+ case TRANSPORT_VPN:
+ return TYPE_VPN;
default:
return TYPE_NONE;
}
@@ -3854,6 +3861,9 @@
networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
testFactory.waitForRequests();
+ // unregister network callback - a no-op, but should not fail
+ mCm.unregisterNetworkCallback(networkCallback);
+
testFactory.unregister();
handlerThread.quit();
}
@@ -4266,25 +4276,6 @@
callback.expectStarted();
ka.stop();
callback.expectStopped();
-
- // Check that the same NATT socket cannot be used by 2 keepalives.
- try (SocketKeepalive ka2 = mCm.createSocketKeepalive(
- myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
- // Check that second keepalive cannot be started if the first one is running.
- ka.start(validKaInterval);
- callback.expectStarted();
- ka2.start(validKaInterval);
- callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
- ka.stop();
- callback.expectStopped();
-
- // Check that second keepalive can be started/stopped normally if the first one is
- // stopped.
- ka2.start(validKaInterval);
- callback.expectStarted();
- ka2.stop();
- callback.expectStopped();
- }
}
// Check that deleting the IP address stops the keepalive.
@@ -4348,10 +4339,6 @@
testSocket.close();
testSocket2.close();
}
-
- // Check that the closed socket cannot be used to start keepalive.
- ka.start(validKaInterval);
- callback.expectError(SocketKeepalive.ERROR_INVALID_SOCKET);
}
// Check that there is no port leaked after all keepalives and sockets are closed.
@@ -5390,6 +5377,58 @@
}
@Test
+ public void testVpnUnvalidated() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+
+ // Bring up Ethernet.
+ mEthernetNetworkAgent = new MockNetworkAgent(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.connect(true);
+ callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+ callback.assertNoCallback();
+
+ // Bring up a VPN that has the INTERNET capability, initially unvalidated.
+ final int uid = Process.myUid();
+ final 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(false /* validated */, true /* hasInternet */);
+ mMockVpn.connect();
+
+ // Even though the VPN is unvalidated, it becomes the default network for our app.
+ callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ // TODO: this looks like a spurious callback.
+ callback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
+ callback.assertNoCallback();
+
+ assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
+ assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
+ assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
+ assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
+
+ assertFalse(NetworkMonitorUtils.isValidationRequired(vpnNetworkAgent.mNetworkCapabilities));
+ assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
+ vpnNetworkAgent.mNetworkCapabilities));
+
+ // Pretend that the VPN network validates.
+ vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ // Expect to see the validated capability, but no other changes, because the VPN is already
+ // the default network for the app.
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
+ callback.assertNoCallback();
+
+ vpnNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
+ callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
+ }
+
+ @Test
public void testVpnSetUnderlyingNetworks() {
final int uid = Process.myUid();
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 6b5a220..4a35015 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -118,7 +118,6 @@
INetd mMockNetd;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
IpSecService mIpSecService;
- int mUid = Os.getuid();
@Before
public void setUp() throws Exception {
@@ -666,99 +665,4 @@
mIpSecService.releaseNetId(releasedNetId);
assertEquals(releasedNetId, mIpSecService.reserveNetId());
}
-
- @Test
- public void testLockEncapSocketForNattKeepalive() throws Exception {
- IpSecUdpEncapResponse udpEncapResp =
- mIpSecService.openUdpEncapsulationSocket(0, new Binder());
- assertNotNull(udpEncapResp);
- assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
-
- // Verify no NATT keepalive records upon startup
- IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
-
- int nattKeepaliveResourceId =
- mIpSecService.lockEncapSocketForNattKeepalive(udpEncapResp.resourceId, mUid);
-
- // Validate response, and record was added
- assertNotEquals(IpSecManager.INVALID_RESOURCE_ID, nattKeepaliveResourceId);
- assertEquals(1, userRecord.mNattKeepaliveRecords.size());
-
- // Validate keepalive can be released and removed.
- mIpSecService.releaseNattKeepalive(nattKeepaliveResourceId, mUid);
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
-
- mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
- }
-
- @Test
- public void testLockEncapSocketForNattKeepaliveInvalidUid() throws Exception {
- IpSecUdpEncapResponse udpEncapResp =
- mIpSecService.openUdpEncapsulationSocket(0, new Binder());
- assertNotNull(udpEncapResp);
- assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
-
- // Verify no NATT keepalive records upon startup
- IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
-
- try {
- int nattKeepaliveResourceId =
- mIpSecService.lockEncapSocketForNattKeepalive(
- udpEncapResp.resourceId, mUid + 1);
- fail("Expected SecurityException for invalid user");
- } catch (SecurityException expected) {
- }
-
- // Validate keepalive was not added to lists
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
- }
-
- @Test
- public void testLockEncapSocketForNattKeepaliveInvalidResourceId() throws Exception {
- // Verify no NATT keepalive records upon startup
- IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
-
- try {
- int nattKeepaliveResourceId =
- mIpSecService.lockEncapSocketForNattKeepalive(12345, mUid);
- fail("Expected IllegalArgumentException for invalid resource ID");
- } catch (IllegalArgumentException expected) {
- }
-
- // Validate keepalive was not added to lists
- assertEquals(0, userRecord.mNattKeepaliveRecords.size());
- }
-
- @Test
- public void testEncapSocketReleasedBeforeKeepaliveReleased() throws Exception {
- IpSecUdpEncapResponse udpEncapResp =
- mIpSecService.openUdpEncapsulationSocket(0, new Binder());
- assertNotNull(udpEncapResp);
- assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
-
- // Get encap socket record, verify initial starting refcount.
- IpSecService.UserRecord userRecord = mIpSecService.mUserResourceTracker.getUserRecord(mUid);
- IpSecService.RefcountedResource encapSocketRefcountedRecord =
- userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
- udpEncapResp.resourceId);
- assertEquals(1, encapSocketRefcountedRecord.mRefCount);
-
- // Verify that the reference was added
- int nattKeepaliveResourceId =
- mIpSecService.lockEncapSocketForNattKeepalive(udpEncapResp.resourceId, mUid);
- assertNotEquals(IpSecManager.INVALID_RESOURCE_ID, nattKeepaliveResourceId);
- assertEquals(2, encapSocketRefcountedRecord.mRefCount);
-
- // Close UDP encap socket, but expect the refcountedRecord to still have a reference.
- mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
- assertEquals(1, encapSocketRefcountedRecord.mRefCount);
-
- // Verify UDP encap socket cleaned up once reference is removed. Expect -1 if cleanup
- // was properly completed.
- mIpSecService.releaseNattKeepalive(nattKeepaliveResourceId, mUid);
- assertEquals(-1, encapSocketRefcountedRecord.mRefCount);
- }
}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index a83faf3..fb84611 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity.ipmemorystore;
+package com.android.server.net.ipmemorystore;
import static org.junit.Assert.assertEquals;