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