1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.wifiaware.testcase; 18 19 import static com.android.cts.verifier.wifiaware.CallbackUtils.CALLBACK_TIMEOUT_SEC; 20 21 import android.content.Context; 22 import android.net.ConnectivityManager; 23 import android.net.Network; 24 import android.net.NetworkCapabilities; 25 import android.net.NetworkRequest; 26 import android.net.wifi.aware.DiscoverySession; 27 import android.net.wifi.aware.PeerHandle; 28 import android.net.wifi.aware.PublishConfig; 29 import android.net.wifi.aware.PublishDiscoverySession; 30 import android.net.wifi.aware.SubscribeConfig; 31 import android.net.wifi.aware.SubscribeDiscoverySession; 32 import android.net.wifi.aware.WifiAwareManager; 33 import android.net.wifi.aware.WifiAwareSession; 34 import android.util.Log; 35 import android.util.Pair; 36 37 import com.android.cts.verifier.R; 38 import com.android.cts.verifier.wifiaware.BaseTestCase; 39 import com.android.cts.verifier.wifiaware.CallbackUtils; 40 41 /** 42 *Test case for data-path, out-of-band (OOB) test cases: 43 * open/passphrase * responder/initiator. 44 * 45 * OOB assumes that there's an alternative channel over which to communicate the discovery MAC 46 * address of the Aware interface. That channel (e.g. bluetooth or a host test device) is not 47 * readily available (don't want to have Aware tests dependent on BLE). Instead will fake the OOB 48 * channel using Aware itself: will do normal discovery during which the devices will exchange their 49 * MAC addresses, then destroy the discovery sessions and use the MAC addresses to perform OOB data- 50 * path requests. 51 * 52 * Responder test sequence: 53 * 1. Attach (with identity listener) 54 * wait for results (session) 55 * wait for identity 56 * 2. Publish 57 * wait for results (publish session) 58 * 3. Wait for rx message (with MAC) 59 * 4. Send message with MAC 60 * wait for success 61 * 5. Destroy discovery session 62 * 6. Request network (as Responder) 63 * wait for network 64 * 65 * Initiator test sequence: 66 * 1. Attach (with identity listener) 67 * wait for results (session) 68 * wait for identity 69 * 2. Subscribe 70 * wait for results (subscribe session) 71 * 3. Wait for discovery 72 * 4. Send message with MAC 73 * wait for success 74 * 5. Wait for rx message (with MAC) 75 * 6. Destroy discovery session 76 * 7. Sleep for 5 seconds to let Responder time to set up 77 * 8. Request network (as Initiator) 78 * wait for network 79 */ 80 public class DataPathOutOfBandTestCase extends BaseTestCase { 81 private static final String TAG = "DataPathOutOfBandTestCase"; 82 private static final boolean DBG = true; 83 84 private static final int MAC_BYTES_LEN = 6; 85 86 private static final String SERVICE_NAME = "CtsVerifierTestService"; 87 private static final String PASSPHRASE = "Some super secret password"; 88 private static final int MESSAGE_ID = 1234; 89 90 private boolean mIsSecurityOpen; 91 private boolean mIsResponder; 92 93 private final Object mLock = new Object(); 94 95 private String mFailureReason; 96 private WifiAwareSession mWifiAwareSession; 97 private DiscoverySession mWifiAwareDiscoverySession; 98 private byte[] mDiscoveryMac; 99 100 private static int sSDKLevel = android.os.Build.VERSION.SDK_INT; 101 DataPathOutOfBandTestCase(Context context, boolean isSecurityOpen, boolean isResponder)102 public DataPathOutOfBandTestCase(Context context, boolean isSecurityOpen, 103 boolean isResponder) { 104 super(context); 105 mIsSecurityOpen = isSecurityOpen; 106 mIsResponder = isResponder; 107 } 108 109 @Override executeTest()110 protected boolean executeTest() throws InterruptedException { 111 if (DBG) { 112 Log.d(TAG, "executeTest: mIsSecurityOpen=" + mIsSecurityOpen + ", mIsResponder=" 113 + mIsResponder); 114 } 115 116 // 1. attach (with identity listener) 117 CallbackUtils.AttachCb attachCb = new CallbackUtils.AttachCb(); 118 CallbackUtils.IdentityListenerSingleShot identityL = new CallbackUtils 119 .IdentityListenerSingleShot(); 120 mWifiAwareManager.attach(attachCb, identityL, mHandler); 121 Pair<Integer, WifiAwareSession> results = attachCb.waitForAttach(); 122 switch (results.first) { 123 case CallbackUtils.AttachCb.TIMEOUT: 124 setFailureReason(mContext.getString(R.string.aware_status_attach_timeout)); 125 Log.e(TAG, "executeTest: attach TIMEOUT"); 126 return false; 127 case CallbackUtils.AttachCb.ON_ATTACH_FAILED: 128 setFailureReason(mContext.getString(R.string.aware_status_attach_fail)); 129 Log.e(TAG, "executeTest: attach ON_ATTACH_FAILED"); 130 return false; 131 } 132 mWifiAwareSession = results.second; 133 if (mWifiAwareSession == null) { 134 setFailureReason(mContext.getString(R.string.aware_status_attach_fail)); 135 Log.e(TAG, "executeTest: attach callback succeeded but null session returned!?"); 136 return false; 137 } 138 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_attached)); 139 if (DBG) { 140 Log.d(TAG, "executeTest: attach succeeded"); 141 } 142 mDiscoveryMac = identityL.waitForMac(); 143 if (mDiscoveryMac == null) { 144 setFailureReason(mContext.getString(R.string.aware_status_identity_fail)); 145 Log.e(TAG, "executeTest: identity callback not triggered"); 146 return false; 147 } 148 mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_identity, 149 bytesToHex(mDiscoveryMac, ':'))); 150 if (DBG) { 151 Log.d(TAG, "executeTest: identity received: " + bytesToHex(mDiscoveryMac, ':')); 152 } 153 154 if (mIsResponder) { 155 return executeTestResponder(); 156 } else { 157 return executeTestInitiator(); 158 } 159 } 160 setFailureReason(String reason)161 private void setFailureReason(String reason) { 162 synchronized (mLock) { 163 mFailureReason = reason; 164 } 165 } 166 167 @Override getFailureReason()168 protected String getFailureReason() { 169 synchronized (mLock) { 170 return mFailureReason; 171 } 172 } 173 174 @Override tearDown()175 protected void tearDown() { 176 if (mWifiAwareDiscoverySession != null) { 177 mWifiAwareDiscoverySession.close(); 178 mWifiAwareDiscoverySession = null; 179 } 180 if (mWifiAwareSession != null) { 181 mWifiAwareSession.close(); 182 mWifiAwareSession = null; 183 } 184 super.tearDown(); 185 } 186 executeTestResponder()187 private boolean executeTestResponder() throws InterruptedException { 188 if (DBG) Log.d(TAG, "executeTestResponder"); 189 CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb(); 190 191 // 2. publish 192 PublishConfig publishConfig = new PublishConfig.Builder().setServiceName( 193 SERVICE_NAME).build(); 194 if (DBG) Log.d(TAG, "executeTestResponder: publishConfig=" + publishConfig); 195 mWifiAwareSession.publish(publishConfig, discoveryCb, mHandler); 196 197 // wait for results - publish session 198 CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks( 199 CallbackUtils.DiscoveryCb.ON_PUBLISH_STARTED 200 | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED); 201 switch (callbackData.callback) { 202 case CallbackUtils.DiscoveryCb.TIMEOUT: 203 setFailureReason(mContext.getString(R.string.aware_status_publish_timeout)); 204 Log.e(TAG, "executeTestResponder: publish TIMEOUT"); 205 return false; 206 case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED: 207 setFailureReason(mContext.getString(R.string.aware_status_publish_failed)); 208 Log.e(TAG, "executeTestResponder: publish ON_SESSION_CONFIG_FAILED"); 209 return false; 210 } 211 PublishDiscoverySession discoverySession = callbackData.publishDiscoverySession; 212 mWifiAwareDiscoverySession = discoverySession; 213 if (discoverySession == null) { 214 setFailureReason(mContext.getString(R.string.aware_status_publish_null_session)); 215 Log.e(TAG, "executeTestResponder: publish succeeded but null session returned"); 216 return false; 217 } 218 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_publish_started)); 219 if (DBG) Log.d(TAG, "executeTestResponder: publish succeeded"); 220 221 // 3. Wait for rx message (with MAC) 222 callbackData = discoveryCb.waitForCallbacks(CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED); 223 switch (callbackData.callback) { 224 case CallbackUtils.DiscoveryCb.TIMEOUT: 225 setFailureReason(mContext.getString(R.string.aware_status_receive_timeout)); 226 Log.e(TAG, "executeTestResponder: receive message TIMEOUT"); 227 return false; 228 } 229 230 if (callbackData.serviceSpecificInfo == null 231 || callbackData.serviceSpecificInfo.length != MAC_BYTES_LEN) { 232 setFailureReason(mContext.getString(R.string.aware_status_receive_failure)); 233 Log.e(TAG, "executeTestResponder: receive message message content mismatch: " 234 + bytesToHex(callbackData.serviceSpecificInfo, ':')); 235 return false; 236 } 237 238 PeerHandle peerHandle = callbackData.peerHandle; 239 byte[] peerMac = callbackData.serviceSpecificInfo; 240 241 mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_received_mac, 242 bytesToHex(peerMac, ':'))); 243 if (DBG) { 244 Log.d(TAG, "executeTestResponder: received MAC address: " + bytesToHex(peerMac, ':')); 245 } 246 247 // 4. Send message with MAC and wait for success 248 discoverySession.sendMessage(peerHandle, MESSAGE_ID, mDiscoveryMac); 249 callbackData = discoveryCb.waitForCallbacks( 250 CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED 251 | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED); 252 switch (callbackData.callback) { 253 case CallbackUtils.DiscoveryCb.TIMEOUT: 254 setFailureReason(mContext.getString(R.string.aware_status_send_timeout)); 255 Log.e(TAG, "executeTestResponder: send message TIMEOUT"); 256 return false; 257 case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED: 258 setFailureReason(mContext.getString(R.string.aware_status_send_failed)); 259 Log.e(TAG, "executeTestResponder: send message ON_MESSAGE_SEND_FAILED"); 260 return false; 261 } 262 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success)); 263 if (DBG) Log.d(TAG, "executeTestResponder: send message succeeded"); 264 265 if (callbackData.messageId != MESSAGE_ID) { 266 setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter)); 267 Log.e(TAG, "executeTestResponder: send message message ID mismatch: " 268 + callbackData.messageId); 269 return false; 270 } 271 272 // 5. Destroy discovery session 273 discoverySession.close(); 274 mWifiAwareDiscoverySession = null; 275 276 // 6. Request network (as Responder) and wait for network 277 ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( 278 Context.CONNECTIVITY_SERVICE); 279 NetworkRequest nr = new NetworkRequest.Builder().addTransportType( 280 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier( 281 mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen( 282 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac) 283 : mWifiAwareSession.createNetworkSpecifierPassphrase( 284 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac, 285 PASSPHRASE)).build(); 286 CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb(); 287 cm.requestNetwork(nr, networkCb, CALLBACK_TIMEOUT_SEC * 1000); 288 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested)); 289 if (DBG) Log.d(TAG, "executeTestResponder: requested network"); 290 Pair<Network, NetworkCapabilities> info = networkCb.waitForNetworkCapabilities(); 291 cm.unregisterNetworkCallback(networkCb); 292 if (info == null) { 293 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 294 Log.e(TAG, "executeTestResponder: network request rejected - ON_UNAVAILABLE"); 295 return false; 296 } 297 if (sSDKLevel <= android.os.Build.VERSION_CODES.P){ 298 if (info.second.getNetworkSpecifier() != null) { 299 setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak)); 300 Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!"); 301 return false; 302 } 303 } 304 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success)); 305 if (DBG) Log.d(TAG, "executeTestResponder: network request granted - AVAILABLE"); 306 307 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok)); 308 return true; 309 } 310 executeTestInitiator()311 private boolean executeTestInitiator() throws InterruptedException { 312 if (DBG) Log.d(TAG, "executeTestInitiator"); 313 CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb(); 314 315 // 2. Subscribe 316 SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName( 317 SERVICE_NAME).build(); 318 if (DBG) Log.d(TAG, "executeTestInitiator: subscribeConfig=" + subscribeConfig); 319 mWifiAwareSession.subscribe(subscribeConfig, discoveryCb, mHandler); 320 321 // wait for results - subscribe session 322 CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks( 323 CallbackUtils.DiscoveryCb.ON_SUBSCRIBE_STARTED 324 | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED); 325 switch (callbackData.callback) { 326 case CallbackUtils.DiscoveryCb.TIMEOUT: 327 setFailureReason(mContext.getString(R.string.aware_status_subscribe_timeout)); 328 Log.e(TAG, "executeTestInitiator: subscribe TIMEOUT"); 329 return false; 330 case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED: 331 setFailureReason(mContext.getString(R.string.aware_status_subscribe_failed)); 332 Log.e(TAG, "executeTestInitiator: subscribe ON_SESSION_CONFIG_FAILED"); 333 return false; 334 } 335 SubscribeDiscoverySession discoverySession = callbackData.subscribeDiscoverySession; 336 mWifiAwareDiscoverySession = discoverySession; 337 if (discoverySession == null) { 338 setFailureReason(mContext.getString(R.string.aware_status_subscribe_null_session)); 339 Log.e(TAG, "executeTestInitiator: subscribe succeeded but null session returned"); 340 return false; 341 } 342 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_subscribe_started)); 343 if (DBG) Log.d(TAG, "executeTestInitiator: subscribe succeeded"); 344 345 // 3. Wait for discovery 346 callbackData = discoveryCb.waitForCallbacks( 347 CallbackUtils.DiscoveryCb.ON_SERVICE_DISCOVERED); 348 switch (callbackData.callback) { 349 case CallbackUtils.DiscoveryCb.TIMEOUT: 350 setFailureReason(mContext.getString(R.string.aware_status_discovery_timeout)); 351 Log.e(TAG, "executeTestInitiator: waiting for discovery TIMEOUT"); 352 return false; 353 } 354 PeerHandle peerHandle = callbackData.peerHandle; 355 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_discovery)); 356 if (DBG) Log.d(TAG, "executeTestInitiator: discovery"); 357 358 // 4. Send message with MAC and wait for success 359 discoverySession.sendMessage(peerHandle, MESSAGE_ID, mDiscoveryMac); 360 callbackData = discoveryCb.waitForCallbacks( 361 CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED 362 | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED); 363 switch (callbackData.callback) { 364 case CallbackUtils.DiscoveryCb.TIMEOUT: 365 setFailureReason(mContext.getString(R.string.aware_status_send_timeout)); 366 Log.e(TAG, "executeTestInitiator: send message TIMEOUT"); 367 return false; 368 case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED: 369 setFailureReason(mContext.getString(R.string.aware_status_send_failed)); 370 Log.e(TAG, "executeTestInitiator: send message ON_MESSAGE_SEND_FAILED"); 371 return false; 372 } 373 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success)); 374 if (DBG) Log.d(TAG, "executeTestInitiator: send message succeeded"); 375 376 if (callbackData.messageId != MESSAGE_ID) { 377 setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter)); 378 Log.e(TAG, "executeTestInitiator: send message message ID mismatch: " 379 + callbackData.messageId); 380 return false; 381 } 382 383 // 5. Wait for rx message (with MAC) 384 callbackData = discoveryCb.waitForCallbacks(CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED); 385 switch (callbackData.callback) { 386 case CallbackUtils.DiscoveryCb.TIMEOUT: 387 setFailureReason(mContext.getString(R.string.aware_status_receive_timeout)); 388 Log.e(TAG, "executeTestInitiator: receive message TIMEOUT"); 389 return false; 390 } 391 392 if (callbackData.serviceSpecificInfo == null 393 || callbackData.serviceSpecificInfo.length != MAC_BYTES_LEN) { 394 setFailureReason(mContext.getString(R.string.aware_status_receive_failure)); 395 Log.e(TAG, "executeTestInitiator: receive message message content mismatch: " 396 + bytesToHex(callbackData.serviceSpecificInfo, ':')); 397 return false; 398 } 399 400 byte[] peerMac = callbackData.serviceSpecificInfo; 401 402 mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_received_mac, 403 bytesToHex(peerMac, ':'))); 404 if (DBG) { 405 Log.d(TAG, "executeTestInitiator: received MAC address: " + bytesToHex(peerMac, ':')); 406 } 407 408 // 6. Destroy discovery session 409 discoverySession.close(); 410 mWifiAwareDiscoverySession = null; 411 412 // 7. Sleep for 5 seconds to let Responder time to set up 413 mListener.onTestMsgReceived( 414 mContext.getString(R.string.aware_status_sleeping_wait_for_responder)); 415 Thread.sleep(5000); 416 417 // 8. Request network (as Initiator) and wait for network 418 ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService( 419 Context.CONNECTIVITY_SERVICE); 420 NetworkRequest nr = new NetworkRequest.Builder().addTransportType( 421 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier( 422 mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen( 423 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac) 424 : mWifiAwareSession.createNetworkSpecifierPassphrase( 425 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac, 426 PASSPHRASE)).build(); 427 CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb(); 428 cm.requestNetwork(nr, networkCb, CALLBACK_TIMEOUT_SEC * 1000); 429 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested)); 430 if (DBG) Log.d(TAG, "executeTestInitiator: requested network"); 431 Pair<Network, NetworkCapabilities> info = networkCb.waitForNetworkCapabilities(); 432 cm.unregisterNetworkCallback(networkCb); 433 if (info == null) { 434 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 435 Log.e(TAG, "executeTestInitiator: network request rejected - ON_UNAVAILABLE"); 436 return false; 437 } 438 if (sSDKLevel <= android.os.Build.VERSION_CODES.P){ 439 if(info.second.getNetworkSpecifier() != null) { 440 setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak)); 441 Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!"); 442 return false; 443 } 444 } 445 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success)); 446 if (DBG) Log.d(TAG, "executeTestInitiator: network request granted - AVAILABLE"); 447 448 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok)); 449 return true; 450 } 451 } 452