Merge "Add test for updating IKE about IP address changes"
diff --git a/framework-t/src/android/net/NetworkStatsAccess.java b/framework-t/src/android/net/NetworkStatsAccess.java
index 0585756..23902dc 100644
--- a/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,7 +17,6 @@
package android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
@@ -33,6 +32,8 @@
import android.os.UserHandle;
import android.telephony.TelephonyManager;
+import com.android.net.module.util.PermissionUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -100,6 +101,7 @@
* <li>Device owners.
* <li>Carrier-privileged applications.
* <li>The system UID.
+ * <li>NetworkStack application.
* </ul>
*/
int DEVICE = 3;
@@ -125,9 +127,9 @@
final int appId = UserHandle.getAppId(callingUid);
- final boolean isNetworkStack = context.checkPermission(
- android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
- == PERMISSION_GRANTED;
+ final boolean isNetworkStack = PermissionUtils.checkAnyPermissionOf(
+ context, callingPid, callingUid, android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
if (hasCarrierPrivileges || isDeviceOwner
|| appId == Process.SYSTEM_UID || isNetworkStack) {
diff --git a/framework/src/android/net/LinkAddress.java b/framework/src/android/net/LinkAddress.java
index d48b8c7..90f55b3 100644
--- a/framework/src/android/net/LinkAddress.java
+++ b/framework/src/android/net/LinkAddress.java
@@ -487,17 +487,23 @@
*/
@SystemApi
public boolean isGlobalPreferred() {
- /**
- * Note that addresses flagged as IFA_F_OPTIMISTIC are
- * simultaneously flagged as IFA_F_TENTATIVE (when the tentative
- * state has cleared either DAD has succeeded or failed, and both
- * flags are cleared regardless).
- */
- int flags = getFlags();
return (scope == RT_SCOPE_UNIVERSE
&& !isIpv6ULA()
- && (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
- && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L));
+ && isPreferred());
+ }
+
+ /**
+ * Checks if the address is a preferred address.
+ *
+ * @hide
+ */
+ public boolean isPreferred() {
+ // Note that addresses flagged as IFA_F_OPTIMISTIC are simultaneously flagged as
+ // IFA_F_TENTATIVE (when the tentative state has cleared either DAD has succeeded or
+ // failed, and both flags are cleared regardless).
+ int flags = getFlags();
+ return (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
+ && ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L);
}
/**
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 8fe20de..177f7e3 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -281,9 +281,8 @@
*
* arg1 = the hardware slot number of the keepalive to start
* arg2 = interval in seconds
- * obj = AutomaticKeepaliveInfo object
+ * obj = KeepalivePacketData object describing the data to be sent
*
- * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
* @hide
*/
public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
@@ -436,6 +435,14 @@
public static final int CMD_DSCP_POLICY_STATUS = BASE + 28;
/**
+ * Sent by the NetworkAgent to ConnectivityService to notify that this network is expected to be
+ * replaced within the specified time by a similar network.
+ * arg1 = timeout in milliseconds
+ * @hide
+ */
+ public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
+
+ /**
* DSCP policy was successfully added.
*/
public static final int DSCP_POLICY_STATUS_SUCCESS = 0;
@@ -477,27 +484,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface DscpPolicyStatus {}
- /**
- * Sent by the NetworkAgent to ConnectivityService to notify that this network is expected to be
- * replaced within the specified time by a similar network.
- * arg1 = timeout in milliseconds
- * @hide
- */
- public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29;
-
- /**
- * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
- * automatic keepalive request.
- *
- * NATT keepalives have an automatic mode where the system only sends keepalive packets when
- * TCP sockets are open over a VPN. The system will check periodically for presence of
- * such open sockets, and this message is what triggers the re-evaluation.
- *
- * obj = A Binder object associated with the keepalive.
- * @hide
- */
- public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 30;
-
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
config.legacyTypeName, config.legacySubTypeName);
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index a658791..c5104d8 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1330,6 +1330,9 @@
mDeps = deps;
mMdnsSocketProvider = deps.makeMdnsSocketProvider(ctx, handler.getLooper());
+ // Netlink monitor starts on boot, and intentionally never stopped, to ensure that all
+ // address events are received.
+ handler.post(mMdnsSocketProvider::startNetLinkMonitor);
mMdnsSocketClient =
new MdnsMultinetworkSocketClient(handler.getLooper(), mMdnsSocketProvider);
mMdnsDiscoveryManager =
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 0952e88..8017ee0 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -36,10 +36,12 @@
import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.mdns.util.MdnsLogger;
import java.io.IOException;
@@ -65,6 +67,7 @@
// Note: mdnsresponder mDNSEmbeddedAPI.h uses 8940 for Ethernet jumbo frames.
private static final int READ_BUFFER_SIZE = 2048;
private static final MdnsLogger LOGGER = new MdnsLogger(TAG);
+ private static final int IFACE_IDX_NOT_EXIST = -1;
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final Handler mHandler;
@@ -81,6 +84,9 @@
new ArrayMap<>();
private final List<String> mLocalOnlyInterfaces = new ArrayList<>();
private final List<String> mTetheredInterfaces = new ArrayList<>();
+ // mIfaceIdxToLinkProperties should not be cleared in maybeStopMonitoringSockets() because
+ // the netlink monitor is never stop and the old states must be kept.
+ private final SparseArray<LinkProperties> mIfaceIdxToLinkProperties = new SparseArray<>();
private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
private boolean mMonitoringSockets = false;
private boolean mRequestStop = false;
@@ -126,8 +132,8 @@
}
};
- mSocketNetlinkMonitor = SocketNetLinkMonitorFactory.createNetLinkMonitor(mHandler,
- LOGGER.mLog);
+ mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler, LOGGER.mLog,
+ new NetLinkMessageProcessor());
}
/**
@@ -148,8 +154,83 @@
@NonNull byte[] packetReadBuffer) throws IOException {
return new MdnsInterfaceSocket(networkInterface, port, looper, packetReadBuffer);
}
- }
+ /*** Get network interface by given interface name */
+ public int getNetworkInterfaceIndexByName(@NonNull final String ifaceName) {
+ final NetworkInterface iface;
+ try {
+ iface = NetworkInterface.getByName(ifaceName);
+ } catch (SocketException e) {
+ Log.e(TAG, "Error querying interface", e);
+ return IFACE_IDX_NOT_EXIST;
+ }
+ if (iface == null) {
+ Log.e(TAG, "Interface not found: " + ifaceName);
+ return IFACE_IDX_NOT_EXIST;
+ }
+ return iface.getIndex();
+ }
+ /*** Creates a SocketNetlinkMonitor */
+ public ISocketNetLinkMonitor createSocketNetlinkMonitor(@NonNull final Handler handler,
+ @NonNull final SharedLog log,
+ @NonNull final NetLinkMonitorCallBack cb) {
+ return SocketNetLinkMonitorFactory.createNetLinkMonitor(handler, log, cb);
+ }
+ }
+ /**
+ * The callback interface for the netlink monitor messages.
+ */
+ public interface NetLinkMonitorCallBack {
+ /**
+ * Handles the interface address add or update.
+ */
+ void addOrUpdateInterfaceAddress(int ifaceIdx, @NonNull LinkAddress newAddress);
+
+
+ /**
+ * Handles the interface address delete.
+ */
+ void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress);
+ }
+ private class NetLinkMessageProcessor implements NetLinkMonitorCallBack {
+
+ @Override
+ public void addOrUpdateInterfaceAddress(int ifaceIdx,
+ @NonNull final LinkAddress newAddress) {
+
+ LinkProperties linkProperties;
+ linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
+ if (linkProperties == null) {
+ linkProperties = new LinkProperties();
+ mIfaceIdxToLinkProperties.put(ifaceIdx, linkProperties);
+ }
+ boolean updated = linkProperties.addLinkAddress(newAddress);
+
+ if (!updated) {
+ return;
+ }
+ maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
+ }
+
+ @Override
+ public void deleteInterfaceAddress(int ifaceIdx, @NonNull LinkAddress deleteAddress) {
+ LinkProperties linkProperties;
+ boolean updated = false;
+ linkProperties = mIfaceIdxToLinkProperties.get(ifaceIdx);
+ if (linkProperties != null) {
+ updated = linkProperties.removeLinkAddress(deleteAddress);
+ if (linkProperties.getLinkAddresses().isEmpty()) {
+ mIfaceIdxToLinkProperties.remove(ifaceIdx);
+ }
+ }
+
+ if (linkProperties == null || !updated) {
+ return;
+ }
+ maybeUpdateTetheringSocketAddress(ifaceIdx, linkProperties.getLinkAddresses());
+
+ }
+ }
/*** Data class for storing socket related info */
private static class SocketInfo {
final MdnsInterfaceSocket mSocket;
@@ -190,6 +271,15 @@
}
mMonitoringSockets = true;
}
+ /**
+ * Start netlink monitor.
+ */
+ public void startNetLinkMonitor() {
+ ensureRunningOnHandlerThread(mHandler);
+ if (mSocketNetlinkMonitor.isSupported()) {
+ mSocketNetlinkMonitor.startMonitoring();
+ }
+ }
private void maybeStopMonitoringSockets() {
if (!mMonitoringSockets) return; // Already unregistered.
@@ -203,10 +293,6 @@
final TetheringManager tetheringManager = mContext.getSystemService(
TetheringManager.class);
tetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
-
- if (mSocketNetlinkMonitor.isSupported()) {
- mHandler.post(mSocketNetlinkMonitor::stopMonitoring);
- }
// Clear all saved status.
mActiveNetworksLinkProperties.clear();
mNetworkSockets.clear();
@@ -215,6 +301,8 @@
mTetheredInterfaces.clear();
mMonitoringSockets = false;
}
+ // The netlink monitor is not stopped here because the MdnsSocketProvider need to listen
+ // to all the netlink updates when the system is up and running.
}
/*** Request to stop monitoring sockets and unregister callbacks */
@@ -259,21 +347,38 @@
if (socketInfo == null) {
createSocket(networkKey, lp);
} else {
- // Update the addresses of this socket.
- final List<LinkAddress> addresses = lp.getLinkAddresses();
- socketInfo.mAddresses.clear();
- socketInfo.mAddresses.addAll(addresses);
- // Try to join the group again.
- socketInfo.mSocket.joinGroup(addresses);
-
- notifyAddressesChanged(network, socketInfo.mSocket, lp);
+ updateSocketInfoAddress(network, socketInfo, lp.getLinkAddresses());
+ }
+ }
+ private void maybeUpdateTetheringSocketAddress(int ifaceIndex,
+ @NonNull final List<LinkAddress> updatedAddresses) {
+ for (int i = 0; i < mTetherInterfaceSockets.size(); ++i) {
+ String tetheringInterfaceName = mTetherInterfaceSockets.keyAt(i);
+ if (mDependencies.getNetworkInterfaceIndexByName(tetheringInterfaceName)
+ == ifaceIndex) {
+ updateSocketInfoAddress(null /* network */,
+ mTetherInterfaceSockets.valueAt(i), updatedAddresses);
+ return;
+ }
}
}
- private static LinkProperties createLPForTetheredInterface(String interfaceName) {
- final LinkProperties linkProperties = new LinkProperties();
+ private void updateSocketInfoAddress(@Nullable final Network network,
+ @NonNull final SocketInfo socketInfo,
+ @NonNull final List<LinkAddress> addresses) {
+ // Update the addresses of this socket.
+ socketInfo.mAddresses.clear();
+ socketInfo.mAddresses.addAll(addresses);
+ // Try to join the group again.
+ socketInfo.mSocket.joinGroup(addresses);
+
+ notifyAddressesChanged(network, socketInfo.mSocket, addresses);
+ }
+ private LinkProperties createLPForTetheredInterface(@NonNull final String interfaceName,
+ int ifaceIndex) {
+ final LinkProperties linkProperties =
+ new LinkProperties(mIfaceIdxToLinkProperties.get(ifaceIndex));
linkProperties.setInterfaceName(interfaceName);
- // TODO: Use NetlinkMonitor to update addresses for tethering interfaces.
return linkProperties;
}
@@ -292,7 +397,8 @@
final CompareResult<String> interfaceDiff = new CompareResult<>(
current, updated);
for (String name : interfaceDiff.added) {
- createSocket(LOCAL_NET, createLPForTetheredInterface(name));
+ int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
+ createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
}
for (String name : interfaceDiff.removed) {
removeTetherInterfaceSocket(name);
@@ -332,14 +438,10 @@
final MdnsInterfaceSocket socket = mDependencies.createMdnsInterfaceSocket(
networkInterface.getNetworkInterface(), MdnsConstants.MDNS_PORT, mLooper,
mPacketReadBuffer);
- final List<LinkAddress> addresses;
+ final List<LinkAddress> addresses = lp.getLinkAddresses();
if (networkKey == LOCAL_NET) {
- addresses = CollectionUtils.map(
- networkInterface.getInterfaceAddresses(),
- i -> new LinkAddress(i.getAddress(), i.getNetworkPrefixLength()));
mTetherInterfaceSockets.put(interfaceName, new SocketInfo(socket, addresses));
} else {
- addresses = lp.getLinkAddresses();
mNetworkSockets.put(((NetworkAsKey) networkKey).mNetwork,
new SocketInfo(socket, addresses));
}
@@ -422,12 +524,12 @@
}
private void notifyAddressesChanged(Network network, MdnsInterfaceSocket socket,
- LinkProperties lp) {
+ List<LinkAddress> addresses) {
for (int i = 0; i < mCallbacksToRequestedNetworks.size(); i++) {
final Network requestedNetwork = mCallbacksToRequestedNetworks.valueAt(i);
if (isNetworkMatched(requestedNetwork, network)) {
mCallbacksToRequestedNetworks.keyAt(i)
- .onAddressesChanged(network, socket, lp.getLinkAddresses());
+ .onAddressesChanged(network, socket, addresses);
}
}
}
@@ -451,7 +553,10 @@
private void retrieveAndNotifySocketFromInterface(String interfaceName, SocketCallback cb) {
final SocketInfo socketInfo = mTetherInterfaceSockets.get(interfaceName);
if (socketInfo == null) {
- createSocket(LOCAL_NET, createLPForTetheredInterface(interfaceName));
+ int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(interfaceName);
+ createSocket(
+ LOCAL_NET,
+ createLPForTetheredInterface(interfaceName, ifaceIndex));
} else {
// Notify the socket for requested network.
cb.onSocketCreated(
diff --git a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
index 8f6aecc..4650255 100644
--- a/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
+++ b/service-t/src/com/android/server/connectivity/mdns/SocketNetLinkMonitorFactory.java
@@ -31,8 +31,8 @@
* Creates a new netlink monitor.
*/
public static ISocketNetLinkMonitor createNetLinkMonitor(@NonNull final Handler handler,
- @NonNull SharedLog log) {
- return new SocketNetlinkMonitor(handler, log);
+ @NonNull SharedLog log, @NonNull MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+ return new SocketNetlinkMonitor(handler, log, cb);
}
private SocketNetLinkMonitorFactory() {
diff --git a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
index e053413..6395b53 100644
--- a/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
+++ b/service-t/src/com/android/server/connectivity/mdns/internal/SocketNetlinkMonitor.java
@@ -17,28 +17,64 @@
package com.android.server.connectivity.mdns.internal;
import android.annotation.NonNull;
+import android.net.LinkAddress;
import android.os.Handler;
import android.system.OsConstants;
+import android.util.Log;
import com.android.net.module.util.SharedLog;
import com.android.net.module.util.ip.NetlinkMonitor;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkMessage;
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.net.module.util.netlink.StructIfaddrMsg;
import com.android.server.connectivity.mdns.ISocketNetLinkMonitor;
+import com.android.server.connectivity.mdns.MdnsSocketProvider;
/**
* The netlink monitor for MdnsSocketProvider.
*/
public class SocketNetlinkMonitor extends NetlinkMonitor implements ISocketNetLinkMonitor {
- public SocketNetlinkMonitor(@NonNull final Handler handler, @NonNull SharedLog log) {
- super(handler, log, SocketNetlinkMonitor.class.getSimpleName(), OsConstants.NETLINK_ROUTE,
- NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
- }
+ public static final String TAG = SocketNetlinkMonitor.class.getSimpleName();
+ @NonNull
+ private final MdnsSocketProvider.NetLinkMonitorCallBack mCb;
+ public SocketNetlinkMonitor(@NonNull final Handler handler,
+ @NonNull SharedLog log,
+ @NonNull final MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+ super(handler, log, TAG, OsConstants.NETLINK_ROUTE,
+ NetlinkConstants.RTMGRP_IPV4_IFADDR | NetlinkConstants.RTMGRP_IPV6_IFADDR);
+ mCb = cb;
+ }
@Override
public void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
+ if (nlMsg instanceof RtNetlinkAddressMessage) {
+ processRtNetlinkAddressMessage((RtNetlinkAddressMessage) nlMsg);
+ }
+ }
+ /**
+ * Process the RTM_NEWADDR and RTM_DELADDR netlink message.
+ */
+ private void processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg) {
+ final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
+ final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
+ msg.getFlags(), ifaddrMsg.scope);
+ if (!la.isPreferred()) {
+ // Skip the unusable ip address.
+ return;
+ }
+ switch (msg.getHeader().nlmsg_type) {
+ case NetlinkConstants.RTM_NEWADDR:
+ mCb.addOrUpdateInterfaceAddress(ifaddrMsg.index, la);
+ break;
+ case NetlinkConstants.RTM_DELADDR:
+ mCb.deleteInterfaceAddress(ifaddrMsg.index, la);
+ break;
+ default:
+ Log.e(TAG, "Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
+ }
}
@Override
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 2af30dd..ba503e0 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -98,6 +98,7 @@
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
import static com.android.net.module.util.NetworkMonitorUtils.isPrivateDnsValidationRequired;
+import static com.android.net.module.util.PermissionUtils.checkAnyPermissionOf;
import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
@@ -463,7 +464,11 @@
private String mCurrentTcpBufferSizes;
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
- new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
+ new Class[] {
+ ConnectivityService.class,
+ NetworkAgent.class,
+ NetworkAgentInfo.class,
+ AutomaticOnOffKeepaliveTracker.class });
private enum ReapUnvalidatedNetworks {
// Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -2324,11 +2329,12 @@
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
- if (!checkAnyPermissionOf(callerPid, callerUid, android.Manifest.permission.NETWORK_STACK,
+ if (!checkAnyPermissionOf(mContext, callerPid, callerUid,
+ android.Manifest.permission.NETWORK_STACK,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)) {
newNc.setAdministratorUids(new int[0]);
}
- if (!checkAnyPermissionOf(
+ if (!checkAnyPermissionOf(mContext,
callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
newNc.setAllowedUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
@@ -2837,15 +2843,6 @@
setUidBlockedReasons(uid, blockedReasons);
}
- private boolean checkAnyPermissionOf(int pid, int uid, String... permissions) {
- for (String permission : permissions) {
- if (mContext.checkPermission(permission, pid, uid) == PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -3004,13 +3001,13 @@
}
private boolean checkNetworkStackPermission(int pid, int uid) {
- return checkAnyPermissionOf(pid, uid,
+ return checkAnyPermissionOf(mContext, pid, uid,
android.Manifest.permission.NETWORK_STACK,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
- return checkAnyPermissionOf(pid, uid,
+ return checkAnyPermissionOf(mContext, pid, uid,
android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_SETTINGS);
@@ -5008,7 +5005,7 @@
}
private RequestInfoPerUidCounter getRequestCounter(NetworkRequestInfo nri) {
- return checkAnyPermissionOf(
+ return checkAnyPermissionOf(mContext,
nri.mPid, nri.mUid, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
? mSystemNetworkRequestCounter : mNetworkRequestCounter;
}
@@ -5605,12 +5602,13 @@
handleConfigureAlwaysOnNetworks();
break;
}
- // Sent by KeepaliveTracker to process an app request on the state machine thread.
- case NetworkAgent.CMD_START_SOCKET_KEEPALIVE: {
+ // Sent by AutomaticOnOffKeepaliveTracker to process an app request on the
+ // handler thread.
+ case AutomaticOnOffKeepaliveTracker.CMD_REQUEST_START_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
break;
}
- case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
+ case AutomaticOnOffKeepaliveTracker.CMD_MONITOR_AUTOMATIC_KEEPALIVE: {
final AutomaticOnOffKeepalive ki =
mKeepaliveTracker.getKeepaliveForBinder((IBinder) msg.obj);
if (null == ki) return; // The callback was unregistered before the alarm fired
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index acce95d..881c92d 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
import static android.net.SocketKeepalive.SUCCESS_PAUSED;
@@ -40,7 +39,6 @@
import android.net.ISocketKeepaliveCallback;
import android.net.MarkMaskParcel;
import android.net.Network;
-import android.net.NetworkAgent;
import android.net.SocketKeepalive.InvalidSocketException;
import android.os.FileUtils;
import android.os.Handler;
@@ -94,6 +92,29 @@
private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
"automatic_on_off_keepalive_version";
+
+ // ConnectivityService parses message constants from itself and AutomaticOnOffKeepaliveTracker
+ // with MessageUtils for debugging purposes, and crashes if some messages have the same values.
+ private static final int BASE = 2000;
+ /**
+ * Sent by AutomaticOnOffKeepaliveTracker periodically (when relevant) to trigger monitor
+ * automatic keepalive request.
+ *
+ * NATT keepalives have an automatic mode where the system only sends keepalive packets when
+ * TCP sockets are open over a VPN. The system will check periodically for presence of
+ * such open sockets, and this message is what triggers the re-evaluation.
+ *
+ * obj = A Binder object associated with the keepalive.
+ */
+ public static final int CMD_MONITOR_AUTOMATIC_KEEPALIVE = BASE + 1;
+
+ /**
+ * Sent by AutomaticOnOffKeepaliveTracker to ConnectivityService to start a keepalive.
+ *
+ * obj = AutomaticKeepaliveInfo object
+ */
+ public static final int CMD_REQUEST_START_KEEPALIVE = BASE + 2;
+
/**
* States for {@code #AutomaticOnOffKeepalive}.
*
@@ -202,7 +223,7 @@
throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
}
mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
- NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
+ CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
.sendToTarget();
} else {
mAutomaticOnOffState = STATE_ALWAYS_ON;
@@ -482,9 +503,8 @@
+ " → " + dstAddrString + ":" + dstPort
+ " auto=" + autoKi
+ " underpinned=" + underpinnedNetwork);
- mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
- // TODO : move ConnectivityService#encodeBool to a static lib.
- automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
+ mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
+ .sendToTarget();
} catch (InvalidSocketException e) {
mKeepaliveTracker.notifyErrorCallback(cb, e.error);
}
@@ -517,9 +537,8 @@
+ " → " + dstAddrString + ":" + dstPort
+ " auto=" + autoKi
+ " underpinned=" + underpinnedNetwork);
- mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
- // TODO : move ConnectivityService#encodeBool to a static lib.
- automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
+ mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
+ .sendToTarget();
} catch (InvalidSocketException e) {
mKeepaliveTracker.notifyErrorCallback(cb, e.error);
}
@@ -547,7 +566,7 @@
final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
false /* autoOnOff, tcp keepalives are never auto on/off */,
null /* underpinnedNetwork, tcp keepalives do not refer to this */);
- mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, autoKi)
+ mConnectivityServiceHandler.obtainMessage(CMD_REQUEST_START_KEEPALIVE, autoKi)
.sendToTarget();
} catch (InvalidSocketException e) {
mKeepaliveTracker.notifyErrorCallback(cb, e.error);
diff --git a/tests/unit/java/android/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
index a74056b..8b86211 100644
--- a/tests/unit/java/android/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -78,6 +78,7 @@
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
setHasReadHistoryPermission(false);
setHasNetworkStackPermission(false);
+ setHasMainlineNetworkStackPermission(false);
}
@After
@@ -154,6 +155,10 @@
setHasNetworkStackPermission(false);
assertEquals(NetworkStatsAccess.Level.DEFAULT,
NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
+
+ setHasMainlineNetworkStackPermission(true);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_PID, TEST_UID, TEST_PKG));
}
private void setHasCarrierPrivileges(boolean hasPrivileges) {
@@ -189,4 +194,10 @@
TEST_PID, TEST_UID)).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED);
}
+
+ private void setHasMainlineNetworkStackPermission(boolean hasPermission) {
+ when(mContext.checkPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ TEST_PID, TEST_UID)).thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 696eff4..3eb1b26 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -47,7 +47,6 @@
import android.net.MarkMaskParcel;
import android.net.NattKeepalivePacketData;
import android.net.Network;
-import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Binder;
@@ -268,11 +267,11 @@
@Override
public void handleMessage(@NonNull final Message msg) {
switch (msg.what) {
- case NetworkAgent.CMD_START_SOCKET_KEEPALIVE:
- Log.d(TAG, "Test handler received CMD_START_SOCKET_KEEPALIVE : " + msg);
+ case AutomaticOnOffKeepaliveTracker.CMD_REQUEST_START_KEEPALIVE:
+ Log.d(TAG, "Test handler received CMD_REQUEST_START_KEEPALIVE : " + msg);
mAOOKeepaliveTracker.handleStartKeepalive(msg);
break;
- case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE:
+ case AutomaticOnOffKeepaliveTracker.CMD_MONITOR_AUTOMATIC_KEEPALIVE:
Log.d(TAG, "Test handler received CMD_MONITOR_AUTOMATIC_KEEPALIVE : " + msg);
mLastAutoKi = mAOOKeepaliveTracker.getKeepaliveForBinder((IBinder) msg.obj);
break;
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 426ee1a..1cf0ad3 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -1320,12 +1320,12 @@
}
private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass,
- int errorCode, String[] packageName, VpnProfileState... profileState) {
+ int errorCode, String[] packageName, @NonNull VpnProfileState... profileState) {
final Context userContext =
mContext.createContextAsUser(UserHandle.of(PRIMARY_USER.id), 0 /* flags */);
final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
- final int verifyTimes = (profileState == null) ? 1 : profileState.length;
+ final int verifyTimes = profileState.length;
verify(userContext, times(verifyTimes)).startService(intentArgumentCaptor.capture());
for (int i = 0; i < verifyTimes; i++) {
@@ -1356,10 +1356,8 @@
VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES));
}
- if (profileState != null) {
- assertEquals(profileState[i], intent.getParcelableExtra(
- VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
- }
+ assertEquals(profileState[i], intent.getParcelableExtra(
+ VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
}
reset(userContext);
}
@@ -1368,7 +1366,11 @@
// CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
// errorCode won't be set.
verifyVpnManagerEvent(sessionKey, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
- -1 /* errorClass */, -1 /* errorCode */, packageName, null /* profileState */);
+ -1 /* errorClass */, -1 /* errorCode */, packageName,
+ // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+ // important here. Verify that the state as it is, i.e. CONNECTING state.
+ new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+ sessionKey, false /* alwaysOn */, false /* lockdown */));
}
private void verifyAlwaysOnStateChanged(String[] packageName, VpnProfileState... profileState) {
@@ -1610,7 +1612,10 @@
verifyPowerSaveTempWhitelistApp(TEST_VPN_PKG);
reset(mDeviceIdleInternal);
verifyVpnManagerEvent(sessionKey, category, errorType, errorCode,
- new String[] {TEST_VPN_PKG}, null /* profileState */);
+ // VPN NetworkAgnet does not switch to CONNECTED in the test, and the state is not
+ // important here. Verify that the state as it is, i.e. CONNECTING state.
+ new String[] {TEST_VPN_PKG}, new VpnProfileState(VpnProfileState.STATE_CONNECTING,
+ sessionKey, false /* alwaysOn */, false /* lockdown */));
if (errorType == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
.unregisterNetworkCallback(eq(cb));
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index d9420b8..2d73c98 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -21,6 +21,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static com.android.testutils.ContextUtils.mockService;
import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -49,9 +52,19 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.net.module.util.ArrayTrackRecord;
+import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
+import com.android.net.module.util.netlink.StructIfaddrMsg;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
import com.android.server.connectivity.mdns.MdnsSocketProvider.Dependencies;
+import com.android.server.connectivity.mdns.internal.SocketNetlinkMonitor;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
@@ -64,20 +77,28 @@
import org.mockito.MockitoAnnotations;
import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
@RunWith(DevSdkIgnoreRunner.class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class MdnsSocketProviderTest {
+ private static final String TAG = MdnsSocketProviderTest.class.getSimpleName();
private static final String TEST_IFACE_NAME = "test";
private static final String LOCAL_ONLY_IFACE_NAME = "local_only";
private static final String TETHERED_IFACE_NAME = "tethered";
+ private static final int TETHERED_IFACE_IDX = 32;
private static final long DEFAULT_TIMEOUT = 2000L;
private static final long NO_CALLBACK_TIMEOUT = 200L;
private static final LinkAddress LINKADDRV4 = new LinkAddress("192.0.2.0/24");
private static final LinkAddress LINKADDRV6 =
new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64");
+
+ private static final LinkAddress LINKADDRV6_FLAG_CHANGE =
+ new LinkAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", 1 /* flags */,
+ 0 /* scope */);
private static final Network TEST_NETWORK = new Network(123);
@Mock private Context mContext;
@Mock private Dependencies mDeps;
@@ -91,6 +112,7 @@
private NetworkCallback mNetworkCallback;
private TetheringEventCallback mTetheringEventCallback;
+ private TestNetLinkMonitor mTestSocketNetLinkMonitor;
@Before
public void setUp() throws IOException {
MockitoAnnotations.initMocks(this);
@@ -116,9 +138,21 @@
doReturn(mTetheredIfaceWrapper).when(mDeps).getNetworkInterfaceByName(TETHERED_IFACE_NAME);
doReturn(mock(MdnsInterfaceSocket.class))
.when(mDeps).createMdnsInterfaceSocket(any(), anyInt(), any(), any());
+ doReturn(TETHERED_IFACE_IDX).when(mDeps).getNetworkInterfaceIndexByName(
+ TETHERED_IFACE_NAME);
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start();
mHandler = new Handler(thread.getLooper());
+
+ doReturn(mTestSocketNetLinkMonitor).when(mDeps).createSocketNetlinkMonitor(any(), any(),
+ any());
+ doAnswer(inv -> {
+ mTestSocketNetLinkMonitor = new TestNetLinkMonitor(inv.getArgument(0),
+ inv.getArgument(1),
+ inv.getArgument(2));
+ return mTestSocketNetLinkMonitor;
+ }).when(mDeps).createSocketNetlinkMonitor(any(), any(),
+ any());
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps);
}
@@ -135,6 +169,23 @@
mNetworkCallback = nwCallbackCaptor.getValue();
mTetheringEventCallback = teCallbackCaptor.getValue();
+
+ mHandler.post(mSocketProvider::startNetLinkMonitor);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ }
+
+ private static class TestNetLinkMonitor extends SocketNetlinkMonitor {
+ TestNetLinkMonitor(@NonNull Handler handler,
+ @NonNull SharedLog log,
+ @Nullable MdnsSocketProvider.NetLinkMonitorCallBack cb) {
+ super(handler, log, cb);
+ }
+
+ @Override
+ public void startMonitoring() { }
+
+ @Override
+ public void stopMonitoring() { }
}
private class TestSocketCallback implements MdnsSocketProvider.SocketCallback {
@@ -301,6 +352,87 @@
testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
}
+ private RtNetlinkAddressMessage createNetworkAddressUpdateNetLink(
+ short msgType, LinkAddress linkAddress, int ifIndex, int flags) {
+ final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+ nlmsghdr.nlmsg_type = msgType;
+ nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlmsghdr.nlmsg_seq = 1;
+
+ InetAddress ip = linkAddress.getAddress();
+
+ final byte family =
+ (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+ StructIfaddrMsg structIfaddrMsg = new StructIfaddrMsg(family,
+ (short) linkAddress.getPrefixLength(),
+ (short) linkAddress.getFlags(), (short) linkAddress.getScope(), ifIndex);
+
+ return new RtNetlinkAddressMessage(nlmsghdr, structIfaddrMsg, ip,
+ null /* structIfacacheInfo */, flags);
+ }
+
+ @Test
+ public void testDownstreamNetworkAddressUpdateFromNetlink() {
+ startMonitoringSockets();
+ final TestSocketCallback testCallbackAll = new TestSocketCallback();
+ mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+ // Address add message arrived before the interface is created.
+ RtNetlinkAddressMessage addIpv4AddrMsg = createNetworkAddressUpdateNetLink(
+ NetlinkConstants.RTM_NEWADDR,
+ LINKADDRV4,
+ TETHERED_IFACE_IDX,
+ 0 /* flags */);
+ mHandler.post(
+ () -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv4AddrMsg,
+ 0 /* whenMs */));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+ // Interface is created.
+ mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+ List.of(TETHERED_IFACE_NAME)));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ verify(mTetheredIfaceWrapper).getNetworkInterface();
+ testCallbackAll.expectedSocketCreatedForNetwork(null /* network */, List.of(LINKADDRV4));
+
+ // Old Address removed.
+ RtNetlinkAddressMessage removeIpv4AddrMsg = createNetworkAddressUpdateNetLink(
+ NetlinkConstants.RTM_DELADDR,
+ LINKADDRV4,
+ TETHERED_IFACE_IDX,
+ 0 /* flags */);
+ mHandler.post(
+ () -> mTestSocketNetLinkMonitor.processNetlinkMessage(removeIpv4AddrMsg,
+ 0 /* whenMs */));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of());
+
+ // New address added.
+ RtNetlinkAddressMessage addIpv6AddrMsg = createNetworkAddressUpdateNetLink(
+ NetlinkConstants.RTM_NEWADDR,
+ LINKADDRV6,
+ TETHERED_IFACE_IDX,
+ 0 /* flags */);
+ mHandler.post(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
+ 0 /* whenMs */));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of(LINKADDRV6));
+
+ // Address updated
+ RtNetlinkAddressMessage updateIpv6AddrMsg = createNetworkAddressUpdateNetLink(
+ NetlinkConstants.RTM_NEWADDR,
+ LINKADDRV6,
+ TETHERED_IFACE_IDX,
+ 1 /* flags */);
+ mHandler.post(
+ () -> mTestSocketNetLinkMonitor.processNetlinkMessage(updateIpv6AddrMsg,
+ 0 /* whenMs */));
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ testCallbackAll.expectedAddressesChangedForNetwork(null /* network */,
+ List.of(LINKADDRV6_FLAG_CHANGE));
+ }
+
@Test
public void testAddressesChanged() throws Exception {
startMonitoringSockets();