blob: cb78f7232eb2673d31c0f8e5bf28e367230d09d6 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi.nan;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyShort;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.AppOpsManager;
import android.app.test.MockAnswerUtil;
import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.wifi.RttManager;
import android.net.wifi.nan.ConfigRequest;
import android.net.wifi.nan.IWifiNanDiscoverySessionCallback;
import android.net.wifi.nan.IWifiNanEventCallback;
import android.net.wifi.nan.PublishConfig;
import android.net.wifi.nan.SubscribeConfig;
import android.net.wifi.nan.WifiNanDiscoverySessionCallback;
import android.net.wifi.nan.WifiNanEventCallback;
import android.net.wifi.nan.WifiNanManager;
import android.os.Message;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import libcore.util.HexEncoding;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
/**
* Unit test harness for WifiNanStateManager.
*/
@SmallTest
public class WifiNanStateManagerTest {
private TestLooper mMockLooper;
private Random mRandomNg = new Random(15687);
private WifiNanStateManager mDut;
@Mock private WifiNanNative mMockNative;
@Mock private Context mMockContext;
@Mock private AppOpsManager mMockAppOpsManager;
@Mock private WifiNanRttStateManager mMockNanRttStateManager;
TestAlarmManager mAlarmManager;
@Mock private WifiNanDataPathStateManager mMockNanDataPathStatemanager;
@Rule
public ErrorCollector collector = new ErrorCollector();
private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
/**
* Pre-test configuration. Initialize and install mocks.
*/
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mAlarmManager = new TestAlarmManager();
when(mMockContext.getSystemService(Context.ALARM_SERVICE))
.thenReturn(mAlarmManager.getAlarmManager());
when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mock(ConnectivityManager.class));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
when(mMockContext.checkPermission(eq(android.Manifest.permission.ACCESS_FINE_LOCATION),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(),
anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
mMockLooper = new TestLooper();
mDut = installNewNanStateManager();
mDut.start(mMockContext, mMockLooper.getLooper());
installMocksInStateManager(mDut, mMockNanRttStateManager, mMockNanDataPathStatemanager);
when(mMockNative.enableAndConfigure(anyShort(), any(ConfigRequest.class), anyBoolean()))
.thenReturn(true);
when(mMockNative.disable(anyShort())).thenReturn(true);
when(mMockNative.publish(anyShort(), anyInt(), any(PublishConfig.class))).thenReturn(true);
when(mMockNative.subscribe(anyShort(), anyInt(), any(SubscribeConfig.class)))
.thenReturn(true);
when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class),
any(byte[].class), anyInt())).thenReturn(true);
when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
installMockWifiNanNative(mMockNative);
}
/**
* Validate that NAN data-path interfaces are brought up and down correctly.
*/
@Test
public void testNanDataPathInterfaceUpDown() throws Exception {
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mMockNanDataPathStatemanager);
// (1) enable usage
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, true);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
inOrder.verify(mMockNanDataPathStatemanager).createAllInterfaces();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
// (2) disable usage
mDut.disableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNanDataPathStatemanager).onNanDownCleanupDataPaths();
inOrder.verify(mMockNative).disable((short) 0);
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, false);
inOrder.verify(mMockNanDataPathStatemanager).deleteAllInterfaces();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
verifyNoMoreInteractions(mMockNative, mMockNanDataPathStatemanager);
}
/**
* Validate that APIs aren't functional when usage is disabled.
*/
@Test
public void testDisableUsageDisablesApis() throws Exception {
final int clientId = 12314;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, true);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
// (2) disable usage and validate state
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
inOrder.verify(mMockNative).disable((short) 0);
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, false);
// (3) try connecting and validate that get nothing (app should be aware of non-availability
// through state change broadcast and/or query API)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/**
* Validate that when API usage is disabled while in the middle of a connection that internal
* state is cleaned-up, and that all subsequent operations are NOP. Then enable usage again and
* validate that operates correctly.
*/
@Test
public void testDisableUsageFlow() throws Exception {
final int clientId = 12341;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, true);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
// (2) connect (successfully)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (3) disable usage & verify callbacks
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
inOrder.verify(mMockNative).disable((short) 0);
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, false);
validateInternalClientInfoCleanedUp(clientId);
// (4) try connecting again and validate that just get an onNanDown
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
// (5) disable usage again and validate that not much happens
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
// (6) enable usage
mDut.enableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
inOrder.verify(mMockNative).deInitNan();
validateCorrectNanStatusChangeBroadcast(inOrder, true);
// (7) connect (should be successful)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/**
* Validates that all events are delivered with correct arguments. Validates
* that IdentityChanged not delivered if configuration disables delivery.
*/
@Test
public void testNanEventsDelivery() throws Exception {
final int clientId1 = 1005;
final int clientId2 = 1007;
final int clusterLow = 5;
final int clusterHigh = 100;
final int masterPref = 111;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reason = WifiNanEventCallback.REASON_OTHER;
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] someMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref)
.setEnableIdentityChangeCallback(false).build();
ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref)
.setEnableIdentityChangeCallback(true).build();
IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class);
IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class);
ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionIdCapture.capture());
mDut.onCapabilitiesUpdateResponse(transactionIdCapture.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect 1st and 2nd clients
mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1);
mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest2);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
eq(configRequest1), eq(true));
short transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
// (2) finish connection of 2nd client
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
eq(configRequest2), eq(false));
transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onConnectSuccess(clientId2);
// (3) deliver NAN events - without LOCATIONING permission
mDut.onClusterChangeNotification(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac);
mDut.onInterfaceAddressChangeNotification(someMac);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
// (4) deliver new identity - still without LOCATIONING permission (should get an event)
mDut.onInterfaceAddressChangeNotification(someMac2);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
// (5) deliver same identity - still without LOCATIONING permission (should
// not get an event)
mDut.onInterfaceAddressChangeNotification(someMac2);
mMockLooper.dispatchAll();
// (6) deliver new identity - with LOCATIONING permission
when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
anyString())).thenReturn(AppOpsManager.MODE_ALLOWED);
mDut.onInterfaceAddressChangeNotification(someMac);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(someMac);
// (7) NAN down (no feedback)
mDut.onNanDownNotification(reason);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId1);
validateInternalClientInfoCleanedUp(clientId2);
verifyNoMoreInteractions(mockCallback1, mockCallback2, mMockNative);
}
/**
* Validate that when the HAL doesn't respond we get a TIMEOUT (which
* results in a failure response) at which point we can process additional
* commands. Steps: (1) connect, (2) publish - timeout, (3) publish +
* success.
*/
@Test
public void testHalNoResponseTimeout() throws Exception {
final int clientId = 12341;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect (successfully)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish + timeout
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig));
assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_COMMAND_TIMEOUT_TAG));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback)
.onSessionConfigFail(WifiNanDiscoverySessionCallback.REASON_OTHER);
validateInternalNoSessions(clientId);
// (3) publish + success
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, 9999);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validates publish flow: (1) initial publish (2) fail. Expected: get a
* failure callback.
*/
@Test
public void testPublishFail() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonFail = WifiNanDiscoverySessionCallback.REASON_NO_RESOURCES;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (2) publish failure
mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
validateInternalNoSessions(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validates the publish flow: (1) initial publish (2) success (3)
* termination (e.g. DONE) (4) update session attempt (5) terminateSession
* (6) update session attempt. Expected: session ID callback + session
* cleaned-up.
*/
@Test
public void testPublishSuccessTerminated() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonTerminate = WifiNanDiscoverySessionCallback.TERMINATE_REASON_DONE;
final int publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) publish termination (from firmware - not app!)
mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
// (4) app update session (race condition: app didn't get termination
// yet)
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
// (5) app terminates session
mDut.terminateSession(clientId, sessionId.getValue());
mMockLooper.dispatchAll();
// (6) app updates session (app already knows that terminated - will get
// a local FAIL).
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
verifyNoMoreInteractions(mockSessionCallback, mMockNative);
}
/**
* Validate the publish flow: (1) initial publish + (2) success + (3) update
* + (4) update fails + (5) update + (6). Expected: session is still alive
* after update failure so second update succeeds (no callbacks).
*/
@Test
public void testPublishUpdateFail() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int publishId = 15;
final int reasonFail = WifiNanDiscoverySessionCallback.REASON_INVALID_ARGS;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) update publish
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
eq(publishConfig));
// (4) update fails
mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
// (5) another update publish
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
eq(publishConfig));
// (6) update succeeds
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate race condition: publish pending but session terminated (due to
* disconnect - can't terminate such a session directly from app). Need to
* make sure that once publish succeeds (failure isn't a problem) the
* session is immediately terminated since no-one is listening for it.
*/
@Test
public void testDisconnectWhilePublishPending() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (2) disconnect (but doesn't get executed until get response for
// publish command)
mDut.disconnect(clientId);
mMockLooper.dispatchAll();
// (3) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopPublish(transactionId.capture(), eq(publishId));
inOrder.verify(mMockNative).disable((short) 0);
validateInternalClientInfoCleanedUp(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validates subscribe flow: (1) initial subscribe (2) fail. Expected: get a
* failure callback.
*/
@Test
public void testSubscribeFail() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonFail = WifiNanDiscoverySessionCallback.REASON_NO_RESOURCES;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
// (2) subscribe failure
mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
validateInternalNoSessions(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validates the subscribe flow: (1) initial subscribe (2) success (3)
* termination (e.g. DONE) (4) update session attempt (5) terminateSession
* (6) update session attempt. Expected: session ID callback + session
* cleaned-up
*/
@Test
public void testSubscribeSuccessTerminated() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int reasonTerminate = WifiNanDiscoverySessionCallback.TERMINATE_REASON_DONE;
final int subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) subscribe termination (from firmware - not app!)
mDut.onSessionTerminatedNotification(subscribeId, reasonTerminate, false);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
// (4) app update session (race condition: app didn't get termination
// yet)
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
// (5) app terminates session
mDut.terminateSession(clientId, sessionId.getValue());
mMockLooper.dispatchAll();
// (6) app updates session
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
verifyNoMoreInteractions(mockSessionCallback, mMockNative);
}
/**
* Validate the subscribe flow: (1) initial subscribe + (2) success + (3)
* update + (4) update fails + (5) update + (6). Expected: session is still
* alive after update failure so second update succeeds (no callbacks).
*/
@Test
public void testSubscribeUpdateFail() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int subscribeId = 15;
final int reasonFail = WifiNanDiscoverySessionCallback.REASON_INVALID_ARGS;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) update subscribe
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
eq(subscribeConfig));
// (4) update fails
mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
// (5) another update subscribe
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
eq(subscribeConfig));
// (6) update succeeds
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigSuccess();
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate race condition: subscribe pending but session terminated (due to
* disconnect - can't terminate such a session directly from app). Need to
* make sure that once subscribe succeeds (failure isn't a problem) the
* session is immediately terminated since no-one is listening for it.
*/
@Test
public void testDisconnectWhileSubscribePending() throws Exception {
final int clientId = 2005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
// (2) disconnect (but doesn't get executed until get response for
// subscribe command)
mDut.disconnect(clientId);
mMockLooper.dispatchAll();
// (3) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopSubscribe((short) 0, subscribeId);
inOrder.verify(mMockNative).disable((short) 0);
validateInternalClientInfoCleanedUp(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate (1) subscribe (success), (2) match (i.e. discovery), (3) message reception,
* (4) message transmission failed (after ok queuing), (5) message transmission success.
*/
@Test
public void testMatchAndMessages() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int subscribeCount = 7;
final int reasonFail = WifiNanDiscoverySessionCallback.REASON_TX_FAIL;
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final String peerMsg = "some message from peer";
final int messageId = 6948;
final int messageId2 = 6949;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi)
.setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
.setSubscribeCount(subscribeCount).build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) message Rx
mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes());
// (4) message Tx successful queuing
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
short tid1 = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid1);
mMockLooper.dispatchAll();
// (5) message Tx successful queuing
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId2,
0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2));
short tid2 = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid2);
mMockLooper.dispatchAll();
// (4) and (5) final Tx results (on-air results)
mDut.onMessageSendFailNotification(tid1, reasonFail);
mDut.onMessageSendSuccessNotification(tid2);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, reasonFail);
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId2);
validateInternalSendMessageQueuesCleanedUp(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId2);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Summary: in a single publish session interact with multiple peers
* (different MAC addresses).
*/
@Test
public void testMultipleMessageSources() throws Exception {
final int clientId = 300;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
final int publishId = 88;
final int peerId1 = 568;
final int peerId2 = 873;
final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String msgFromPeer1 = "hey from 000102...";
final String msgFromPeer2 = "hey from 0607...";
final String msgToPeer1 = "hey there 000102...";
final String msgToPeer2 = "hey there 0506...";
final int msgToPeerId1 = 546;
final int msgToPeerId2 = 9654;
final int reason = WifiNanDiscoverySessionCallback.REASON_OTHER;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) message received from peers 1 & 2
mDut.onMessageReceivedNotification(publishId, peerId1, peerMac1, msgFromPeer1.getBytes());
mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes());
inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes());
// (4) sending messages back to same peers: one Tx fails, other succeeds
mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
msgToPeerId2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
short transactionIdVal = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
mDut.onMessageSendSuccessNotification(transactionIdVal);
mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
msgToPeerId1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
transactionIdVal = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
mDut.onMessageSendFailNotification(transactionIdVal, reason);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason);
validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Summary: interact with a peer which changed its identity (MAC address)
* but which keeps its requestor instance ID. Should be transparent.
*/
@Test
public void testMessageWhilePeerChangesIdentity() throws Exception {
final int clientId = 300;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
final int publishId = 88;
final int peerId = 568;
final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String msgFromPeer1 = "hey from 000102...";
final String msgFromPeer2 = "hey from 0607...";
final String msgToPeer1 = "hey there 000102...";
final String msgToPeer2 = "hey there 0506...";
final int msgToPeerId1 = 546;
final int msgToPeerId2 = 9654;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) message received & responded to
mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes());
mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
msgToPeerId1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes());
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1);
validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
// (4) message received with same peer ID but different MAC
mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater,
msgFromPeer2.getBytes());
mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
msgToPeerId2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes());
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validate that get failure (with correct code) when trying to send a
* message to an invalid peer ID.
*/
@Test
public void testSendMessageToInvalidPeerId() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int messageId = 6948;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) send message to invalid peer ID
mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
WifiNanDiscoverySessionCallback.REASON_NO_MATCH_SESSION);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that on send message timeout correct callback is dispatched and that a later
* firmware notification is ignored.
*/
@Test
public void testSendMessageTimeout() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int messageId = 6948;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) send 2 messages and enqueue successfully
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
short transactionId1 = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionId1);
mMockLooper.dispatchAll();
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
messageId + 1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 1));
short transactionId2 = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionId2);
mMockLooper.dispatchAll();
// (4) message send timeout
assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
validateInternalSendMessageQueuesCleanedUp(messageId);
// (5) firmware response (unlikely - but good to check)
mDut.onMessageSendSuccessNotification(transactionId1);
mDut.onMessageSendSuccessNotification(transactionId2);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId + 1);
validateInternalSendMessageQueuesCleanedUp(messageId + 1);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that when sending a message with a retry count the message is retried the specified
* number of times. Scenario ending with success.
*/
@Test
public void testSendMessageRetransmitSuccess() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int messageId = 6948;
final int retryCount = 3;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) send message and enqueue successfully
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
messageId, retryCount);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (4) loop and fail until reach retryCount
for (int i = 0; i < retryCount; ++i) {
mDut.onMessageSendFailNotification(transactionId.getValue(),
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
}
// (5) succeed on last retry
mDut.onMessageSendSuccessNotification(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that when sending a message with a retry count the message is retried the specified
* number of times. Scenario ending with failure.
*/
@Test
public void testSendMessageRetransmitFail() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int messageId = 6948;
final int retryCount = 3;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) send message and enqueue successfully
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId,
retryCount);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (4) loop and fail until reach retryCount+1
for (int i = 0; i < retryCount + 1; ++i) {
mDut.onMessageSendFailNotification(transactionId.getValue(),
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
mMockLooper.dispatchAll();
if (i != retryCount) {
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
}
}
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that can send a NULL message successfully.
*/
@Test
public void testSendMessageNull() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int subscribeCount = 7;
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final byte[] msg = null;
final int messageId = 6948;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi)
.setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
.setSubscribeCount(subscribeCount).build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) message Tx successful queuing
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, msg, messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(msg), eq(messageId));
short tid1 = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid1);
mMockLooper.dispatchAll();
// (4) final Tx results (on-air results)
mDut.onMessageSendSuccessNotification(tid1);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
@Test
public void testSendMessageQueueAllQueueFail() throws Exception {
WifiNanNative.Capabilities cap = getCapabilities();
testSendMessageQueue(SendMessageAnswer.OP_QUEUE_FAIL, cap,
cap.maxQueuedTransmitMessages + 5);
}
@Test
public void testSendMessageQueueAllTxSuccess() throws Exception {
WifiNanNative.Capabilities cap = getCapabilities();
testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_OK, cap,
cap.maxQueuedTransmitMessages + 5);
}
@Test
public void testSendMessageQueueAllTxFailRetxOk() throws Exception {
WifiNanNative.Capabilities cap = getCapabilities();
testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK, cap,
cap.maxQueuedTransmitMessages + 5);
}
@Test
public void testSendMessageQueueAllTxFail() throws Exception {
WifiNanNative.Capabilities cap = getCapabilities();
testSendMessageQueue(SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL, cap,
cap.maxQueuedTransmitMessages + 5);
}
@Test
public void testSendMessageQueueRandomize() throws Exception {
WifiNanNative.Capabilities cap = getCapabilities();
testSendMessageQueue(SendMessageAnswer.OP_QUEUE_RANDOMIZE, cap,
cap.maxQueuedTransmitMessages * 10);
}
/**
* Validate that when sending more messages than can be queued by the firmware (based on
* capability information) they are queued. Support all possible test success/failure codes.
* @param behavior: SendMessageAnswer.OP_*.
*/
private void testSendMessageQueue(int behavior, WifiNanNative.Capabilities cap, int numMessages)
throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String ssi = "some much longer and more arbitrary data";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int messageId = 6948;
final int retryCount = 3;
final int reason = WifiNanDiscoverySessionCallback.REASON_OTHER;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> msgId = ArgumentCaptor.forClass(Integer.class);
// (0) initial conditions
mDut.enableUsage();
mMockLooper.dispatchAll();
verify(mMockNative).deInitNan();
verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), cap);
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
verify(mockSessionCallback).onSessionStarted(sessionId.capture());
verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) send large number of messages
SendMessageAnswer answerObj = new SendMessageAnswer(behavior);
when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class),
any(byte[].class), anyInt())).thenAnswer(answerObj);
for (int i = 0; i < numMessages; ++i) {
mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
messageId + i, retryCount);
}
mMockLooper.dispatchAll();
int numSends = answerObj.ops[SendMessageAnswer.OP_QUEUE_FAIL]
+ answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_OK]
+ answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK] * 2
+ answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL] * (retryCount + 1);
int numOnSendSuccess = answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_OK]
+ answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_OK];
int numOnSendFail = answerObj.ops[SendMessageAnswer.OP_QUEUE_OK_SEND_RETX_FAIL];
Log.v("WifiNanStateManagerTest",
"testSendMessageQueue: ops=" + Arrays.toString(answerObj.ops) + ", numSends="
+ numSends + ", numOnSendSuccess=" + numOnSendSuccess + ", numOnSendFail="
+ numOnSendFail);
verify(mMockNative, times(numSends)).sendMessage(anyShort(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), anyInt());
verify(mockSessionCallback, times(numOnSendSuccess)).onMessageSendSuccess(anyInt());
verify(mockSessionCallback, times(numOnSendFail)).onMessageSendFail(anyInt(), anyInt());
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
private class SendMessageAnswer extends MockAnswerUtil.AnswerWithArguments {
public static final int OP_QUEUE_FAIL = 0;
public static final int OP_QUEUE_OK_SEND_OK = 1;
public static final int OP_QUEUE_OK_SEND_RETX_OK = 2;
public static final int OP_QUEUE_OK_SEND_RETX_FAIL = 3;
/* psuedo operation: randomly pick from the above 4 operations */
public static final int OP_QUEUE_RANDOMIZE = -1;
/* the number of operations which can be executed. Doesn't cound RANDOMIZE since it is
* resolved to one of the 4 types */
private static final int NUM_OPS = 4;
public int[] ops = new int[NUM_OPS];
private int mBehavior = 0;
private SparseIntArray mPacketBehavior = new SparseIntArray();
SendMessageAnswer(int behavior) {
mBehavior = behavior;
}
public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
byte[] dest, byte[] message, int messageId) throws Exception {
Log.v("WifiNanStateManagerTest",
"SendMessageAnswer.answer: mBehavior=" + mBehavior + ", transactionId="
+ transactionId + ", messageId=" + messageId
+ ", mPacketBehavior[messageId]" + mPacketBehavior.get(messageId, -1));
int behavior = mBehavior;
if (behavior == OP_QUEUE_RANDOMIZE) {
behavior = mRandomNg.nextInt(NUM_OPS);
}
boolean packetRetx = mPacketBehavior.get(messageId, -1) != -1;
if (packetRetx) {
behavior = mPacketBehavior.get(messageId);
} else {
mPacketBehavior.put(messageId, behavior);
}
if (behavior == OP_QUEUE_FAIL) {
ops[OP_QUEUE_FAIL]++;
mDut.onMessageSendQueuedFailResponse(transactionId,
WifiNanDiscoverySessionCallback.REASON_OTHER);
} else if (behavior == OP_QUEUE_OK_SEND_OK) {
ops[OP_QUEUE_OK_SEND_OK]++;
mDut.onMessageSendQueuedSuccessResponse(transactionId);
mDut.onMessageSendSuccessNotification(transactionId);
} else if (behavior == OP_QUEUE_OK_SEND_RETX_OK) {
mDut.onMessageSendQueuedSuccessResponse(transactionId);
if (!packetRetx) {
mDut.onMessageSendFailNotification(transactionId,
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
} else {
ops[OP_QUEUE_OK_SEND_RETX_OK]++;
mDut.onMessageSendSuccessNotification(transactionId);
}
} else if (behavior == OP_QUEUE_OK_SEND_RETX_FAIL) {
mDut.onMessageSendQueuedSuccessResponse(transactionId);
if (!packetRetx) {
ops[OP_QUEUE_OK_SEND_RETX_FAIL]++;
}
mDut.onMessageSendFailNotification(transactionId,
WifiNanDiscoverySessionCallback.REASON_TX_FAIL);
}
return true;
}
}
/**
* Validate that start ranging function fills-in correct MAC addresses for peer IDs and
* passed along to RTT module.
*/
@Test
public void testStartRanging() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final String peerSsi = "some peer ssi data";
final String peerMatchFilter = "filter binary array represented as string";
final int rangingId = 18423;
final RttManager.RttParams[] params = new RttManager.RttParams[2];
params[0] = new RttManager.RttParams();
params[0].bssid = Integer.toString(requestorId);
params[1] = new RttManager.RttParams();
params[1].bssid = Integer.toString(requestorId + 5);
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<WifiNanClientState> clientCaptor =
ArgumentCaptor.forClass(WifiNanClientState.class);
ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
ArgumentCaptor.forClass(RttManager.RttParams[].class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative,
mMockNanRttStateManager);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe & match
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
peerMatchFilter.getBytes());
// (3) start ranging: pass along a valid peer ID and an invalid one
mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
mMockLooper.dispatchAll();
inOrder.verify(mMockNanRttStateManager).startRanging(eq(rangingId), clientCaptor.capture(),
rttParamsCaptor.capture());
collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B",
equalTo(rttParamsCaptor.getValue()[0].bssid));
collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative,
mMockNanRttStateManager);
}
/**
* Test sequence of configuration: (1) config1, (2) config2 - incompatible,
* (3) config3 - compatible with config1 (requiring upgrade), (4) disconnect
* config3 (should get a downgrade), (5) disconnect config1 (should get a
* disable).
*/
@Test
public void testConfigs() throws Exception {
final int clientId1 = 9999;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int clusterLow1 = 5;
final int clusterHigh1 = 100;
final int masterPref1 = 111;
final int clientId2 = 1001;
final boolean support5g2 = true;
final int clusterLow2 = 7;
final int clusterHigh2 = 155;
final int masterPref2 = 0;
final int clientId3 = 55;
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
.setClusterHigh(clusterHigh1).setMasterPreference(masterPref1)
.setEnableIdentityChangeCallback(false).build();
ConfigRequest configRequest2 = new ConfigRequest.Builder().setSupport5gBand(support5g2)
.setClusterLow(clusterLow2).setClusterHigh(clusterHigh2)
.setMasterPreference(masterPref2).build();
ConfigRequest configRequest3 = new ConfigRequest.Builder().setClusterLow(clusterLow1)
.setClusterHigh(clusterHigh1).setMasterPreference(masterPref1)
.setEnableIdentityChangeCallback(true).build();
IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class);
IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class);
IWifiNanEventCallback mockCallback3 = mock(IWifiNanEventCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) config1 (valid)
mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(true));
collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
// (2) config2 (incompatible with config1)
mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest2);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2)
.onConnectFail(WifiNanEventCallback.REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG);
validateInternalClientInfoCleanedUp(clientId2);
// (3) config3 (compatible with config1 but requires upgrade - i.e. no
// OTA changes)
mDut.connect(clientId3, uid, pid, callingPackage, mockCallback3, configRequest3);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(false));
collector.checkThat("merge: stage 3: support 5g", crCapture.getValue().mSupport5gBand,
equalTo(false));
collector.checkThat("merge: stage 3: master pref", crCapture.getValue().mMasterPreference,
equalTo(masterPref1));
collector.checkThat("merge: stage 3: cluster low", crCapture.getValue().mClusterLow,
equalTo(clusterLow1));
collector.checkThat("merge: stage 3: cluster high", crCapture.getValue().mClusterHigh,
equalTo(clusterHigh1));
collector.checkThat("merge: stage 3: enable identity change callback",
crCapture.getValue().mEnableIdentityChangeCallback, equalTo(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
// (4) disconnect config3: want a downgrade
mDut.disconnect(clientId3);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId3);
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(false));
collector.checkThat("merge: stage 4", crCapture.getValue(), equalTo(configRequest1));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (5) disconnect config1: disable
mDut.disconnect(clientId1);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId1);
inOrder.verify(mMockNative).disable((short) 0);
verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
}
/**
* Summary: disconnect a client while there are pending transactions.
*/
@Test
public void testDisconnectWithPendingTransactions() throws Exception {
final int clientId = 125;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int clusterLow = 5;
final int clusterHigh = 100;
final int masterPref = 111;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int publishCount = 7;
final int reason = WifiNanDiscoverySessionCallback.TERMINATE_REASON_DONE;
final int publishId = 22;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED)
.setPublishCount(publishCount).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish (no response yet)
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (3) disconnect (but doesn't get executed until get a RESPONSE to the
// previous publish)
mDut.disconnect(clientId);
mMockLooper.dispatchAll();
// (4) get successful response to the publish
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrder.verify(mMockNative).stopPublish((short) 0, publishId);
inOrder.verify(mMockNative).disable((short) 0);
validateInternalClientInfoCleanedUp(clientId);
// (5) trying to publish on the same client: NOP
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
// (6) got some callback on original publishId - should be ignored
mDut.onSessionTerminatedNotification(publishId, reason, true);
mMockLooper.dispatchAll();
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validate that an unknown transaction (i.e. a callback from HAL with an
* unknown type) is simply ignored - but also cleans up its state.
*/
@Test
public void testUnknownTransactionType() throws Exception {
final int clientId = 129;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int clusterLow = 15;
final int clusterHigh = 192;
final int masterPref = 234;
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int publishCount = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED)
.setPublishCount(publishCount).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockPublishSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish - no response
mDut.publish(clientId, publishConfig, mockPublishSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback);
}
/**
* Validate that a NoOp transaction (i.e. a callback from HAL which doesn't
* require any action except clearing up state) actually cleans up its state
* (and does nothing else).
*/
@Test
public void testNoOpTransaction() throws Exception {
final int clientId = 1294;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect (no response)
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validate that getting callbacks from HAL with unknown (expired)
* transaction ID or invalid publish/subscribe ID session doesn't have any
* impact.
*/
@Test
public void testInvalidCallbackIdParameters() throws Exception {
final int pubSubId = 1235;
final int clientId = 132;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect and succeed
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
short transactionIdConfig = transactionId.getValue();
mDut.onConfigSuccessResponse(transactionIdConfig);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) use the same transaction ID to send a bunch of other responses
mDut.onConfigSuccessResponse(transactionIdConfig);
mDut.onConfigFailedResponse(transactionIdConfig, -1);
mDut.onSessionConfigFailResponse(transactionIdConfig, true, -1);
mDut.onMessageSendQueuedSuccessResponse(transactionIdConfig);
mDut.onMessageSendQueuedFailResponse(transactionIdConfig, -1);
mDut.onSessionConfigFailResponse(transactionIdConfig, false, -1);
mDut.onMatchNotification(-1, -1, new byte[0], new byte[0], new byte[0]);
mDut.onSessionTerminatedNotification(-1, -1, true);
mDut.onSessionTerminatedNotification(-1, -1, false);
mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[0]);
mDut.onSessionConfigSuccessResponse(transactionIdConfig, true, pubSubId);
mDut.onSessionConfigSuccessResponse(transactionIdConfig, false, pubSubId);
mMockLooper.dispatchAll();
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/**
* Validate that trying to update-subscribe on a publish session fails.
*/
@Test
public void testSubscribeOnPublishSessionType() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int publishId = 25;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) update-subscribe -> failure
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback)
.onSessionConfigFail(WifiNanDiscoverySessionCallback.REASON_OTHER);
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validate that trying to (re)subscribe on a publish session or (re)publish
* on a subscribe session fails.
*/
@Test
public void testPublishOnSubscribeSessionType() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final int subscribeId = 25;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (3) update-publish -> error
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback)
.onSessionConfigFail(WifiNanDiscoverySessionCallback.REASON_OTHER);
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
}
/**
* Validate that the session ID increments monotonically
*/
@Test
public void testSessionIdIncrement() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
int loopCount = 100;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class);
IWifiNanDiscoverySessionCallback mockSessionCallback = mock(
IWifiNanDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).deInitNan();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
int prevId = 0;
for (int i = 0; i < loopCount; ++i) {
// (2) publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
// (3) publish-success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
if (i != 0) {
assertTrue("Session ID incrementing", sessionId.getValue() > prevId);
}
prevId = sessionId.getValue();
}
}
/*
* Tests of internal state of WifiNanStateManager: very limited (not usually
* a good idea). However, these test that the internal state is cleaned-up
* appropriately. Alternatively would cause issues with memory leaks or
* information leak between sessions.
*/
/**
* Utility routine used to validate that the internal state is cleaned-up
* after a client is disconnected. To be used in every test which terminates
* a client.
*
* @param clientId The ID of the client which should be deleted.
*/
private void validateInternalClientInfoCleanedUp(int clientId) throws Exception {
WifiNanClientState client = getInternalClientState(mDut, clientId);
collector.checkThat("Client record not cleared up for clientId=" + clientId, client,
nullValue());
}
/**
* Utility routine used to validate that the internal state is cleaned-up
* (deleted) after a session is terminated through API (not callback!). To
* be used in every test which terminates a session.
*
* @param clientId The ID of the client containing the session.
* @param sessionId The ID of the terminated session.
*/
private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId)
throws Exception {
WifiNanClientState client = getInternalClientState(mDut, clientId);
collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
WifiNanDiscoverySessionState session = getInternalSessionState(client, sessionId);
collector.checkThat("Client record not cleaned-up for sessionId=" + sessionId, session,
nullValue());
}
/**
* Utility routine used to validate that the internal state is cleaned-up
* (deleted) correctly. Checks that a specific client has no sessions
* attached to it.
*
* @param clientId The ID of the client which we want to check.
*/
private void validateInternalNoSessions(int clientId) throws Exception {
WifiNanClientState client = getInternalClientState(mDut, clientId);
collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
Field field = WifiNanClientState.class.getDeclaredField("mSessions");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiNanDiscoverySessionState> sessions =
(SparseArray<WifiNanDiscoverySessionState>) field.get(client);
collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
equalTo(0));
}
/**
* Validates that the broadcast sent on NAN status change is correct.
*
* @param expectedEnabled The expected change status - i.e. are we expected
* to announce that NAN is enabled (true) or disabled (false).
*/
private void validateCorrectNanStatusChangeBroadcast(InOrder inOrder, boolean expectedEnabled) {
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL));
collector.checkThat("intent action", intent.getValue().getAction(),
equalTo(WifiNanManager.ACTION_WIFI_NAN_STATE_CHANGED));
collector.checkThat("intent contains wifi status key",
intent.getValue().getExtras().containsKey(WifiNanManager.EXTRA_WIFI_STATE),
equalTo(true));
collector.checkThat("intnent wifi status key value",
intent.getValue().getExtras().getInt(WifiNanManager.EXTRA_WIFI_STATE),
equalTo(expectedEnabled ? WifiNanManager.WIFI_NAN_STATE_ENABLED
: WifiNanManager.WIFI_NAN_STATE_DISABLED));
}
/*
* Utilities
*/
private static WifiNanStateManager installNewNanStateManager()
throws Exception {
Constructor<WifiNanStateManager> ctr = WifiNanStateManager.class.getDeclaredConstructor();
ctr.setAccessible(true);
WifiNanStateManager nanStateManager = ctr.newInstance();
Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton");
field.setAccessible(true);
field.set(null, nanStateManager);
return WifiNanStateManager.getInstance();
}
private static void installMocksInStateManager(WifiNanStateManager nanStateManager,
WifiNanRttStateManager mockRtt, WifiNanDataPathStateManager mockDpMgr)
throws Exception {
Field field = WifiNanStateManager.class.getDeclaredField("mRtt");
field.setAccessible(true);
field.set(nanStateManager, mockRtt);
field = WifiNanStateManager.class.getDeclaredField("mDataPathMgr");
field.setAccessible(true);
field.set(nanStateManager, mockDpMgr);
}
private static void installMockWifiNanNative(WifiNanNative obj) throws Exception {
Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton");
field.setAccessible(true);
field.set(null, obj);
}
private static WifiNanClientState getInternalClientState(WifiNanStateManager dut, int clientId)
throws Exception {
Field field = WifiNanStateManager.class.getDeclaredField("mClients");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiNanClientState> clients = (SparseArray<WifiNanClientState>) field.get(dut);
return clients.get(clientId);
}
private static WifiNanDiscoverySessionState getInternalSessionState(WifiNanClientState client,
int sessionId) throws Exception {
Field field = WifiNanClientState.class.getDeclaredField("mSessions");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiNanDiscoverySessionState> sessions =
(SparseArray<WifiNanDiscoverySessionState>) field.get(client);
return sessions.get(sessionId);
}
private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
Field field = WifiNanStateManager.class.getDeclaredField("mSm");
field.setAccessible(true);
WifiNanStateManager.WifiNanStateMachine sm =
(WifiNanStateManager.WifiNanStateMachine) field.get(mDut);
field = WifiNanStateManager.WifiNanStateMachine.class.getDeclaredField(
"mHostQueuedSendMessages");
field.setAccessible(true);
SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
field = WifiNanStateManager.WifiNanStateMachine.class.getDeclaredField(
"mFwQueuedSendMessages");
field.setAccessible(true);
Map<Short, Message> fwQueuedSendMessages = (Map<Short, Message>) field.get(sm);
for (int i = 0; i < hostQueuedSendMessages.size(); ++i) {
Message msg = hostQueuedSendMessages.valueAt(i);
if (msg.getData().getInt("message_id") == messageId) {
collector.checkThat(
"Message not cleared-up from host queue. Message ID=" + messageId, msg,
nullValue());
}
}
for (Message msg: fwQueuedSendMessages.values()) {
if (msg.getData().getInt("message_id") == messageId) {
collector.checkThat(
"Message not cleared-up from firmware queue. Message ID=" + messageId, msg,
nullValue());
}
}
}
private static WifiNanNative.Capabilities getCapabilities() {
WifiNanNative.Capabilities cap = new WifiNanNative.Capabilities();
cap.maxConcurrentNanClusters = 1;
cap.maxPublishes = 2;
cap.maxSubscribes = 2;
cap.maxServiceNameLen = 255;
cap.maxMatchFilterLen = 255;
cap.maxTotalMatchFilterLen = 255;
cap.maxServiceSpecificInfoLen = 255;
cap.maxVsaDataLen = 255;
cap.maxMeshDataLen = 255;
cap.maxNdiInterfaces = 1;
cap.maxNdpSessions = 1;
cap.maxAppInfoLen = 255;
cap.maxQueuedTransmitMessages = 6;
return cap;
}
}