| /* |
| * Copyright (C) 2012 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.wifi.cts; |
| |
| import static org.junit.Assert.assertNotEquals; |
| |
| import android.app.UiAutomation; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.net.ConnectivityManager; |
| import android.net.ConnectivityManager.NetworkCallback; |
| import android.net.MacAddress; |
| import android.net.Network; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkInfo; |
| import android.net.NetworkRequest; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.p2p.WifiP2pConfig; |
| import android.net.wifi.p2p.WifiP2pDevice; |
| import android.net.wifi.p2p.WifiP2pGroup; |
| import android.net.wifi.p2p.WifiP2pGroupList; |
| import android.net.wifi.p2p.WifiP2pInfo; |
| import android.net.wifi.p2p.WifiP2pManager; |
| import android.net.wifi.p2p.WifiP2pManager.ExternalApproverRequestListener; |
| import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; |
| import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; |
| import android.os.Build; |
| import android.platform.test.annotations.AppModeFull; |
| import android.provider.Settings; |
| import android.util.Log; |
| |
| import androidx.test.filters.SdkSuppress; |
| import androidx.test.platform.app.InstrumentationRegistry; |
| |
| import com.android.compatibility.common.util.ShellIdentityUtils; |
| import com.android.compatibility.common.util.SystemUtil; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.stream.Collectors; |
| |
| @AppModeFull(reason = "Cannot get WifiManager in instant app mode") |
| public class ConcurrencyTest extends WifiJUnit3TestBase { |
| private class MySync { |
| static final int WIFI_STATE = 0; |
| static final int P2P_STATE = 1; |
| static final int DISCOVERY_STATE = 2; |
| static final int NETWORK_INFO = 3; |
| |
| public BitSet pendingSync = new BitSet(); |
| |
| public int expectedWifiState; |
| public int expectedP2pState; |
| public int expectedDiscoveryState; |
| public NetworkInfo expectedNetworkInfo; |
| } |
| |
| private class MyResponse { |
| public boolean valid = false; |
| |
| public boolean success; |
| public int failureReason; |
| public int p2pState; |
| public int discoveryState; |
| public NetworkInfo networkInfo; |
| public WifiP2pInfo p2pInfo; |
| public String deviceName; |
| public WifiP2pGroupList persistentGroups; |
| public WifiP2pGroup group = new WifiP2pGroup(); |
| |
| // External approver |
| public boolean isAttached; |
| public boolean isDetached; |
| public int detachReason; |
| public MacAddress targetPeer; |
| |
| public void reset() { |
| valid = false; |
| |
| networkInfo = null; |
| p2pInfo = null; |
| deviceName = null; |
| persistentGroups = null; |
| group = null; |
| |
| isAttached = false; |
| isDetached = false; |
| targetPeer = null; |
| } |
| } |
| |
| private WifiManager mWifiManager; |
| private WifiP2pManager mWifiP2pManager; |
| private WifiP2pManager.Channel mWifiP2pChannel; |
| private MySync mMySync = new MySync(); |
| private MyResponse mMyResponse = new MyResponse(); |
| private boolean mWasVerboseLoggingEnabled; |
| private WifiP2pConfig mTestWifiP2pPeerConfig; |
| |
| private static final String TAG = "ConcurrencyTest"; |
| private static final int TIMEOUT_MSEC = 6000; |
| private static final int WAIT_MSEC = 60; |
| private static final int DURATION = 5000; |
| private IntentFilter mIntentFilter; |
| private final BroadcastReceiver mReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final String action = intent.getAction(); |
| if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { |
| synchronized (mMySync) { |
| mMySync.pendingSync.set(MySync.WIFI_STATE); |
| mMySync.expectedWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, |
| WifiManager.WIFI_STATE_DISABLED); |
| mMySync.notify(); |
| } |
| } else if(action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) { |
| synchronized (mMySync) { |
| mMySync.pendingSync.set(MySync.P2P_STATE); |
| mMySync.expectedP2pState = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, |
| WifiP2pManager.WIFI_P2P_STATE_DISABLED); |
| mMySync.notify(); |
| } |
| } else if (action.equals(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION)) { |
| synchronized (mMySync) { |
| mMySync.pendingSync.set(MySync.DISCOVERY_STATE); |
| mMySync.expectedDiscoveryState = intent.getIntExtra( |
| WifiP2pManager.EXTRA_DISCOVERY_STATE, |
| WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED); |
| mMySync.notify(); |
| } |
| } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { |
| synchronized (mMySync) { |
| mMySync.pendingSync.set(MySync.NETWORK_INFO); |
| mMySync.expectedNetworkInfo = (NetworkInfo) intent.getExtra( |
| WifiP2pManager.EXTRA_NETWORK_INFO, null); |
| Log.d(TAG, "Get WIFI_P2P_CONNECTION_CHANGED_ACTION: " |
| + mMySync.expectedNetworkInfo); |
| mMySync.notify(); |
| } |
| } |
| } |
| }; |
| |
| private WifiP2pManager.ActionListener mActionListener = new WifiP2pManager.ActionListener() { |
| @Override |
| public void onSuccess() { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.success = true; |
| mMyResponse.notify(); |
| } |
| } |
| |
| @Override |
| public void onFailure(int reason) { |
| synchronized (mMyResponse) { |
| Log.d(TAG, "failure reason: " + reason); |
| mMyResponse.valid = true; |
| mMyResponse.success = false; |
| mMyResponse.failureReason = reason; |
| mMyResponse.notify(); |
| } |
| } |
| }; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| if (!WifiFeature.isWifiSupported(getContext()) && |
| !WifiFeature.isP2pSupported(getContext())) { |
| // skip the test if WiFi && p2p are not supported |
| return; |
| } |
| |
| mIntentFilter = new IntentFilter(); |
| mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); |
| mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); |
| mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION); |
| mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); |
| |
| mContext.registerReceiver(mReceiver, mIntentFilter); |
| mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); |
| assertNotNull(mWifiManager); |
| if (mWifiManager.isWifiEnabled()) { |
| SystemUtil.runShellCommand("svc wifi disable"); |
| Thread.sleep(DURATION); |
| } |
| |
| // turn on verbose logging for tests |
| mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.isVerboseLoggingEnabled()); |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.setVerboseLoggingEnabled(true)); |
| |
| assertTrue(!mWifiManager.isWifiEnabled()); |
| mMySync.expectedWifiState = WifiManager.WIFI_STATE_DISABLED; |
| mMySync.expectedP2pState = WifiP2pManager.WIFI_P2P_STATE_DISABLED; |
| mMySync.expectedDiscoveryState = WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED; |
| mMySync.expectedNetworkInfo = null; |
| |
| // for general connect command |
| mTestWifiP2pPeerConfig = new WifiP2pConfig(); |
| mTestWifiP2pPeerConfig.deviceAddress = "aa:bb:cc:dd:ee:ff"; |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (!WifiFeature.isWifiSupported(getContext()) && |
| !WifiFeature.isP2pSupported(getContext())) { |
| // skip the test if WiFi and p2p are not supported |
| super.tearDown(); |
| return; |
| } |
| if (null != mWifiP2pManager) { |
| removeAllPersistentGroups(); |
| } |
| mContext.unregisterReceiver(mReceiver); |
| |
| ShellIdentityUtils.invokeWithShellPermissions( |
| () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled)); |
| |
| enableWifi(); |
| super.tearDown(); |
| } |
| |
| private boolean waitForBroadcasts(List<Integer> waitSyncList) { |
| synchronized (mMySync) { |
| long timeout = System.currentTimeMillis() + TIMEOUT_MSEC; |
| while (System.currentTimeMillis() < timeout) { |
| List<Integer> handledSyncList = waitSyncList.stream() |
| .filter(w -> mMySync.pendingSync.get(w)) |
| .collect(Collectors.toList()); |
| handledSyncList.forEach(w -> mMySync.pendingSync.clear(w)); |
| waitSyncList.removeAll(handledSyncList); |
| if (waitSyncList.isEmpty()) { |
| break; |
| } |
| try { |
| mMySync.wait(WAIT_MSEC); |
| } catch (InterruptedException e) { } |
| } |
| if (!waitSyncList.isEmpty()) { |
| Log.i(TAG, "Missing broadcast: " + waitSyncList); |
| } |
| return waitSyncList.isEmpty(); |
| } |
| } |
| |
| private boolean waitForBroadcasts(int waitSingleSync) { |
| return waitForBroadcasts( |
| new LinkedList<Integer>(Arrays.asList(waitSingleSync))); |
| } |
| |
| private NetworkInfo.DetailedState waitForNextNetworkState() { |
| assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); |
| assertNotNull(mMySync.expectedNetworkInfo); |
| return mMySync.expectedNetworkInfo.getDetailedState(); |
| } |
| |
| private boolean waitForConnectedNetworkState() { |
| // The possible orders of network states are: |
| // * IDLE > CONNECTING > CONNECTED for lazy initialization |
| // * DISCONNECTED > CONNECTING > CONNECTED for previous group removal |
| // * CONNECTING > CONNECTED |
| NetworkInfo.DetailedState state = waitForNextNetworkState(); |
| if (state == NetworkInfo.DetailedState.IDLE |
| || state == NetworkInfo.DetailedState.DISCONNECTED) { |
| state = waitForNextNetworkState(); |
| } |
| if (state != NetworkInfo.DetailedState.CONNECTING) { |
| return false; |
| } |
| state = waitForNextNetworkState(); |
| return state == NetworkInfo.DetailedState.CONNECTED; |
| } |
| |
| private boolean waitForServiceResponse(MyResponse waitResponse) { |
| synchronized (waitResponse) { |
| long timeout = System.currentTimeMillis() + TIMEOUT_MSEC; |
| while (System.currentTimeMillis() < timeout) { |
| try { |
| waitResponse.wait(WAIT_MSEC); |
| } catch (InterruptedException e) { } |
| |
| if (waitResponse.valid) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| // Return true if location is enabled. |
| private boolean isLocationEnabled() { |
| return Settings.Secure.getInt(getContext().getContentResolver(), |
| Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) |
| != Settings.Secure.LOCATION_MODE_OFF; |
| } |
| |
| // Returns true if the device has location feature. |
| private boolean hasLocationFeature() { |
| return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION); |
| } |
| |
| private void resetResponse(MyResponse responseObj) { |
| synchronized (responseObj) { |
| responseObj.reset(); |
| } |
| } |
| |
| /* |
| * Enables Wifi and block until connection is established. |
| */ |
| private void enableWifi() throws InterruptedException { |
| if (!mWifiManager.isWifiEnabled()) { |
| SystemUtil.runShellCommand("svc wifi enable"); |
| } |
| |
| ConnectivityManager cm = |
| (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); |
| NetworkRequest request = |
| new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI) |
| .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) |
| .build(); |
| final CountDownLatch latch = new CountDownLatch(1); |
| NetworkCallback networkCallback = new NetworkCallback() { |
| @Override |
| public void onAvailable(Network network) { |
| latch.countDown(); |
| } |
| }; |
| cm.registerNetworkCallback(request, networkCallback); |
| latch.await(DURATION, TimeUnit.MILLISECONDS); |
| |
| cm.unregisterNetworkCallback(networkCallback); |
| } |
| |
| private void removeAllPersistentGroups() { |
| WifiP2pGroupList persistentGroups = getPersistentGroups(); |
| assertNotNull(persistentGroups); |
| for (WifiP2pGroup group: persistentGroups.getGroupList()) { |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.deletePersistentGroup(mWifiP2pChannel, |
| group.getNetworkId(), |
| mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| } |
| persistentGroups = getPersistentGroups(); |
| assertNotNull(persistentGroups); |
| assertEquals(0, persistentGroups.getGroupList().size()); |
| } |
| |
| private boolean setupWifiP2p() { |
| // Cannot support p2p alone |
| if (!WifiFeature.isWifiSupported(getContext())) { |
| assertTrue(!WifiFeature.isP2pSupported(getContext())); |
| return false; |
| } |
| |
| if (!WifiFeature.isP2pSupported(getContext())) { |
| // skip the test if p2p is not supported |
| return false; |
| } |
| |
| if (!hasLocationFeature()) { |
| Log.d(TAG, "Skipping test as location is not supported"); |
| return false; |
| } |
| if (!isLocationEnabled()) { |
| fail("Please enable location for this test - since P-release WiFi Direct" |
| + " needs Location enabled."); |
| } |
| |
| long timeout = System.currentTimeMillis() + TIMEOUT_MSEC; |
| while (!mWifiManager.isWifiEnabled() && System.currentTimeMillis() < timeout) { |
| try { |
| enableWifi(); |
| } catch (InterruptedException e) { } |
| } |
| |
| assertTrue(mWifiManager.isWifiEnabled()); |
| |
| mWifiP2pManager = |
| (WifiP2pManager) getContext().getSystemService(Context.WIFI_P2P_SERVICE); |
| mWifiP2pChannel = mWifiP2pManager.initialize( |
| getContext(), getContext().getMainLooper(), null); |
| |
| assertNotNull(mWifiP2pManager); |
| assertNotNull(mWifiP2pChannel); |
| |
| assertTrue(waitForBroadcasts( |
| new LinkedList<Integer>( |
| Arrays.asList(MySync.WIFI_STATE, MySync.P2P_STATE)))); |
| |
| assertEquals(WifiManager.WIFI_STATE_ENABLED, mMySync.expectedWifiState); |
| assertEquals(WifiP2pManager.WIFI_P2P_STATE_ENABLED, mMySync.expectedP2pState); |
| removeAllPersistentGroups(); |
| |
| return true; |
| } |
| |
| public void testConcurrency() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestP2pState(mWifiP2pChannel, new WifiP2pManager.P2pStateListener() { |
| @Override |
| public void onP2pStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.p2pState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_STATE_ENABLED, mMyResponse.p2pState); |
| } |
| |
| public void testRequestDiscoveryState() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState( |
| mWifiP2pChannel, new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED, mMyResponse.discoveryState); |
| |
| // If there is any saved network and this device is connecting to this saved network, |
| // p2p discovery might be blocked during DHCP provision. |
| int retryCount = 3; |
| while (retryCount > 0) { |
| resetResponse(mMyResponse); |
| mWifiP2pManager.discoverPeers(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| if (mMyResponse.success |
| || mMyResponse.failureReason != WifiP2pManager.BUSY) { |
| break; |
| } |
| Log.w(TAG, "Discovery is blocked, try again!"); |
| try { |
| Thread.sleep(500); |
| } catch (InterruptedException ex) {} |
| retryCount--; |
| } |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.DISCOVERY_STATE)); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState(mWifiP2pChannel, |
| new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED, mMyResponse.discoveryState); |
| |
| mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null); |
| } |
| |
| public void testRequestNetworkInfo() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel, |
| new WifiP2pManager.NetworkInfoListener() { |
| @Override |
| public void onNetworkInfoAvailable(NetworkInfo info) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.networkInfo = info; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertNotNull(mMyResponse.networkInfo); |
| // The state might be IDLE, DISCONNECTED, FAILED before a connection establishment. |
| // Just ensure the state is NOT CONNECTED. |
| assertNotEquals(NetworkInfo.DetailedState.CONNECTED, |
| mMySync.expectedNetworkInfo.getDetailedState()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForConnectedNetworkState()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel, |
| new WifiP2pManager.NetworkInfoListener() { |
| @Override |
| public void onNetworkInfoAvailable(NetworkInfo info) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.networkInfo = info; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertNotNull(mMyResponse.networkInfo); |
| assertEquals(NetworkInfo.DetailedState.CONNECTED, |
| mMyResponse.networkInfo.getDetailedState()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestConnectionInfo(mWifiP2pChannel, |
| new WifiP2pManager.ConnectionInfoListener() { |
| @Override |
| public void onConnectionInfoAvailable(WifiP2pInfo info) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.p2pInfo = new WifiP2pInfo(info); |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertNotNull(mMyResponse.p2pInfo); |
| assertTrue(mMyResponse.p2pInfo.groupFormed); |
| assertTrue(mMyResponse.p2pInfo.isGroupOwner); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, |
| new WifiP2pManager.GroupInfoListener() { |
| @Override |
| public void onGroupInfoAvailable(WifiP2pGroup group) { |
| synchronized (mMyResponse) { |
| mMyResponse.group = new WifiP2pGroup(group); |
| mMyResponse.valid = true; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertNotNull(mMyResponse.group); |
| assertNotEquals(0, mMyResponse.group.getFrequency()); |
| assertTrue(mMyResponse.group.getNetworkId() >= 0); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); |
| assertNotNull(mMySync.expectedNetworkInfo); |
| assertEquals(NetworkInfo.DetailedState.DISCONNECTED, |
| mMySync.expectedNetworkInfo.getDetailedState()); |
| } |
| |
| private String getDeviceName() { |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDeviceInfo(mWifiP2pChannel, |
| new WifiP2pManager.DeviceInfoListener() { |
| @Override |
| public void onDeviceInfoAvailable(WifiP2pDevice wifiP2pDevice) { |
| synchronized (mMyResponse) { |
| mMyResponse.deviceName = wifiP2pDevice.deviceName; |
| mMyResponse.valid = true; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| return mMyResponse.deviceName; |
| } |
| |
| public void testSetDeviceName() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| String testDeviceName = "test"; |
| String originalDeviceName = getDeviceName(); |
| assertNotNull(originalDeviceName); |
| |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.setDeviceName( |
| mWifiP2pChannel, testDeviceName, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| String currentDeviceName = getDeviceName(); |
| assertEquals(testDeviceName, currentDeviceName); |
| |
| // restore the device name at the end |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.setDeviceName( |
| mWifiP2pChannel, originalDeviceName, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| } |
| |
| private WifiP2pGroupList getPersistentGroups() { |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.requestPersistentGroupInfo(mWifiP2pChannel, |
| new WifiP2pManager.PersistentGroupInfoListener() { |
| @Override |
| public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) { |
| synchronized (mMyResponse) { |
| mMyResponse.persistentGroups = groups; |
| mMyResponse.valid = true; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| }); |
| return mMyResponse.persistentGroups; |
| } |
| |
| public void testPersistentGroupOperation() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForConnectedNetworkState()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); |
| assertNotNull(mMySync.expectedNetworkInfo); |
| assertEquals(NetworkInfo.DetailedState.DISCONNECTED, |
| mMySync.expectedNetworkInfo.getDetailedState()); |
| |
| WifiP2pGroupList persistentGroups = getPersistentGroups(); |
| assertNotNull(persistentGroups); |
| assertEquals(1, persistentGroups.getGroupList().size()); |
| |
| resetResponse(mMyResponse); |
| final int firstNetworkId = persistentGroups.getGroupList().get(0).getNetworkId(); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.deletePersistentGroup(mWifiP2pChannel, |
| firstNetworkId, |
| mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| persistentGroups = getPersistentGroups(); |
| assertNotNull(persistentGroups); |
| assertEquals(0, persistentGroups.getGroupList().size()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForConnectedNetworkState()); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); |
| assertNotNull(mMySync.expectedNetworkInfo); |
| assertEquals(NetworkInfo.DetailedState.DISCONNECTED, |
| mMySync.expectedNetworkInfo.getDetailedState()); |
| |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.factoryReset(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| persistentGroups = getPersistentGroups(); |
| assertNotNull(persistentGroups); |
| assertEquals(0, persistentGroups.getGroupList().size()); |
| } |
| |
| public void testP2pListening() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel, 6, 11, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.startListening(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.stopListening(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| } |
| |
| public void testP2pService() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| // This only store the listener to the WifiP2pManager internal variable, nothing to fail. |
| mWifiP2pManager.setServiceResponseListener(mWifiP2pChannel, |
| new WifiP2pManager.ServiceResponseListener() { |
| @Override |
| public void onServiceAvailable( |
| int protocolType, byte[] responseData, WifiP2pDevice srcDevice) { |
| } |
| }); |
| |
| resetResponse(mMyResponse); |
| List<String> services = new ArrayList<String>(); |
| services.add("urn:schemas-upnp-org:service:AVTransport:1"); |
| services.add("urn:schemas-upnp-org:service:ConnectionManager:1"); |
| WifiP2pServiceInfo rendererService = WifiP2pUpnpServiceInfo.newInstance( |
| "6859dede-8574-59ab-9332-123456789011", |
| "urn:schemas-upnp-org:device:MediaRenderer:1", |
| services); |
| mWifiP2pManager.addLocalService(mWifiP2pChannel, |
| rendererService, |
| mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.removeLocalService(mWifiP2pChannel, |
| rendererService, |
| mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.clearLocalServices(mWifiP2pChannel, |
| mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| } |
| |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) |
| public void testRemoveClient() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| if (!mWifiP2pManager.isGroupClientRemovalSupported()) return; |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForConnectedNetworkState()); |
| |
| resetResponse(mMyResponse); |
| MacAddress peerMacAddress = MacAddress.fromString(mTestWifiP2pPeerConfig.deviceAddress); |
| mWifiP2pManager.removeClient( |
| mWifiP2pChannel, peerMacAddress, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| } |
| |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) |
| public void testDiscoverPeersOnSpecificFreq() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| if (!mWifiP2pManager.isChannelConstrainedDiscoverySupported()) return; |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState( |
| mWifiP2pChannel, new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED, mMyResponse.discoveryState); |
| |
| // If there is any saved network and this device is connecting to this saved network, |
| // p2p discovery might be blocked during DHCP provision. |
| int retryCount = 3; |
| while (retryCount > 0) { |
| resetResponse(mMyResponse); |
| mWifiP2pManager.discoverPeersOnSpecificFrequency(mWifiP2pChannel, |
| 2412, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| if (mMyResponse.success |
| || mMyResponse.failureReason != WifiP2pManager.BUSY) { |
| break; |
| } |
| Log.w(TAG, "Discovery is blocked, try again!"); |
| try { |
| Thread.sleep(500); |
| } catch (InterruptedException ex) { } |
| retryCount--; |
| } |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.DISCOVERY_STATE)); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState(mWifiP2pChannel, |
| new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED, mMyResponse.discoveryState); |
| |
| mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null); |
| } |
| |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) |
| public void testDiscoverPeersOnSocialChannelsOnly() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| if (!mWifiP2pManager.isChannelConstrainedDiscoverySupported()) return; |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState( |
| mWifiP2pChannel, new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED, mMyResponse.discoveryState); |
| |
| // If there is any saved network and this device is connecting to this saved network, |
| // p2p discovery might be blocked during DHCP provision. |
| int retryCount = 3; |
| while (retryCount > 0) { |
| resetResponse(mMyResponse); |
| mWifiP2pManager.discoverPeersOnSocialChannels(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| if (mMyResponse.success |
| || mMyResponse.failureReason != WifiP2pManager.BUSY) { |
| break; |
| } |
| Log.w(TAG, "Discovery is blocked, try again!"); |
| try { |
| Thread.sleep(500); |
| } catch (InterruptedException ex) { } |
| retryCount--; |
| } |
| assertTrue(mMyResponse.success); |
| assertTrue(waitForBroadcasts(MySync.DISCOVERY_STATE)); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.requestDiscoveryState(mWifiP2pChannel, |
| new WifiP2pManager.DiscoveryStateListener() { |
| @Override |
| public void onDiscoveryStateAvailable(int state) { |
| synchronized (mMyResponse) { |
| mMyResponse.valid = true; |
| mMyResponse.discoveryState = state; |
| mMyResponse.notify(); |
| } |
| } |
| }); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED, mMyResponse.discoveryState); |
| |
| mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null); |
| } |
| |
| public void testP2pSetVendorElements() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| if (!mWifiP2pManager.isSetVendorElementsSupported()) return; |
| |
| // Vendor-Specific EID is 221. |
| List<ScanResult.InformationElement> ies = new ArrayList<>(Arrays.asList( |
| new ScanResult.InformationElement(221, 0, |
| new byte[]{(byte) 1, (byte) 2, (byte) 3, (byte) 4}))); |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| mWifiP2pManager.setVendorElements(mWifiP2pChannel, ies, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.success); |
| }); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.discoverPeers(mWifiP2pChannel, mActionListener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| } |
| |
| /** Test IEs whose size is greater than the maximum allowed size. */ |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) |
| public void testP2pSetVendorElementsOverMaximumAllowedSize() { |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| if (!mWifiP2pManager.isSetVendorElementsSupported()) return; |
| |
| List<ScanResult.InformationElement> ies = new ArrayList<>(); |
| ies.add(new ScanResult.InformationElement(221, 0, |
| new byte[WifiP2pManager.getP2pMaxAllowedVendorElementsLengthBytes() + 1])); |
| resetResponse(mMyResponse); |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| try { |
| mWifiP2pManager.setVendorElements(mWifiP2pChannel, ies, mActionListener); |
| fail("Should raise IllegalArgumentException"); |
| } catch (IllegalArgumentException ex) { |
| // expected |
| return; |
| } |
| }); |
| } |
| |
| /** Test that external approver APIs. */ |
| @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) |
| public void testP2pExternalApprover() { |
| final MacAddress peer = MacAddress.fromString("11:22:33:44:55:66"); |
| if (!setupWifiP2p()) { |
| return; |
| } |
| |
| ExternalApproverRequestListener listener = |
| new ExternalApproverRequestListener() { |
| @Override |
| public void onAttached(MacAddress deviceAddress) { |
| synchronized (mMyResponse) { |
| mMyResponse.targetPeer = deviceAddress; |
| mMyResponse.valid = true; |
| mMyResponse.isAttached = true; |
| mMyResponse.notify(); |
| } |
| } |
| @Override |
| public void onDetached(MacAddress deviceAddress, int reason) { |
| synchronized (mMyResponse) { |
| mMyResponse.targetPeer = deviceAddress; |
| mMyResponse.detachReason = reason; |
| mMyResponse.valid = true; |
| mMyResponse.isDetached = true; |
| mMyResponse.notify(); |
| } |
| } |
| @Override |
| public void onConnectionRequested(int requestType, WifiP2pConfig config, |
| WifiP2pDevice device) { |
| } |
| @Override |
| public void onPinGenerated(MacAddress deviceAddress, String pin) { |
| } |
| }; |
| |
| resetResponse(mMyResponse); |
| |
| UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); |
| try { |
| uiAutomation.adoptShellPermissionIdentity(); |
| mWifiP2pManager.addExternalApprover(mWifiP2pChannel, peer, listener); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.isAttached); |
| assertFalse(mMyResponse.isDetached); |
| assertEquals(peer, mMyResponse.targetPeer); |
| |
| // Just ignore the result as there is no real incoming request. |
| mWifiP2pManager.setConnectionRequestResult(mWifiP2pChannel, peer, |
| WifiP2pManager.CONNECTION_REQUEST_ACCEPT, null); |
| mWifiP2pManager.setConnectionRequestResult(mWifiP2pChannel, peer, |
| WifiP2pManager.CONNECTION_REQUEST_ACCEPT, "12345678", null); |
| |
| resetResponse(mMyResponse); |
| mWifiP2pManager.removeExternalApprover(mWifiP2pChannel, peer, null); |
| assertTrue(waitForServiceResponse(mMyResponse)); |
| assertTrue(mMyResponse.isDetached); |
| assertFalse(mMyResponse.isAttached); |
| assertEquals(peer, mMyResponse.targetPeer); |
| assertEquals(ExternalApproverRequestListener.APPROVER_DETACH_REASON_REMOVE, |
| mMyResponse.detachReason); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| |
| } |
| } |