Merge changes from topic "cherrypicker-L21100000961067008:N34400001375316978" into udc-dev
* changes:
Instrument SDK level and change IDs to help testing
Use "don't actively prefer" timeout when avoiding bad wifi
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 e245ff1..e67c655 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -25,7 +25,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkAddress;
@@ -35,6 +38,9 @@
import android.net.NetworkRequest;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
@@ -51,6 +57,7 @@
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* The {@link MdnsSocketProvider} manages the multiple sockets for mDns.
@@ -92,6 +99,60 @@
private final byte[] mPacketReadBuffer = new byte[READ_BUFFER_SIZE];
private boolean mMonitoringSockets = false;
private boolean mRequestStop = false;
+ private String mWifiP2pTetherInterface = null;
+
+ private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String newP2pIface = getWifiP2pInterface(intent);
+
+ if (!mMonitoringSockets || !hasAllNetworksRequest()) {
+ mWifiP2pTetherInterface = newP2pIface;
+ return;
+ }
+
+ // If already serving from the correct interface, nothing to do.
+ if (Objects.equals(mWifiP2pTetherInterface, newP2pIface)) return;
+
+ if (mWifiP2pTetherInterface != null) {
+ if (newP2pIface != null) {
+ Log.wtf(TAG, "Wifi p2p interface is changed from " + mWifiP2pTetherInterface
+ + " to " + newP2pIface + " without null broadcast");
+ }
+ // Remove the socket.
+ removeTetherInterfaceSocket(mWifiP2pTetherInterface);
+ }
+
+ // Update mWifiP2pTetherInterface
+ mWifiP2pTetherInterface = newP2pIface;
+
+ // Check whether the socket for wifi p2p interface is created or not.
+ final boolean socketAlreadyExists = mTetherInterfaceSockets.get(newP2pIface) != null;
+ if (newP2pIface != null && !socketAlreadyExists) {
+ // Create a socket for wifi p2p interface.
+ final int ifaceIndex =
+ mDependencies.getNetworkInterfaceIndexByName(newP2pIface);
+ createSocket(LOCAL_NET, createLPForTetheredInterface(newP2pIface, ifaceIndex));
+ }
+ }
+ };
+
+ @Nullable
+ private static String getWifiP2pInterface(final Intent intent) {
+ final WifiP2pGroup group =
+ intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
+ final WifiP2pInfo p2pInfo =
+ intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
+ if (group == null || p2pInfo == null) {
+ return null;
+ }
+
+ if (!p2pInfo.groupFormed) {
+ return null;
+ } else {
+ return group.getInterface();
+ }
+ }
public MdnsSocketProvider(@NonNull Context context, @NonNull Looper looper,
@NonNull SharedLog sharedLog) {
@@ -138,6 +199,18 @@
mSocketNetlinkMonitor = mDependencies.createSocketNetlinkMonitor(mHandler,
mSharedLog.forSubComponent("NetlinkMonitor"), new NetLinkMessageProcessor());
+
+ // Register a intent receiver to listen wifi p2p interface changes.
+ // Note: The wifi p2p interface change is only notified via
+ // TetheringEventCallback#onLocalOnlyInterfacesChanged if the device is the wifi p2p group
+ // owner. In this case, MdnsSocketProvider will receive duplicate interface changes and must
+ // ignore the later notification because the socket has already been created. There is only
+ // one notification from the wifi p2p connection change intent if the device is not the wifi
+ // p2p group owner.
+ final IntentFilter intentFilter =
+ new IntentFilter(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ mContext.registerReceiver(
+ mIntentReceiver, intentFilter, null /* broadcastPermission */, mHandler);
}
/**
@@ -376,17 +449,28 @@
if (!hasAllNetworksRequest()) {
// Currently, the network for tethering can not be requested, so the sockets for
// tethering are only created if there is a request for all networks (interfaces).
- // Therefore, this change can skip if there is no such request.
+ // Therefore, only update the interface list and skip this change if no such request.
if (DBG) {
Log.d(TAG, "Ignore tether interfaces change. There is no request for all"
+ " networks.");
}
+ current.clear();
+ current.addAll(updated);
return;
}
final CompareResult<String> interfaceDiff = new CompareResult<>(
current, updated);
for (String name : interfaceDiff.added) {
+ // Check if a socket has been created for the interface
+ final SocketInfo socketInfo = mTetherInterfaceSockets.get(name);
+ if (socketInfo != null) {
+ if (DBG) {
+ mSharedLog.i("Socket is existed for interface:" + name);
+ }
+ continue;
+ }
+
int ifaceIndex = mDependencies.getNetworkInterfaceIndexByName(name);
createSocket(LOCAL_NET, createLPForTetheredInterface(name, ifaceIndex));
}
@@ -578,6 +662,11 @@
for (String tetheredInterface : mTetheredInterfaces) {
retrieveAndNotifySocketFromInterface(tetheredInterface, cb);
}
+
+ if (mWifiP2pTetherInterface != null
+ && !mLocalOnlyInterfaces.contains(mWifiP2pTetherInterface)) {
+ retrieveAndNotifySocketFromInterface(mWifiP2pTetherInterface, cb);
+ }
} else {
retrieveAndNotifySocketFromNetwork(network, cb);
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 9dc7051..5e225c1 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -318,6 +318,7 @@
import java.io.InterruptedIOException;
import java.io.PrintWriter;
import java.io.Writer;
+import java.lang.IllegalArgumentException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -442,6 +443,8 @@
private final Context mContext;
private final ConnectivityResources mResources;
+ private final int mWakeUpMark;
+ private final int mWakeUpMask;
// The Context is created for UserHandle.ALL.
private final Context mUserAllContext;
private final Dependencies mDeps;
@@ -1612,6 +1615,29 @@
mCellularRadioTimesharingCapable =
mResources.get().getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+ int mark = mResources.get().getInteger(R.integer.config_networkWakeupPacketMark);
+ int mask = mResources.get().getInteger(R.integer.config_networkWakeupPacketMask);
+
+ if (SdkLevel.isAtLeastU()) {
+ // U+ default value of both mark & mask, this is the top bit of the skb->mark,
+ // see //system/netd/include/FwMark.h union Fwmark, field ingress_cpu_wakeup
+ final int defaultUMarkMask = 0x80000000; // u32
+
+ if ((mark == 0) || (mask == 0)) {
+ // simply treat unset/disabled as the default U value
+ mark = defaultUMarkMask;
+ mask = defaultUMarkMask;
+ }
+ if ((mark != defaultUMarkMask) || (mask != defaultUMarkMask)) {
+ // invalid device overlay settings
+ throw new IllegalArgumentException(
+ "Bad config_networkWakeupPacketMark/Mask " + mark + "/" + mask);
+ }
+ }
+
+ mWakeUpMark = mark;
+ mWakeUpMask = mask;
+
mNetd = netd;
mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
mHandlerThread = mDeps.makeHandlerThread();
@@ -8085,21 +8111,18 @@
return;
}
- int mark = mResources.get().getInteger(R.integer.config_networkWakeupPacketMark);
- int mask = mResources.get().getInteger(R.integer.config_networkWakeupPacketMask);
-
// Mask/mark of zero will not detect anything interesting.
// Don't install rules unless both values are nonzero.
- if (mark == 0 || mask == 0) {
+ if (mWakeUpMark == 0 || mWakeUpMask == 0) {
return;
}
final String prefix = makeNflogPrefix(iface, nai.network.getNetworkHandle());
try {
if (add) {
- mNetd.wakeupAddInterface(iface, prefix, mark, mask);
+ mNetd.wakeupAddInterface(iface, prefix, mWakeUpMark, mWakeUpMask);
} else {
- mNetd.wakeupDelInterface(iface, prefix, mark, mask);
+ mNetd.wakeupDelInterface(iface, prefix, mWakeUpMark, mWakeUpMask);
}
} catch (Exception e) {
loge("Exception modifying wakeup packet monitoring: " + e);
@@ -8617,10 +8640,18 @@
}
private void maybeCloseSockets(NetworkAgentInfo nai, Set<UidRange> ranges,
- Set<Integer> exemptUids) {
+ UidRangeParcel[] uidRangeParcels, int[] exemptUids) {
if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
try {
- mDeps.destroyLiveTcpSockets(UidRange.toIntRanges(ranges), exemptUids);
+ if (mDeps.isAtLeastU()) {
+ final Set<Integer> exemptUidSet = new ArraySet<>();
+ for (final int uid: exemptUids) {
+ exemptUidSet.add(uid);
+ }
+ mDeps.destroyLiveTcpSockets(UidRange.toIntRanges(ranges), exemptUidSet);
+ } else {
+ mNetd.socketDestroy(uidRangeParcels, exemptUids);
+ }
} catch (Exception e) {
loge("Exception in socket destroy: ", e);
}
@@ -8628,16 +8659,16 @@
}
private void updateVpnUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
- final Set<Integer> exemptUids = new ArraySet<>();
+ int[] exemptUids = new int[2];
// TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
// by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
// starting a legacy VPN, and remove VPN_UID here. (b/176542831)
- exemptUids.add(VPN_UID);
- exemptUids.add(nai.networkCapabilities.getOwnerUid());
+ exemptUids[0] = VPN_UID;
+ exemptUids[1] = nai.networkCapabilities.getOwnerUid();
UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
// Close sockets before modifying uid ranges so that RST packets can reach to the server.
- maybeCloseSockets(nai, uidRanges, exemptUids);
+ maybeCloseSockets(nai, uidRanges, ranges, exemptUids);
try {
if (add) {
mNetd.networkAddUidRangesParcel(new NativeUidRangeConfig(
@@ -8651,7 +8682,7 @@
" on netId " + nai.network.netId + ". " + e);
}
// Close sockets that established connection while requesting netd.
- maybeCloseSockets(nai, uidRanges, exemptUids);
+ maybeCloseSockets(nai, uidRanges, ranges, exemptUids);
}
private boolean isProxySetOnAnyDefaultNetwork() {
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 8473557..cdb0f15 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -387,6 +387,7 @@
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
+import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.DestroySocketsWrapper;
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
import com.android.server.connectivity.ApplicationSelfCertifiedNetworkCapabilities;
import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker;
@@ -540,8 +541,7 @@
private static final int TEST_PACKAGE_UID2 = 321;
private static final int TEST_PACKAGE_UID3 = 456;
- private static final int PACKET_WAKEUP_MASK = 0xffff0000;
- private static final int PACKET_WAKEUP_MARK = 0x88880000;
+ private static final int PACKET_WAKEUP_MARK_MASK = 0x80000000;
private static final String ALWAYS_ON_PACKAGE = "com.android.test.alwaysonvpn";
@@ -615,6 +615,7 @@
@Mock TetheringManager mTetheringManager;
@Mock BroadcastOptionsShim mBroadcastOptionsShim;
@Mock ActivityManager mActivityManager;
+ @Mock DestroySocketsWrapper mDestroySocketsWrapper;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -1865,7 +1866,7 @@
final Context mockResContext = mock(Context.class);
doReturn(mResources).when(mockResContext).getResources();
ConnectivityResources.setResourcesContextForTest(mockResContext);
- mDeps = spy(new ConnectivityServiceDependencies(mockResContext));
+ mDeps = new ConnectivityServiceDependencies(mockResContext);
mAutoOnOffKeepaliveDependencies =
new AutomaticOnOffKeepaliveTrackerDependencies(mServiceContext);
mService = new ConnectivityService(mServiceContext,
@@ -1922,14 +1923,13 @@
doReturn(0).when(mResources).getInteger(R.integer.config_activelyPreferBadWifi);
doReturn(true).when(mResources)
.getBoolean(R.bool.config_cellular_radio_timesharing_capable);
- doReturn(PACKET_WAKEUP_MASK).when(mResources).getInteger(
+ doReturn(PACKET_WAKEUP_MARK_MASK).when(mResources).getInteger(
R.integer.config_networkWakeupPacketMask);
- doReturn(PACKET_WAKEUP_MARK).when(mResources).getInteger(
+ doReturn(PACKET_WAKEUP_MARK_MASK).when(mResources).getInteger(
R.integer.config_networkWakeupPacketMark);
}
- // ConnectivityServiceDependencies is public to use Mockito.spy
- public class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
+ class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
final ConnectivityResources mConnRes;
final ArraySet<Pair<Long, Integer>> mEnabledChangeIds = new ArraySet<>();
@@ -2224,15 +2224,24 @@
}
}
- @Override
- public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
- final Set<Integer> exemptUids) {
- // This function is empty since the invocation of this method is verified by mocks
+ // Class to be mocked and used to verify destroy sockets methods call
+ public class DestroySocketsWrapper {
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids){}
+ public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids){}
}
- @Override
+ @Override @SuppressWarnings("DirectInvocationOnMock")
+ public void destroyLiveTcpSockets(final Set<Range<Integer>> ranges,
+ final Set<Integer> exemptUids) {
+ // Call mocked destroyLiveTcpSockets so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSockets(ranges, exemptUids);
+ }
+
+ @Override @SuppressWarnings("DirectInvocationOnMock")
public void destroyLiveTcpSocketsByOwnerUids(final Set<Integer> ownerUids) {
- // This function is empty since the invocation of this method is verified by mocks
+ // Call mocked destroyLiveTcpSocketsByOwnerUids so that test can verify this method call
+ mDestroySocketsWrapper.destroyLiveTcpSocketsByOwnerUids(ownerUids);
}
final ArrayTrackRecord<Long>.ReadHead mScheduledEvaluationTimeouts =
@@ -10362,7 +10371,7 @@
private void doTestSetFirewallChainEnabledCloseSocket(final int chain,
final boolean isAllowList) throws Exception {
- reset(mDeps);
+ reset(mDestroySocketsWrapper);
mCm.setFirewallChainEnabled(chain, true /* enabled */);
final Set<Integer> uids =
@@ -10370,13 +10379,13 @@
if (isAllowList) {
final Set<Range<Integer>> range = new ArraySet<>(
List.of(new Range<>(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE)));
- verify(mDeps).destroyLiveTcpSockets(range, uids);
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(range, uids);
} else {
- verify(mDeps).destroyLiveTcpSocketsByOwnerUids(uids);
+ verify(mDestroySocketsWrapper).destroyLiveTcpSocketsByOwnerUids(uids);
}
mCm.setFirewallChainEnabled(chain, false /* enabled */);
- verifyNoMoreInteractions(mDeps);
+ verifyNoMoreInteractions(mDestroySocketsWrapper);
}
@Test @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
@@ -12713,11 +12722,18 @@
private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
throws Exception {
- InOrder inOrder = inOrder(mMockNetd, mDeps);
+ InOrder inOrder = inOrder(mMockNetd, mDestroySocketsWrapper);
final Set<Integer> exemptUidSet = new ArraySet<>(List.of(exemptUid, Process.VPN_UID));
+ ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class);
- inOrder.verify(mDeps).destroyLiveTcpSockets(UidRange.toIntRanges(vpnRanges),
- exemptUidSet);
+ if (mDeps.isAtLeastU()) {
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
+ } else {
+ inOrder.verify(mMockNetd).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ }
if (add) {
inOrder.verify(mMockNetd, times(1)).networkAddUidRangesParcel(
@@ -12729,8 +12745,14 @@
toUidRangeStableParcels(vpnRanges), PREFERENCE_ORDER_VPN));
}
- inOrder.verify(mDeps).destroyLiveTcpSockets(UidRange.toIntRanges(vpnRanges),
- exemptUidSet);
+ if (mDeps.isAtLeastU()) {
+ inOrder.verify(mDestroySocketsWrapper).destroyLiveTcpSockets(
+ UidRange.toIntRanges(vpnRanges), exemptUidSet);
+ } else {
+ inOrder.verify(mMockNetd).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ }
}
@Test
@@ -18008,8 +18030,8 @@
final String expectedPrefix = makeNflogPrefix(WIFI_IFNAME,
mWiFiAgent.getNetwork().getNetworkHandle());
- verify(mMockNetd).wakeupAddInterface(WIFI_IFNAME, expectedPrefix, PACKET_WAKEUP_MARK,
- PACKET_WAKEUP_MASK);
+ verify(mMockNetd).wakeupAddInterface(WIFI_IFNAME, expectedPrefix, PACKET_WAKEUP_MARK_MASK,
+ PACKET_WAKEUP_MARK_MASK);
}
@Test
@@ -18022,8 +18044,8 @@
if (mDeps.isAtLeastU()) {
final String expectedPrefix = makeNflogPrefix(MOBILE_IFNAME,
mCellAgent.getNetwork().getNetworkHandle());
- verify(mMockNetd).wakeupAddInterface(MOBILE_IFNAME, expectedPrefix, PACKET_WAKEUP_MARK,
- PACKET_WAKEUP_MASK);
+ verify(mMockNetd).wakeupAddInterface(MOBILE_IFNAME, expectedPrefix,
+ PACKET_WAKEUP_MARK_MASK, PACKET_WAKEUP_MARK_MASK);
} else {
verify(mMockNetd, never()).wakeupAddInterface(eq(MOBILE_IFNAME), anyString(), anyInt(),
anyInt());
@@ -18070,7 +18092,7 @@
final UidRange frozenUidRange = new UidRange(TEST_FROZEN_UID, TEST_FROZEN_UID);
final Set<UidRange> ranges = Collections.singleton(frozenUidRange);
- verify(mDeps).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
+ verify(mDestroySocketsWrapper).destroyLiveTcpSockets(eq(UidRange.toIntRanges(ranges)),
eq(exemptUids));
}
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 4b87556..4f56857 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
@@ -40,7 +41,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkAddress;
@@ -49,6 +52,9 @@
import android.net.NetworkCapabilities;
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringEventCallback;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -88,6 +94,7 @@
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 WIFI_P2P_IFACE_NAME = "p2p_wifi";
private static final String TETHERED_IFACE_NAME = "tethered";
private static final int TETHERED_IFACE_IDX = 32;
private static final long DEFAULT_TIMEOUT = 2000L;
@@ -136,11 +143,15 @@
doReturn(true).when(mTetheredIfaceWrapper).supportsMulticast();
doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
.getNetworkInterfaceByName(LOCAL_ONLY_IFACE_NAME);
+ doReturn(mLocalOnlyIfaceWrapper).when(mDeps)
+ .getNetworkInterfaceByName(WIFI_P2P_IFACE_NAME);
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);
+ doReturn(789).when(mDeps).getNetworkInterfaceIndexByName(
+ WIFI_P2P_IFACE_NAME);
final HandlerThread thread = new HandlerThread("MdnsSocketProviderTest");
thread.start();
mHandler = new Handler(thread.getLooper());
@@ -157,22 +168,41 @@
mSocketProvider = new MdnsSocketProvider(mContext, thread.getLooper(), mDeps, mLog);
}
+ private void runOnHandler(Runnable r) {
+ mHandler.post(r);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ }
+
+ private BroadcastReceiver expectWifiP2PChangeBroadcastReceiver() {
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(),
+ argThat(filter -> filter.hasAction(
+ WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)),
+ any(), any());
+ final BroadcastReceiver originalReceiver = receiverCaptor.getValue();
+ return new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ runOnHandler(() -> originalReceiver.onReceive(context, intent));
+ }
+ };
+ }
+
private void startMonitoringSockets() {
final ArgumentCaptor<NetworkCallback> nwCallbackCaptor =
ArgumentCaptor.forClass(NetworkCallback.class);
final ArgumentCaptor<TetheringEventCallback> teCallbackCaptor =
ArgumentCaptor.forClass(TetheringEventCallback.class);
- mHandler.post(mSocketProvider::startMonitoringSockets);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::startMonitoringSockets);
verify(mCm).registerNetworkCallback(any(), nwCallbackCaptor.capture(), any());
verify(mTm).registerTetheringEventCallback(any(), teCallbackCaptor.capture());
mNetworkCallback = nwCallbackCaptor.getValue();
mTetheringEventCallback = teCallbackCaptor.getValue();
- mHandler.post(mSocketProvider::startNetLinkMonitor);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::startNetLinkMonitor);
}
private static class TestNetlinkMonitor extends SocketNetlinkMonitor {
@@ -281,9 +311,8 @@
testLp.setInterfaceName(TEST_IFACE_NAME);
testLp.setLinkAddresses(List.of(LINKADDRV4));
final NetworkCapabilities testNc = makeCapabilities(transports);
- mHandler.post(() -> mNetworkCallback.onCapabilitiesChanged(TEST_NETWORK, testNc));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mNetworkCallback.onCapabilitiesChanged(TEST_NETWORK, testNc));
+ runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
}
@Test
@@ -291,62 +320,53 @@
startMonitoringSockets();
final TestSocketCallback testCallback1 = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback1));
testCallback1.expectedNoCallback();
postNetworkAvailable(TRANSPORT_WIFI);
testCallback1.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
final TestSocketCallback testCallback2 = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
testCallback1.expectedNoCallback();
testCallback2.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
final TestSocketCallback testCallback3 = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback3));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
- mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
List.of(LOCAL_ONLY_IFACE_NAME)));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
- mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+ runOnHandler(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
List.of(TETHERED_IFACE_NAME)));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mTetheredIfaceWrapper).getNetworkInterface();
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedSocketCreatedForNetwork(null /* network */, List.of());
- mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback1));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback1));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedNoCallback();
- mHandler.post(() -> mNetworkCallback.onLost(TEST_NETWORK));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mNetworkCallback.onLost(TEST_NETWORK));
testCallback1.expectedNoCallback();
testCallback2.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
testCallback3.expectedInterfaceDestroyedForNetwork(TEST_NETWORK);
- mHandler.post(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
testCallback3.expectedInterfaceDestroyedForNetwork(null /* network */);
- mHandler.post(() -> mSocketProvider.unrequestSocket(testCallback3));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.unrequestSocket(testCallback3));
testCallback1.expectedNoCallback();
testCallback2.expectedNoCallback();
// There was still a tethered interface, but no callback should be sent once unregistered
@@ -376,8 +396,7 @@
public void testDownstreamNetworkAddressUpdateFromNetlink() {
startMonitoringSockets();
final TestSocketCallback testCallbackAll = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallbackAll));
// Address add message arrived before the interface is created.
RtNetlinkAddressMessage addIpv4AddrMsg = createNetworkAddressUpdateNetLink(
@@ -385,15 +404,13 @@
LINKADDRV4,
TETHERED_IFACE_IDX,
0 /* flags */);
- mHandler.post(
+ runOnHandler(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv4AddrMsg,
0 /* whenMs */));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
// Interface is created.
- mHandler.post(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
+ runOnHandler(() -> mTetheringEventCallback.onTetheredInterfacesChanged(
List.of(TETHERED_IFACE_NAME)));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
verify(mTetheredIfaceWrapper).getNetworkInterface();
testCallbackAll.expectedSocketCreatedForNetwork(null /* network */, List.of(LINKADDRV4));
@@ -403,10 +420,9 @@
LINKADDRV4,
TETHERED_IFACE_IDX,
0 /* flags */);
- mHandler.post(
+ runOnHandler(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(removeIpv4AddrMsg,
0 /* whenMs */));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of());
// New address added.
@@ -415,9 +431,8 @@
LINKADDRV6,
TETHERED_IFACE_IDX,
0 /* flags */);
- mHandler.post(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
+ runOnHandler(() -> mTestSocketNetLinkMonitor.processNetlinkMessage(addIpv6AddrMsg,
0 /* whenMs */));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */, List.of(LINKADDRV6));
// Address updated
@@ -426,10 +441,9 @@
LINKADDRV6,
TETHERED_IFACE_IDX,
1 /* flags */);
- mHandler.post(
+ runOnHandler(
() -> mTestSocketNetLinkMonitor.processNetlinkMessage(updateIpv6AddrMsg,
0 /* whenMs */));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
testCallbackAll.expectedAddressesChangedForNetwork(null /* network */,
List.of(LINKADDRV6_FLAG_CHANGE));
}
@@ -439,8 +453,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
testCallback.expectedNoCallback();
postNetworkAvailable(TRANSPORT_WIFI);
@@ -449,8 +462,7 @@
final LinkProperties newTestLp = new LinkProperties();
newTestLp.setInterfaceName(TEST_IFACE_NAME);
newTestLp.setLinkAddresses(List.of(LINKADDRV4, LINKADDRV6));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, newTestLp));
testCallback.expectedAddressesChangedForNetwork(
TEST_NETWORK, List.of(LINKADDRV4, LINKADDRV6));
}
@@ -458,8 +470,7 @@
@Test
public void testStartAndStopMonitoringSockets() {
// Stop monitoring sockets before start. Should not unregister any network callback.
- mHandler.post(mSocketProvider::requestStopWhenInactive);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::requestStopWhenInactive);
verify(mCm, never()).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, never()).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
@@ -467,39 +478,32 @@
startMonitoringSockets();
// Request a socket then unrequest it. Expect no network callback unregistration.
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
testCallback.expectedNoCallback();
- mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback));
verify(mCm, never()).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, never()).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
// Request stop and it should unregister network callback immediately because there is no
// socket request.
- mHandler.post(mSocketProvider::requestStopWhenInactive);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::requestStopWhenInactive);
verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, times(1)).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
// Start sockets monitoring and request a socket again.
- mHandler.post(mSocketProvider::startMonitoringSockets);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::startMonitoringSockets);
verify(mCm, times(2)).registerNetworkCallback(any(), any(NetworkCallback.class), any());
verify(mTm, times(2)).registerTetheringEventCallback(
any(), any(TetheringEventCallback.class));
final TestSocketCallback testCallback2 = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback2));
testCallback2.expectedNoCallback();
// Try to stop monitoring sockets but should be ignored and wait until all socket are
// unrequested.
- mHandler.post(mSocketProvider::requestStopWhenInactive);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::requestStopWhenInactive);
verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, times(1)).unregisterTetheringEventCallback(any());
// Unrequest the socket then network callbacks should be unregistered.
- mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback2));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback2));
verify(mCm, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
verify(mTm, times(2)).unregisterTetheringEventCallback(any(TetheringEventCallback.class));
}
@@ -510,24 +514,20 @@
// Request a socket with null network.
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
testCallback.expectedNoCallback();
// Notify a LinkPropertiesChanged with TEST_NETWORK.
final LinkProperties testLp = new LinkProperties();
testLp.setInterfaceName(TEST_IFACE_NAME);
testLp.setLinkAddresses(List.of(LINKADDRV4));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(TEST_NETWORK, testLp));
verify(mTestNetworkIfaceWrapper, times(1)).getNetworkInterface();
testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
// Try to stop monitoring and unrequest the socket.
- mHandler.post(mSocketProvider::requestStopWhenInactive);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
- mHandler.post(()-> mSocketProvider.unrequestSocket(testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::requestStopWhenInactive);
+ runOnHandler(()-> mSocketProvider.unrequestSocket(testCallback));
// No callback sent when unregistered
testCallback.expectedNoCallback();
verify(mCm, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -535,13 +535,11 @@
// Start sockets monitoring and request a socket again. Expected no socket created callback
// because all saved LinkProperties has been cleared.
- mHandler.post(mSocketProvider::startMonitoringSockets);
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(mSocketProvider::startMonitoringSockets);
verify(mCm, times(2)).registerNetworkCallback(any(), any(NetworkCallback.class), any());
verify(mTm, times(2)).registerTetheringEventCallback(
any(), any(TetheringEventCallback.class));
- mHandler.post(() -> mSocketProvider.requestSocket(null, testCallback));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
testCallback.expectedNoCallback();
// Notify a LinkPropertiesChanged with another network.
@@ -550,8 +548,7 @@
final Network otherNetwork = new Network(456);
otherLp.setInterfaceName("test2");
otherLp.setLinkAddresses(List.of(otherAddress));
- mHandler.post(() -> mNetworkCallback.onLinkPropertiesChanged(otherNetwork, otherLp));
- HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ runOnHandler(() -> mNetworkCallback.onLinkPropertiesChanged(otherNetwork, otherLp));
verify(mTestNetworkIfaceWrapper, times(2)).getNetworkInterface();
testCallback.expectedSocketCreatedForNetwork(otherNetwork, List.of(otherAddress));
}
@@ -561,7 +558,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_CELLULAR);
testCallback.expectedNoCallback();
@@ -573,7 +570,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_BLUETOOTH);
testCallback.expectedNoCallback();
@@ -585,7 +582,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_BLUETOOTH);
testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
@@ -597,7 +594,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_BLUETOOTH);
testCallback.expectedNoCallback();
@@ -611,7 +608,7 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_VPN, TRANSPORT_WIFI);
testCallback.expectedNoCallback();
@@ -623,9 +620,146 @@
startMonitoringSockets();
final TestSocketCallback testCallback = new TestSocketCallback();
- mHandler.post(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
+ runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
postNetworkAvailable(TRANSPORT_WIFI);
testCallback.expectedSocketCreatedForNetwork(TEST_NETWORK, List.of(LINKADDRV4));
}
+
+ private Intent buildWifiP2PConnectionChangedIntent(boolean groupFormed) {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ final WifiP2pInfo formedInfo = new WifiP2pInfo();
+ formedInfo.groupFormed = groupFormed;
+ final WifiP2pGroup group;
+ if (groupFormed) {
+ group = mock(WifiP2pGroup.class);
+ doReturn(WIFI_P2P_IFACE_NAME).when(group).getInterface();
+ } else {
+ group = null;
+ }
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, formedInfo);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+ return intent;
+ }
+
+ @Test
+ public void testWifiP2PInterfaceChange() {
+ final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+ startMonitoringSockets();
+
+ // Request a socket with null network.
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+
+ // Wifi p2p is connected and the interface is up. Get a wifi p2p change intent then expect
+ // a socket creation.
+ final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+ receiver.onReceive(mContext, formedIntent);
+ verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+ // Wifi p2p is disconnected. Get a wifi p2p change intent then expect the socket destroy.
+ final Intent unformedIntent = buildWifiP2PConnectionChangedIntent(false /* groupFormed */);
+ receiver.onReceive(mContext, unformedIntent);
+ testCallback.expectedInterfaceDestroyedForNetwork(null /* network */);
+ }
+
+ @Test
+ public void testWifiP2PInterfaceChangeBeforeStartMonitoringSockets() {
+ final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+
+ // Get a wifi p2p change intent before start monitoring sockets.
+ final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+ receiver.onReceive(mContext, formedIntent);
+
+ // Start monitoring sockets and request a socket with null network.
+ startMonitoringSockets();
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+ verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+ }
+
+ @Test
+ public void testWifiP2PInterfaceChangeBeforeGetAllNetworksRequest() {
+ final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+ startMonitoringSockets();
+
+ // Get a wifi p2p change intent before request socket for all networks.
+ final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+ receiver.onReceive(mContext, formedIntent);
+
+ // Request a socket with null network.
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+ verify(mLocalOnlyIfaceWrapper).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+ }
+
+ @Test
+ public void testNoDuplicatedSocketCreation() {
+ final BroadcastReceiver receiver = expectWifiP2PChangeBroadcastReceiver();
+ startMonitoringSockets();
+
+ // Request a socket with null network.
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback));
+ testCallback.expectedNoCallback();
+
+ // Receive an interface added change for the wifi p2p interface. Expect a socket creation
+ // callback.
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+ List.of(WIFI_P2P_IFACE_NAME)));
+ verify(mLocalOnlyIfaceWrapper, times(1)).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+ // Receive a wifi p2p connected intent. Expect no callback because the socket is created.
+ final Intent formedIntent = buildWifiP2PConnectionChangedIntent(true /* groupFormed */);
+ receiver.onReceive(mContext, formedIntent);
+ testCallback.expectedNoCallback();
+
+ // Request other socket with null network. Should receive socket created callback once.
+ final TestSocketCallback testCallback2 = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null, testCallback2));
+ testCallback2.expectedSocketCreatedForNetwork(null /* network */, List.of());
+ testCallback2.expectedNoCallback();
+
+ // Receive a wifi p2p disconnected intent. Expect a socket destroy callback.
+ final Intent unformedIntent = buildWifiP2PConnectionChangedIntent(false /* groupFormed */);
+ receiver.onReceive(mContext, unformedIntent);
+ testCallback.expectedInterfaceDestroyedForNetwork(null /* network */);
+
+ // Receive an interface removed change for the wifi p2p interface. Expect no callback
+ // because the socket is destroyed.
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(List.of()));
+ testCallback.expectedNoCallback();
+
+ // Receive a wifi p2p connected intent again. Expect a socket creation callback.
+ receiver.onReceive(mContext, formedIntent);
+ verify(mLocalOnlyIfaceWrapper, times(2)).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+
+ // Receive an interface added change for the wifi p2p interface again. Expect no callback
+ // because the socket is created.
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+ List.of(WIFI_P2P_IFACE_NAME)));
+ testCallback.expectedNoCallback();
+ }
+
+ @Test
+ public void testTetherInterfacesChangedBeforeGetAllNetworksRequest() {
+ startMonitoringSockets();
+
+ // Receive an interface added change for the wifi p2p interface. Expect a socket creation
+ // callback.
+ runOnHandler(() -> mTetheringEventCallback.onLocalOnlyInterfacesChanged(
+ List.of(TETHERED_IFACE_NAME)));
+ verify(mTetheredIfaceWrapper, never()).getNetworkInterface();
+
+ // Request a socket with null network.
+ final TestSocketCallback testCallback = new TestSocketCallback();
+ runOnHandler(() -> mSocketProvider.requestSocket(null /* network */, testCallback));
+ verify(mTetheredIfaceWrapper).getNetworkInterface();
+ testCallback.expectedSocketCreatedForNetwork(null /* network */, List.of());
+ }
}