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.PublishDiscoverySession; 27 import android.net.wifi.aware.WifiAwareNetworkInfo; 28 import android.net.wifi.aware.WifiAwareNetworkSpecifier; 29 import android.util.Log; 30 import android.util.Pair; 31 32 import com.android.cts.verifier.R; 33 import com.android.cts.verifier.wifiaware.CallbackUtils; 34 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.OutputStream; 38 import java.net.Inet6Address; 39 import java.net.ServerSocket; 40 import java.net.Socket; 41 import java.util.Arrays; 42 43 /** 44 * Test case for data-path, in-band test cases: 45 * open/passphrase * solicited/unsolicited * publish/subscribe. 46 * 47 * Subscribe test sequence: 48 * 1. Attach 49 * wait for results (session) 50 * 2. Subscribe 51 * wait for results (subscribe session) 52 * 3. Wait for discovery (possibly with ranging) 53 * 4. Send message 54 * Wait for success 55 * 5. Wait for rx message 56 * 6. Request network 57 * Wait for network 58 * 7. Create socket and bind to server 59 * 8. Send/receive data to validate connection 60 * 9. Destroy session 61 * 62 * Publish test sequence: 63 * 1. Attach 64 * wait for results (session) 65 * 2. Publish 66 * wait for results (publish session) 67 * 3. Wait for rx message 68 * 4. Start a ServerSocket 69 * 5. Request network 70 * 6. Send message 71 * Wait for success 72 * 7. Wait for network 73 * 8. Receive/Send data to validate connection 74 * 9. Destroy session 75 */ 76 public class DataPathInBandTestCase extends DiscoveryBaseTestCase { 77 private static final String TAG = "DataPathInBandTestCase"; 78 private static final boolean DBG = true; 79 80 private static final byte[] MSG_PUB_TO_SUB = "Ready".getBytes(); 81 private static final String PASSPHRASE = "Some super secret password"; 82 private static final byte[] PMK = "01234567890123456789012345678901".getBytes(); 83 84 private static final byte[] MSG_CLIENT_TO_SERVER = "GET SOME BYTES".getBytes(); 85 private static final byte[] MSG_SERVER_TO_CLIENT = "PUT SOME OTHER BYTES".getBytes(); 86 87 private boolean mIsSecurityOpen; 88 private boolean mUsePmk; 89 private boolean mIsPublish; 90 private boolean mIsAcceptAny; 91 private Thread mClientServerThread; 92 private ConnectivityManager mCm; 93 private CallbackUtils.NetworkCb mNetworkCb; 94 95 private static int sSDKLevel = android.os.Build.VERSION.SDK_INT; 96 DataPathInBandTestCase(Context context, boolean isSecurityOpen, boolean isPublish, boolean isUnsolicited, boolean usePmk, boolean acceptAny)97 public DataPathInBandTestCase(Context context, boolean isSecurityOpen, boolean isPublish, 98 boolean isUnsolicited, boolean usePmk, boolean acceptAny) { 99 super(context, isUnsolicited, false); 100 101 mIsSecurityOpen = isSecurityOpen; 102 mUsePmk = usePmk; 103 mIsPublish = isPublish; 104 mIsAcceptAny = acceptAny; 105 } 106 107 @Override executeTest()108 protected boolean executeTest() throws InterruptedException { 109 if (DBG) { 110 Log.d(TAG, 111 "executeTest: mIsSecurityOpen=" + mIsSecurityOpen + ", mIsPublish=" + mIsPublish 112 + ", mIsUnsolicited=" + mIsUnsolicited); 113 } 114 115 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 116 mClientServerThread = null; 117 mNetworkCb = null; 118 119 boolean success; 120 if (mIsPublish) { 121 success = executeTestPublisher(); 122 } else { 123 success = executeTestSubscriber(); 124 } 125 if (!success) { 126 return false; 127 } 128 129 // destroy session 130 mWifiAwareDiscoverySession.close(); 131 mWifiAwareDiscoverySession = null; 132 133 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok)); 134 return true; 135 } 136 137 @Override tearDown()138 protected void tearDown() { 139 if (mClientServerThread != null) { 140 mClientServerThread.interrupt(); 141 } 142 if (mNetworkCb != null) { 143 mCm.unregisterNetworkCallback(mNetworkCb); 144 } 145 super.tearDown(); 146 } 147 148 executeTestSubscriber()149 private boolean executeTestSubscriber() throws InterruptedException { 150 if (DBG) Log.d(TAG, "executeTestSubscriber"); 151 if (!executeSubscribe()) { 152 return false; 153 } 154 155 // 5. wait to receive message 156 CallbackUtils.DiscoveryCb.CallbackData callbackData = mDiscoveryCb.waitForCallbacks( 157 CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED); 158 switch (callbackData.callback) { 159 case CallbackUtils.DiscoveryCb.TIMEOUT: 160 setFailureReason(mContext.getString(R.string.aware_status_receive_timeout)); 161 Log.e(TAG, "executeTestSubscriber: receive message TIMEOUT"); 162 return false; 163 } 164 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_received)); 165 if (DBG) Log.d(TAG, "executeTestSubscriber: received message"); 166 167 // validate that received the expected message 168 if (!Arrays.equals(MSG_PUB_TO_SUB, callbackData.serviceSpecificInfo)) { 169 setFailureReason(mContext.getString(R.string.aware_status_receive_failure)); 170 Log.e(TAG, "executeTestSubscriber: receive message message content mismatch: rx='" 171 + new String(callbackData.serviceSpecificInfo) + "'"); 172 return false; 173 } 174 175 // 6. request network 176 WifiAwareNetworkSpecifier.Builder nsBuilder = 177 new WifiAwareNetworkSpecifier.Builder(mWifiAwareDiscoverySession, mPeerHandle); 178 if (!mIsSecurityOpen) { 179 if (mUsePmk) { 180 nsBuilder.setPmk(PMK); 181 } else { 182 nsBuilder.setPskPassphrase(PASSPHRASE); 183 } 184 } 185 NetworkRequest nr = new NetworkRequest.Builder().addTransportType( 186 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier( 187 nsBuilder.build()).build(); 188 mNetworkCb = new CallbackUtils.NetworkCb(); 189 mCm.requestNetwork(nr, mNetworkCb, CALLBACK_TIMEOUT_SEC * 1000); 190 mListener.onTestMsgReceived( 191 mContext.getString(R.string.aware_status_network_requested)); 192 if (DBG) Log.d(TAG, "executeTestSubscriber: requested network"); 193 194 // 7. wait for network 195 Pair<Network, NetworkCapabilities> info = mNetworkCb.waitForNetworkCapabilities(); 196 if (info == null) { 197 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 198 Log.e(TAG, "executeTestSubscriber: network request rejected or timed-out"); 199 return false; 200 } 201 if (info.first == null || info.second == null) { 202 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 203 Log.e(TAG, "executeTestSubscriber: received a null Network or NetworkCapabilities!?"); 204 return false; 205 } 206 if (sSDKLevel <= android.os.Build.VERSION_CODES.P) { 207 if (info.second.getNetworkSpecifier() != null) { 208 setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak)); 209 Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!"); 210 return false; 211 } 212 } 213 214 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success)); 215 if (DBG) Log.d(TAG, "executeTestSubscriber: network request granted - AVAILABLE"); 216 217 if (!mIsSecurityOpen) { 218 if (!(info.second.getTransportInfo() instanceof WifiAwareNetworkInfo)) { 219 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 220 Log.e(TAG, "executeTestSubscriber: did not get WifiAwareNetworkInfo from peer!?"); 221 return false; 222 } 223 WifiAwareNetworkInfo peerAwareInfo = 224 (WifiAwareNetworkInfo) info.second.getTransportInfo(); 225 Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr(); 226 int peerPort = peerAwareInfo.getPort(); 227 int peerTransportProtocol = peerAwareInfo.getTransportProtocol(); 228 mListener.onTestMsgReceived( 229 mContext.getString(R.string.aware_status_socket_server_info_rx, 230 peerIpv6.toString(), 231 peerPort)); 232 if (DBG) { 233 Log.d(TAG, 234 "executeTestPublisher: rx peer info IPv6=" + peerIpv6 + ", port=" + peerPort 235 + ", transportProtocol=" + peerTransportProtocol); 236 } 237 if (peerTransportProtocol != 6) { // 6 == TCP: hard coded at peer 238 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 239 Log.e(TAG, "executeTestSubscriber: Got incorrect transport protocol from peer"); 240 return false; 241 } 242 if (peerPort <= 0) { 243 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 244 Log.e(TAG, "executeTestSubscriber: Got invalid port from peer (<=0)"); 245 return false; 246 } 247 248 // 8. send/receive - can happen inline here - no need for another thread 249 String currentMethod = ""; 250 try { 251 currentMethod = "createSocket"; 252 Socket socket = info.first.getSocketFactory().createSocket(peerIpv6, peerPort); 253 254 // simple interaction: write X bytes, read Y bytes 255 currentMethod = "getOutputStream()"; 256 OutputStream os = socket.getOutputStream(); 257 currentMethod = "write()"; 258 os.write(MSG_CLIENT_TO_SERVER, 0, MSG_CLIENT_TO_SERVER.length); 259 260 byte[] buffer = new byte[1024]; 261 currentMethod = "getInputStream()"; 262 InputStream is = socket.getInputStream(); 263 currentMethod = "read()"; 264 int numBytes = is.read(buffer, 0, MSG_SERVER_TO_CLIENT.length); 265 266 mListener.onTestMsgReceived( 267 mContext.getString(R.string.aware_status_socket_server_message_from_peer, 268 new String(buffer, 0, numBytes))); 269 270 if (numBytes != MSG_SERVER_TO_CLIENT.length) { 271 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 272 Log.e(TAG, 273 "executeTestSubscriber: didn't read expected number of bytes - only " 274 + "got -- " + numBytes); 275 return false; 276 } 277 if (!Arrays.equals(MSG_SERVER_TO_CLIENT, 278 Arrays.copyOf(buffer, MSG_SERVER_TO_CLIENT.length))) { 279 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 280 Log.e(TAG, "executeTestSubscriber: did not get expected message from server."); 281 return false; 282 } 283 // Sleep 3 second for transmit and receive. 284 Thread.sleep(3000); 285 currentMethod = "close()"; 286 os.close(); 287 } catch (IOException e) { 288 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 289 Log.e(TAG, "executeTestSubscriber: failure while executing " + currentMethod); 290 return false; 291 } 292 } 293 294 return true; 295 } 296 executeTestPublisher()297 private boolean executeTestPublisher() throws InterruptedException { 298 if (DBG) Log.d(TAG, "executeTestPublisher"); 299 if (!executePublish()) { 300 return false; 301 } 302 303 // 4. create a ServerSocket 304 int port = 0; 305 if (!mIsSecurityOpen) { 306 ServerSocket server; 307 try { 308 server = new ServerSocket(0); 309 } catch (IOException e) { 310 setFailureReason( 311 mContext.getString(R.string.aware_status_socket_failure)); 312 Log.e(TAG, "executeTestPublisher: failure creating a ServerSocket -- " + e); 313 return false; 314 } 315 port = server.getLocalPort(); 316 mListener.onTestMsgReceived( 317 mContext.getString(R.string.aware_status_socket_server_socket_started, port)); 318 if (DBG) Log.d(TAG, "executeTestPublisher: server socket started on port=" + port); 319 320 // accept connections on the server socket - has to be done in a separate thread! 321 mClientServerThread = new Thread(() -> { 322 String currentMethod = ""; 323 324 try { 325 currentMethod = "accept()"; 326 Socket socket = server.accept(); 327 currentMethod = "getInputStream()"; 328 InputStream is = socket.getInputStream(); 329 330 // simple interaction: read X bytes, write Y bytes 331 byte[] buffer = new byte[1024]; 332 currentMethod = "read()"; 333 int numBytes = is.read(buffer, 0, MSG_CLIENT_TO_SERVER.length); 334 335 mListener.onTestMsgReceived(mContext.getString( 336 R.string.aware_status_socket_server_message_from_peer, 337 new String(buffer, 0, numBytes))); 338 339 if (numBytes != MSG_CLIENT_TO_SERVER.length) { 340 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 341 Log.e(TAG, 342 "executeTestPublisher: didn't read expected number of bytes - only " 343 + "got -- " + numBytes); 344 return; 345 } 346 if (!Arrays.equals(MSG_CLIENT_TO_SERVER, 347 Arrays.copyOf(buffer, MSG_CLIENT_TO_SERVER.length))) { 348 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 349 Log.e(TAG, 350 "executeTestPublisher: did not get expected message from client."); 351 return; 352 } 353 354 currentMethod = "getOutputStream()"; 355 OutputStream os = socket.getOutputStream(); 356 currentMethod = "write()"; 357 os.write(MSG_SERVER_TO_CLIENT, 0, MSG_SERVER_TO_CLIENT.length); 358 // Sleep 3 second for transmit and receive. 359 Thread.sleep(3000); 360 currentMethod = "close()"; 361 os.close(); 362 } catch (IOException | InterruptedException e) { 363 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 364 Log.e(TAG, "executeTestPublisher: failure while executing " + currentMethod); 365 return; 366 } 367 }); 368 mClientServerThread.start(); 369 } 370 371 // 5. Request network 372 WifiAwareNetworkSpecifier.Builder nsBuilder; 373 if (mIsAcceptAny) { 374 nsBuilder = new WifiAwareNetworkSpecifier 375 .Builder((PublishDiscoverySession) mWifiAwareDiscoverySession); 376 } else { 377 nsBuilder = new WifiAwareNetworkSpecifier 378 .Builder(mWifiAwareDiscoverySession, mPeerHandle); 379 } 380 if (!mIsSecurityOpen) { 381 if (mUsePmk) { 382 nsBuilder.setPmk(PMK); 383 } else { 384 nsBuilder.setPskPassphrase(PASSPHRASE); 385 } 386 nsBuilder.setPort(port).setTransportProtocol(6); // 6 == TCP 387 } 388 NetworkRequest nr = new NetworkRequest.Builder().addTransportType( 389 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier( 390 nsBuilder.build()).build(); 391 mNetworkCb = new CallbackUtils.NetworkCb(); 392 mCm.requestNetwork(nr, mNetworkCb, CALLBACK_TIMEOUT_SEC * 1000); 393 mListener.onTestMsgReceived( 394 mContext.getString(R.string.aware_status_network_requested)); 395 if (DBG) Log.d(TAG, "executeTestPublisher: requested network"); 396 397 // 6. send message & wait for send status 398 mWifiAwareDiscoverySession.sendMessage(mPeerHandle, MESSAGE_ID, MSG_PUB_TO_SUB); 399 CallbackUtils.DiscoveryCb.CallbackData callbackData = mDiscoveryCb.waitForCallbacks( 400 CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED 401 | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED); 402 switch (callbackData.callback) { 403 case CallbackUtils.DiscoveryCb.TIMEOUT: 404 setFailureReason(mContext.getString(R.string.aware_status_send_timeout)); 405 Log.e(TAG, "executeTestPublisher: send message TIMEOUT"); 406 return false; 407 case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED: 408 setFailureReason(mContext.getString(R.string.aware_status_send_failed)); 409 Log.e(TAG, "executeTestPublisher: send message ON_MESSAGE_SEND_FAILED"); 410 return false; 411 } 412 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success)); 413 if (DBG) Log.d(TAG, "executeTestPublisher: send message succeeded"); 414 415 if (callbackData.messageId != MESSAGE_ID) { 416 setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter)); 417 Log.e(TAG, "executeTestPublisher: send message succeeded but message ID mismatch : " 418 + callbackData.messageId); 419 return false; 420 } 421 422 // 7. wait for network 423 Pair<Network, NetworkCapabilities> info = mNetworkCb.waitForNetworkCapabilities(); 424 if (info == null) { 425 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 426 Log.e(TAG, "executeTestPublisher: request network rejected - ON_UNAVAILABLE"); 427 return false; 428 } 429 if (info.first == null || info.second == null) { 430 setFailureReason(mContext.getString(R.string.aware_status_network_failed)); 431 Log.e(TAG, "executeTestPublisher: received a null Network or NetworkCapabilities!?"); 432 return false; 433 } 434 if (sSDKLevel <= android.os.Build.VERSION_CODES.P) { 435 if (info.second.getNetworkSpecifier() != null) { 436 setFailureReason(mContext.getString(R.string.aware_status_network_failed_leak)); 437 Log.e(TAG, "executeTestSubscriber: network request accepted - but leaks NS!"); 438 return false; 439 } 440 } 441 mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success)); 442 if (DBG) Log.d(TAG, "executeTestPublisher: network request granted - AVAILABLE"); 443 444 // 8. Send/Receive data to validate connection - happens on thread above 445 if (!mIsSecurityOpen) { 446 mClientServerThread.join(CALLBACK_TIMEOUT_SEC * 1000); 447 if (mClientServerThread.isAlive()) { 448 setFailureReason(mContext.getString(R.string.aware_status_socket_failure)); 449 Log.e(TAG, 450 "executeTestPublisher: failure while waiting for client-server thread to " 451 + "finish"); 452 return false; 453 } 454 } 455 456 return true; 457 } 458 } 459