1/*
2 * Copyright (C) 2016 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
17package com.android.server.wifi.nan;
18
19import static org.hamcrest.core.IsEqual.equalTo;
20import static org.hamcrest.core.IsNull.nullValue;
21import static org.junit.Assert.assertTrue;
22import static org.mockito.Matchers.anyShort;
23import static org.mockito.Matchers.eq;
24import static org.mockito.Mockito.inOrder;
25import static org.mockito.Mockito.mock;
26import static org.mockito.Mockito.verifyNoMoreInteractions;
27import static org.mockito.Mockito.verifyZeroInteractions;
28
29import android.net.wifi.nan.ConfigRequest;
30import android.net.wifi.nan.IWifiNanEventListener;
31import android.net.wifi.nan.IWifiNanSessionListener;
32import android.net.wifi.nan.PublishData;
33import android.net.wifi.nan.PublishSettings;
34import android.net.wifi.nan.SubscribeData;
35import android.net.wifi.nan.SubscribeSettings;
36import android.net.wifi.nan.WifiNanEventListener;
37import android.net.wifi.nan.WifiNanSessionListener;
38import android.test.suitebuilder.annotation.SmallTest;
39import android.util.SparseArray;
40
41import com.android.server.wifi.MockLooper;
42
43import libcore.util.HexEncoding;
44
45import org.junit.Before;
46import org.junit.Rule;
47import org.junit.Test;
48import org.junit.rules.ErrorCollector;
49import org.mockito.ArgumentCaptor;
50import org.mockito.InOrder;
51import org.mockito.Mock;
52import org.mockito.MockitoAnnotations;
53
54import java.lang.reflect.Constructor;
55import java.lang.reflect.Field;
56
57/**
58 * Unit test harness for WifiNanStateManager.
59 */
60@SmallTest
61public class WifiNanStateManagerTest {
62    private MockLooper mMockLooper;
63    private WifiNanStateManager mDut;
64    @Mock private WifiNanNative mMockNative;
65
66    @Rule
67    public ErrorCollector collector = new ErrorCollector();
68
69    @Before
70    public void setUp() throws Exception {
71        MockitoAnnotations.initMocks(this);
72
73        mMockLooper = new MockLooper();
74
75        mDut = installNewNanStateManagerAndResetState();
76        mDut.start(mMockLooper.getLooper());
77
78        installMockWifiNanNative(mMockNative);
79    }
80
81    @Test
82    public void testNanEventsDelivered() throws Exception {
83        final int uid = 1005;
84        final int clusterLow1 = 5;
85        final int clusterHigh1 = 100;
86        final int masterPref1 = 111;
87        final int clusterLow2 = 7;
88        final int clusterHigh2 = 155;
89        final int masterPref2 = 0;
90        final int reason = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
91        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
92
93        ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
94                .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
95
96        ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow2)
97                .setClusterHigh(clusterHigh2).setMasterPreference(masterPref2).build();
98
99        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
100        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
101        InOrder inOrder = inOrder(mockListener, mMockNative);
102
103        mDut.connect(uid, mockListener,
104                WifiNanEventListener.LISTEN_CONFIG_COMPLETED
105                        | WifiNanEventListener.LISTEN_CONFIG_FAILED
106                        | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
107                        | WifiNanEventListener.LISTEN_NAN_DOWN);
108        mDut.requestConfig(uid, configRequest1);
109        mMockLooper.dispatchAll();
110
111        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
112        short transactionId1 = transactionId.getValue();
113
114        mDut.requestConfig(uid, configRequest2);
115        mMockLooper.dispatchAll();
116
117        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest2));
118        short transactionId2 = transactionId.getValue();
119
120        mDut.onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac);
121        mDut.onConfigCompleted(transactionId1);
122        mDut.onConfigFailed(transactionId2, reason);
123        mDut.onInterfaceAddressChange(someMac);
124        mDut.onNanDown(reason);
125        mMockLooper.dispatchAll();
126
127        inOrder.verify(mockListener).onIdentityChanged();
128        inOrder.verify(mockListener).onConfigCompleted(configRequest1);
129        inOrder.verify(mockListener).onConfigFailed(configRequest2, reason);
130        inOrder.verify(mockListener).onIdentityChanged();
131        inOrder.verify(mockListener).onNanDown(reason);
132        verifyNoMoreInteractions(mockListener);
133
134        validateInternalTransactionInfoCleanedUp(transactionId1);
135        validateInternalTransactionInfoCleanedUp(transactionId2);
136    }
137
138    @Test
139    public void testNanEventsNotDelivered() throws Exception {
140        final int uid = 1005;
141        final int clusterLow1 = 5;
142        final int clusterHigh1 = 100;
143        final int masterPref1 = 111;
144        final int clusterLow2 = 7;
145        final int clusterHigh2 = 155;
146        final int masterPref2 = 0;
147        final int reason = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
148        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
149
150        ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
151                .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
152
153        ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow2)
154                .setClusterHigh(clusterHigh2).setMasterPreference(masterPref2).build();
155
156        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
157        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
158        InOrder inOrder = inOrder(mockListener, mMockNative);
159
160        mDut.connect(uid, mockListener, 0);
161        mDut.requestConfig(uid, configRequest1);
162        mMockLooper.dispatchAll();
163
164        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
165        short transactionId1 = transactionId.getValue();
166
167        mDut.requestConfig(uid, configRequest2);
168        mMockLooper.dispatchAll();
169
170        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest2));
171        short transactionId2 = transactionId.getValue();
172
173        mDut.onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, someMac);
174        mDut.onConfigCompleted(transactionId1);
175        mDut.onConfigFailed(transactionId2, reason);
176        mDut.onInterfaceAddressChange(someMac);
177        mDut.onNanDown(reason);
178        mMockLooper.dispatchAll();
179
180        verifyZeroInteractions(mockListener);
181
182        validateInternalTransactionInfoCleanedUp(transactionId1);
183        validateInternalTransactionInfoCleanedUp(transactionId2);
184    }
185
186    @Test
187    public void testPublish() throws Exception {
188        final int uid = 1005;
189        final int sessionId = 20;
190        final String serviceName = "some-service-name";
191        final String ssi = "some much longer and more arbitrary data";
192        final int publishCount = 7;
193        final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
194        final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
195        final int publishId1 = 15;
196        final int publishId2 = 22;
197
198        PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
199                .setServiceSpecificInfo(ssi).build();
200
201        PublishSettings publishSettings = new PublishSettings.Builder()
202                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
203                .setPublishCount(publishCount).build();
204
205        IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
206        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
207        InOrder inOrder = inOrder(mockListener, mMockNative);
208
209        int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
210                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
211                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
212                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
213                | WifiNanSessionListener.LISTEN_MATCH
214                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
215                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
216                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
217
218        mDut.connect(uid, null, 0);
219        mDut.createSession(uid, sessionId, mockListener, allEvents);
220
221        // publish - fail
222        mDut.publish(uid, sessionId, publishData, publishSettings);
223        mMockLooper.dispatchAll();
224
225        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
226                eq(publishSettings));
227
228        mDut.onPublishFail(transactionId.getValue(), reasonFail);
229        mMockLooper.dispatchAll();
230
231        inOrder.verify(mockListener).onPublishFail(reasonFail);
232        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
233
234        // publish - success/terminate
235        mDut.publish(uid, sessionId, publishData, publishSettings);
236        mMockLooper.dispatchAll();
237
238        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
239                eq(publishSettings));
240
241        mDut.onPublishSuccess(transactionId.getValue(), publishId1);
242        mMockLooper.dispatchAll();
243
244        mDut.onPublishTerminated(publishId1, reasonTerminate);
245        mMockLooper.dispatchAll();
246
247        inOrder.verify(mockListener).onPublishTerminated(reasonTerminate);
248        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
249
250        // re-publish
251        mDut.publish(uid, sessionId, publishData, publishSettings);
252        mMockLooper.dispatchAll();
253
254        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
255                eq(publishSettings));
256
257        mDut.onPublishSuccess(transactionId.getValue(), publishId2);
258        mDut.publish(uid, sessionId, publishData, publishSettings);
259        mMockLooper.dispatchAll();
260
261        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
262        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId2),
263                eq(publishData), eq(publishSettings));
264        verifyNoMoreInteractions(mockListener, mMockNative);
265    }
266
267    @Test
268    public void testPublishNoCallbacks() throws Exception {
269        final int uid = 1005;
270        final int sessionId = 20;
271        final String serviceName = "some-service-name";
272        final String ssi = "some much longer and more arbitrary data";
273        final int publishCount = 7;
274        final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
275        final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
276        final int publishId = 15;
277
278        PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
279                .setServiceSpecificInfo(ssi).build();
280
281        PublishSettings publishSettings = new PublishSettings.Builder()
282                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
283                .setPublishCount(publishCount).build();
284
285        IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
286        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
287        InOrder inOrder = inOrder(mockListener, mMockNative);
288
289        int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
290                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
291                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
292                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
293                | WifiNanSessionListener.LISTEN_MATCH
294                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
295                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
296                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
297
298        mDut.connect(uid, null, 0);
299        mDut.createSession(uid, sessionId, mockListener,
300                allEvents & ~WifiNanSessionListener.LISTEN_PUBLISH_FAIL
301                        & ~WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED);
302
303        // publish - fail
304        mDut.publish(uid, sessionId, publishData, publishSettings);
305        mMockLooper.dispatchAll();
306
307        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
308                eq(publishSettings));
309
310        mDut.onPublishFail(transactionId.getValue(), reasonFail);
311        mMockLooper.dispatchAll();
312
313        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
314
315        // publish - success/terminate
316        mDut.publish(uid, sessionId, publishData, publishSettings);
317        mMockLooper.dispatchAll();
318
319        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
320                eq(publishSettings));
321
322        mDut.onPublishSuccess(transactionId.getValue(), publishId);
323        mMockLooper.dispatchAll();
324
325        mDut.onPublishTerminated(publishId, reasonTerminate);
326        mMockLooper.dispatchAll();
327
328        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
329        verifyNoMoreInteractions(mockListener, mMockNative);
330    }
331
332    @Test
333    public void testSubscribe() throws Exception {
334        final int uid = 1005;
335        final int sessionId = 20;
336        final String serviceName = "some-service-name";
337        final String ssi = "some much longer and more arbitrary data";
338        final int subscribeCount = 7;
339        final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
340        final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
341        final int subscribeId1 = 15;
342        final int subscribeId2 = 10;
343
344        SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
345                .setServiceSpecificInfo(ssi).build();
346
347        SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
348                .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
349                .setSubscribeCount(subscribeCount).build();
350
351        IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
352        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
353        InOrder inOrder = inOrder(mockListener, mMockNative);
354
355        int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
356                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
357                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
358                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
359                | WifiNanSessionListener.LISTEN_MATCH
360                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
361                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
362                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
363
364        mDut.connect(uid, null, 0);
365        mDut.createSession(uid, sessionId, mockListener, allEvents);
366
367        // subscribe - fail
368        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
369        mMockLooper.dispatchAll();
370
371        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
372                eq(subscribeSettings));
373
374        mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
375        mMockLooper.dispatchAll();
376
377        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
378        inOrder.verify(mockListener).onSubscribeFail(reasonFail);
379
380        // subscribe - success/terminate
381        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
382        mMockLooper.dispatchAll();
383
384        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
385                eq(subscribeSettings));
386
387        mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId1);
388        mMockLooper.dispatchAll();
389
390        mDut.onSubscribeTerminated(subscribeId1, reasonTerminate);
391        mMockLooper.dispatchAll();
392
393        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
394        inOrder.verify(mockListener).onSubscribeTerminated(reasonTerminate);
395
396        // re-subscribe
397        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
398        mMockLooper.dispatchAll();
399
400        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
401                eq(subscribeSettings));
402
403        mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId2);
404        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
405        mMockLooper.dispatchAll();
406
407        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
408        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId2),
409                eq(subscribeData), eq(subscribeSettings));
410        verifyNoMoreInteractions(mockListener, mMockNative);
411    }
412
413    @Test
414    public void testSubscribeNoCallbacks() throws Exception {
415        final int uid = 1005;
416        final int sessionId = 20;
417        final String serviceName = "some-service-name";
418        final String ssi = "some much longer and more arbitrary data";
419        final int subscribeCount = 7;
420        final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
421        final int reasonTerminate = WifiNanSessionListener.TERMINATE_REASON_DONE;
422        final int subscribeId = 15;
423
424        SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
425                .setServiceSpecificInfo(ssi).build();
426
427        SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
428                .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
429                .setSubscribeCount(subscribeCount).build();
430
431        IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
432        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
433        InOrder inOrder = inOrder(mockListener, mMockNative);
434
435        int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
436                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
437                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
438                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
439                | WifiNanSessionListener.LISTEN_MATCH
440                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
441                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
442                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
443
444        mDut.connect(uid, null, 0);
445        mDut.createSession(uid, sessionId, mockListener,
446                allEvents & ~WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
447                        & ~WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED);
448
449        // subscribe - fail
450        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
451        mMockLooper.dispatchAll();
452
453        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
454                eq(subscribeSettings));
455
456        mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
457        mMockLooper.dispatchAll();
458
459        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
460
461        // subscribe - success/terminate
462        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
463        mMockLooper.dispatchAll();
464
465        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
466                eq(subscribeSettings));
467
468        mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId);
469        mMockLooper.dispatchAll();
470
471        mDut.onSubscribeTerminated(subscribeId, reasonTerminate);
472        mMockLooper.dispatchAll();
473
474        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
475        verifyNoMoreInteractions(mockListener, mMockNative);
476    }
477
478    @Test
479    public void testMatchAndMessages() throws Exception {
480        final int uid = 1005;
481        final int sessionId = 20;
482        final String serviceName = "some-service-name";
483        final String ssi = "some much longer and more arbitrary data";
484        final int subscribeCount = 7;
485        final int reasonFail = WifiNanSessionListener.FAIL_REASON_NO_RESOURCES;
486        final int subscribeId = 15;
487        final int requestorId = 22;
488        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
489        final String peerSsi = "some peer ssi data";
490        final String peerMatchFilter = "filter binary array represented as string";
491        final String peerMsg = "some message from peer";
492        final int messageId = 6948;
493
494        SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
495                .setServiceSpecificInfo(ssi).build();
496
497        SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
498                .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
499                .setSubscribeCount(subscribeCount).build();
500
501        IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
502        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
503        InOrder inOrder = inOrder(mockListener, mMockNative);
504
505        int allEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
506                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
507                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
508                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
509                | WifiNanSessionListener.LISTEN_MATCH
510                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
511                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
512                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
513
514        mDut.connect(uid, null, 0);
515        mDut.createSession(uid, sessionId, mockListener, allEvents);
516        mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
517        mMockLooper.dispatchAll();
518
519        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
520                eq(subscribeSettings));
521
522        mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId);
523        mDut.onMatch(subscribeId, requestorId, peerMac, peerSsi.getBytes(), peerSsi.length(),
524                peerMatchFilter.getBytes(), peerMatchFilter.length());
525        mDut.onMessageReceived(subscribeId, requestorId, peerMac, peerMsg.getBytes(),
526                peerMsg.length());
527        mMockLooper.dispatchAll();
528
529        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
530        inOrder.verify(mockListener).onMatch(requestorId, peerSsi.getBytes(), peerSsi.length(),
531                peerMatchFilter.getBytes(), peerMatchFilter.length());
532        inOrder.verify(mockListener).onMessageReceived(requestorId, peerMsg.getBytes(),
533                peerMsg.length());
534
535        mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
536        mMockLooper.dispatchAll();
537
538        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
539                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()));
540
541        mDut.onMessageSendFail(transactionId.getValue(), reasonFail);
542        mMockLooper.dispatchAll();
543
544        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
545        inOrder.verify(mockListener).onMessageSendFail(messageId, reasonFail);
546
547        mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
548        mMockLooper.dispatchAll();
549
550        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
551                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()));
552
553        mDut.onMessageSendSuccess(transactionId.getValue());
554        mMockLooper.dispatchAll();
555
556        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
557        inOrder.verify(mockListener).onMessageSendSuccess(messageId);
558
559        verifyNoMoreInteractions(mockListener, mMockNative);
560    }
561
562    /**
563     * Summary: in a single publish session interact with multiple peers
564     * (different MAC addresses).
565     */
566    @Test
567    public void testMultipleMessageSources() throws Exception {
568        final int uid = 300;
569        final int clusterLow = 7;
570        final int clusterHigh = 7;
571        final int masterPref = 0;
572        final int sessionId = 26;
573        final String serviceName = "some-service-name";
574        final int publishId = 88;
575        final int peerId1 = 568;
576        final int peerId2 = 873;
577        final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
578        final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
579        final String msgFromPeer1 = "hey from 000102...";
580        final String msgFromPeer2 = "hey from 0607...";
581        final String msgToPeer1 = "hey there 000102...";
582        final String msgToPeer2 = "hey there 0506...";
583        final int msgToPeerId1 = 546;
584        final int msgToPeerId2 = 9654;
585        final int reason = WifiNanSessionListener.FAIL_REASON_OTHER;
586
587        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
588                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
589
590        PublishData publishData = new PublishData.Builder().setServiceName(serviceName).build();
591
592        PublishSettings publishSettings = new PublishSettings.Builder()
593                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED).build();
594
595        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
596        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
597        IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
598        InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
599
600        int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
601                | WifiNanEventListener.LISTEN_CONFIG_FAILED
602                | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
603                | WifiNanEventListener.LISTEN_NAN_DOWN;
604
605        int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
606                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
607                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
608                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
609                | WifiNanSessionListener.LISTEN_MATCH
610                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
611                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
612                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
613
614        mDut.connect(uid, mockListener, allEvents);
615        mDut.requestConfig(uid, configRequest);
616        mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
617        mDut.publish(uid, sessionId, publishData, publishSettings);
618        mMockLooper.dispatchAll();
619
620        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
621        short transactionIdConfig = transactionId.getValue();
622
623        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
624                eq(publishSettings));
625        short transactionIdPublish = transactionId.getValue();
626
627        mDut.onConfigCompleted(transactionIdConfig);
628        mDut.onPublishSuccess(transactionIdPublish, publishId);
629        mDut.onMessageReceived(publishId, peerId1, peerMac1, msgFromPeer1.getBytes(),
630                msgFromPeer1.length());
631        mDut.onMessageReceived(publishId, peerId2, peerMac2, msgFromPeer2.getBytes(),
632                msgFromPeer2.length());
633        mDut.sendMessage(uid, sessionId, peerId2, msgToPeer2.getBytes(), msgToPeer2.length(),
634                msgToPeerId2);
635        mDut.sendMessage(uid, sessionId, peerId1, msgToPeer1.getBytes(), msgToPeer1.length(),
636                msgToPeerId1);
637        mMockLooper.dispatchAll();
638
639        validateInternalTransactionInfoCleanedUp(transactionIdConfig);
640        validateInternalTransactionInfoCleanedUp(transactionIdPublish);
641        inOrder.verify(mockListener).onConfigCompleted(configRequest);
642        inOrder.verify(mockSessionListener).onMessageReceived(peerId1, msgFromPeer1.getBytes(),
643                msgFromPeer1.length());
644        inOrder.verify(mockSessionListener).onMessageReceived(peerId2, msgFromPeer2.getBytes(),
645                msgFromPeer2.length());
646        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
647                eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()));
648        short transactionIdMsg2 = transactionId.getValue();
649        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
650                eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length()));
651        short transactionIdMsg1 = transactionId.getValue();
652
653        mDut.onMessageSendFail(transactionIdMsg1, reason);
654        mDut.onMessageSendSuccess(transactionIdMsg2);
655        mMockLooper.dispatchAll();
656
657        validateInternalTransactionInfoCleanedUp(transactionIdMsg1);
658        validateInternalTransactionInfoCleanedUp(transactionIdMsg2);
659        inOrder.verify(mockSessionListener).onMessageSendFail(msgToPeerId1, reason);
660        inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
661        verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
662    }
663
664    /**
665     * Summary: interact with a peer which changed its identity (MAC address)
666     * but which keeps its requestor instance ID. Should be transparent.
667     */
668    @Test
669    public void testMessageWhilePeerChangesIdentity() throws Exception {
670        final int uid = 300;
671        final int clusterLow = 7;
672        final int clusterHigh = 7;
673        final int masterPref = 0;
674        final int sessionId = 26;
675        final String serviceName = "some-service-name";
676        final int publishId = 88;
677        final int peerId = 568;
678        final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
679        final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
680        final String msgFromPeer1 = "hey from 000102...";
681        final String msgFromPeer2 = "hey from 0607...";
682        final String msgToPeer1 = "hey there 000102...";
683        final String msgToPeer2 = "hey there 0506...";
684        final int msgToPeerId1 = 546;
685        final int msgToPeerId2 = 9654;
686        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
687                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
688
689        PublishData publishData = new PublishData.Builder().setServiceName(serviceName).build();
690
691        PublishSettings publishSettings = new PublishSettings.Builder()
692                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED).build();
693
694        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
695        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
696        IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
697        InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
698
699        int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
700                | WifiNanEventListener.LISTEN_CONFIG_FAILED
701                | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
702                | WifiNanEventListener.LISTEN_NAN_DOWN;
703
704        int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
705                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
706                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
707                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
708                | WifiNanSessionListener.LISTEN_MATCH
709                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
710                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
711                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
712
713        mDut.connect(uid, mockListener, allEvents);
714        mDut.requestConfig(uid, configRequest);
715        mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
716        mDut.publish(uid, sessionId, publishData, publishSettings);
717        mMockLooper.dispatchAll();
718
719        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
720        short transactionIdConfig = transactionId.getValue();
721
722        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
723                eq(publishSettings));
724        short transactionIdPublish = transactionId.getValue();
725
726        mDut.onConfigCompleted(transactionIdConfig);
727        mDut.onPublishSuccess(transactionIdPublish, publishId);
728        mDut.onMessageReceived(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes(),
729                msgFromPeer1.length());
730        mDut.sendMessage(uid, sessionId, peerId, msgToPeer1.getBytes(), msgToPeer1.length(),
731                msgToPeerId1);
732        mMockLooper.dispatchAll();
733
734        validateInternalTransactionInfoCleanedUp(transactionIdConfig);
735        validateInternalTransactionInfoCleanedUp(transactionIdPublish);
736        inOrder.verify(mockListener).onConfigCompleted(configRequest);
737        inOrder.verify(mockSessionListener).onMessageReceived(peerId, msgFromPeer1.getBytes(),
738                msgFromPeer1.length());
739        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
740                eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length()));
741        short transactionIdMsg = transactionId.getValue();
742
743        mDut.onMessageSendSuccess(transactionIdMsg);
744        mDut.onMessageReceived(publishId, peerId, peerMacLater, msgFromPeer2.getBytes(),
745                msgFromPeer2.length());
746        mDut.sendMessage(uid, sessionId, peerId, msgToPeer2.getBytes(), msgToPeer2.length(),
747                msgToPeerId2);
748        mMockLooper.dispatchAll();
749
750        validateInternalTransactionInfoCleanedUp(transactionIdMsg);
751        inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId1);
752        inOrder.verify(mockSessionListener).onMessageReceived(peerId, msgFromPeer2.getBytes(),
753                msgFromPeer2.length());
754        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
755                eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length()));
756        transactionIdMsg = transactionId.getValue();
757
758        mDut.onMessageSendSuccess(transactionIdMsg);
759        mMockLooper.dispatchAll();
760
761        validateInternalTransactionInfoCleanedUp(transactionIdMsg);
762        inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
763
764        verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
765    }
766
767    @Test
768    public void testConfigs() throws Exception {
769        final int uid1 = 9999;
770        final int clusterLow1 = 5;
771        final int clusterHigh1 = 100;
772        final int masterPref1 = 111;
773        final int uid2 = 1001;
774        final boolean support5g2 = true;
775        final int clusterLow2 = 7;
776        final int clusterHigh2 = 155;
777        final int masterPref2 = 0;
778        final int uid3 = 55;
779
780        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
781        ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
782
783        ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
784                .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1).build();
785
786        ConfigRequest configRequest2 = new ConfigRequest.Builder().setSupport5gBand(support5g2)
787                .setClusterLow(clusterLow2).setClusterHigh(clusterHigh2)
788                .setMasterPreference(masterPref2).build();
789
790        ConfigRequest configRequest3 = new ConfigRequest.Builder().build();
791
792        IWifiNanEventListener mockListener1 = mock(IWifiNanEventListener.class);
793        IWifiNanEventListener mockListener2 = mock(IWifiNanEventListener.class);
794        IWifiNanEventListener mockListener3 = mock(IWifiNanEventListener.class);
795
796        InOrder inOrder = inOrder(mMockNative, mockListener1, mockListener2, mockListener3);
797
798        mDut.connect(uid1, mockListener1, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
799        mDut.requestConfig(uid1, configRequest1);
800        mMockLooper.dispatchAll();
801
802        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
803                crCapture.capture());
804        collector.checkThat("merge: stage 0", configRequest1, equalTo(crCapture.getValue()));
805
806        mDut.onConfigCompleted(transactionId.getValue());
807        mMockLooper.dispatchAll();
808
809        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
810        inOrder.verify(mockListener1).onConfigCompleted(configRequest1);
811
812        mDut.connect(uid2, mockListener2, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
813        mDut.requestConfig(uid2, configRequest2);
814        mMockLooper.dispatchAll();
815
816        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
817                crCapture.capture());
818        collector.checkThat("merge: stage 1: support 5g", crCapture.getValue().mSupport5gBand,
819                equalTo(true));
820        collector.checkThat("merge: stage 1: master pref", crCapture.getValue().mMasterPreference,
821                equalTo(Math.max(masterPref1, masterPref2)));
822        collector.checkThat("merge: stage 1: cluster low", crCapture.getValue().mClusterLow,
823                equalTo(Math.min(clusterLow1, clusterLow2)));
824        collector.checkThat("merge: stage 1: cluster high", crCapture.getValue().mClusterHigh,
825                equalTo(Math.max(clusterHigh1, clusterHigh2)));
826
827        mDut.onConfigCompleted(transactionId.getValue());
828        mMockLooper.dispatchAll();
829
830        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
831        inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
832
833        mDut.connect(uid3, mockListener3, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
834        mDut.requestConfig(uid3, configRequest3);
835        mMockLooper.dispatchAll();
836
837        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
838                crCapture.capture());
839        collector.checkThat("merge: stage 2: support 5g", crCapture.getValue().mSupport5gBand,
840                equalTo(true));
841        collector.checkThat("merge: stage 2: master pref", crCapture.getValue().mMasterPreference,
842                equalTo(Math.max(masterPref1, masterPref2)));
843        collector.checkThat("merge: stage 2: cluster low", crCapture.getValue().mClusterLow,
844                equalTo(Math.min(clusterLow1, clusterLow2)));
845        collector.checkThat("merge: stage 2: cluster high", crCapture.getValue().mClusterHigh,
846                equalTo(Math.max(clusterHigh1, clusterHigh2)));
847
848        mDut.onConfigCompleted(transactionId.getValue());
849        mMockLooper.dispatchAll();
850
851        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
852        inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
853
854        mDut.disconnect(uid2);
855        mMockLooper.dispatchAll();
856
857        validateInternalClientInfoCleanedUp(uid2);
858        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
859                crCapture.capture());
860        collector.checkThat("merge: stage 3", configRequest1, equalTo(crCapture.getValue()));
861
862        mDut.onConfigCompleted(transactionId.getValue());
863        mMockLooper.dispatchAll();
864
865        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
866        inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
867
868        mDut.disconnect(uid1);
869        mMockLooper.dispatchAll();
870
871        validateInternalClientInfoCleanedUp(uid2);
872        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
873                crCapture.capture());
874        collector.checkThat("merge: stage 4", configRequest3, equalTo(crCapture.getValue()));
875
876        mDut.onConfigCompleted(transactionId.getValue());
877        mMockLooper.dispatchAll();
878
879        validateInternalTransactionInfoCleanedUp(transactionId.getValue());
880        inOrder.verify(mockListener3).onConfigCompleted(crCapture.getValue());
881
882        mDut.disconnect(uid3);
883        mMockLooper.dispatchAll();
884
885        validateInternalClientInfoCleanedUp(uid2);
886        inOrder.verify(mMockNative).disable(anyShort());
887
888        verifyNoMoreInteractions(mMockNative);
889    }
890
891    /**
892     * Summary: disconnect a client while there are pending transactions.
893     * Validate that no callbacks are called and that internal state is
894     * cleaned-up.
895     */
896    @Test
897    public void testDisconnectWithPendingTransactions() throws Exception {
898        final int uid = 125;
899        final int clusterLow = 5;
900        final int clusterHigh = 100;
901        final int masterPref = 111;
902        final int sessionId = 20;
903        final String serviceName = "some-service-name";
904        final String ssi = "some much longer and more arbitrary data";
905        final int publishCount = 7;
906        final int reason = WifiNanSessionListener.TERMINATE_REASON_DONE;
907        final int publishId = 22;
908
909        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
910                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
911
912        PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
913                .setServiceSpecificInfo(ssi).build();
914
915        PublishSettings publishSettings = new PublishSettings.Builder()
916                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
917                .setPublishCount(publishCount).build();
918
919        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
920        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
921        IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
922        InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
923
924        int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
925                | WifiNanEventListener.LISTEN_CONFIG_FAILED
926                | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
927                | WifiNanEventListener.LISTEN_NAN_DOWN;
928
929        int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
930                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
931                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
932                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
933                | WifiNanSessionListener.LISTEN_MATCH
934                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
935                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
936                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
937
938        mDut.connect(uid, mockListener, allEvents);
939        mDut.createSession(uid, sessionId, mockSessionListener, allSessionEvents);
940        mDut.requestConfig(uid, configRequest);
941        mDut.publish(uid, sessionId, publishData, publishSettings);
942        mDut.disconnect(uid);
943        mMockLooper.dispatchAll();
944
945        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
946        short transactionIdConfig = transactionId.getValue();
947
948        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
949                eq(publishSettings));
950        short transactionIdPublish = transactionId.getValue();
951
952        validateInternalClientInfoCleanedUp(uid);
953        validateInternalTransactionInfoCleanedUp(transactionIdPublish);
954
955        mDut.onConfigCompleted(transactionIdConfig);
956        mDut.onPublishSuccess(transactionIdPublish, publishId);
957        mMockLooper.dispatchAll();
958
959        mDut.onPublishTerminated(publishId, reason);
960        mMockLooper.dispatchAll();
961
962        verifyZeroInteractions(mockListener, mockSessionListener);
963    }
964
965    /**
966     * Summary: destroy a session while there are pending transactions. Validate
967     * that no callbacks are called and that internal state is cleaned-up.
968     */
969    @Test
970    public void testDestroySessionWithPendingTransactions() throws Exception {
971        final int uid = 128;
972        final int clusterLow = 15;
973        final int clusterHigh = 192;
974        final int masterPref = 234;
975        final int publishSessionId = 19;
976        final int subscribeSessionId = 24;
977        final String serviceName = "some-service-name";
978        final String ssi = "some much longer and more arbitrary data";
979        final int publishCount = 15;
980        final int subscribeCount = 22;
981        final int reason = WifiNanSessionListener.TERMINATE_REASON_DONE;
982        final int publishId = 23;
983        final int subscribeId = 55;
984
985        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
986                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
987
988        PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
989                .setServiceSpecificInfo(ssi).build();
990
991        PublishSettings publishSettings = new PublishSettings.Builder()
992                .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
993                .setPublishCount(publishCount).build();
994
995        SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
996                .setServiceSpecificInfo(ssi).build();
997
998        SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
999                .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
1000                .setSubscribeCount(subscribeCount).build();
1001
1002        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1003        IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
1004        IWifiNanSessionListener mockPublishSessionListener = mock(IWifiNanSessionListener.class);
1005        IWifiNanSessionListener mockSubscribeSessionListener = mock(IWifiNanSessionListener.class);
1006        InOrder inOrder = inOrder(mMockNative, mockListener, mockPublishSessionListener,
1007                mockSubscribeSessionListener);
1008
1009        int allEvents = WifiNanEventListener.LISTEN_CONFIG_COMPLETED
1010                | WifiNanEventListener.LISTEN_CONFIG_FAILED
1011                | WifiNanEventListener.LISTEN_IDENTITY_CHANGED
1012                | WifiNanEventListener.LISTEN_NAN_DOWN;
1013
1014        int allSessionEvents = WifiNanSessionListener.LISTEN_PUBLISH_FAIL
1015                | WifiNanSessionListener.LISTEN_PUBLISH_TERMINATED
1016                | WifiNanSessionListener.LISTEN_SUBSCRIBE_FAIL
1017                | WifiNanSessionListener.LISTEN_SUBSCRIBE_TERMINATED
1018                | WifiNanSessionListener.LISTEN_MATCH
1019                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_SUCCESS
1020                | WifiNanSessionListener.LISTEN_MESSAGE_SEND_FAIL
1021                | WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED;
1022
1023        mDut.connect(uid, mockListener, allEvents);
1024        mDut.requestConfig(uid, configRequest);
1025        mDut.createSession(uid, publishSessionId, mockPublishSessionListener, allSessionEvents);
1026        mDut.publish(uid, publishSessionId, publishData, publishSettings);
1027        mDut.createSession(uid, subscribeSessionId, mockSubscribeSessionListener, allSessionEvents);
1028        mDut.subscribe(uid, subscribeSessionId, subscribeData, subscribeSettings);
1029        mDut.destroySession(uid, publishSessionId);
1030        mMockLooper.dispatchAll();
1031
1032        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest));
1033        short transactionIdConfig = transactionId.getValue();
1034
1035        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
1036                eq(publishSettings));
1037        short transactionIdPublish = transactionId.getValue();
1038
1039        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
1040                eq(subscribeSettings));
1041        short transactionIdSubscribe = transactionId.getValue();
1042
1043        validateInternalTransactionInfoCleanedUp(transactionIdPublish);
1044
1045        mDut.onConfigCompleted(transactionIdConfig);
1046        mDut.onPublishSuccess(transactionIdPublish, publishId);
1047        mDut.onSubscribeSuccess(transactionIdSubscribe, subscribeId);
1048        mMockLooper.dispatchAll();
1049
1050        mDut.onPublishTerminated(publishId, reason);
1051        mDut.destroySession(uid, subscribeSessionId);
1052        mMockLooper.dispatchAll();
1053
1054        inOrder.verify(mockListener).onConfigCompleted(configRequest);
1055        verifyZeroInteractions(mockPublishSessionListener);
1056        verifyNoMoreInteractions(mockSubscribeSessionListener);
1057    }
1058
1059    @Test
1060    public void testTransactionIdIncrement() {
1061        int loopCount = 100;
1062
1063        short prevId = 0;
1064        for (int i = 0; i < loopCount; ++i) {
1065            short id = mDut.createNextTransactionId();
1066            if (i != 0) {
1067                assertTrue("Transaction ID incrementing", id > prevId);
1068            }
1069            prevId = id;
1070        }
1071    }
1072
1073    /*
1074     * Tests of internal state of WifiNanStateManager: very limited (not usually
1075     * a good idea). However, these test that the internal state is cleaned-up
1076     * appropriately. Alternatively would cause issues with memory leaks or
1077     * information leak between sessions.
1078     */
1079
1080    /**
1081     * Utility routine used to validate that the internal state is cleaned-up
1082     * after the specific transaction ID. To be used in every test which
1083     * involves a transaction.
1084     *
1085     * @param transactionId The transaction ID whose state should be erased.
1086     */
1087    public void validateInternalTransactionInfoCleanedUp(short transactionId) throws Exception {
1088        Object info = getInternalPendingTransactionInfo(mDut, transactionId);
1089        collector.checkThat("Transaction record not cleared up for transactionId=" + transactionId,
1090                info, nullValue());
1091    }
1092
1093    /**
1094     * Utility routine used to validate that the internal state is cleaned-up
1095     * after a client is disconnected. To be used in every test which terminates
1096     * a client.
1097     *
1098     * @param uid The ID of the client which should be deleted.
1099     */
1100    public void validateInternalClientInfoCleanedUp(int uid) throws Exception {
1101        WifiNanClientState client = getInternalClientState(mDut, uid);
1102        collector.checkThat("Client record not cleared up for uid=" + uid, client, nullValue());
1103    }
1104
1105    /*
1106     * Utilities
1107     */
1108
1109    private static WifiNanStateManager installNewNanStateManagerAndResetState() throws Exception {
1110        Constructor<WifiNanStateManager> ctr = WifiNanStateManager.class.getDeclaredConstructor();
1111        ctr.setAccessible(true);
1112        WifiNanStateManager nanStateManager = ctr.newInstance();
1113
1114        Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton");
1115        field.setAccessible(true);
1116        field.set(null, nanStateManager);
1117
1118        return WifiNanStateManager.getInstance();
1119    }
1120
1121    private static void installMockWifiNanNative(WifiNanNative obj) throws Exception {
1122        Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton");
1123        field.setAccessible(true);
1124        field.set(null, obj);
1125    }
1126
1127    private static Object getInternalPendingTransactionInfo(WifiNanStateManager dut,
1128            short transactionId) throws Exception {
1129        Field field = WifiNanStateManager.class.getDeclaredField("mPendingResponses");
1130        field.setAccessible(true);
1131        @SuppressWarnings("unchecked")
1132        SparseArray<Object> pendingResponses = (SparseArray<Object>) field.get(dut);
1133
1134        return pendingResponses.get(transactionId);
1135    }
1136
1137    private static WifiNanClientState getInternalClientState(WifiNanStateManager dut,
1138            int uid) throws Exception {
1139        Field field = WifiNanStateManager.class.getDeclaredField("mClients");
1140        field.setAccessible(true);
1141        @SuppressWarnings("unchecked")
1142        SparseArray<WifiNanClientState> clients = (SparseArray<WifiNanClientState>) field.get(dut);
1143
1144        return clients.get(uid);
1145    }
1146}
1147
1148