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 reasonFail = NanStatusType.INTERNAL_FAILURE;
1039        final int subscribeId = 15;
1040        final int requestorId = 22;
1041        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1042        final String peerSsi = "some peer ssi data";
1043        final String peerMatchFilter = "filter binary array represented as string";
1044        final String peerMsg = "some message from peer";
1045        final int messageId = 6948;
1046        final int messageId2 = 6949;
1047
1048        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1049        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1050                .setServiceSpecificInfo(ssi.getBytes())
1051                .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
1052                .build();
1053
1054        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1055        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1056                IWifiAwareDiscoverySessionCallback.class);
1057        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1058        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1059        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1060
1061        mDut.enableUsage();
1062        mMockLooper.dispatchAll();
1063        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1064        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1065        mMockLooper.dispatchAll();
1066
1067        // (0) connect
1068        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1069        mMockLooper.dispatchAll();
1070        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1071                eq(configRequest), eq(false), eq(true));
1072        mDut.onConfigSuccessResponse(transactionId.getValue());
1073        mMockLooper.dispatchAll();
1074        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1075
1076        // (1) subscribe
1077        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1078        mMockLooper.dispatchAll();
1079        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1080        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1081        mMockLooper.dispatchAll();
1082        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1083
1084        // (2) match
1085        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1086                peerMatchFilter.getBytes());
1087        mMockLooper.dispatchAll();
1088        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1089                peerMatchFilter.getBytes());
1090
1091        // (3) message Rx
1092        mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
1093        mMockLooper.dispatchAll();
1094        inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes());
1095
1096        // (4) message Tx successful queuing
1097        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId, 0);
1098        mMockLooper.dispatchAll();
1099        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1100                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1101        short tid1 = transactionId.getValue();
1102        mDut.onMessageSendQueuedSuccessResponse(tid1);
1103        mMockLooper.dispatchAll();
1104
1105        // (5) message Tx successful queuing
1106        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId2,
1107                0);
1108        mMockLooper.dispatchAll();
1109        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1110                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2));
1111        short tid2 = transactionId.getValue();
1112        mDut.onMessageSendQueuedSuccessResponse(tid2);
1113        mMockLooper.dispatchAll();
1114
1115        // (4) and (5) final Tx results (on-air results)
1116        mDut.onMessageSendFailNotification(tid1, reasonFail);
1117        mDut.onMessageSendSuccessNotification(tid2);
1118        mMockLooper.dispatchAll();
1119        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, reasonFail);
1120        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId2);
1121        validateInternalSendMessageQueuesCleanedUp(messageId);
1122        validateInternalSendMessageQueuesCleanedUp(messageId2);
1123
1124        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1125    }
1126
1127    /**
1128     * Summary: in a single publish session interact with multiple peers
1129     * (different MAC addresses).
1130     */
1131    @Test
1132    public void testMultipleMessageSources() throws Exception {
1133        final int clientId = 300;
1134        final int uid = 1000;
1135        final int pid = 2000;
1136        final String callingPackage = "com.google.somePackage";
1137        final int clusterLow = 7;
1138        final int clusterHigh = 7;
1139        final int masterPref = 0;
1140        final String serviceName = "some-service-name";
1141        final int publishId = 88;
1142        final int peerId1 = 568;
1143        final int peerId2 = 873;
1144        final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
1145        final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
1146        final String msgFromPeer1 = "hey from 000102...";
1147        final String msgFromPeer2 = "hey from 0607...";
1148        final String msgToPeer1 = "hey there 000102...";
1149        final String msgToPeer2 = "hey there 0506...";
1150        final int msgToPeerId1 = 546;
1151        final int msgToPeerId2 = 9654;
1152        final int reason = NanStatusType.INTERNAL_FAILURE;
1153
1154        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
1155                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
1156
1157        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
1158                .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
1159
1160        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1161        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1162        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1163        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1164                IWifiAwareDiscoverySessionCallback.class);
1165        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
1166
1167        mDut.enableUsage();
1168        mMockLooper.dispatchAll();
1169        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1170        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1171        mMockLooper.dispatchAll();
1172
1173        // (1) connect
1174        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1175        mMockLooper.dispatchAll();
1176        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1177                eq(false), eq(true));
1178        mDut.onConfigSuccessResponse(transactionId.getValue());
1179        mMockLooper.dispatchAll();
1180        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1181
1182        // (2) publish
1183        mDut.publish(clientId, publishConfig, mockSessionCallback);
1184        mMockLooper.dispatchAll();
1185        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
1186        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
1187        mMockLooper.dispatchAll();
1188        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1189
1190        // (3) message received from peers 1 & 2
1191        mDut.onMessageReceivedNotification(publishId, peerId1, peerMac1, msgFromPeer1.getBytes());
1192        mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes());
1193        mMockLooper.dispatchAll();
1194        inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes());
1195        inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes());
1196
1197        // (4) sending messages back to same peers: one Tx fails, other succeeds
1198        mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
1199                msgToPeerId2, 0);
1200        mMockLooper.dispatchAll();
1201        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
1202                eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
1203        short transactionIdVal = transactionId.getValue();
1204        mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
1205        mDut.onMessageSendSuccessNotification(transactionIdVal);
1206
1207        mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
1208                msgToPeerId1, 0);
1209        mMockLooper.dispatchAll();
1210        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
1211        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
1212                eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
1213        transactionIdVal = transactionId.getValue();
1214        mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
1215        mDut.onMessageSendFailNotification(transactionIdVal, reason);
1216        mMockLooper.dispatchAll();
1217        inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason);
1218        validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
1219        validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
1220
1221        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
1222    }
1223
1224    /**
1225     * Summary: interact with a peer which changed its identity (MAC address)
1226     * but which keeps its requestor instance ID. Should be transparent.
1227     */
1228    @Test
1229    public void testMessageWhilePeerChangesIdentity() throws Exception {
1230        final int clientId = 300;
1231        final int uid = 1000;
1232        final int pid = 2000;
1233        final String callingPackage = "com.google.somePackage";
1234        final int clusterLow = 7;
1235        final int clusterHigh = 7;
1236        final int masterPref = 0;
1237        final String serviceName = "some-service-name";
1238        final int publishId = 88;
1239        final int peerId = 568;
1240        final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
1241        final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
1242        final String msgFromPeer1 = "hey from 000102...";
1243        final String msgFromPeer2 = "hey from 0607...";
1244        final String msgToPeer1 = "hey there 000102...";
1245        final String msgToPeer2 = "hey there 0506...";
1246        final int msgToPeerId1 = 546;
1247        final int msgToPeerId2 = 9654;
1248        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
1249                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
1250
1251        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
1252                .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
1253
1254        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1255        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1256        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1257        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1258                IWifiAwareDiscoverySessionCallback.class);
1259        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
1260
1261        mDut.enableUsage();
1262        mMockLooper.dispatchAll();
1263        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1264        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1265        mMockLooper.dispatchAll();
1266
1267        // (1) connect
1268        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1269        mMockLooper.dispatchAll();
1270        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1271                eq(false), eq(true));
1272        mDut.onConfigSuccessResponse(transactionId.getValue());
1273        mMockLooper.dispatchAll();
1274        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1275
1276        // (2) publish
1277        mDut.publish(clientId, publishConfig, mockSessionCallback);
1278        mMockLooper.dispatchAll();
1279        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
1280        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
1281        mMockLooper.dispatchAll();
1282        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1283
1284        // (3) message received & responded to
1285        mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes());
1286        mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
1287                msgToPeerId1, 0);
1288        mMockLooper.dispatchAll();
1289        inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes());
1290        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
1291                eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
1292        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1293        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1294        mMockLooper.dispatchAll();
1295        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1);
1296        validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
1297
1298        // (4) message received with same peer ID but different MAC
1299        mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater,
1300                msgFromPeer2.getBytes());
1301        mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
1302                msgToPeerId2, 0);
1303        mMockLooper.dispatchAll();
1304        inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes());
1305        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
1306                eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
1307        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1308        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1309        mMockLooper.dispatchAll();
1310        inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
1311        validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
1312
1313        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
1314    }
1315
1316    /**
1317     * Validate that get failure (with correct code) when trying to send a
1318     * message to an invalid peer ID.
1319     */
1320    @Test
1321    public void testSendMessageToInvalidPeerId() throws Exception {
1322        final int clientId = 1005;
1323        final int uid = 1000;
1324        final int pid = 2000;
1325        final String callingPackage = "com.google.somePackage";
1326        final String ssi = "some much longer and more arbitrary data";
1327        final int subscribeId = 15;
1328        final int requestorId = 22;
1329        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1330        final String peerSsi = "some peer ssi data";
1331        final String peerMatchFilter = "filter binary array represented as string";
1332        final int messageId = 6948;
1333
1334        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1335        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1336
1337        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1338        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1339                IWifiAwareDiscoverySessionCallback.class);
1340        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1341        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1342        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1343
1344        mDut.enableUsage();
1345        mMockLooper.dispatchAll();
1346        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1347        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1348        mMockLooper.dispatchAll();
1349
1350        // (1) connect
1351        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1352        mMockLooper.dispatchAll();
1353        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1354                eq(false), eq(true));
1355        mDut.onConfigSuccessResponse(transactionId.getValue());
1356        mMockLooper.dispatchAll();
1357        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1358
1359        // (2) subscribe & match
1360        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1361        mMockLooper.dispatchAll();
1362        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1363        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1364        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1365                peerMatchFilter.getBytes());
1366        mMockLooper.dispatchAll();
1367        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1368        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1369                peerMatchFilter.getBytes());
1370
1371        // (3) send message to invalid peer ID
1372        mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
1373                messageId, 0);
1374        mMockLooper.dispatchAll();
1375        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1376                NanStatusType.INTERNAL_FAILURE);
1377        validateInternalSendMessageQueuesCleanedUp(messageId);
1378
1379        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1380    }
1381
1382    /**
1383     * Validate that on send message errors are handled correctly: immediate send error, queue fail
1384     * error (not queue full), and timeout. Behavior: correct callback is dispatched and a later
1385     * firmware notification is ignored. Intersperse with one successfull transmission.
1386     */
1387    @Test
1388    public void testSendMessageErrorsImmediateQueueTimeout() throws Exception {
1389        final int clientId = 1005;
1390        final int uid = 1000;
1391        final int pid = 2000;
1392        final String callingPackage = "com.google.somePackage";
1393        final String ssi = "some much longer and more arbitrary data";
1394        final int subscribeId = 15;
1395        final int requestorId = 22;
1396        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1397        final String peerSsi = "some peer ssi data";
1398        final String peerMatchFilter = "filter binary array represented as string";
1399        final int messageId = 6948;
1400
1401        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1402        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1403
1404        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1405        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1406                IWifiAwareDiscoverySessionCallback.class);
1407        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1408        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1409        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1410
1411        mDut.enableUsage();
1412        mMockLooper.dispatchAll();
1413        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1414        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1415        mMockLooper.dispatchAll();
1416
1417        // (1) connect
1418        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1419        mMockLooper.dispatchAll();
1420        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1421                eq(false), eq(true));
1422        mDut.onConfigSuccessResponse(transactionId.getValue());
1423        mMockLooper.dispatchAll();
1424        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1425
1426        // (2) subscribe & match
1427        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1428        mMockLooper.dispatchAll();
1429        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1430        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1431        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1432                peerMatchFilter.getBytes());
1433        mMockLooper.dispatchAll();
1434        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1435        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1436                peerMatchFilter.getBytes());
1437
1438        // (3) send 2 messages and enqueue successfully
1439        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1440                messageId, 0);
1441        mMockLooper.dispatchAll();
1442        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1443                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1444        short transactionId1 = transactionId.getValue();
1445        mDut.onMessageSendQueuedSuccessResponse(transactionId1);
1446        mMockLooper.dispatchAll();
1447
1448        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1449                messageId + 1, 0);
1450        mMockLooper.dispatchAll();
1451        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1452                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 1));
1453        short transactionId2 = transactionId.getValue();
1454        mDut.onMessageSendQueuedSuccessResponse(transactionId2);
1455        mMockLooper.dispatchAll();
1456
1457        // (4) send a message and get a queueing failure (not queue full)
1458        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 2,
1459                0);
1460        mMockLooper.dispatchAll();
1461        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1462                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2));
1463        short transactionId3 = transactionId.getValue();
1464        mDut.onMessageSendQueuedFailResponse(transactionId3, NanStatusType.INTERNAL_FAILURE);
1465        mMockLooper.dispatchAll();
1466        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 2,
1467                NanStatusType.INTERNAL_FAILURE);
1468        validateInternalSendMessageQueuesCleanedUp(messageId + 2);
1469
1470        // (5) send a message and get an immediate failure (configure first)
1471        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1472                any(), anyInt())).thenReturn(false);
1473
1474        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 3,
1475                0);
1476        mMockLooper.dispatchAll();
1477        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1478                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3));
1479        short transactionId4 = transactionId.getValue();
1480        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 3,
1481                NanStatusType.INTERNAL_FAILURE);
1482        validateInternalSendMessageQueuesCleanedUp(messageId + 3);
1483
1484        // (6) message send timeout
1485        assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
1486        mMockLooper.dispatchAll();
1487        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1488                NanStatusType.INTERNAL_FAILURE);
1489        validateInternalSendMessageQueuesCleanedUp(messageId);
1490
1491        // (7) firmware response (unlikely - but good to check)
1492        mDut.onMessageSendSuccessNotification(transactionId1);
1493        mDut.onMessageSendSuccessNotification(transactionId2);
1494
1495        // bogus: these didn't even go to firmware or weren't queued
1496        mDut.onMessageSendSuccessNotification(transactionId3);
1497        mDut.onMessageSendFailNotification(transactionId4, NanStatusType.INTERNAL_FAILURE);
1498        mMockLooper.dispatchAll();
1499        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId + 1);
1500
1501        validateInternalSendMessageQueuesCleanedUp(messageId + 1);
1502
1503        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1504    }
1505
1506    /**
1507     * Validate that when sending a message with a retry count the message is retried the specified
1508     * number of times. Scenario ending with success.
1509     */
1510    @Test
1511    public void testSendMessageRetransmitSuccess() throws Exception {
1512        final int clientId = 1005;
1513        final int uid = 1000;
1514        final int pid = 2000;
1515        final String callingPackage = "com.google.somePackage";
1516        final String ssi = "some much longer and more arbitrary data";
1517        final int subscribeId = 15;
1518        final int requestorId = 22;
1519        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1520        final String peerSsi = "some peer ssi data";
1521        final String peerMatchFilter = "filter binary array represented as string";
1522        final int messageId = 6948;
1523        final int retryCount = 3;
1524
1525        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1526        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1527
1528        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1529        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1530                IWifiAwareDiscoverySessionCallback.class);
1531        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1532        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1533        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1534
1535        mDut.enableUsage();
1536        mMockLooper.dispatchAll();
1537        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1538        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1539        mMockLooper.dispatchAll();
1540
1541        // (1) connect
1542        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1543        mMockLooper.dispatchAll();
1544        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1545                eq(false), eq(true));
1546        mDut.onConfigSuccessResponse(transactionId.getValue());
1547        mMockLooper.dispatchAll();
1548        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1549
1550        // (2) subscribe & match
1551        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1552        mMockLooper.dispatchAll();
1553        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1554        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1555        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1556                peerMatchFilter.getBytes());
1557        mMockLooper.dispatchAll();
1558        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1559        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1560                peerMatchFilter.getBytes());
1561
1562        // (3) send message and enqueue successfully
1563        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
1564                messageId, retryCount);
1565        mMockLooper.dispatchAll();
1566        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1567                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1568        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1569        mMockLooper.dispatchAll();
1570
1571        // (4) loop and fail until reach retryCount
1572        for (int i = 0; i < retryCount; ++i) {
1573            mDut.onMessageSendFailNotification(transactionId.getValue(), NanStatusType.NO_OTA_ACK);
1574            mMockLooper.dispatchAll();
1575            inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1576                    eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1577            mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1578            mMockLooper.dispatchAll();
1579        }
1580
1581        // (5) succeed on last retry
1582        mDut.onMessageSendSuccessNotification(transactionId.getValue());
1583        mMockLooper.dispatchAll();
1584
1585        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1586        validateInternalSendMessageQueuesCleanedUp(messageId);
1587
1588        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1589    }
1590
1591    /**
1592     * Validate that when sending a message with a retry count the message is retried the specified
1593     * number of times. Scenario ending with failure.
1594     */
1595    @Test
1596    public void testSendMessageRetransmitFail() throws Exception {
1597        final int clientId = 1005;
1598        final int uid = 1000;
1599        final int pid = 2000;
1600        final String callingPackage = "com.google.somePackage";
1601        final String ssi = "some much longer and more arbitrary data";
1602        final int subscribeId = 15;
1603        final int requestorId = 22;
1604        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1605        final String peerSsi = "some peer ssi data";
1606        final String peerMatchFilter = "filter binary array represented as string";
1607        final int messageId = 6948;
1608        final int retryCount = 3;
1609
1610        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1611        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
1612
1613        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1614        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1615                IWifiAwareDiscoverySessionCallback.class);
1616        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1617        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1618        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1619
1620        mDut.enableUsage();
1621        mMockLooper.dispatchAll();
1622        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1623        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1624        mMockLooper.dispatchAll();
1625
1626        // (1) connect
1627        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1628        mMockLooper.dispatchAll();
1629        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
1630                eq(false), eq(true));
1631        mDut.onConfigSuccessResponse(transactionId.getValue());
1632        mMockLooper.dispatchAll();
1633        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1634
1635        // (2) subscribe & match
1636        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1637        mMockLooper.dispatchAll();
1638        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1639        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1640        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1641                peerMatchFilter.getBytes());
1642        mMockLooper.dispatchAll();
1643        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1644        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1645                peerMatchFilter.getBytes());
1646
1647        // (3) send message and enqueue successfully
1648        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId,
1649                retryCount);
1650        mMockLooper.dispatchAll();
1651        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1652                eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1653        mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1654        mMockLooper.dispatchAll();
1655
1656        // (4) loop and fail until reach retryCount+1
1657        for (int i = 0; i < retryCount + 1; ++i) {
1658            mDut.onMessageSendFailNotification(transactionId.getValue(), NanStatusType.NO_OTA_ACK);
1659            mMockLooper.dispatchAll();
1660
1661            if (i != retryCount) {
1662                inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1663                        eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
1664                mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
1665                mMockLooper.dispatchAll();
1666            }
1667        }
1668
1669        inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
1670                NanStatusType.NO_OTA_ACK);
1671        validateInternalSendMessageQueuesCleanedUp(messageId);
1672
1673        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
1674    }
1675
1676    /**
1677     * Validate that the host-side message queue functions. Tests the perfect case of queue always
1678     * succeeds and all messages are received on first attempt.
1679     */
1680    @Test
1681    public void testSendMessageQueueSequence() throws Exception {
1682        final int clientId = 1005;
1683        final int uid = 1000;
1684        final int pid = 2000;
1685        final String callingPackage = "com.google.somePackage";
1686        final String serviceName = "some-service-name";
1687        final int subscribeId = 15;
1688        final int requestorId = 22;
1689        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1690        final int messageIdBase = 6948;
1691        final int numberOfMessages = 30;
1692        final int queueDepth = 6;
1693
1694        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1695        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1696                .build();
1697
1698        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1699        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1700                IWifiAwareDiscoverySessionCallback.class);
1701        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1702        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1703        ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
1704        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1705
1706        mDut.enableUsage();
1707        mMockLooper.dispatchAll();
1708        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1709        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1710        mMockLooper.dispatchAll();
1711
1712        // (0) connect
1713        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1714        mMockLooper.dispatchAll();
1715        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1716                eq(configRequest), eq(false), eq(true));
1717        mDut.onConfigSuccessResponse(transactionId.getValue());
1718        mMockLooper.dispatchAll();
1719        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1720
1721        // (1) subscribe
1722        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1723        mMockLooper.dispatchAll();
1724        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1725        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1726        mMockLooper.dispatchAll();
1727        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1728
1729        // (2) match
1730        mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
1731        mMockLooper.dispatchAll();
1732        inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
1733
1734        // (3) transmit messages
1735        SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
1736                null, null, null);
1737        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1738                any(), anyInt())).thenAnswer(answerObj);
1739
1740        int remainingMessages = numberOfMessages;
1741        for (int i = 0; i < numberOfMessages; ++i) {
1742            mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
1743                    0);
1744            mMockLooper.dispatchAll();
1745            // at 1/2 interval have the system simulate transmitting a queued message over-the-air
1746            if (i % 2 == 1) {
1747                assertTrue(answerObj.process());
1748                remainingMessages--;
1749                mMockLooper.dispatchAll();
1750            }
1751        }
1752        for (int i = 0; i < remainingMessages; ++i) {
1753            assertTrue(answerObj.process());
1754            mMockLooper.dispatchAll();
1755        }
1756        assertEquals("queue empty", 0, answerObj.queueSize());
1757
1758        inOrder.verify(mockSessionCallback, times(numberOfMessages)).onMessageSendSuccess(
1759                messageIdCaptor.capture());
1760        for (int i = 0; i < numberOfMessages; ++i) {
1761            assertEquals("message ID: " + i, (long) messageIdBase + i,
1762                    (long) messageIdCaptor.getAllValues().get(i));
1763        }
1764
1765        verifyNoMoreInteractions(mockCallback, mockSessionCallback);
1766    }
1767
1768    /**
1769     * Validate that the host-side message queue functions. A combination of imperfect conditions:
1770     * - Failure to queue: synchronous firmware error
1771     * - Failure to queue: asyncronous firmware error
1772     * - Failure to transmit: OTA (which will be retried)
1773     * - Failure to transmit: other
1774     */
1775    @Test
1776    public void testSendMessageQueueSequenceImperfect() throws Exception {
1777        final int clientId = 1005;
1778        final int uid = 1000;
1779        final int pid = 2000;
1780        final String callingPackage = "com.google.somePackage";
1781        final String serviceName = "some-service-name";
1782        final int subscribeId = 15;
1783        final int requestorId = 22;
1784        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1785        final int messageIdBase = 6948;
1786        final int numberOfMessages = 300;
1787        final int queueDepth = 6;
1788        final int retransmitCount = 3; // not the maximum
1789
1790        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1791        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1792                .build();
1793
1794        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1795        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1796                IWifiAwareDiscoverySessionCallback.class);
1797        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1798        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1799        ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
1800        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1801
1802        mDut.enableUsage();
1803        mMockLooper.dispatchAll();
1804        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1805        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1806        mMockLooper.dispatchAll();
1807
1808        // (0) connect
1809        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1810        mMockLooper.dispatchAll();
1811        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1812                eq(configRequest), eq(false), eq(true));
1813        mDut.onConfigSuccessResponse(transactionId.getValue());
1814        mMockLooper.dispatchAll();
1815        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1816
1817        // (1) subscribe
1818        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1819        mMockLooper.dispatchAll();
1820        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1821        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1822        mMockLooper.dispatchAll();
1823        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1824
1825        // (2) match
1826        mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null);
1827        mMockLooper.dispatchAll();
1828        inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
1829
1830        // (3) transmit messages: configure a mix of failures/success
1831        Set<Integer> failQueueCommandImmediately = new HashSet<>();
1832        Set<Integer> failQueueCommandLater = new HashSet<>();
1833        Map<Integer, Integer> numberOfRetries = new HashMap<>();
1834
1835        int numOfSuccesses = 0;
1836        int numOfFailuresInternalFailure = 0;
1837        int numOfFailuresNoOta = 0;
1838        for (int i = 0; i < numberOfMessages; ++i) {
1839            // random results:
1840            // - 0-50: success
1841            // - 51-60: retransmit value (which will fail for >5)
1842            // - 61-70: fail queue later
1843            // - 71-80: fail queue immediately
1844            // - 81-90: fail retransmit with non-OTA failure
1845            int random = mRandomNg.nextInt(90);
1846            if (random <= 50) {
1847                numberOfRetries.put(messageIdBase + i, 0);
1848                numOfSuccesses++;
1849            } else if (random <= 60) {
1850                numberOfRetries.put(messageIdBase + i, random - 51);
1851                if (random - 51 > retransmitCount) {
1852                    numOfFailuresNoOta++;
1853                } else {
1854                    numOfSuccesses++;
1855                }
1856            } else if (random <= 70) {
1857                failQueueCommandLater.add(messageIdBase + i);
1858                numOfFailuresInternalFailure++;
1859            } else if (random <= 80) {
1860                failQueueCommandImmediately.add(messageIdBase + i);
1861                numOfFailuresInternalFailure++;
1862            } else {
1863                numberOfRetries.put(messageIdBase + i, -1);
1864                numOfFailuresInternalFailure++;
1865            }
1866        }
1867
1868        Log.v("WifiAwareStateManagerTest",
1869                "failQueueCommandImmediately=" + failQueueCommandImmediately
1870                        + ", failQueueCommandLater=" + failQueueCommandLater + ", numberOfRetries="
1871                        + numberOfRetries + ", numOfSuccesses=" + numOfSuccesses
1872                        + ", numOfFailuresInternalFailure=" + numOfFailuresInternalFailure
1873                        + ", numOfFailuresNoOta=" + numOfFailuresNoOta);
1874
1875        SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
1876                failQueueCommandImmediately, failQueueCommandLater, numberOfRetries);
1877        when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
1878                any(), anyInt())).thenAnswer(answerObj);
1879
1880        for (int i = 0; i < numberOfMessages; ++i) {
1881            mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
1882                    retransmitCount);
1883            mMockLooper.dispatchAll();
1884        }
1885
1886        while (answerObj.queueSize() != 0) {
1887            assertTrue(answerObj.process());
1888            mMockLooper.dispatchAll();
1889        }
1890
1891        verify(mockSessionCallback, times(numOfSuccesses)).onMessageSendSuccess(anyInt());
1892        verify(mockSessionCallback, times(numOfFailuresInternalFailure)).onMessageSendFail(anyInt(),
1893                eq(NanStatusType.INTERNAL_FAILURE));
1894        verify(mockSessionCallback, times(numOfFailuresNoOta)).onMessageSendFail(anyInt(),
1895                eq(NanStatusType.NO_OTA_ACK));
1896
1897        verifyNoMoreInteractions(mockCallback, mockSessionCallback);
1898    }
1899
1900    /**
1901     * Validate that can send empty message successfully: null, byte[0], ""
1902     */
1903    @Test
1904    public void testSendEmptyMessages() throws Exception {
1905        final int clientId = 1005;
1906        final int uid = 1000;
1907        final int pid = 2000;
1908        final String callingPackage = "com.google.somePackage";
1909        final String serviceName = "some-service-name";
1910        final String ssi = "some much longer and more arbitrary data";
1911        final int subscribeId = 15;
1912        final int requestorId = 22;
1913        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
1914        final String peerSsi = "some peer ssi data";
1915        final String peerMatchFilter = "filter binary array represented as string";
1916        final int messageId = 6948;
1917
1918        ConfigRequest configRequest = new ConfigRequest.Builder().build();
1919        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
1920                .setServiceSpecificInfo(ssi.getBytes())
1921                .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
1922                .build();
1923
1924        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
1925        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
1926                IWifiAwareDiscoverySessionCallback.class);
1927        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
1928        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
1929        ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class);
1930        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
1931
1932        mDut.enableUsage();
1933        mMockLooper.dispatchAll();
1934        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
1935        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
1936        mMockLooper.dispatchAll();
1937
1938        // (0) connect
1939        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
1940        mMockLooper.dispatchAll();
1941        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
1942                eq(configRequest), eq(false), eq(true));
1943        mDut.onConfigSuccessResponse(transactionId.getValue());
1944        mMockLooper.dispatchAll();
1945        inOrder.verify(mockCallback).onConnectSuccess(clientId);
1946
1947        // (1) subscribe
1948        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
1949        mMockLooper.dispatchAll();
1950        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
1951        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
1952        mMockLooper.dispatchAll();
1953        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
1954
1955        // (2) match
1956        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
1957                peerMatchFilter.getBytes());
1958        mMockLooper.dispatchAll();
1959        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
1960                peerMatchFilter.getBytes());
1961
1962        // (3) message null Tx successful queuing
1963        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageId, 0);
1964        mMockLooper.dispatchAll();
1965        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1966                eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId));
1967        short tid = transactionId.getValue();
1968        mDut.onMessageSendQueuedSuccessResponse(tid);
1969        mMockLooper.dispatchAll();
1970
1971        // (4) final Tx results (on-air results)
1972        mDut.onMessageSendSuccessNotification(tid);
1973        mMockLooper.dispatchAll();
1974        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1975        validateInternalSendMessageQueuesCleanedUp(messageId);
1976
1977        // (5) message byte[0] Tx successful queuing
1978        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, new byte[0], messageId, 0);
1979        mMockLooper.dispatchAll();
1980        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1981                eq(requestorId), eq(peerMac), eq(new byte[0]), eq(messageId));
1982        tid = transactionId.getValue();
1983        mDut.onMessageSendQueuedSuccessResponse(tid);
1984        mMockLooper.dispatchAll();
1985
1986        // (6) final Tx results (on-air results)
1987        mDut.onMessageSendSuccessNotification(tid);
1988        mMockLooper.dispatchAll();
1989        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
1990        validateInternalSendMessageQueuesCleanedUp(messageId);
1991
1992        // (7) message "" Tx successful queuing
1993        mDut.sendMessage(clientId, sessionId.getValue(), requestorId, "".getBytes(), messageId, 0);
1994        mMockLooper.dispatchAll();
1995        inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
1996                eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId));
1997        collector.checkThat("Empty message contents", "",
1998                equalTo(new String(byteArrayCaptor.getValue())));
1999        tid = transactionId.getValue();
2000        mDut.onMessageSendQueuedSuccessResponse(tid);
2001        mMockLooper.dispatchAll();
2002
2003        // (8) final Tx results (on-air results)
2004        mDut.onMessageSendSuccessNotification(tid);
2005        mMockLooper.dispatchAll();
2006        inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
2007        validateInternalSendMessageQueuesCleanedUp(messageId);
2008
2009        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
2010    }
2011
2012    private class SendMessageQueueModelAnswer extends MockAnswerUtil.AnswerWithArguments {
2013        private final int mMaxQueueDepth;
2014
2015        // keyed by message ID
2016        private final Set<Integer> mFailQueueCommandImmediately; // return a false
2017        private final Set<Integer> mFailQueueCommandLater; // return an error != TX_QUEUE_FULL
2018
2019        // # of times to return NO_OTA_ACK before returning SUCCESS. So a 0 means success on first
2020        // try, a very large number means - never succeed (since max retry is 5).
2021        // a -1 impiles a non-OTA failure: on first attempt
2022        private final Map<Integer, Integer> mRetryLimit;
2023
2024        private final LinkedList<Short> mQueue = new LinkedList<>(); // transaction ID (tid)
2025        private final Map<Short, Integer> mMessageIdsByTid = new HashMap<>(); // tid -> message ID
2026        private final Map<Integer, Integer> mTriesUsedByMid = new HashMap<>(); // mid -> # of retx
2027
2028        SendMessageQueueModelAnswer(int maxQueueDepth, Set<Integer> failQueueCommandImmediately,
2029                Set<Integer> failQueueCommandLater, Map<Integer, Integer> numberOfRetries) {
2030            mMaxQueueDepth = maxQueueDepth;
2031            mFailQueueCommandImmediately = failQueueCommandImmediately;
2032            mFailQueueCommandLater = failQueueCommandLater;
2033            mRetryLimit = numberOfRetries;
2034
2035            if (mRetryLimit != null) {
2036                for (int mid : mRetryLimit.keySet()) {
2037                    mTriesUsedByMid.put(mid, 0);
2038                }
2039            }
2040        }
2041
2042        public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
2043                byte[] dest, byte[] message, int messageId) throws Exception {
2044            if (mFailQueueCommandImmediately != null && mFailQueueCommandImmediately.contains(
2045                    messageId)) {
2046                return false;
2047            }
2048
2049            if (mFailQueueCommandLater != null && mFailQueueCommandLater.contains(messageId)) {
2050                mDut.onMessageSendQueuedFailResponse(transactionId, NanStatusType.INTERNAL_FAILURE);
2051            } else {
2052                if (mQueue.size() <= mMaxQueueDepth) {
2053                    mQueue.addLast(transactionId);
2054                    mMessageIdsByTid.put(transactionId, messageId);
2055                    mDut.onMessageSendQueuedSuccessResponse(transactionId);
2056                } else {
2057                    mDut.onMessageSendQueuedFailResponse(transactionId,
2058                            NanStatusType.FOLLOWUP_TX_QUEUE_FULL);
2059                }
2060            }
2061
2062            return true;
2063        }
2064
2065        /**
2066         * Processes the first message in the queue: i.e. responds as if sent over-the-air
2067         * (successfully or failed)
2068         */
2069        boolean process() {
2070            if (mQueue.size() == 0) {
2071                return false;
2072            }
2073            short tid = mQueue.poll();
2074            int mid = mMessageIdsByTid.get(tid);
2075
2076            if (mRetryLimit != null && mRetryLimit.containsKey(mid)) {
2077                int numRetries = mRetryLimit.get(mid);
2078                if (numRetries == -1) {
2079                    mDut.onMessageSendFailNotification(tid, NanStatusType.INTERNAL_FAILURE);
2080                } else {
2081                    int currentRetries = mTriesUsedByMid.get(mid);
2082                    if (currentRetries > numRetries) {
2083                        return false; // shouldn't be retrying!?
2084                    } else if (currentRetries == numRetries) {
2085                        mDut.onMessageSendSuccessNotification(tid);
2086                    } else {
2087                        mDut.onMessageSendFailNotification(tid, NanStatusType.NO_OTA_ACK);
2088                    }
2089                    mTriesUsedByMid.put(mid, currentRetries + 1);
2090                }
2091            } else {
2092                mDut.onMessageSendSuccessNotification(tid);
2093            }
2094
2095            return true;
2096        }
2097
2098        /**
2099         * Returns the number of elements in the queue.
2100         */
2101        int queueSize() {
2102            return mQueue.size();
2103        }
2104    }
2105
2106    /**
2107     * Validate that start ranging function fills-in correct MAC addresses for peer IDs and
2108     * passed along to RTT module.
2109     */
2110    @Test
2111    public void testStartRanging() throws Exception {
2112        final int clientId = 1005;
2113        final int uid = 1000;
2114        final int pid = 2000;
2115        final String callingPackage = "com.google.somePackage";
2116        final int subscribeId = 15;
2117        final int requestorId = 22;
2118        final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
2119        final String peerSsi = "some peer ssi data";
2120        final String peerMatchFilter = "filter binary array represented as string";
2121        final int rangingId = 18423;
2122        final RttManager.RttParams[] params = new RttManager.RttParams[2];
2123        params[0] = new RttManager.RttParams();
2124        params[0].bssid = Integer.toString(requestorId);
2125        params[1] = new RttManager.RttParams();
2126        params[1].bssid = Integer.toString(requestorId + 5);
2127
2128        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2129        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2130
2131        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2132        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2133                IWifiAwareDiscoverySessionCallback.class);
2134
2135        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2136        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2137        ArgumentCaptor<WifiAwareClientState> clientCaptor =
2138                ArgumentCaptor.forClass(WifiAwareClientState.class);
2139        ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
2140                ArgumentCaptor.forClass(RttManager.RttParams[].class);
2141
2142        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative,
2143                mMockAwareRttStateManager);
2144
2145        mDut.enableUsage();
2146        mMockLooper.dispatchAll();
2147        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2148        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2149        mMockLooper.dispatchAll();
2150
2151        // (1) connect
2152        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2153        mMockLooper.dispatchAll();
2154        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2155                eq(false), eq(true));
2156        mDut.onConfigSuccessResponse(transactionId.getValue());
2157        mMockLooper.dispatchAll();
2158        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2159
2160        // (2) subscribe & match
2161        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
2162        mMockLooper.dispatchAll();
2163        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
2164        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
2165        mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
2166                peerMatchFilter.getBytes());
2167        mMockLooper.dispatchAll();
2168        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2169        inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
2170                peerMatchFilter.getBytes());
2171
2172        // (3) start ranging: pass along a valid peer ID and an invalid one
2173        mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
2174        mMockLooper.dispatchAll();
2175        inOrder.verify(mMockAwareRttStateManager).startRanging(eq(rangingId),
2176                clientCaptor.capture(), rttParamsCaptor.capture());
2177        collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B",
2178                equalTo(rttParamsCaptor.getValue()[0].bssid));
2179        collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid));
2180
2181        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative,
2182                mMockAwareRttStateManager);
2183    }
2184
2185    /**
2186     * Test sequence of configuration: (1) config1, (2) config2 - incompatible,
2187     * (3) config3 - compatible with config1 (requiring upgrade), (4) disconnect
2188     * config3 (should get a downgrade), (5) disconnect config1 (should get a
2189     * disable).
2190     */
2191    @Test
2192    public void testConfigs() throws Exception {
2193        final int clientId1 = 9999;
2194        final int clientId2 = 1001;
2195        final int clientId3 = 1005;
2196        final int uid = 1000;
2197        final int pid = 2000;
2198        final String callingPackage = "com.google.somePackage";
2199        final int masterPref1 = 111;
2200        final int masterPref3 = 115;
2201        final int dwInterval1Band24 = 2;
2202        final int dwInterval3Band24 = 1;
2203        final int dwInterval3Band5 = 0;
2204
2205        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2206        ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
2207
2208        ConfigRequest configRequest1 = new ConfigRequest.Builder()
2209                .setClusterLow(5).setClusterHigh(100)
2210                .setMasterPreference(masterPref1)
2211                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval1Band24)
2212                .build();
2213
2214        ConfigRequest configRequest2 = new ConfigRequest.Builder()
2215                .setSupport5gBand(true) // compatible
2216                .setClusterLow(7).setClusterHigh(155) // incompatible!
2217                .setMasterPreference(0) // compatible
2218                .build();
2219
2220        ConfigRequest configRequest3  = new ConfigRequest.Builder()
2221                .setSupport5gBand(true) // compatible (will use true)
2222                .setClusterLow(5).setClusterHigh(100) // identical (hence compatible)
2223                .setMasterPreference(masterPref3) // compatible (will use max)
2224                // compatible: will use min
2225                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval3Band24)
2226                // compatible: will use interval3 since interval1 not init
2227                .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwInterval3Band5)
2228                .build();
2229
2230        IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
2231        IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
2232        IWifiAwareEventCallback mockCallback3 = mock(IWifiAwareEventCallback.class);
2233
2234        InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
2235
2236        mDut.enableUsage();
2237        mMockLooper.dispatchAll();
2238        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2239        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2240        mMockLooper.dispatchAll();
2241
2242        // (1) config1 (valid)
2243        mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1, false);
2244        mMockLooper.dispatchAll();
2245        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2246                crCapture.capture(), eq(false), eq(true));
2247        collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
2248        mDut.onConfigSuccessResponse(transactionId.getValue());
2249        mMockLooper.dispatchAll();
2250        inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
2251
2252        // (2) config2 (incompatible with config1)
2253        mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest2, false);
2254        mMockLooper.dispatchAll();
2255        inOrder.verify(mockCallback2).onConnectFail(NanStatusType.INTERNAL_FAILURE);
2256        validateInternalClientInfoCleanedUp(clientId2);
2257
2258        // (3) config3 (compatible with config1)
2259        mDut.connect(clientId3, uid, pid, callingPackage, mockCallback3, configRequest3, true);
2260        mMockLooper.dispatchAll();
2261        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2262                crCapture.capture(), eq(true), eq(false));
2263        mDut.onConfigSuccessResponse(transactionId.getValue());
2264        mMockLooper.dispatchAll();
2265        inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
2266
2267        collector.checkThat("support 5g: or", true, equalTo(crCapture.getValue().mSupport5gBand));
2268        collector.checkThat("master preference: max", Math.max(masterPref1, masterPref3),
2269                equalTo(crCapture.getValue().mMasterPreference));
2270        collector.checkThat("dw interval on 2.4: ~min",
2271                Math.min(dwInterval1Band24, dwInterval3Band24),
2272                equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
2273                        .NAN_BAND_24GHZ]));
2274        collector.checkThat("dw interval on 5: ~min", dwInterval3Band5,
2275                equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
2276                        .NAN_BAND_5GHZ]));
2277
2278        // (4) disconnect config3: downgrade to config1
2279        mDut.disconnect(clientId3);
2280        mMockLooper.dispatchAll();
2281        validateInternalClientInfoCleanedUp(clientId3);
2282        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2283                crCapture.capture(), eq(false), eq(false));
2284
2285        collector.checkThat("configRequest1", configRequest1, equalTo(crCapture.getValue()));
2286
2287        mDut.onConfigSuccessResponse(transactionId.getValue());
2288        mMockLooper.dispatchAll();
2289
2290        // (5) disconnect config1: disable
2291        mDut.disconnect(clientId1);
2292        mMockLooper.dispatchAll();
2293        validateInternalClientInfoCleanedUp(clientId1);
2294        inOrder.verify(mMockNative).disable((short) 0);
2295
2296        verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
2297    }
2298
2299    /**
2300     * Summary: disconnect a client while there are pending transactions.
2301     */
2302    @Test
2303    public void testDisconnectWithPendingTransactions() throws Exception {
2304        final int clientId = 125;
2305        final int uid = 1000;
2306        final int pid = 2000;
2307        final String callingPackage = "com.google.somePackage";
2308        final int clusterLow = 5;
2309        final int clusterHigh = 100;
2310        final int masterPref = 111;
2311        final String serviceName = "some-service-name";
2312        final String ssi = "some much longer and more arbitrary data";
2313        final int publishId = 22;
2314
2315        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
2316                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
2317
2318        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
2319                serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
2320                PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
2321
2322        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2323        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2324        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2325                IWifiAwareDiscoverySessionCallback.class);
2326        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2327
2328        mDut.enableUsage();
2329        mMockLooper.dispatchAll();
2330        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2331        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2332        mMockLooper.dispatchAll();
2333
2334        // (1) connect
2335        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2336        mMockLooper.dispatchAll();
2337        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2338                eq(false), eq(true));
2339        mDut.onConfigSuccessResponse(transactionId.getValue());
2340        mMockLooper.dispatchAll();
2341        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2342
2343        // (2) publish (no response yet)
2344        mDut.publish(clientId, publishConfig, mockSessionCallback);
2345        mMockLooper.dispatchAll();
2346        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2347
2348        // (3) disconnect (but doesn't get executed until get a RESPONSE to the
2349        // previous publish)
2350        mDut.disconnect(clientId);
2351        mMockLooper.dispatchAll();
2352
2353        // (4) get successful response to the publish
2354        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
2355        mMockLooper.dispatchAll();
2356        inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
2357        inOrder.verify(mMockNative).stopPublish((short) 0, publishId);
2358        inOrder.verify(mMockNative).disable((short) 0);
2359
2360        validateInternalClientInfoCleanedUp(clientId);
2361
2362        // (5) trying to publish on the same client: NOP
2363        mDut.publish(clientId, publishConfig, mockSessionCallback);
2364        mMockLooper.dispatchAll();
2365
2366        // (6) got some callback on original publishId - should be ignored
2367        mDut.onSessionTerminatedNotification(publishId, 0, true);
2368        mMockLooper.dispatchAll();
2369
2370        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2371    }
2372
2373    /**
2374     * Validate that an unknown transaction (i.e. a callback from HAL with an
2375     * unknown type) is simply ignored - but also cleans up its state.
2376     */
2377    @Test
2378    public void testUnknownTransactionType() throws Exception {
2379        final int clientId = 129;
2380        final int uid = 1000;
2381        final int pid = 2000;
2382        final String callingPackage = "com.google.somePackage";
2383        final int clusterLow = 15;
2384        final int clusterHigh = 192;
2385        final int masterPref = 234;
2386        final String serviceName = "some-service-name";
2387        final String ssi = "some much longer and more arbitrary data";
2388
2389        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
2390                .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
2391
2392        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
2393                serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
2394                PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
2395
2396        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2397        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2398        IWifiAwareDiscoverySessionCallback mockPublishSessionCallback = mock(
2399                IWifiAwareDiscoverySessionCallback.class);
2400        InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
2401
2402        mDut.enableUsage();
2403        mMockLooper.dispatchAll();
2404        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2405        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2406        mMockLooper.dispatchAll();
2407
2408        // (1) connect
2409        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2410        mMockLooper.dispatchAll();
2411        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2412                eq(false), eq(true));
2413        mDut.onConfigSuccessResponse(transactionId.getValue());
2414        mMockLooper.dispatchAll();
2415        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2416
2417        // (2) publish - no response
2418        mDut.publish(clientId, publishConfig, mockPublishSessionCallback);
2419        mMockLooper.dispatchAll();
2420        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2421
2422        verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback);
2423    }
2424
2425    /**
2426     * Validate that a NoOp transaction (i.e. a callback from HAL which doesn't
2427     * require any action except clearing up state) actually cleans up its state
2428     * (and does nothing else).
2429     */
2430    @Test
2431    public void testNoOpTransaction() throws Exception {
2432        final int clientId = 1294;
2433        final int uid = 1000;
2434        final int pid = 2000;
2435        final String callingPackage = "com.google.somePackage";
2436
2437        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2438
2439        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2440        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2441        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2442                IWifiAwareDiscoverySessionCallback.class);
2443        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2444
2445        mDut.enableUsage();
2446        mMockLooper.dispatchAll();
2447        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2448        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2449        mMockLooper.dispatchAll();
2450
2451        // (1) connect (no response)
2452        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2453        mMockLooper.dispatchAll();
2454        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2455                eq(false), eq(true));
2456
2457        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2458    }
2459
2460    /**
2461     * Validate that getting callbacks from HAL with unknown (expired)
2462     * transaction ID or invalid publish/subscribe ID session doesn't have any
2463     * impact.
2464     */
2465    @Test
2466    public void testInvalidCallbackIdParameters() throws Exception {
2467        final int pubSubId = 1235;
2468        final int clientId = 132;
2469        final int uid = 1000;
2470        final int pid = 2000;
2471        final String callingPackage = "com.google.somePackage";
2472
2473        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2474
2475        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2476        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2477        InOrder inOrder = inOrder(mMockNative, mockCallback);
2478
2479        mDut.enableUsage();
2480        mMockLooper.dispatchAll();
2481        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2482        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2483        mMockLooper.dispatchAll();
2484
2485        // (1) connect and succeed
2486        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2487        mMockLooper.dispatchAll();
2488        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2489                eq(false), eq(true));
2490        short transactionIdConfig = transactionId.getValue();
2491        mDut.onConfigSuccessResponse(transactionIdConfig);
2492        mMockLooper.dispatchAll();
2493        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2494
2495        // (2) use the same transaction ID to send a bunch of other responses
2496        mDut.onConfigSuccessResponse(transactionIdConfig);
2497        mDut.onConfigFailedResponse(transactionIdConfig, -1);
2498        mDut.onSessionConfigFailResponse(transactionIdConfig, true, -1);
2499        mDut.onMessageSendQueuedSuccessResponse(transactionIdConfig);
2500        mDut.onMessageSendQueuedFailResponse(transactionIdConfig, -1);
2501        mDut.onSessionConfigFailResponse(transactionIdConfig, false, -1);
2502        mDut.onMatchNotification(-1, -1, new byte[0], new byte[0], new byte[0]);
2503        mDut.onSessionTerminatedNotification(-1, -1, true);
2504        mDut.onSessionTerminatedNotification(-1, -1, false);
2505        mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[0]);
2506        mDut.onSessionConfigSuccessResponse(transactionIdConfig, true, pubSubId);
2507        mDut.onSessionConfigSuccessResponse(transactionIdConfig, false, pubSubId);
2508        mMockLooper.dispatchAll();
2509
2510        verifyNoMoreInteractions(mMockNative, mockCallback);
2511    }
2512
2513    /**
2514     * Validate that trying to update-subscribe on a publish session fails.
2515     */
2516    @Test
2517    public void testSubscribeOnPublishSessionType() throws Exception {
2518        final int clientId = 188;
2519        final int uid = 1000;
2520        final int pid = 2000;
2521        final String callingPackage = "com.google.somePackage";
2522        final int publishId = 25;
2523
2524        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2525        PublishConfig publishConfig = new PublishConfig.Builder().build();
2526        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2527
2528        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2529        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2530        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2531        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2532                IWifiAwareDiscoverySessionCallback.class);
2533        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2534
2535        mDut.enableUsage();
2536        mMockLooper.dispatchAll();
2537        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2538        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2539        mMockLooper.dispatchAll();
2540
2541        // (1) connect
2542        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2543        mMockLooper.dispatchAll();
2544        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
2545                eq(false), eq(true));
2546        mDut.onConfigSuccessResponse(transactionId.getValue());
2547        mMockLooper.dispatchAll();
2548        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2549
2550        // (2) publish
2551        mDut.publish(clientId, publishConfig, mockSessionCallback);
2552        mMockLooper.dispatchAll();
2553        inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2554        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
2555        mMockLooper.dispatchAll();
2556        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2557
2558        // (3) update-subscribe -> failure
2559        mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
2560        mMockLooper.dispatchAll();
2561        inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
2562
2563        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2564    }
2565
2566    /**
2567     * Validate that trying to (re)subscribe on a publish session or (re)publish
2568     * on a subscribe session fails.
2569     */
2570    @Test
2571    public void testPublishOnSubscribeSessionType() throws Exception {
2572        final int clientId = 188;
2573        final int uid = 1000;
2574        final int pid = 2000;
2575        final String callingPackage = "com.google.somePackage";
2576        final int subscribeId = 25;
2577
2578        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2579        PublishConfig publishConfig = new PublishConfig.Builder().build();
2580        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
2581
2582        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2583        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2584        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2585        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2586                IWifiAwareDiscoverySessionCallback.class);
2587        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2588
2589        mDut.enableUsage();
2590        mMockLooper.dispatchAll();
2591        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2592        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2593        mMockLooper.dispatchAll();
2594
2595        // (1) connect
2596        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2597        mMockLooper.dispatchAll();
2598        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2599                eq(configRequest), eq(false), eq(true));
2600        mDut.onConfigSuccessResponse(transactionId.getValue());
2601        mMockLooper.dispatchAll();
2602        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2603
2604        // (2) subscribe
2605        mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
2606        mMockLooper.dispatchAll();
2607        inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
2608        mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
2609        mMockLooper.dispatchAll();
2610        inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2611
2612        // (3) update-publish -> error
2613        mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
2614        mMockLooper.dispatchAll();
2615        inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
2616
2617        verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
2618    }
2619
2620    /**
2621     * Validate that the session ID increments monotonically
2622     */
2623    @Test
2624    public void testSessionIdIncrement() throws Exception {
2625        final int clientId = 188;
2626        final int uid = 1000;
2627        final int pid = 2000;
2628        final String callingPackage = "com.google.somePackage";
2629        int loopCount = 100;
2630
2631        ConfigRequest configRequest = new ConfigRequest.Builder().build();
2632        PublishConfig publishConfig = new PublishConfig.Builder().build();
2633
2634        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
2635        ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
2636        IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
2637        IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
2638                IWifiAwareDiscoverySessionCallback.class);
2639        InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
2640
2641        mDut.enableUsage();
2642        mMockLooper.dispatchAll();
2643        inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
2644        mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
2645        mMockLooper.dispatchAll();
2646
2647        // (1) connect
2648        mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
2649        mMockLooper.dispatchAll();
2650        inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
2651                eq(configRequest), eq(false), eq(true));
2652        mDut.onConfigSuccessResponse(transactionId.getValue());
2653        mMockLooper.dispatchAll();
2654        inOrder.verify(mockCallback).onConnectSuccess(clientId);
2655
2656        int prevId = 0;
2657        for (int i = 0; i < loopCount; ++i) {
2658            // (2) publish
2659            mDut.publish(clientId, publishConfig, mockSessionCallback);
2660            mMockLooper.dispatchAll();
2661            inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
2662
2663            // (3) publish-success
2664            mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1);
2665            mMockLooper.dispatchAll();
2666            inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
2667
2668            if (i != 0) {
2669                assertTrue("Session ID incrementing", sessionId.getValue() > prevId);
2670            }
2671            prevId = sessionId.getValue();
2672        }
2673    }
2674
2675    /*
2676     * Tests of internal state of WifiAwareStateManager: very limited (not usually
2677     * a good idea). However, these test that the internal state is cleaned-up
2678     * appropriately. Alternatively would cause issues with memory leaks or
2679     * information leak between sessions.
2680     */
2681
2682    /**
2683     * Utility routine used to validate that the internal state is cleaned-up
2684     * after a client is disconnected. To be used in every test which terminates
2685     * a client.
2686     *
2687     * @param clientId The ID of the client which should be deleted.
2688     */
2689    private void validateInternalClientInfoCleanedUp(int clientId) throws Exception {
2690        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2691        collector.checkThat("Client record not cleared up for clientId=" + clientId, client,
2692                nullValue());
2693    }
2694
2695    /**
2696     * Utility routine used to validate that the internal state is cleaned-up
2697     * (deleted) after a session is terminated through API (not callback!). To
2698     * be used in every test which terminates a session.
2699     *
2700     * @param clientId The ID of the client containing the session.
2701     * @param sessionId The ID of the terminated session.
2702     */
2703    private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId)
2704            throws Exception {
2705        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2706        collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
2707        WifiAwareDiscoverySessionState session = getInternalSessionState(client, sessionId);
2708        collector.checkThat("Client record not cleaned-up for sessionId=" + sessionId, session,
2709                nullValue());
2710    }
2711
2712    /**
2713     * Utility routine used to validate that the internal state is cleaned-up
2714     * (deleted) correctly. Checks that a specific client has no sessions
2715     * attached to it.
2716     *
2717     * @param clientId The ID of the client which we want to check.
2718     */
2719    private void validateInternalNoSessions(int clientId) throws Exception {
2720        WifiAwareClientState client = getInternalClientState(mDut, clientId);
2721        collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
2722
2723        Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
2724        field.setAccessible(true);
2725        @SuppressWarnings("unchecked")
2726        SparseArray<WifiAwareDiscoverySessionState> sessions =
2727                (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
2728
2729        collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
2730                equalTo(0));
2731    }
2732
2733    /**
2734     * Validates that the broadcast sent on Aware status change is correct.
2735     *
2736     * @param expectedEnabled The expected change status - i.e. are we expected
2737     *            to announce that Aware is enabled (true) or disabled (false).
2738     */
2739    private void validateCorrectAwareStatusChangeBroadcast(InOrder inOrder,
2740            boolean expectedEnabled) {
2741        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
2742
2743        inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL));
2744
2745        collector.checkThat("intent action", intent.getValue().getAction(),
2746                equalTo(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED));
2747    }
2748
2749    /*
2750     * Utilities
2751     */
2752    private void dumpDut(String prefix) {
2753        StringWriter sw = new StringWriter();
2754        mDut.dump(null, new PrintWriter(sw), null);
2755        Log.e("WifiAwareStateManagerTest", prefix + sw.toString());
2756    }
2757
2758    private static void installMocksInStateManager(WifiAwareStateManager awareStateManager,
2759            WifiAwareRttStateManager mockRtt, WifiAwareDataPathStateManager mockDpMgr)
2760            throws Exception {
2761        Field field = WifiAwareStateManager.class.getDeclaredField("mRtt");
2762        field.setAccessible(true);
2763        field.set(awareStateManager, mockRtt);
2764
2765        field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr");
2766        field.setAccessible(true);
2767        field.set(awareStateManager, mockDpMgr);
2768    }
2769
2770    private static WifiAwareClientState getInternalClientState(WifiAwareStateManager dut,
2771            int clientId) throws Exception {
2772        Field field = WifiAwareStateManager.class.getDeclaredField("mClients");
2773        field.setAccessible(true);
2774        @SuppressWarnings("unchecked")
2775        SparseArray<WifiAwareClientState> clients = (SparseArray<WifiAwareClientState>) field.get(
2776                dut);
2777
2778        return clients.get(clientId);
2779    }
2780
2781    private static WifiAwareDiscoverySessionState getInternalSessionState(
2782            WifiAwareClientState client, int sessionId) throws Exception {
2783        Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
2784        field.setAccessible(true);
2785        @SuppressWarnings("unchecked")
2786        SparseArray<WifiAwareDiscoverySessionState> sessions =
2787                (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
2788
2789        return sessions.get(sessionId);
2790    }
2791
2792    private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
2793        Field field = WifiAwareStateManager.class.getDeclaredField("mSm");
2794        field.setAccessible(true);
2795        WifiAwareStateManager.WifiAwareStateMachine sm =
2796                (WifiAwareStateManager.WifiAwareStateMachine) field.get(mDut);
2797
2798        field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
2799                "mHostQueuedSendMessages");
2800        field.setAccessible(true);
2801        SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
2802
2803        field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
2804                "mFwQueuedSendMessages");
2805        field.setAccessible(true);
2806        Map<Short, Message> fwQueuedSendMessages = (Map<Short, Message>) field.get(sm);
2807
2808        for (int i = 0; i < hostQueuedSendMessages.size(); ++i) {
2809            Message msg = hostQueuedSendMessages.valueAt(i);
2810            if (msg.getData().getInt("message_id") == messageId) {
2811                collector.checkThat(
2812                        "Message not cleared-up from host queue. Message ID=" + messageId, msg,
2813                        nullValue());
2814            }
2815        }
2816
2817        for (Message msg: fwQueuedSendMessages.values()) {
2818            if (msg.getData().getInt("message_id") == messageId) {
2819                collector.checkThat(
2820                        "Message not cleared-up from firmware queue. Message ID=" + messageId, msg,
2821                        nullValue());
2822            }
2823        }
2824    }
2825
2826    private static Capabilities getCapabilities() {
2827        Capabilities cap = new Capabilities();
2828        cap.maxConcurrentAwareClusters = 1;
2829        cap.maxPublishes = 2;
2830        cap.maxSubscribes = 2;
2831        cap.maxServiceNameLen = 255;
2832        cap.maxMatchFilterLen = 255;
2833        cap.maxTotalMatchFilterLen = 255;
2834        cap.maxServiceSpecificInfoLen = 255;
2835        cap.maxExtendedServiceSpecificInfoLen = 255;
2836        cap.maxNdiInterfaces = 1;
2837        cap.maxNdpSessions = 1;
2838        cap.maxAppInfoLen = 255;
2839        cap.maxQueuedTransmitMessages = 6;
2840        return cap;
2841    }
2842}
2843
2844