WifiAwareStateManagerTest.java revision 9fdded9b33d8b519b713d682ca63dffc8a2191bc
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.aware;
18
19import static org.hamcrest.core.IsEqual.equalTo;
20import static org.hamcrest.core.IsNull.notNullValue;
21import static org.hamcrest.core.IsNull.nullValue;
22import static org.junit.Assert.assertEquals;
23import static org.junit.Assert.assertTrue;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.ArgumentMatchers.anyBoolean;
26import static org.mockito.ArgumentMatchers.anyInt;
27import static org.mockito.ArgumentMatchers.anyShort;
28import static org.mockito.ArgumentMatchers.eq;
29import static org.mockito.ArgumentMatchers.isNull;
30import static org.mockito.Mockito.inOrder;
31import static org.mockito.Mockito.mock;
32import static org.mockito.Mockito.times;
33import static org.mockito.Mockito.verify;
34import static org.mockito.Mockito.verifyNoMoreInteractions;
35import static org.mockito.Mockito.when;
36
37import android.Manifest;
38import android.app.AppOpsManager;
39import android.app.test.MockAnswerUtil;
40import android.app.test.TestAlarmManager;
41import android.content.Context;
42import android.content.Intent;
43import android.content.pm.PackageManager;
44import android.hardware.wifi.V1_0.NanStatusType;
45import android.net.ConnectivityManager;
46import android.net.wifi.RttManager;
47import android.net.wifi.aware.ConfigRequest;
48import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
49import android.net.wifi.aware.IWifiAwareEventCallback;
50import android.net.wifi.aware.PublishConfig;
51import android.net.wifi.aware.SubscribeConfig;
52import android.net.wifi.aware.WifiAwareManager;
53import android.os.Message;
54import android.os.UserHandle;
55import android.os.test.TestLooper;
56import android.test.suitebuilder.annotation.SmallTest;
57import android.util.Log;
58import android.util.SparseArray;
59
60import libcore.util.HexEncoding;
61
62import org.junit.Before;
63import org.junit.Rule;
64import org.junit.Test;
65import org.junit.rules.ErrorCollector;
66import org.mockito.ArgumentCaptor;
67import org.mockito.InOrder;
68import org.mockito.Mock;
69import org.mockito.MockitoAnnotations;
70
71import java.io.PrintWriter;
72import java.io.StringWriter;
73import java.lang.reflect.Field;
74import java.util.HashMap;
75import java.util.HashSet;
76import java.util.LinkedList;
77import java.util.Map;
78import java.util.Random;
79import java.util.Set;
80
81/**
82 * Unit test harness for WifiAwareStateManager.
83 */
84@SmallTest
85public class WifiAwareStateManagerTest {
86    private TestLooper mMockLooper;
87    private Random mRandomNg = new Random(15687);
88    private WifiAwareStateManager mDut;
89    @Mock private WifiAwareNativeApi mMockNative;
90    @Mock private Context mMockContext;
91    @Mock private AppOpsManager mMockAppOpsManager;
92    @Mock private WifiAwareRttStateManager mMockAwareRttStateManager;
93    TestAlarmManager mAlarmManager;
94    @Mock private WifiAwareDataPathStateManager mMockAwareDataPathStatemanager;
95
96    @Rule
97    public ErrorCollector collector = new ErrorCollector();
98
99    private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
100
101    /**
102     * Pre-test configuration. Initialize and install mocks.
103     */
104    @Before
105    public void setUp() throws Exception {
106        MockitoAnnotations.initMocks(this);
107
108        mAlarmManager = new TestAlarmManager();
109        when(mMockContext.getSystemService(Context.ALARM_SERVICE))
110                .thenReturn(mAlarmManager.getAlarmManager());
111
112        when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
113                mock(ConnectivityManager.class));
114        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
115        when(mMockContext.checkPermission(eq(android.Manifest.permission.ACCESS_FINE_LOCATION),
116                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
117        when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
118                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
119        when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(),
120                any())).thenReturn(AppOpsManager.MODE_ERRORED);
121        when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
122                any())).thenReturn(AppOpsManager.MODE_ERRORED);
123
124        mMockLooper = new TestLooper();
125
126        mDut = new WifiAwareStateManager();
127        mDut.setNative(mMockNative);
128        mDut.start(mMockContext, mMockLooper.getLooper());
129        installMocksInStateManager(mDut, mMockAwareRttStateManager, mMockAwareDataPathStatemanager);
130
131        when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
132                anyBoolean())).thenReturn(true);
133        when(mMockNative.disable(anyShort())).thenReturn(true);
134        when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(true);
135        when(mMockNative.subscribe(anyShort(), anyInt(), any()))
136                .thenReturn(true);
137        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
138                any(), anyInt())).thenReturn(true);
139        when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
140        when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
141        when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
142    }
143
144    /**
145     * Validate that Aware data-path interfaces are brought up and down correctly.
146     */
147    @Test
148    public void testAwareDataPathInterfaceUpDown() throws Exception {
149        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
150        InOrder inOrder = inOrder(mMockContext, mMockNative, mMockAwareDataPathStatemanager);
151
152        // (1) enable usage
153        mDut.enableUsage();
154        mMockLooper.dispatchAll();
155        validateCorrectAwareStatusChangeBroadcast(inOrder, true);
156        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
157        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
158        mMockLooper.dispatchAll();
159        inOrder.verify(mMockAwareDataPathStatemanager).createAllInterfaces();
160        collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
161
162        // (2) disable usage
163        mDut.disableUsage();
164        mMockLooper.dispatchAll();
165        inOrder.verify(mMockAwareDataPathStatemanager).onAwareDownCleanupDataPaths();
166        inOrder.verify(mMockNative).disable((short) 0);
167        validateCorrectAwareStatusChangeBroadcast(inOrder, false);
168        inOrder.verify(mMockAwareDataPathStatemanager).deleteAllInterfaces();
169        collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
170
171        verifyNoMoreInteractions(mMockNative, mMockAwareDataPathStatemanager);
172    }
173
174    /**
175     * Validate that APIs aren't functional when usage is disabled.
176     */
177    @Test
178    public void testDisableUsageDisablesApis() throws Exception {
179        final int clientId = 12314;
180        final int uid = 1000;
181        final int pid = 2000;
182        final String callingPackage = "com.google.somePackage";
183        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
184
185        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
186        InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
187
188        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
189
190        // (1) check initial state
191        mDut.enableUsage();
192        mMockLooper.dispatchAll();
193        validateCorrectAwareStatusChangeBroadcast(inOrder, true);
194        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
195        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
196        mMockLooper.dispatchAll();
197        collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
198
199        // (2) disable usage and validate state
200        mDut.disableUsage();
201        mMockLooper.dispatchAll();
202        collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
203        inOrder.verify(mMockNative).disable((short) 0);
204        validateCorrectAwareStatusChangeBroadcast(inOrder, false);
205
206        // (3) try connecting and validate that get nothing (app should be aware of non-availability
207        // through state change broadcast and/or query API)
208        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
209        mMockLooper.dispatchAll();
210
211        verifyNoMoreInteractions(mMockNative, mockCallback);
212    }
213
214    /**
215     * Validate that when API usage is disabled while in the middle of a connection that internal
216     * state is cleaned-up, and that all subsequent operations are NOP. Then enable usage again and
217     * validate that operates correctly.
218     */
219    @Test
220    public void testDisableUsageFlow() throws Exception {
221        final int clientId = 12341;
222        final int uid = 1000;
223        final int pid = 2000;
224        final String callingPackage = "com.google.somePackage";
225        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
226
227        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
228        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
229        InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
230
231        // (1) check initial state
232        mDut.enableUsage();
233        mMockLooper.dispatchAll();
234        validateCorrectAwareStatusChangeBroadcast(inOrder, true);
235        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
236        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
237        mMockLooper.dispatchAll();
238
239        collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
240
241        // (2) connect (successfully)
242        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
243        mMockLooper.dispatchAll();
244        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
245                eq(false), eq(true));
246        mDut.onConfigSuccessResponse(transactionId.getValue());
247        mMockLooper.dispatchAll();
248        inOrder.verify(mockCallback).onConnectSuccess(clientId);
249
250        // (3) disable usage & verify callbacks
251        mDut.disableUsage();
252        mMockLooper.dispatchAll();
253        collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
254        inOrder.verify(mMockNative).disable((short) 0);
255        validateCorrectAwareStatusChangeBroadcast(inOrder, false);
256        validateInternalClientInfoCleanedUp(clientId);
257
258        // (4) try connecting again and validate that just get an onAwareDown
259        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
260        mMockLooper.dispatchAll();
261
262        // (5) disable usage again and validate that not much happens
263        mDut.disableUsage();
264        mMockLooper.dispatchAll();
265        collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
266
267        // (6) enable usage
268        mDut.enableUsage();
269        mMockLooper.dispatchAll();
270        collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
271        validateCorrectAwareStatusChangeBroadcast(inOrder, true);
272
273        // (7) connect (should be successful)
274        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
275        mMockLooper.dispatchAll();
276        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
277                eq(false), eq(true));
278        mDut.onConfigSuccessResponse(transactionId.getValue());
279        mMockLooper.dispatchAll();
280        inOrder.verify(mockCallback).onConnectSuccess(clientId);
281
282        verifyNoMoreInteractions(mMockNative, mockCallback);
283    }
284
285    /**
286     * Validates that a HAL failure on enable and configure results in failed callback.
287     */
288    @Test
289    public void testHalFailureEnableAndConfigure() throws Exception {
290        final int clientId = 12341;
291        final int uid = 1000;
292        final int pid = 2000;
293        final String callingPackage = "com.google.somePackage";
294        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
295
296        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
297        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
298        InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
299
300        when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
301                anyBoolean())).thenReturn(false);
302
303        // (1) check initial state
304        mDut.enableUsage();
305        mMockLooper.dispatchAll();
306        validateCorrectAwareStatusChangeBroadcast(inOrder, true);
307        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
308        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
309        mMockLooper.dispatchAll();
310
311        // (2) connect with HAL failure
312        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
313        mMockLooper.dispatchAll();
314        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
315                eq(false), eq(true));
316        inOrder.verify(mockCallback).onConnectFail(NanStatusType.INTERNAL_FAILURE);
317
318        validateInternalClientInfoCleanedUp(clientId);
319        verifyNoMoreInteractions(mMockNative, mockCallback);
320    }
321
322    /**
323     * Validates that all events are delivered with correct arguments. Validates
324     * that IdentityChanged not delivered if configuration disables delivery.
325     */
326    @Test
327    public void testAwareEventsDelivery() throws Exception {
328        final int clientId1 = 1005;
329        final int clientId2 = 1007;
330        final int clusterLow = 5;
331        final int clusterHigh = 100;
332        final int masterPref = 111;
333        final int uid = 1000;
334        final int pid = 2000;
335        final String callingPackage = "com.google.somePackage";
336        final int reason = NanStatusType.INTERNAL_FAILURE;
337        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
338        final byte[] someMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
339
340        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
341                .setClusterHigh(clusterHigh).setMasterPreference(masterPref)
342                .build();
343
344        IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
345        IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
346        ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class);
347        InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative);
348
349        mDut.enableUsage();
350        mMockLooper.dispatchAll();
351        inOrder.verify(mMockNative).getCapabilities(transactionIdCapture.capture());
352        mDut.onCapabilitiesUpdateResponse(transactionIdCapture.getValue(), getCapabilities());
353        mMockLooper.dispatchAll();
354
355        // (1) connect 1st and 2nd clients
356        mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest, false);
357        mMockLooper.dispatchAll();
358        inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
359                eq(configRequest), eq(false), eq(true));
360        short transactionId = transactionIdCapture.getValue();
361        mDut.onConfigSuccessResponse(transactionId);
362        mMockLooper.dispatchAll();
363        inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
364
365        mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest, true);
366        mMockLooper.dispatchAll();
367        inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
368                eq(configRequest), eq(true), eq(false));
369        transactionId = transactionIdCapture.getValue();
370        mDut.onConfigSuccessResponse(transactionId);
371        mMockLooper.dispatchAll();
372        inOrder.verify(mockCallback2).onConnectSuccess(clientId2);
373
374        // (2) deliver Aware events - without LOCATIONING permission
375        mDut.onClusterChangeNotification(WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED,
376                someMac);
377        mDut.onInterfaceAddressChangeNotification(someMac);
378        mMockLooper.dispatchAll();
379
380        inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
381
382        // (3) deliver new identity - still without LOCATIONING permission (should get an event)
383        mDut.onInterfaceAddressChangeNotification(someMac2);
384        mMockLooper.dispatchAll();
385
386        inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
387
388        // (4) deliver same identity - still without LOCATIONING permission (should
389        // not get an event)
390        mDut.onInterfaceAddressChangeNotification(someMac2);
391        mMockLooper.dispatchAll();
392
393        // (5) deliver new identity - with LOCATIONING permission
394        when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
395                anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
396        when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
397                any())).thenReturn(AppOpsManager.MODE_ALLOWED);
398        mDut.onInterfaceAddressChangeNotification(someMac);
399        mMockLooper.dispatchAll();
400
401        inOrder.verify(mockCallback2).onIdentityChanged(someMac);
402
403        // (6) Aware down (no feedback)
404        mDut.onAwareDownNotification(reason);
405        mMockLooper.dispatchAll();
406
407        validateInternalClientInfoCleanedUp(clientId1);
408        validateInternalClientInfoCleanedUp(clientId2);
409
410        verifyNoMoreInteractions(mockCallback1, mockCallback2, mMockNative);
411    }
412
413    /**
414     * Validate that when the HAL doesn't respond we get a TIMEOUT (which
415     * results in a failure response) at which point we can process additional
416     * commands. Steps: (1) connect, (2) publish - timeout, (3) publish +
417     * success.
418     */
419    @Test
420    public void testHalNoResponseTimeout() throws Exception {
421        final int clientId = 12341;
422        final int uid = 1000;
423        final int pid = 2000;
424        final String callingPackage = "com.google.somePackage";
425        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
426        final PublishConfig publishConfig = new PublishConfig.Builder().build();
427
428        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
429        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
430                IWifiAwareDiscoverySessionCallback.class);
431        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
432        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
433
434        mDut.enableUsage();
435        mMockLooper.dispatchAll();
436        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
437        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
438        mMockLooper.dispatchAll();
439
440        // (1) connect (successfully)
441        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
442        mMockLooper.dispatchAll();
443        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
444                eq(false), eq(true));
445        mDut.onConfigSuccessResponse(transactionId.getValue());
446        mMockLooper.dispatchAll();
447        inOrder.verify(mockCallback).onConnectSuccess(clientId);
448
449        // (2) publish + timeout
450        mDut.publish(clientId, publishConfig, mockSessionCallback);
451        mMockLooper.dispatchAll();
452        inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig));
453        assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_COMMAND_TIMEOUT_TAG));
454        mMockLooper.dispatchAll();
455        inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
456        validateInternalNoSessions(clientId);
457
458        // (3) publish + success
459        mDut.publish(clientId, publishConfig, mockSessionCallback);
460        mMockLooper.dispatchAll();
461        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
462        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, 9999);
463        mMockLooper.dispatchAll();
464        inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
465
466        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
467    }
468
469    /**
470     * Validates publish flow: (1) initial publish (2) fail informed by notification, (3) fail due
471     * to immediate HAL failure. Expected: get a failure callback.
472     */
473    @Test
474    public void testPublishFail() throws Exception {
475        final int clientId = 1005;
476        final int uid = 1000;
477        final int pid = 2000;
478        final String callingPackage = "com.google.somePackage";
479        final int reasonFail = NanStatusType.INTERNAL_FAILURE;
480
481        ConfigRequest configRequest = new ConfigRequest.Builder().build();
482        PublishConfig publishConfig = new PublishConfig.Builder().build();
483
484        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
485        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
486                IWifiAwareDiscoverySessionCallback.class);
487        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
488        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
489
490        mDut.enableUsage();
491        mMockLooper.dispatchAll();
492        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
493        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
494        mMockLooper.dispatchAll();
495
496        // (0) connect
497        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
498        mMockLooper.dispatchAll();
499        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
500                eq(configRequest), eq(false), eq(true));
501        mDut.onConfigSuccessResponse(transactionId.getValue());
502        mMockLooper.dispatchAll();
503        inOrder.verify(mockCallback).onConnectSuccess(clientId);
504
505        // (1) initial publish
506        mDut.publish(clientId, publishConfig, mockSessionCallback);
507        mMockLooper.dispatchAll();
508        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
509
510        // (2) publish failure callback (i.e. firmware tried and failed)
511        mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
512        mMockLooper.dispatchAll();
513        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
514        validateInternalNoSessions(clientId);
515
516        // (3) publish and get immediate failure (i.e. HAL failed)
517        when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
518
519        mDut.publish(clientId, publishConfig, mockSessionCallback);
520        mMockLooper.dispatchAll();
521        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
522
523        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
524        validateInternalNoSessions(clientId);
525
526        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
527    }
528
529    /**
530     * Validates the publish flow: (1) initial publish (2) success (3)
531     * termination (e.g. DONE) (4) update session attempt (5) terminateSession
532     * (6) update session attempt. Expected: session ID callback + session
533     * cleaned-up.
534     */
535    @Test
536    public void testPublishSuccessTerminated() throws Exception {
537        final int clientId = 2005;
538        final int uid = 1000;
539        final int pid = 2000;
540        final String callingPackage = "com.google.somePackage";
541        final int reasonTerminate = NanStatusType.SUCCESS;
542        final int publishId = 15;
543
544        ConfigRequest configRequest = new ConfigRequest.Builder().build();
545        PublishConfig publishConfig = new PublishConfig.Builder().build();
546
547        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
548        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
549                IWifiAwareDiscoverySessionCallback.class);
550        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
551        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
552        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
553
554        mDut.enableUsage();
555        mMockLooper.dispatchAll();
556        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
557        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
558        mMockLooper.dispatchAll();
559
560        // (0) connect
561        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
562        mMockLooper.dispatchAll();
563        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
564                eq(configRequest), eq(false), eq(true));
565        mDut.onConfigSuccessResponse(transactionId.getValue());
566        mMockLooper.dispatchAll();
567        inOrder.verify(mockCallback).onConnectSuccess(clientId);
568
569        // (1) initial publish
570        mDut.publish(clientId, publishConfig, mockSessionCallback);
571        mMockLooper.dispatchAll();
572        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
573
574        // (2) publish success
575        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
576        mMockLooper.dispatchAll();
577        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
578
579        // (3) publish termination (from firmware - not app!)
580        mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true);
581        mMockLooper.dispatchAll();
582        inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
583
584        // (4) app update session (race condition: app didn't get termination
585        // yet)
586        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
587        mMockLooper.dispatchAll();
588
589        // (5) app terminates session
590        mDut.terminateSession(clientId, sessionId.getValue());
591        mMockLooper.dispatchAll();
592
593        // (6) app updates session (app already knows that terminated - will get
594        // a local FAIL).
595        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
596        mMockLooper.dispatchAll();
597
598        validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
599
600        verifyNoMoreInteractions(mockSessionCallback, mMockNative);
601    }
602
603    /**
604     * Validate the publish flow: (1) initial publish + (2) success + (3) update + (4) update
605     * fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
606     * update failure so second update succeeds (no callbacks) + (7) update + immediate failure from
607     * HAL.
608     */
609    @Test
610    public void testPublishUpdateFail() throws Exception {
611        final int clientId = 2005;
612        final int uid = 1000;
613        final int pid = 2000;
614        final String callingPackage = "com.google.somePackage";
615        final int publishId = 15;
616        final int reasonFail = NanStatusType.INTERNAL_FAILURE;
617
618        ConfigRequest configRequest = new ConfigRequest.Builder().build();
619        PublishConfig publishConfig = new PublishConfig.Builder().build();
620
621        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
622        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
623                IWifiAwareDiscoverySessionCallback.class);
624        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
625        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
626        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
627
628        mDut.enableUsage();
629        mMockLooper.dispatchAll();
630        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
631        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
632        mMockLooper.dispatchAll();
633
634        // (0) connect
635        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
636        mMockLooper.dispatchAll();
637        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
638                eq(false), eq(true));
639        mDut.onConfigSuccessResponse(transactionId.getValue());
640        mMockLooper.dispatchAll();
641        inOrder.verify(mockCallback).onConnectSuccess(clientId);
642
643        // (1) initial publish
644        mDut.publish(clientId, publishConfig, mockSessionCallback);
645        mMockLooper.dispatchAll();
646        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
647
648        // (2) publish success
649        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
650        mMockLooper.dispatchAll();
651        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
652
653        // (3) update publish
654        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
655        mMockLooper.dispatchAll();
656        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
657                eq(publishConfig));
658
659        // (4) update fails
660        mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
661        mMockLooper.dispatchAll();
662        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
663
664        // (5) another update publish
665        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
666        mMockLooper.dispatchAll();
667        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
668                eq(publishConfig));
669
670        // (6) update succeeds
671        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
672        mMockLooper.dispatchAll();
673        inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
674
675        // (7) another update + immediate failure
676        when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
677
678        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
679        mMockLooper.dispatchAll();
680        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
681                eq(publishConfig));
682        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
683
684        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
685    }
686
687    /**
688     * Validate race condition: publish pending but session terminated (due to
689     * disconnect - can't terminate such a session directly from app). Need to
690     * make sure that once publish succeeds (failure isn't a problem) the
691     * session is immediately terminated since no-one is listening for it.
692     */
693    @Test
694    public void testDisconnectWhilePublishPending() throws Exception {
695        final int clientId = 2005;
696        final int uid = 1000;
697        final int pid = 2000;
698        final String callingPackage = "com.google.somePackage";
699        final int publishId = 15;
700
701        ConfigRequest configRequest = new ConfigRequest.Builder().build();
702        PublishConfig publishConfig = new PublishConfig.Builder().build();
703
704        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
705        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
706                IWifiAwareDiscoverySessionCallback.class);
707        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
708        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
709
710        mDut.enableUsage();
711        mMockLooper.dispatchAll();
712        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
713        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
714        mMockLooper.dispatchAll();
715
716        // (0) connect
717        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
718        mMockLooper.dispatchAll();
719        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
720                eq(false), eq(true));
721        mDut.onConfigSuccessResponse(transactionId.getValue());
722        mMockLooper.dispatchAll();
723        inOrder.verify(mockCallback).onConnectSuccess(clientId);
724
725        // (1) initial publish
726        mDut.publish(clientId, publishConfig, mockSessionCallback);
727        mMockLooper.dispatchAll();
728        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
729
730        // (2) disconnect (but doesn't get executed until get response for
731        // publish command)
732        mDut.disconnect(clientId);
733        mMockLooper.dispatchAll();
734
735        // (3) publish success
736        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
737        mMockLooper.dispatchAll();
738        inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
739        inOrder.verify(mMockNative).stopPublish(transactionId.capture(), eq(publishId));
740        inOrder.verify(mMockNative).disable((short) 0);
741
742        validateInternalClientInfoCleanedUp(clientId);
743
744        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
745    }
746
747    /**
748     * Validates subscribe flow: (1) initial subscribe (2) fail (callback from firmware), (3) fail
749     * due to immeidate HAL failure. Expected: get a failure callback.
750     */
751    @Test
752    public void testSubscribeFail() throws Exception {
753        final int clientId = 1005;
754        final int uid = 1000;
755        final int pid = 2000;
756        final String callingPackage = "com.google.somePackage";
757        final int reasonFail = NanStatusType.INTERNAL_FAILURE;
758
759        ConfigRequest configRequest = new ConfigRequest.Builder().build();
760        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
761
762        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
763        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
764                IWifiAwareDiscoverySessionCallback.class);
765        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
766        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
767
768        mDut.enableUsage();
769        mMockLooper.dispatchAll();
770        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
771        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
772        mMockLooper.dispatchAll();
773
774        // (0) connect
775        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
776        mMockLooper.dispatchAll();
777        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
778                eq(false), eq(true));
779        mDut.onConfigSuccessResponse(transactionId.getValue());
780        mMockLooper.dispatchAll();
781        inOrder.verify(mockCallback).onConnectSuccess(clientId);
782
783        // (1) initial subscribe
784        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
785        mMockLooper.dispatchAll();
786        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
787
788        // (2) subscribe failure
789        mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
790        mMockLooper.dispatchAll();
791        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
792        validateInternalNoSessions(clientId);
793
794        // (3) subscribe and get immediate failure (i.e. HAL failed)
795        when(mMockNative.subscribe(anyShort(), anyInt(), any()))
796                .thenReturn(false);
797
798        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
799        mMockLooper.dispatchAll();
800        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
801
802        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
803        validateInternalNoSessions(clientId);
804
805        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
806    }
807
808    /**
809     * Validates the subscribe flow: (1) initial subscribe (2) success (3)
810     * termination (e.g. DONE) (4) update session attempt (5) terminateSession
811     * (6) update session attempt. Expected: session ID callback + session
812     * cleaned-up
813     */
814    @Test
815    public void testSubscribeSuccessTerminated() throws Exception {
816        final int clientId = 2005;
817        final int uid = 1000;
818        final int pid = 2000;
819        final String callingPackage = "com.google.somePackage";
820        final int reasonTerminate = NanStatusType.SUCCESS;
821        final int subscribeId = 15;
822
823        ConfigRequest configRequest = new ConfigRequest.Builder().build();
824        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
825
826        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
827        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
828                IWifiAwareDiscoverySessionCallback.class);
829        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
830        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
831        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
832
833        mDut.enableUsage();
834        mMockLooper.dispatchAll();
835        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
836        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
837        mMockLooper.dispatchAll();
838
839        // (0) connect
840        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
841        mMockLooper.dispatchAll();
842        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
843                eq(false), eq(true));
844        mDut.onConfigSuccessResponse(transactionId.getValue());
845        mMockLooper.dispatchAll();
846        inOrder.verify(mockCallback).onConnectSuccess(clientId);
847
848        // (1) initial subscribe
849        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
850        mMockLooper.dispatchAll();
851        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
852
853        // (2) subscribe success
854        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
855        mMockLooper.dispatchAll();
856        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
857
858        // (3) subscribe termination (from firmware - not app!)
859        mDut.onSessionTerminatedNotification(subscribeId, reasonTerminate, false);
860        mMockLooper.dispatchAll();
861        inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
862
863        // (4) app update session (race condition: app didn't get termination
864        // yet)
865        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
866        mMockLooper.dispatchAll();
867
868        // (5) app terminates session
869        mDut.terminateSession(clientId, sessionId.getValue());
870        mMockLooper.dispatchAll();
871
872        // (6) app updates session
873        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
874        mMockLooper.dispatchAll();
875
876        validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
877
878        verifyNoMoreInteractions(mockSessionCallback, mMockNative);
879    }
880
881    /**
882     * Validate the subscribe flow: (1) initial subscribe + (2) success + (3) update + (4) update
883     * fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
884     * update failure so second update succeeds (no callbacks). + (7) update + immediate failure
885     * from HAL.
886     */
887    @Test
888    public void testSubscribeUpdateFail() throws Exception {
889        final int clientId = 2005;
890        final int uid = 1000;
891        final int pid = 2000;
892        final String callingPackage = "com.google.somePackage";
893        final int subscribeId = 15;
894        final int reasonFail = NanStatusType.INTERNAL_FAILURE;
895
896        ConfigRequest configRequest = new ConfigRequest.Builder().build();
897        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
898
899        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
900        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
901                IWifiAwareDiscoverySessionCallback.class);
902        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
903        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
904        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
905
906        mDut.enableUsage();
907        mMockLooper.dispatchAll();
908        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
909        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
910        mMockLooper.dispatchAll();
911
912        // (0) connect
913        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
914        mMockLooper.dispatchAll();
915        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
916                eq(false), eq(true));
917        mDut.onConfigSuccessResponse(transactionId.getValue());
918        mMockLooper.dispatchAll();
919        inOrder.verify(mockCallback).onConnectSuccess(clientId);
920
921        // (1) initial subscribe
922        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
923        mMockLooper.dispatchAll();
924        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
925
926        // (2) subscribe success
927        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
928        mMockLooper.dispatchAll();
929        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
930
931        // (3) update subscribe
932        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
933        mMockLooper.dispatchAll();
934        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
935                eq(subscribeConfig));
936
937        // (4) update fails
938        mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
939        mMockLooper.dispatchAll();
940        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
941
942        // (5) another update subscribe
943        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
944        mMockLooper.dispatchAll();
945        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
946                eq(subscribeConfig));
947
948        // (6) update succeeds
949        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
950        mMockLooper.dispatchAll();
951        inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
952
953        // (7) another update + immediate failure
954        when(mMockNative.subscribe(anyShort(), anyInt(), any()))
955                .thenReturn(false);
956
957        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
958        mMockLooper.dispatchAll();
959        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
960                eq(subscribeConfig));
961        inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
962
963        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
964    }
965
966    /**
967     * Validate race condition: subscribe pending but session terminated (due to
968     * disconnect - can't terminate such a session directly from app). Need to
969     * make sure that once subscribe succeeds (failure isn't a problem) the
970     * session is immediately terminated since no-one is listening for it.
971     */
972    @Test
973    public void testDisconnectWhileSubscribePending() throws Exception {
974        final int clientId = 2005;
975        final int uid = 1000;
976        final int pid = 2000;
977        final String callingPackage = "com.google.somePackage";
978        final int subscribeId = 15;
979
980        ConfigRequest configRequest = new ConfigRequest.Builder().build();
981        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
982
983        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
984        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
985                IWifiAwareDiscoverySessionCallback.class);
986        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
987        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
988
989        mDut.enableUsage();
990        mMockLooper.dispatchAll();
991        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
992        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
993        mMockLooper.dispatchAll();
994
995        // (0) connect
996        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
997        mMockLooper.dispatchAll();
998        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
999                eq(false), eq(true));
1000        mDut.onConfigSuccessResponse(transactionId.getValue());
1001        mMockLooper.dispatchAll();
1002        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1003
1004        // (1) initial subscribe
1005        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1006        mMockLooper.dispatchAll();
1007        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1008
1009        // (2) disconnect (but doesn't get executed until get response for
1010        // subscribe command)
1011        mDut.disconnect(clientId);
1012        mMockLooper.dispatchAll();
1013
1014        // (3) subscribe success
1015        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1016        mMockLooper.dispatchAll();
1017        inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
1018        inOrder.verify(mMockNative).stopSubscribe((short) 0, subscribeId);
1019        inOrder.verify(mMockNative).disable((short) 0);
1020
1021        validateInternalClientInfoCleanedUp(clientId);
1022
1023        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1024    }
1025
1026    /**
1027     * Validate (1) subscribe (success), (2) match (i.e. discovery), (3) message reception,
1028     * (4) message transmission failed (after ok queuing), (5) message transmission success.
1029     */
1030    @Test
1031    public void testMatchAndMessages() throws Exception {
1032        final int clientId = 1005;
1033        final int uid = 1000;
1034        final int pid = 2000;
1035        final String callingPackage = "com.google.somePackage";
1036        final String serviceName = "some-service-name";
1037        final String ssi = "some much longer and more arbitrary data";
1038        final int subscribeCount = 7;
1039        final int reasonFail = NanStatusType.INTERNAL_FAILURE;
1040        final int subscribeId = 15;
1041        final int requestorId = 22;
1042        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1043        final String peerSsi = "some peer ssi data";
1044        final String peerMatchFilter = "filter binary array represented as string";
1045        final String peerMsg = "some message from peer";
1046        final int messageId = 6948;
1047        final int messageId2 = 6949;
1048
1049        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1050        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1051                .setServiceSpecificInfo(ssi.getBytes())
1052                .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
1053                .setSubscribeCount(subscribeCount).build();
1054
1055        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1056        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1057                IWifiAwareDiscoverySessionCallback.class);
1058        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1059        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1060        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1061
1062        mDut.enableUsage();
1063        mMockLooper.dispatchAll();
1064        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1065        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1066        mMockLooper.dispatchAll();
1067
1068        // (0) connect
1069        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1070        mMockLooper.dispatchAll();
1071        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1072                eq(configRequest), eq(false), eq(true));
1073        mDut.onConfigSuccessResponse(transactionId.getValue());
1074        mMockLooper.dispatchAll();
1075        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1076
1077        // (1) subscribe
1078        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1079        mMockLooper.dispatchAll();
1080        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1081        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1082        mMockLooper.dispatchAll();
1083        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1084
1085        // (2) match
1086        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1087                peerMatchFilter.getBytes());
1088        mMockLooper.dispatchAll();
1089        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1090                peerMatchFilter.getBytes());
1091
1092        // (3) message Rx
1093        mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
1094        mMockLooper.dispatchAll();
1095        inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes());
1096
1097        // (4) message Tx successful queuing
1098        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId, 0);
1099        mMockLooper.dispatchAll();
1100        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1101                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1102        short tid1 = transactionId.getValue();
1103        mDut.onMessageSendQueuedSuccessResponse(tid1);
1104        mMockLooper.dispatchAll();
1105
1106        // (5) message Tx successful queuing
1107        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId2,
1108                0);
1109        mMockLooper.dispatchAll();
1110        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1111                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2));
1112        short tid2 = transactionId.getValue();
1113        mDut.onMessageSendQueuedSuccessResponse(tid2);
1114        mMockLooper.dispatchAll();
1115
1116        // (4) and (5) final Tx results (on-air results)
1117        mDut.onMessageSendFailNotification(tid1, reasonFail);
1118        mDut.onMessageSendSuccessNotification(tid2);
1119        mMockLooper.dispatchAll();
1120        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, reasonFail);
1121        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId2);
1122        validateInternalSendMessageQueuesCleanedUp(messageId);
1123        validateInternalSendMessageQueuesCleanedUp(messageId2);
1124
1125        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1126    }
1127
1128    /**
1129     * Summary: in a single publish session interact with multiple peers
1130     * (different MAC addresses).
1131     */
1132    @Test
1133    public void testMultipleMessageSources() throws Exception {
1134        final int clientId = 300;
1135        final int uid = 1000;
1136        final int pid = 2000;
1137        final String callingPackage = "com.google.somePackage";
1138        final int clusterLow = 7;
1139        final int clusterHigh = 7;
1140        final int masterPref = 0;
1141        final String serviceName = "some-service-name";
1142        final int publishId = 88;
1143        final int peerId1 = 568;
1144        final int peerId2 = 873;
1145        final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
1146        final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
1147        final String msgFromPeer1 = "hey from 000102...";
1148        final String msgFromPeer2 = "hey from 0607...";
1149        final String msgToPeer1 = "hey there 000102...";
1150        final String msgToPeer2 = "hey there 0506...";
1151        final int msgToPeerId1 = 546;
1152        final int msgToPeerId2 = 9654;
1153        final int reason = NanStatusType.INTERNAL_FAILURE;
1154
1155        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
1156                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
1157
1158        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
1159                .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
1160
1161        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1162        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1163        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1164        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1165                IWifiAwareDiscoverySessionCallback.class);
1166        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
1167
1168        mDut.enableUsage();
1169        mMockLooper.dispatchAll();
1170        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1171        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1172        mMockLooper.dispatchAll();
1173
1174        // (1) connect
1175        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1176        mMockLooper.dispatchAll();
1177        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1178                eq(false), eq(true));
1179        mDut.onConfigSuccessResponse(transactionId.getValue());
1180        mMockLooper.dispatchAll();
1181        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1182
1183        // (2) publish
1184        mDut.publish(clientId, publishConfig, mockSessionCallback);
1185        mMockLooper.dispatchAll();
1186        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
1187        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
1188        mMockLooper.dispatchAll();
1189        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1190
1191        // (3) message received from peers 1 & 2
1192        mDut.onMessageReceivedNotification(publishId, peerId1, peerMac1, msgFromPeer1.getBytes());
1193        mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes());
1194        mMockLooper.dispatchAll();
1195        inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes());
1196        inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes());
1197
1198        // (4) sending messages back to same peers: one Tx fails, other succeeds
1199        mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
1200                msgToPeerId2, 0);
1201        mMockLooper.dispatchAll();
1202        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
1203                eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
1204        short transactionIdVal = transactionId.getValue();
1205        mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
1206        mDut.onMessageSendSuccessNotification(transactionIdVal);
1207
1208        mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
1209                msgToPeerId1, 0);
1210        mMockLooper.dispatchAll();
1211        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
1212        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
1213                eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
1214        transactionIdVal = transactionId.getValue();
1215        mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
1216        mDut.onMessageSendFailNotification(transactionIdVal, reason);
1217        mMockLooper.dispatchAll();
1218        inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason);
1219        validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
1220        validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
1221
1222        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
1223    }
1224
1225    /**
1226     * Summary: interact with a peer which changed its identity (MAC address)
1227     * but which keeps its requestor instance ID. Should be transparent.
1228     */
1229    @Test
1230    public void testMessageWhilePeerChangesIdentity() throws Exception {
1231        final int clientId = 300;
1232        final int uid = 1000;
1233        final int pid = 2000;
1234        final String callingPackage = "com.google.somePackage";
1235        final int clusterLow = 7;
1236        final int clusterHigh = 7;
1237        final int masterPref = 0;
1238        final String serviceName = "some-service-name";
1239        final int publishId = 88;
1240        final int peerId = 568;
1241        final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
1242        final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
1243        final String msgFromPeer1 = "hey from 000102...";
1244        final String msgFromPeer2 = "hey from 0607...";
1245        final String msgToPeer1 = "hey there 000102...";
1246        final String msgToPeer2 = "hey there 0506...";
1247        final int msgToPeerId1 = 546;
1248        final int msgToPeerId2 = 9654;
1249        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
1250                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
1251
1252        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
1253                .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
1254
1255        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1256        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1257        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1258        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1259                IWifiAwareDiscoverySessionCallback.class);
1260        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
1261
1262        mDut.enableUsage();
1263        mMockLooper.dispatchAll();
1264        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1265        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1266        mMockLooper.dispatchAll();
1267
1268        // (1) connect
1269        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1270        mMockLooper.dispatchAll();
1271        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1272                eq(false), eq(true));
1273        mDut.onConfigSuccessResponse(transactionId.getValue());
1274        mMockLooper.dispatchAll();
1275        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1276
1277        // (2) publish
1278        mDut.publish(clientId, publishConfig, mockSessionCallback);
1279        mMockLooper.dispatchAll();
1280        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
1281        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
1282        mMockLooper.dispatchAll();
1283        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1284
1285        // (3) message received & responded to
1286        mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes());
1287        mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
1288                msgToPeerId1, 0);
1289        mMockLooper.dispatchAll();
1290        inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes());
1291        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
1292                eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
1293        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1294        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1295        mMockLooper.dispatchAll();
1296        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1);
1297        validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
1298
1299        // (4) message received with same peer ID but different MAC
1300        mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater,
1301                msgFromPeer2.getBytes());
1302        mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
1303                msgToPeerId2, 0);
1304        mMockLooper.dispatchAll();
1305        inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes());
1306        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
1307                eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
1308        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1309        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1310        mMockLooper.dispatchAll();
1311        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
1312        validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
1313
1314        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
1315    }
1316
1317    /**
1318     * Validate that get failure (with correct code) when trying to send a
1319     * message to an invalid peer ID.
1320     */
1321    @Test
1322    public void testSendMessageToInvalidPeerId() throws Exception {
1323        final int clientId = 1005;
1324        final int uid = 1000;
1325        final int pid = 2000;
1326        final String callingPackage = "com.google.somePackage";
1327        final String ssi = "some much longer and more arbitrary data";
1328        final int subscribeId = 15;
1329        final int requestorId = 22;
1330        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1331        final String peerSsi = "some peer ssi data";
1332        final String peerMatchFilter = "filter binary array represented as string";
1333        final int messageId = 6948;
1334
1335        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1336        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1337
1338        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1339        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1340                IWifiAwareDiscoverySessionCallback.class);
1341        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1342        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1343        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1344
1345        mDut.enableUsage();
1346        mMockLooper.dispatchAll();
1347        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1348        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1349        mMockLooper.dispatchAll();
1350
1351        // (1) connect
1352        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1353        mMockLooper.dispatchAll();
1354        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1355                eq(false), eq(true));
1356        mDut.onConfigSuccessResponse(transactionId.getValue());
1357        mMockLooper.dispatchAll();
1358        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1359
1360        // (2) subscribe & match
1361        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1362        mMockLooper.dispatchAll();
1363        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1364        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1365        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1366                peerMatchFilter.getBytes());
1367        mMockLooper.dispatchAll();
1368        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1369        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1370                peerMatchFilter.getBytes());
1371
1372        // (3) send message to invalid peer ID
1373        mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
1374                messageId, 0);
1375        mMockLooper.dispatchAll();
1376        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1377                NanStatusType.INTERNAL_FAILURE);
1378        validateInternalSendMessageQueuesCleanedUp(messageId);
1379
1380        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1381    }
1382
1383    /**
1384     * Validate that on send message errors are handled correctly: immediate send error, queue fail
1385     * error (not queue full), and timeout. Behavior: correct callback is dispatched and a later
1386     * firmware notification is ignored. Intersperse with one successfull transmission.
1387     */
1388    @Test
1389    public void testSendMessageErrorsImmediateQueueTimeout() throws Exception {
1390        final int clientId = 1005;
1391        final int uid = 1000;
1392        final int pid = 2000;
1393        final String callingPackage = "com.google.somePackage";
1394        final String ssi = "some much longer and more arbitrary data";
1395        final int subscribeId = 15;
1396        final int requestorId = 22;
1397        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1398        final String peerSsi = "some peer ssi data";
1399        final String peerMatchFilter = "filter binary array represented as string";
1400        final int messageId = 6948;
1401
1402        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1403        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1404
1405        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1406        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1407                IWifiAwareDiscoverySessionCallback.class);
1408        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1409        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1410        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1411
1412        mDut.enableUsage();
1413        mMockLooper.dispatchAll();
1414        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1415        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1416        mMockLooper.dispatchAll();
1417
1418        // (1) connect
1419        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1420        mMockLooper.dispatchAll();
1421        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1422                eq(false), eq(true));
1423        mDut.onConfigSuccessResponse(transactionId.getValue());
1424        mMockLooper.dispatchAll();
1425        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1426
1427        // (2) subscribe & match
1428        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1429        mMockLooper.dispatchAll();
1430        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1431        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1432        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1433                peerMatchFilter.getBytes());
1434        mMockLooper.dispatchAll();
1435        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1436        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1437                peerMatchFilter.getBytes());
1438
1439        // (3) send 2 messages and enqueue successfully
1440        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1441                messageId, 0);
1442        mMockLooper.dispatchAll();
1443        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1444                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1445        short transactionId1 = transactionId.getValue();
1446        mDut.onMessageSendQueuedSuccessResponse(transactionId1);
1447        mMockLooper.dispatchAll();
1448
1449        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1450                messageId + 1, 0);
1451        mMockLooper.dispatchAll();
1452        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1453                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 1));
1454        short transactionId2 = transactionId.getValue();
1455        mDut.onMessageSendQueuedSuccessResponse(transactionId2);
1456        mMockLooper.dispatchAll();
1457
1458        // (4) send a message and get a queueing failure (not queue full)
1459        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 2,
1460                0);
1461        mMockLooper.dispatchAll();
1462        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1463                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2));
1464        short transactionId3 = transactionId.getValue();
1465        mDut.onMessageSendQueuedFailResponse(transactionId3, NanStatusType.INTERNAL_FAILURE);
1466        mMockLooper.dispatchAll();
1467        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 2,
1468                NanStatusType.INTERNAL_FAILURE);
1469        validateInternalSendMessageQueuesCleanedUp(messageId + 2);
1470
1471        // (5) send a message and get an immediate failure (configure first)
1472        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1473                any(), anyInt())).thenReturn(false);
1474
1475        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 3,
1476                0);
1477        mMockLooper.dispatchAll();
1478        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1479                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3));
1480        short transactionId4 = transactionId.getValue();
1481        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 3,
1482                NanStatusType.INTERNAL_FAILURE);
1483        validateInternalSendMessageQueuesCleanedUp(messageId + 3);
1484
1485        // (6) message send timeout
1486        assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
1487        mMockLooper.dispatchAll();
1488        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1489                NanStatusType.INTERNAL_FAILURE);
1490        validateInternalSendMessageQueuesCleanedUp(messageId);
1491
1492        // (7) firmware response (unlikely - but good to check)
1493        mDut.onMessageSendSuccessNotification(transactionId1);
1494        mDut.onMessageSendSuccessNotification(transactionId2);
1495
1496        // bogus: these didn't even go to firmware or weren't queued
1497        mDut.onMessageSendSuccessNotification(transactionId3);
1498        mDut.onMessageSendFailNotification(transactionId4, NanStatusType.INTERNAL_FAILURE);
1499        mMockLooper.dispatchAll();
1500        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId + 1);
1501
1502        validateInternalSendMessageQueuesCleanedUp(messageId + 1);
1503
1504        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1505    }
1506
1507    /**
1508     * Validate that when sending a message with a retry count the message is retried the specified
1509     * number of times. Scenario ending with success.
1510     */
1511    @Test
1512    public void testSendMessageRetransmitSuccess() throws Exception {
1513        final int clientId = 1005;
1514        final int uid = 1000;
1515        final int pid = 2000;
1516        final String callingPackage = "com.google.somePackage";
1517        final String ssi = "some much longer and more arbitrary data";
1518        final int subscribeId = 15;
1519        final int requestorId = 22;
1520        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1521        final String peerSsi = "some peer ssi data";
1522        final String peerMatchFilter = "filter binary array represented as string";
1523        final int messageId = 6948;
1524        final int retryCount = 3;
1525
1526        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1527        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1528
1529        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1530        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1531                IWifiAwareDiscoverySessionCallback.class);
1532        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1533        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1534        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1535
1536        mDut.enableUsage();
1537        mMockLooper.dispatchAll();
1538        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1539        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1540        mMockLooper.dispatchAll();
1541
1542        // (1) connect
1543        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1544        mMockLooper.dispatchAll();
1545        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1546                eq(false), eq(true));
1547        mDut.onConfigSuccessResponse(transactionId.getValue());
1548        mMockLooper.dispatchAll();
1549        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1550
1551        // (2) subscribe & match
1552        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1553        mMockLooper.dispatchAll();
1554        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1555        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1556        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1557                peerMatchFilter.getBytes());
1558        mMockLooper.dispatchAll();
1559        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1560        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1561                peerMatchFilter.getBytes());
1562
1563        // (3) send message and enqueue successfully
1564        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1565                messageId, retryCount);
1566        mMockLooper.dispatchAll();
1567        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1568                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1569        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1570        mMockLooper.dispatchAll();
1571
1572        // (4) loop and fail until reach retryCount
1573        for (int i = 0; i < retryCount; ++i) {
1574            mDut.onMessageSendFailNotification(transactionId.getValue(), NanStatusType.NO_OTA_ACK);
1575            mMockLooper.dispatchAll();
1576            inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1577                    eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1578            mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1579            mMockLooper.dispatchAll();
1580        }
1581
1582        // (5) succeed on last retry
1583        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1584        mMockLooper.dispatchAll();
1585
1586        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1587        validateInternalSendMessageQueuesCleanedUp(messageId);
1588
1589        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1590    }
1591
1592    /**
1593     * Validate that when sending a message with a retry count the message is retried the specified
1594     * number of times. Scenario ending with failure.
1595     */
1596    @Test
1597    public void testSendMessageRetransmitFail() throws Exception {
1598        final int clientId = 1005;
1599        final int uid = 1000;
1600        final int pid = 2000;
1601        final String callingPackage = "com.google.somePackage";
1602        final String ssi = "some much longer and more arbitrary data";
1603        final int subscribeId = 15;
1604        final int requestorId = 22;
1605        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1606        final String peerSsi = "some peer ssi data";
1607        final String peerMatchFilter = "filter binary array represented as string";
1608        final int messageId = 6948;
1609        final int retryCount = 3;
1610
1611        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1612        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1613
1614        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1615        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1616                IWifiAwareDiscoverySessionCallback.class);
1617        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1618        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1619        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1620
1621        mDut.enableUsage();
1622        mMockLooper.dispatchAll();
1623        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1624        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1625        mMockLooper.dispatchAll();
1626
1627        // (1) connect
1628        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1629        mMockLooper.dispatchAll();
1630        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1631                eq(false), eq(true));
1632        mDut.onConfigSuccessResponse(transactionId.getValue());
1633        mMockLooper.dispatchAll();
1634        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1635
1636        // (2) subscribe & match
1637        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1638        mMockLooper.dispatchAll();
1639        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1640        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1641        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1642                peerMatchFilter.getBytes());
1643        mMockLooper.dispatchAll();
1644        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1645        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1646                peerMatchFilter.getBytes());
1647
1648        // (3) send message and enqueue successfully
1649        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId,
1650                retryCount);
1651        mMockLooper.dispatchAll();
1652        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1653                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1654        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1655        mMockLooper.dispatchAll();
1656
1657        // (4) loop and fail until reach retryCount+1
1658        for (int i = 0; i < retryCount + 1; ++i) {
1659            mDut.onMessageSendFailNotification(transactionId.getValue(), NanStatusType.NO_OTA_ACK);
1660            mMockLooper.dispatchAll();
1661
1662            if (i != retryCount) {
1663                inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1664                        eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1665                mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1666                mMockLooper.dispatchAll();
1667            }
1668        }
1669
1670        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1671                NanStatusType.NO_OTA_ACK);
1672        validateInternalSendMessageQueuesCleanedUp(messageId);
1673
1674        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1675    }
1676
1677    /**
1678     * Validate that the host-side message queue functions. Tests the perfect case of queue always
1679     * succeeds and all messages are received on first attempt.
1680     */
1681    @Test
1682    public void testSendMessageQueueSequence() throws Exception {
1683        final int clientId = 1005;
1684        final int uid = 1000;
1685        final int pid = 2000;
1686        final String callingPackage = "com.google.somePackage";
1687        final String serviceName = "some-service-name";
1688        final int subscribeId = 15;
1689        final int requestorId = 22;
1690        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1691        final int messageIdBase = 6948;
1692        final int numberOfMessages = 30;
1693        final int queueDepth = 6;
1694
1695        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1696        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1697                .build();
1698
1699        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1700        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1701                IWifiAwareDiscoverySessionCallback.class);
1702        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1703        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1704        ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
1705        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1706
1707        mDut.enableUsage();
1708        mMockLooper.dispatchAll();
1709        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1710        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1711        mMockLooper.dispatchAll();
1712
1713        // (0) connect
1714        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1715        mMockLooper.dispatchAll();
1716        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1717                eq(configRequest), eq(false), eq(true));
1718        mDut.onConfigSuccessResponse(transactionId.getValue());
1719        mMockLooper.dispatchAll();
1720        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1721
1722        // (1) subscribe
1723        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1724        mMockLooper.dispatchAll();
1725        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1726        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1727        mMockLooper.dispatchAll();
1728        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1729
1730        // (2) match
1731        mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
1732        mMockLooper.dispatchAll();
1733        inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
1734
1735        // (3) transmit messages
1736        SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
1737                null, null, null);
1738        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1739                any(), anyInt())).thenAnswer(answerObj);
1740
1741        int remainingMessages = numberOfMessages;
1742        for (int i = 0; i < numberOfMessages; ++i) {
1743            mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
1744                    0);
1745            mMockLooper.dispatchAll();
1746            // at 1/2 interval have the system simulate transmitting a queued message over-the-air
1747            if (i % 2 == 1) {
1748                assertTrue(answerObj.process());
1749                remainingMessages--;
1750                mMockLooper.dispatchAll();
1751            }
1752        }
1753        for (int i = 0; i < remainingMessages; ++i) {
1754            assertTrue(answerObj.process());
1755            mMockLooper.dispatchAll();
1756        }
1757        assertEquals("queue empty", 0, answerObj.queueSize());
1758
1759        inOrder.verify(mockSessionCallback, times(numberOfMessages)).onMessageSendSuccess(
1760                messageIdCaptor.capture());
1761        for (int i = 0; i < numberOfMessages; ++i) {
1762            assertEquals("message ID: " + i, (long) messageIdBase + i,
1763                    (long) messageIdCaptor.getAllValues().get(i));
1764        }
1765
1766        verifyNoMoreInteractions(mockCallback, mockSessionCallback);
1767    }
1768
1769    /**
1770     * Validate that the host-side message queue functions. A combination of imperfect conditions:
1771     * - Failure to queue: synchronous firmware error
1772     * - Failure to queue: asyncronous firmware error
1773     * - Failure to transmit: OTA (which will be retried)
1774     * - Failure to transmit: other
1775     */
1776    @Test
1777    public void testSendMessageQueueSequenceImperfect() throws Exception {
1778        final int clientId = 1005;
1779        final int uid = 1000;
1780        final int pid = 2000;
1781        final String callingPackage = "com.google.somePackage";
1782        final String serviceName = "some-service-name";
1783        final int subscribeId = 15;
1784        final int requestorId = 22;
1785        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1786        final int messageIdBase = 6948;
1787        final int numberOfMessages = 300;
1788        final int queueDepth = 6;
1789        final int retransmitCount = 3; // not the maximum
1790
1791        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1792        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1793                .build();
1794
1795        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1796        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1797                IWifiAwareDiscoverySessionCallback.class);
1798        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1799        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1800        ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
1801        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1802
1803        mDut.enableUsage();
1804        mMockLooper.dispatchAll();
1805        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1806        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1807        mMockLooper.dispatchAll();
1808
1809        // (0) connect
1810        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1811        mMockLooper.dispatchAll();
1812        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1813                eq(configRequest), eq(false), eq(true));
1814        mDut.onConfigSuccessResponse(transactionId.getValue());
1815        mMockLooper.dispatchAll();
1816        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1817
1818        // (1) subscribe
1819        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1820        mMockLooper.dispatchAll();
1821        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1822        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1823        mMockLooper.dispatchAll();
1824        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1825
1826        // (2) match
1827        mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
1828        mMockLooper.dispatchAll();
1829        inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
1830
1831        // (3) transmit messages: configure a mix of failures/success
1832        Set<Integer> failQueueCommandImmediately = new HashSet<>();
1833        Set<Integer> failQueueCommandLater = new HashSet<>();
1834        Map<Integer, Integer> numberOfRetries = new HashMap<>();
1835
1836        int numOfSuccesses = 0;
1837        int numOfFailuresInternalFailure = 0;
1838        int numOfFailuresNoOta = 0;
1839        for (int i = 0; i < numberOfMessages; ++i) {
1840            // random results:
1841            // - 0-50: success
1842            // - 51-60: retransmit value (which will fail for >5)
1843            // - 61-70: fail queue later
1844            // - 71-80: fail queue immediately
1845            // - 81-90: fail retransmit with non-OTA failure
1846            int random = mRandomNg.nextInt(90);
1847            if (random <= 50) {
1848                numberOfRetries.put(messageIdBase + i, 0);
1849                numOfSuccesses++;
1850            } else if (random <= 60) {
1851                numberOfRetries.put(messageIdBase + i, random - 51);
1852                if (random - 51 > retransmitCount) {
1853                    numOfFailuresNoOta++;
1854                } else {
1855                    numOfSuccesses++;
1856                }
1857            } else if (random <= 70) {
1858                failQueueCommandLater.add(messageIdBase + i);
1859                numOfFailuresInternalFailure++;
1860            } else if (random <= 80) {
1861                failQueueCommandImmediately.add(messageIdBase + i);
1862                numOfFailuresInternalFailure++;
1863            } else {
1864                numberOfRetries.put(messageIdBase + i, -1);
1865                numOfFailuresInternalFailure++;
1866            }
1867        }
1868
1869        Log.v("WifiAwareStateManagerTest",
1870                "failQueueCommandImmediately=" + failQueueCommandImmediately
1871                        + ", failQueueCommandLater=" + failQueueCommandLater + ", numberOfRetries="
1872                        + numberOfRetries + ", numOfSuccesses=" + numOfSuccesses
1873                        + ", numOfFailuresInternalFailure=" + numOfFailuresInternalFailure
1874                        + ", numOfFailuresNoOta=" + numOfFailuresNoOta);
1875
1876        SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
1877                failQueueCommandImmediately, failQueueCommandLater, numberOfRetries);
1878        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1879                any(), anyInt())).thenAnswer(answerObj);
1880
1881        for (int i = 0; i < numberOfMessages; ++i) {
1882            mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
1883                    retransmitCount);
1884            mMockLooper.dispatchAll();
1885        }
1886
1887        while (answerObj.queueSize() != 0) {
1888            assertTrue(answerObj.process());
1889            mMockLooper.dispatchAll();
1890        }
1891
1892        verify(mockSessionCallback, times(numOfSuccesses)).onMessageSendSuccess(anyInt());
1893        verify(mockSessionCallback, times(numOfFailuresInternalFailure)).onMessageSendFail(anyInt(),
1894                eq(NanStatusType.INTERNAL_FAILURE));
1895        verify(mockSessionCallback, times(numOfFailuresNoOta)).onMessageSendFail(anyInt(),
1896                eq(NanStatusType.NO_OTA_ACK));
1897
1898        verifyNoMoreInteractions(mockCallback, mockSessionCallback);
1899    }
1900
1901    /**
1902     * Validate that can send empty message successfully: null, byte[0], ""
1903     */
1904    @Test
1905    public void testSendEmptyMessages() throws Exception {
1906        final int clientId = 1005;
1907        final int uid = 1000;
1908        final int pid = 2000;
1909        final String callingPackage = "com.google.somePackage";
1910        final String serviceName = "some-service-name";
1911        final String ssi = "some much longer and more arbitrary data";
1912        final int subscribeCount = 7;
1913        final int subscribeId = 15;
1914        final int requestorId = 22;
1915        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1916        final String peerSsi = "some peer ssi data";
1917        final String peerMatchFilter = "filter binary array represented as string";
1918        final int messageId = 6948;
1919
1920        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1921        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1922                .setServiceSpecificInfo(ssi.getBytes())
1923                .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
1924                .setSubscribeCount(subscribeCount).build();
1925
1926        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1927        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1928                IWifiAwareDiscoverySessionCallback.class);
1929        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1930        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1931        ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class);
1932        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1933
1934        mDut.enableUsage();
1935        mMockLooper.dispatchAll();
1936        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1937        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1938        mMockLooper.dispatchAll();
1939
1940        // (0) connect
1941        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1942        mMockLooper.dispatchAll();
1943        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1944                eq(configRequest), eq(false), eq(true));
1945        mDut.onConfigSuccessResponse(transactionId.getValue());
1946        mMockLooper.dispatchAll();
1947        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1948
1949        // (1) subscribe
1950        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1951        mMockLooper.dispatchAll();
1952        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1953        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1954        mMockLooper.dispatchAll();
1955        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1956
1957        // (2) match
1958        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1959                peerMatchFilter.getBytes());
1960        mMockLooper.dispatchAll();
1961        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1962                peerMatchFilter.getBytes());
1963
1964        // (3) message null Tx successful queuing
1965        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageId, 0);
1966        mMockLooper.dispatchAll();
1967        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1968                eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId));
1969        short tid = transactionId.getValue();
1970        mDut.onMessageSendQueuedSuccessResponse(tid);
1971        mMockLooper.dispatchAll();
1972
1973        // (4) final Tx results (on-air results)
1974        mDut.onMessageSendSuccessNotification(tid);
1975        mMockLooper.dispatchAll();
1976        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1977        validateInternalSendMessageQueuesCleanedUp(messageId);
1978
1979        // (5) message byte[0] Tx successful queuing
1980        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, new byte[0], messageId, 0);
1981        mMockLooper.dispatchAll();
1982        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1983                eq(requestorId), eq(peerMac), eq(new byte[0]), eq(messageId));
1984        tid = transactionId.getValue();
1985        mDut.onMessageSendQueuedSuccessResponse(tid);
1986        mMockLooper.dispatchAll();
1987
1988        // (6) final Tx results (on-air results)
1989        mDut.onMessageSendSuccessNotification(tid);
1990        mMockLooper.dispatchAll();
1991        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1992        validateInternalSendMessageQueuesCleanedUp(messageId);
1993
1994        // (7) message "" Tx successful queuing
1995        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, "".getBytes(), messageId, 0);
1996        mMockLooper.dispatchAll();
1997        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1998                eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId));
1999        collector.checkThat("Empty message contents", "",
2000                equalTo(new String(byteArrayCaptor.getValue())));
2001        tid = transactionId.getValue();
2002        mDut.onMessageSendQueuedSuccessResponse(tid);
2003        mMockLooper.dispatchAll();
2004
2005        // (8) final Tx results (on-air results)
2006        mDut.onMessageSendSuccessNotification(tid);
2007        mMockLooper.dispatchAll();
2008        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
2009        validateInternalSendMessageQueuesCleanedUp(messageId);
2010
2011        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
2012    }
2013
2014    private class SendMessageQueueModelAnswer extends MockAnswerUtil.AnswerWithArguments {
2015        private final int mMaxQueueDepth;
2016
2017        // keyed by message ID
2018        private final Set<Integer> mFailQueueCommandImmediately; // return a false
2019        private final Set<Integer> mFailQueueCommandLater; // return an error != TX_QUEUE_FULL
2020
2021        // # of times to return NO_OTA_ACK before returning SUCCESS. So a 0 means success on first
2022        // try, a very large number means - never succeed (since max retry is 5).
2023        // a -1 impiles a non-OTA failure: on first attempt
2024        private final Map<Integer, Integer> mRetryLimit;
2025
2026        private final LinkedList<Short> mQueue = new LinkedList<>(); // transaction ID (tid)
2027        private final Map<Short, Integer> mMessageIdsByTid = new HashMap<>(); // tid -> message ID
2028        private final Map<Integer, Integer> mTriesUsedByMid = new HashMap<>(); // mid -> # of retx
2029
2030        SendMessageQueueModelAnswer(int maxQueueDepth, Set<Integer> failQueueCommandImmediately,
2031                Set<Integer> failQueueCommandLater, Map<Integer, Integer> numberOfRetries) {
2032            mMaxQueueDepth = maxQueueDepth;
2033            mFailQueueCommandImmediately = failQueueCommandImmediately;
2034            mFailQueueCommandLater = failQueueCommandLater;
2035            mRetryLimit = numberOfRetries;
2036
2037            if (mRetryLimit != null) {
2038                for (int mid : mRetryLimit.keySet()) {
2039                    mTriesUsedByMid.put(mid, 0);
2040                }
2041            }
2042        }
2043
2044        public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
2045                byte[] dest, byte[] message, int messageId) throws Exception {
2046            if (mFailQueueCommandImmediately != null && mFailQueueCommandImmediately.contains(
2047                    messageId)) {
2048                return false;
2049            }
2050
2051            if (mFailQueueCommandLater != null && mFailQueueCommandLater.contains(messageId)) {
2052                mDut.onMessageSendQueuedFailResponse(transactionId, NanStatusType.INTERNAL_FAILURE);
2053            } else {
2054                if (mQueue.size() <= mMaxQueueDepth) {
2055                    mQueue.addLast(transactionId);
2056                    mMessageIdsByTid.put(transactionId, messageId);
2057                    mDut.onMessageSendQueuedSuccessResponse(transactionId);
2058                } else {
2059                    mDut.onMessageSendQueuedFailResponse(transactionId,
2060                            NanStatusType.FOLLOWUP_TX_QUEUE_FULL);
2061                }
2062            }
2063
2064            return true;
2065        }
2066
2067        /**
2068         * Processes the first message in the queue: i.e. responds as if sent over-the-air
2069         * (successfully or failed)
2070         */
2071        boolean process() {
2072            if (mQueue.size() == 0) {
2073                return false;
2074            }
2075            short tid = mQueue.poll();
2076            int mid = mMessageIdsByTid.get(tid);
2077
2078            if (mRetryLimit != null && mRetryLimit.containsKey(mid)) {
2079                int numRetries = mRetryLimit.get(mid);
2080                if (numRetries == -1) {
2081                    mDut.onMessageSendFailNotification(tid, NanStatusType.INTERNAL_FAILURE);
2082                } else {
2083                    int currentRetries = mTriesUsedByMid.get(mid);
2084                    if (currentRetries > numRetries) {
2085                        return false; // shouldn't be retrying!?
2086                    } else if (currentRetries == numRetries) {
2087                        mDut.onMessageSendSuccessNotification(tid);
2088                    } else {
2089                        mDut.onMessageSendFailNotification(tid, NanStatusType.NO_OTA_ACK);
2090                    }
2091                    mTriesUsedByMid.put(mid, currentRetries + 1);
2092                }
2093            } else {
2094                mDut.onMessageSendSuccessNotification(tid);
2095            }
2096
2097            return true;
2098        }
2099
2100        /**
2101         * Returns the number of elements in the queue.
2102         */
2103        int queueSize() {
2104            return mQueue.size();
2105        }
2106    }
2107
2108    /**
2109     * Validate that start ranging function fills-in correct MAC addresses for peer IDs and
2110     * passed along to RTT module.
2111     */
2112    @Test
2113    public void testStartRanging() throws Exception {
2114        final int clientId = 1005;
2115        final int uid = 1000;
2116        final int pid = 2000;
2117        final String callingPackage = "com.google.somePackage";
2118        final int subscribeId = 15;
2119        final int requestorId = 22;
2120        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
2121        final String peerSsi = "some peer ssi data";
2122        final String peerMatchFilter = "filter binary array represented as string";
2123        final int rangingId = 18423;
2124        final RttManager.RttParams[] params = new RttManager.RttParams[2];
2125        params[0] = new RttManager.RttParams();
2126        params[0].bssid = Integer.toString(requestorId);
2127        params[1] = new RttManager.RttParams();
2128        params[1].bssid = Integer.toString(requestorId + 5);
2129
2130        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2131        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2132
2133        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2134        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2135                IWifiAwareDiscoverySessionCallback.class);
2136
2137        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2138        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2139        ArgumentCaptor<WifiAwareClientState> clientCaptor =
2140                ArgumentCaptor.forClass(WifiAwareClientState.class);
2141        ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
2142                ArgumentCaptor.forClass(RttManager.RttParams[].class);
2143
2144        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative,
2145                mMockAwareRttStateManager);
2146
2147        mDut.enableUsage();
2148        mMockLooper.dispatchAll();
2149        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2150        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2151        mMockLooper.dispatchAll();
2152
2153        // (1) connect
2154        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2155        mMockLooper.dispatchAll();
2156        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2157                eq(false), eq(true));
2158        mDut.onConfigSuccessResponse(transactionId.getValue());
2159        mMockLooper.dispatchAll();
2160        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2161
2162        // (2) subscribe & match
2163        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
2164        mMockLooper.dispatchAll();
2165        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
2166        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
2167        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
2168                peerMatchFilter.getBytes());
2169        mMockLooper.dispatchAll();
2170        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2171        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
2172                peerMatchFilter.getBytes());
2173
2174        // (3) start ranging: pass along a valid peer ID and an invalid one
2175        mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
2176        mMockLooper.dispatchAll();
2177        inOrder.verify(mMockAwareRttStateManager).startRanging(eq(rangingId),
2178                clientCaptor.capture(), rttParamsCaptor.capture());
2179        collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B",
2180                equalTo(rttParamsCaptor.getValue()[0].bssid));
2181        collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid));
2182
2183        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative,
2184                mMockAwareRttStateManager);
2185    }
2186
2187    /**
2188     * Test sequence of configuration: (1) config1, (2) config2 - incompatible,
2189     * (3) config3 - compatible with config1 (requiring upgrade), (4) disconnect
2190     * config3 (should get a downgrade), (5) disconnect config1 (should get a
2191     * disable).
2192     */
2193    @Test
2194    public void testConfigs() throws Exception {
2195        final int clientId1 = 9999;
2196        final int clientId2 = 1001;
2197        final int clientId3 = 1005;
2198        final int uid = 1000;
2199        final int pid = 2000;
2200        final String callingPackage = "com.google.somePackage";
2201        final int masterPref1 = 111;
2202        final int masterPref3 = 115;
2203        final int dwInterval1Band24 = 2;
2204        final int dwInterval3Band24 = 1;
2205        final int dwInterval3Band5 = 0;
2206
2207        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2208        ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
2209
2210        ConfigRequest configRequest1 = new ConfigRequest.Builder()
2211                .setClusterLow(5).setClusterHigh(100)
2212                .setMasterPreference(masterPref1)
2213                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval1Band24)
2214                .build();
2215
2216        ConfigRequest configRequest2 = new ConfigRequest.Builder()
2217                .setSupport5gBand(true) // compatible
2218                .setClusterLow(7).setClusterHigh(155) // incompatible!
2219                .setMasterPreference(0) // compatible
2220                .build();
2221
2222        ConfigRequest configRequest3  = new ConfigRequest.Builder()
2223                .setSupport5gBand(true) // compatible (will use true)
2224                .setClusterLow(5).setClusterHigh(100) // identical (hence compatible)
2225                .setMasterPreference(masterPref3) // compatible (will use max)
2226                // compatible: will use min
2227                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval3Band24)
2228                // compatible: will use interval3 since interval1 not init
2229                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwInterval3Band5)
2230                .build();
2231
2232        IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
2233        IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
2234        IWifiAwareEventCallback mockCallback3 = mock(IWifiAwareEventCallback.class);
2235
2236        InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
2237
2238        mDut.enableUsage();
2239        mMockLooper.dispatchAll();
2240        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2241        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2242        mMockLooper.dispatchAll();
2243
2244        // (1) config1 (valid)
2245        mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1, false);
2246        mMockLooper.dispatchAll();
2247        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2248                crCapture.capture(), eq(false), eq(true));
2249        collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
2250        mDut.onConfigSuccessResponse(transactionId.getValue());
2251        mMockLooper.dispatchAll();
2252        inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
2253
2254        // (2) config2 (incompatible with config1)
2255        mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest2, false);
2256        mMockLooper.dispatchAll();
2257        inOrder.verify(mockCallback2).onConnectFail(NanStatusType.INTERNAL_FAILURE);
2258        validateInternalClientInfoCleanedUp(clientId2);
2259
2260        // (3) config3 (compatible with config1)
2261        mDut.connect(clientId3, uid, pid, callingPackage, mockCallback3, configRequest3, true);
2262        mMockLooper.dispatchAll();
2263        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2264                crCapture.capture(), eq(true), eq(false));
2265        mDut.onConfigSuccessResponse(transactionId.getValue());
2266        mMockLooper.dispatchAll();
2267        inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
2268
2269        collector.checkThat("support 5g: or", true, equalTo(crCapture.getValue().mSupport5gBand));
2270        collector.checkThat("master preference: max", Math.max(masterPref1, masterPref3),
2271                equalTo(crCapture.getValue().mMasterPreference));
2272        collector.checkThat("dw interval on 2.4: ~min",
2273                Math.min(dwInterval1Band24, dwInterval3Band24),
2274                equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
2275                        .NAN_BAND_24GHZ]));
2276        collector.checkThat("dw interval on 5: ~min", dwInterval3Band5,
2277                equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
2278                        .NAN_BAND_5GHZ]));
2279
2280        // (4) disconnect config3: downgrade to config1
2281        mDut.disconnect(clientId3);
2282        mMockLooper.dispatchAll();
2283        validateInternalClientInfoCleanedUp(clientId3);
2284        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2285                crCapture.capture(), eq(false), eq(false));
2286
2287        collector.checkThat("configRequest1", configRequest1, equalTo(crCapture.getValue()));
2288
2289        mDut.onConfigSuccessResponse(transactionId.getValue());
2290        mMockLooper.dispatchAll();
2291
2292        // (5) disconnect config1: disable
2293        mDut.disconnect(clientId1);
2294        mMockLooper.dispatchAll();
2295        validateInternalClientInfoCleanedUp(clientId1);
2296        inOrder.verify(mMockNative).disable((short) 0);
2297
2298        verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
2299    }
2300
2301    /**
2302     * Summary: disconnect a client while there are pending transactions.
2303     */
2304    @Test
2305    public void testDisconnectWithPendingTransactions() throws Exception {
2306        final int clientId = 125;
2307        final int uid = 1000;
2308        final int pid = 2000;
2309        final String callingPackage = "com.google.somePackage";
2310        final int clusterLow = 5;
2311        final int clusterHigh = 100;
2312        final int masterPref = 111;
2313        final String serviceName = "some-service-name";
2314        final String ssi = "some much longer and more arbitrary data";
2315        final int publishCount = 7;
2316        final int publishId = 22;
2317
2318        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
2319                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
2320
2321        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
2322                serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
2323                PublishConfig.PUBLISH_TYPE_UNSOLICITED).setPublishCount(publishCount).build();
2324
2325        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2326        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2327        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2328                IWifiAwareDiscoverySessionCallback.class);
2329        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2330
2331        mDut.enableUsage();
2332        mMockLooper.dispatchAll();
2333        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2334        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2335        mMockLooper.dispatchAll();
2336
2337        // (1) connect
2338        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2339        mMockLooper.dispatchAll();
2340        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2341                eq(false), eq(true));
2342        mDut.onConfigSuccessResponse(transactionId.getValue());
2343        mMockLooper.dispatchAll();
2344        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2345
2346        // (2) publish (no response yet)
2347        mDut.publish(clientId, publishConfig, mockSessionCallback);
2348        mMockLooper.dispatchAll();
2349        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2350
2351        // (3) disconnect (but doesn't get executed until get a RESPONSE to the
2352        // previous publish)
2353        mDut.disconnect(clientId);
2354        mMockLooper.dispatchAll();
2355
2356        // (4) get successful response to the publish
2357        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
2358        mMockLooper.dispatchAll();
2359        inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
2360        inOrder.verify(mMockNative).stopPublish((short) 0, publishId);
2361        inOrder.verify(mMockNative).disable((short) 0);
2362
2363        validateInternalClientInfoCleanedUp(clientId);
2364
2365        // (5) trying to publish on the same client: NOP
2366        mDut.publish(clientId, publishConfig, mockSessionCallback);
2367        mMockLooper.dispatchAll();
2368
2369        // (6) got some callback on original publishId - should be ignored
2370        mDut.onSessionTerminatedNotification(publishId, 0, true);
2371        mMockLooper.dispatchAll();
2372
2373        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2374    }
2375
2376    /**
2377     * Validate that an unknown transaction (i.e. a callback from HAL with an
2378     * unknown type) is simply ignored - but also cleans up its state.
2379     */
2380    @Test
2381    public void testUnknownTransactionType() throws Exception {
2382        final int clientId = 129;
2383        final int uid = 1000;
2384        final int pid = 2000;
2385        final String callingPackage = "com.google.somePackage";
2386        final int clusterLow = 15;
2387        final int clusterHigh = 192;
2388        final int masterPref = 234;
2389        final String serviceName = "some-service-name";
2390        final String ssi = "some much longer and more arbitrary data";
2391        final int publishCount = 15;
2392
2393        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
2394                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
2395
2396        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
2397                serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
2398                PublishConfig.PUBLISH_TYPE_UNSOLICITED).setPublishCount(publishCount).build();
2399
2400        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2401        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2402        IWifiAwareDiscoverySessionCallback mockPublishSessionCallback = mock(
2403                IWifiAwareDiscoverySessionCallback.class);
2404        InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
2405
2406        mDut.enableUsage();
2407        mMockLooper.dispatchAll();
2408        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2409        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2410        mMockLooper.dispatchAll();
2411
2412        // (1) connect
2413        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2414        mMockLooper.dispatchAll();
2415        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2416                eq(false), eq(true));
2417        mDut.onConfigSuccessResponse(transactionId.getValue());
2418        mMockLooper.dispatchAll();
2419        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2420
2421        // (2) publish - no response
2422        mDut.publish(clientId, publishConfig, mockPublishSessionCallback);
2423        mMockLooper.dispatchAll();
2424        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2425
2426        verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback);
2427    }
2428
2429    /**
2430     * Validate that a NoOp transaction (i.e. a callback from HAL which doesn't
2431     * require any action except clearing up state) actually cleans up its state
2432     * (and does nothing else).
2433     */
2434    @Test
2435    public void testNoOpTransaction() throws Exception {
2436        final int clientId = 1294;
2437        final int uid = 1000;
2438        final int pid = 2000;
2439        final String callingPackage = "com.google.somePackage";
2440
2441        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2442
2443        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2444        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2445        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2446                IWifiAwareDiscoverySessionCallback.class);
2447        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2448
2449        mDut.enableUsage();
2450        mMockLooper.dispatchAll();
2451        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2452        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2453        mMockLooper.dispatchAll();
2454
2455        // (1) connect (no response)
2456        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2457        mMockLooper.dispatchAll();
2458        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2459                eq(false), eq(true));
2460
2461        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2462    }
2463
2464    /**
2465     * Validate that getting callbacks from HAL with unknown (expired)
2466     * transaction ID or invalid publish/subscribe ID session doesn't have any
2467     * impact.
2468     */
2469    @Test
2470    public void testInvalidCallbackIdParameters() throws Exception {
2471        final int pubSubId = 1235;
2472        final int clientId = 132;
2473        final int uid = 1000;
2474        final int pid = 2000;
2475        final String callingPackage = "com.google.somePackage";
2476
2477        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2478
2479        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2480        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2481        InOrder inOrder = inOrder(mMockNative, mockCallback);
2482
2483        mDut.enableUsage();
2484        mMockLooper.dispatchAll();
2485        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2486        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2487        mMockLooper.dispatchAll();
2488
2489        // (1) connect and succeed
2490        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2491        mMockLooper.dispatchAll();
2492        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2493                eq(false), eq(true));
2494        short transactionIdConfig = transactionId.getValue();
2495        mDut.onConfigSuccessResponse(transactionIdConfig);
2496        mMockLooper.dispatchAll();
2497        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2498
2499        // (2) use the same transaction ID to send a bunch of other responses
2500        mDut.onConfigSuccessResponse(transactionIdConfig);
2501        mDut.onConfigFailedResponse(transactionIdConfig, -1);
2502        mDut.onSessionConfigFailResponse(transactionIdConfig, true, -1);
2503        mDut.onMessageSendQueuedSuccessResponse(transactionIdConfig);
2504        mDut.onMessageSendQueuedFailResponse(transactionIdConfig, -1);
2505        mDut.onSessionConfigFailResponse(transactionIdConfig, false, -1);
2506        mDut.onMatchNotification(-1, -1, new byte[0], new byte[0], new byte[0]);
2507        mDut.onSessionTerminatedNotification(-1, -1, true);
2508        mDut.onSessionTerminatedNotification(-1, -1, false);
2509        mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[0]);
2510        mDut.onSessionConfigSuccessResponse(transactionIdConfig, true, pubSubId);
2511        mDut.onSessionConfigSuccessResponse(transactionIdConfig, false, pubSubId);
2512        mMockLooper.dispatchAll();
2513
2514        verifyNoMoreInteractions(mMockNative, mockCallback);
2515    }
2516
2517    /**
2518     * Validate that trying to update-subscribe on a publish session fails.
2519     */
2520    @Test
2521    public void testSubscribeOnPublishSessionType() throws Exception {
2522        final int clientId = 188;
2523        final int uid = 1000;
2524        final int pid = 2000;
2525        final String callingPackage = "com.google.somePackage";
2526        final int publishId = 25;
2527
2528        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2529        PublishConfig publishConfig = new PublishConfig.Builder().build();
2530        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2531
2532        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2533        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2534        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2535        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2536                IWifiAwareDiscoverySessionCallback.class);
2537        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2538
2539        mDut.enableUsage();
2540        mMockLooper.dispatchAll();
2541        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2542        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2543        mMockLooper.dispatchAll();
2544
2545        // (1) connect
2546        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2547        mMockLooper.dispatchAll();
2548        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2549                eq(false), eq(true));
2550        mDut.onConfigSuccessResponse(transactionId.getValue());
2551        mMockLooper.dispatchAll();
2552        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2553
2554        // (2) publish
2555        mDut.publish(clientId, publishConfig, mockSessionCallback);
2556        mMockLooper.dispatchAll();
2557        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2558        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
2559        mMockLooper.dispatchAll();
2560        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2561
2562        // (3) update-subscribe -> failure
2563        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
2564        mMockLooper.dispatchAll();
2565        inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
2566
2567        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2568    }
2569
2570    /**
2571     * Validate that trying to (re)subscribe on a publish session or (re)publish
2572     * on a subscribe session fails.
2573     */
2574    @Test
2575    public void testPublishOnSubscribeSessionType() throws Exception {
2576        final int clientId = 188;
2577        final int uid = 1000;
2578        final int pid = 2000;
2579        final String callingPackage = "com.google.somePackage";
2580        final int subscribeId = 25;
2581
2582        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2583        PublishConfig publishConfig = new PublishConfig.Builder().build();
2584        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2585
2586        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2587        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2588        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2589        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2590                IWifiAwareDiscoverySessionCallback.class);
2591        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2592
2593        mDut.enableUsage();
2594        mMockLooper.dispatchAll();
2595        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2596        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2597        mMockLooper.dispatchAll();
2598
2599        // (1) connect
2600        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2601        mMockLooper.dispatchAll();
2602        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2603                eq(configRequest), eq(false), eq(true));
2604        mDut.onConfigSuccessResponse(transactionId.getValue());
2605        mMockLooper.dispatchAll();
2606        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2607
2608        // (2) subscribe
2609        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
2610        mMockLooper.dispatchAll();
2611        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
2612        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
2613        mMockLooper.dispatchAll();
2614        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2615
2616        // (3) update-publish -> error
2617        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
2618        mMockLooper.dispatchAll();
2619        inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
2620
2621        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2622    }
2623
2624    /**
2625     * Validate that the session ID increments monotonically
2626     */
2627    @Test
2628    public void testSessionIdIncrement() throws Exception {
2629        final int clientId = 188;
2630        final int uid = 1000;
2631        final int pid = 2000;
2632        final String callingPackage = "com.google.somePackage";
2633        int loopCount = 100;
2634
2635        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2636        PublishConfig publishConfig = new PublishConfig.Builder().build();
2637
2638        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2639        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2640        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2641        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2642                IWifiAwareDiscoverySessionCallback.class);
2643        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2644
2645        mDut.enableUsage();
2646        mMockLooper.dispatchAll();
2647        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2648        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2649        mMockLooper.dispatchAll();
2650
2651        // (1) connect
2652        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2653        mMockLooper.dispatchAll();
2654        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2655                eq(configRequest), eq(false), eq(true));
2656        mDut.onConfigSuccessResponse(transactionId.getValue());
2657        mMockLooper.dispatchAll();
2658        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2659
2660        int prevId = 0;
2661        for (int i = 0; i < loopCount; ++i) {
2662            // (2) publish
2663            mDut.publish(clientId, publishConfig, mockSessionCallback);
2664            mMockLooper.dispatchAll();
2665            inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2666
2667            // (3) publish-success
2668            mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1);
2669            mMockLooper.dispatchAll();
2670            inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2671
2672            if (i != 0) {
2673                assertTrue("Session ID incrementing", sessionId.getValue() > prevId);
2674            }
2675            prevId = sessionId.getValue();
2676        }
2677    }
2678
2679    /*
2680     * Tests of internal state of WifiAwareStateManager: very limited (not usually
2681     * a good idea). However, these test that the internal state is cleaned-up
2682     * appropriately. Alternatively would cause issues with memory leaks or
2683     * information leak between sessions.
2684     */
2685
2686    /**
2687     * Utility routine used to validate that the internal state is cleaned-up
2688     * after a client is disconnected. To be used in every test which terminates
2689     * a client.
2690     *
2691     * @param clientId The ID of the client which should be deleted.
2692     */
2693    private void validateInternalClientInfoCleanedUp(int clientId) throws Exception {
2694        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2695        collector.checkThat("Client record not cleared up for clientId=" + clientId, client,
2696                nullValue());
2697    }
2698
2699    /**
2700     * Utility routine used to validate that the internal state is cleaned-up
2701     * (deleted) after a session is terminated through API (not callback!). To
2702     * be used in every test which terminates a session.
2703     *
2704     * @param clientId The ID of the client containing the session.
2705     * @param sessionId The ID of the terminated session.
2706     */
2707    private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId)
2708            throws Exception {
2709        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2710        collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
2711        WifiAwareDiscoverySessionState session = getInternalSessionState(client, sessionId);
2712        collector.checkThat("Client record not cleaned-up for sessionId=" + sessionId, session,
2713                nullValue());
2714    }
2715
2716    /**
2717     * Utility routine used to validate that the internal state is cleaned-up
2718     * (deleted) correctly. Checks that a specific client has no sessions
2719     * attached to it.
2720     *
2721     * @param clientId The ID of the client which we want to check.
2722     */
2723    private void validateInternalNoSessions(int clientId) throws Exception {
2724        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2725        collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
2726
2727        Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
2728        field.setAccessible(true);
2729        @SuppressWarnings("unchecked")
2730        SparseArray<WifiAwareDiscoverySessionState> sessions =
2731                (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
2732
2733        collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
2734                equalTo(0));
2735    }
2736
2737    /**
2738     * Validates that the broadcast sent on Aware status change is correct.
2739     *
2740     * @param expectedEnabled The expected change status - i.e. are we expected
2741     *            to announce that Aware is enabled (true) or disabled (false).
2742     */
2743    private void validateCorrectAwareStatusChangeBroadcast(InOrder inOrder,
2744            boolean expectedEnabled) {
2745        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
2746
2747        inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL));
2748
2749        collector.checkThat("intent action", intent.getValue().getAction(),
2750                equalTo(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED));
2751    }
2752
2753    /*
2754     * Utilities
2755     */
2756    private void dumpDut(String prefix) {
2757        StringWriter sw = new StringWriter();
2758        mDut.dump(null, new PrintWriter(sw), null);
2759        Log.e("WifiAwareStateManagerTest", prefix + sw.toString());
2760    }
2761
2762    private static void installMocksInStateManager(WifiAwareStateManager awareStateManager,
2763            WifiAwareRttStateManager mockRtt, WifiAwareDataPathStateManager mockDpMgr)
2764            throws Exception {
2765        Field field = WifiAwareStateManager.class.getDeclaredField("mRtt");
2766        field.setAccessible(true);
2767        field.set(awareStateManager, mockRtt);
2768
2769        field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr");
2770        field.setAccessible(true);
2771        field.set(awareStateManager, mockDpMgr);
2772    }
2773
2774    private static WifiAwareClientState getInternalClientState(WifiAwareStateManager dut,
2775            int clientId) throws Exception {
2776        Field field = WifiAwareStateManager.class.getDeclaredField("mClients");
2777        field.setAccessible(true);
2778        @SuppressWarnings("unchecked")
2779        SparseArray<WifiAwareClientState> clients = (SparseArray<WifiAwareClientState>) field.get(
2780                dut);
2781
2782        return clients.get(clientId);
2783    }
2784
2785    private static WifiAwareDiscoverySessionState getInternalSessionState(
2786            WifiAwareClientState client, int sessionId) throws Exception {
2787        Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
2788        field.setAccessible(true);
2789        @SuppressWarnings("unchecked")
2790        SparseArray<WifiAwareDiscoverySessionState> sessions =
2791                (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
2792
2793        return sessions.get(sessionId);
2794    }
2795
2796    private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
2797        Field field = WifiAwareStateManager.class.getDeclaredField("mSm");
2798        field.setAccessible(true);
2799        WifiAwareStateManager.WifiAwareStateMachine sm =
2800                (WifiAwareStateManager.WifiAwareStateMachine) field.get(mDut);
2801
2802        field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
2803                "mHostQueuedSendMessages");
2804        field.setAccessible(true);
2805        SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
2806
2807        field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
2808                "mFwQueuedSendMessages");
2809        field.setAccessible(true);
2810        Map<Short, Message> fwQueuedSendMessages = (Map<Short, Message>) field.get(sm);
2811
2812        for (int i = 0; i < hostQueuedSendMessages.size(); ++i) {
2813            Message msg = hostQueuedSendMessages.valueAt(i);
2814            if (msg.getData().getInt("message_id") == messageId) {
2815                collector.checkThat(
2816                        "Message not cleared-up from host queue. Message ID=" + messageId, msg,
2817                        nullValue());
2818            }
2819        }
2820
2821        for (Message msg: fwQueuedSendMessages.values()) {
2822            if (msg.getData().getInt("message_id") == messageId) {
2823                collector.checkThat(
2824                        "Message not cleared-up from firmware queue. Message ID=" + messageId, msg,
2825                        nullValue());
2826            }
2827        }
2828    }
2829
2830    private static Capabilities getCapabilities() {
2831        Capabilities cap = new Capabilities();
2832        cap.maxConcurrentAwareClusters = 1;
2833        cap.maxPublishes = 2;
2834        cap.maxSubscribes = 2;
2835        cap.maxServiceNameLen = 255;
2836        cap.maxMatchFilterLen = 255;
2837        cap.maxTotalMatchFilterLen = 255;
2838        cap.maxServiceSpecificInfoLen = 255;
2839        cap.maxExtendedServiceSpecificInfoLen = 255;
2840        cap.maxNdiInterfaces = 1;
2841        cap.maxNdpSessions = 1;
2842        cap.maxAppInfoLen = 255;
2843        cap.maxQueuedTransmitMessages = 6;
2844        return cap;
2845    }
2846}
2847
2848