blob: 7292fca36cf9781ba89a90f72bc23c3001efa198 [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.aware;
import static android.hardware.wifi.V1_0.NanRangingIndication.EGRESS_MET_MASK;
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.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyByte;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyShort;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
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.app.AppOpsManager;
import android.app.test.MockAnswerUtil;
import android.app.test.TestAlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.wifi.V1_0.NanRangingIndication;
import android.hardware.wifi.V1_0.NanStatusType;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
import android.net.wifi.aware.IWifiAwareEventCallback;
import android.net.wifi.aware.IWifiAwareMacAddressProvider;
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.util.HexEncoding;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.util.Log;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.server.wifi.Clock;
import com.android.server.wifi.WifiBaseTest;
import com.android.server.wifi.util.NetdWrapper;
import com.android.server.wifi.util.WifiPermissionsUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
import org.junit.After;
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 org.mockito.Spy;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
/**
* Unit test harness for WifiAwareStateManager.
*/
@SmallTest
public class WifiAwareStateManagerTest extends WifiBaseTest {
private TestLooper mMockLooper;
private Random mRandomNg = new Random(15687);
private WifiAwareStateManager mDut;
@Mock private WifiAwareNativeManager mMockNativeManager;
@Spy private TestUtils.MonitoredWifiAwareNativeApi mMockNative =
new TestUtils.MonitoredWifiAwareNativeApi();
@Mock private Context mMockContext;
@Mock private AppOpsManager mMockAppOpsManager;
@Mock private WifiAwareMetrics mAwareMetricsMock;
@Mock private WifiPermissionsUtil mWifiPermissionsUtil;
@Mock private WifiPermissionsWrapper mPermissionsWrapperMock;
TestAlarmManager mAlarmManager;
@Mock private PowerManager mMockPowerManager;
@Mock private WifiManager mMockWifiManager;
private BroadcastReceiver mPowerBcastReceiver;
private BroadcastReceiver mLocationModeReceiver;
private BroadcastReceiver mWifiStateChangedReceiver;
@Mock private WifiAwareDataPathStateManager mMockAwareDataPathStatemanager;
@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.WIFI_SERVICE)).thenReturn(mMockWifiManager);
when(mMockWifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED);
mMockLooper = new TestLooper();
when(mMockContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
mock(ConnectivityManager.class));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
when(mMockContext.getSystemServiceName(PowerManager.class)).thenReturn(
Context.POWER_SERVICE);
when(mMockContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
when(mMockContext.checkPermission(eq(android.Manifest.permission.ACCESS_FINE_LOCATION),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(),
any())).thenReturn(AppOpsManager.MODE_ERRORED);
when(mMockPowerManager.isDeviceIdleMode()).thenReturn(false);
when(mMockPowerManager.isInteractive()).thenReturn(true);
when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true);
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true);
ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(
BroadcastReceiver.class);
mDut = new WifiAwareStateManager();
mDut.setNative(mMockNativeManager, mMockNative);
mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock,
mWifiPermissionsUtil, mPermissionsWrapperMock, new Clock(),
mock(NetdWrapper.class));
mDut.startLate();
mMockLooper.dispatchAll();
verify(mMockContext, times(3)).registerReceiver(bcastRxCaptor.capture(),
any(IntentFilter.class));
mPowerBcastReceiver = bcastRxCaptor.getAllValues().get(0);
mLocationModeReceiver = bcastRxCaptor.getAllValues().get(1);
mWifiStateChangedReceiver = bcastRxCaptor.getAllValues().get(2);
installMocksInStateManager(mDut, mMockAwareDataPathStatemanager);
}
/**
* Post-test validation.
*/
@After
public void tearDown() throws Exception {
mMockNative.validateUniqueTransactionIds();
}
/**
* Test that the set parameter shell command executor works when parameters are valid.
*/
@Test
public void testSetParameterShellCommandSuccess() {
setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(1),
true);
}
/**
* Test that the set parameter shell command executor fails on incorrect name.
*/
@Test
public void testSetParameterShellCommandInvalidParameterName() {
setSettableParam("XXX", Integer.toString(1), false);
}
/**
* Test that the set parameter shell command executor fails on invalid value (not convertible
* to an int).
*/
@Test
public void testSetParameterShellCommandInvalidValue() {
setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, "garbage", false);
}
/**
* Test the PeerHandle -> MAC address API:
* - Start up discovery of 2 sessions
* - Get multiple matches (PeerHandles)
* - Request translation as UID of sesssion #1 for PeerHandles of the same UID + of the other
* discovery session (to which we shouldn't have access) + invalid PeerHandle.
* -> validate results
*/
@Test
public void testRequestMacAddresses() throws Exception {
final int clientId1 = 1005;
final int clientId2 = 1006;
final int uid1 = 1000;
final int uid2 = 1001;
final int pid1 = 2000;
final int pid2 = 2001;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final String serviceName = "some-service-name";
final byte subscribeId1 = 15;
final byte subscribeId2 = 16;
final int requestorIdBase = 22;
final byte[] peerMac1 = HexEncoding.decode("060708090A0B".toCharArray(), false);
final byte[] peerMac2 = HexEncoding.decode("010203040506".toCharArray(), false);
final byte[] peerMac3 = HexEncoding.decode("AABBCCDDEEFF".toCharArray(), false);
final int distance = 10;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.build();
IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback1 = mock(
IWifiAwareDiscoverySessionCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback2 = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback1, mockCallback2, mockSessionCallback1,
mockSessionCallback2, mMockNative);
// (0) enable
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect 2 clients
mDut.connect(clientId1, uid1, pid1, callingPackage, callingFeature, mockCallback1,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
mDut.connect(clientId2, uid2, pid2, callingPackage, callingFeature, mockCallback2,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onConnectSuccess(clientId2);
// (2) subscribe both clients
mDut.subscribe(clientId1, subscribeConfig, mockSessionCallback1);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId1);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback1).onSessionStarted(sessionId.capture());
mDut.subscribe(clientId2, subscribeConfig, mockSessionCallback2);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId2);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback2).onSessionStarted(sessionId.capture());
// (3) 2 matches on session 1 (second one with distance), 1 match on session 2
mDut.onMatchNotification(subscribeId1, requestorIdBase, peerMac1, null, null, 0, 0);
mDut.onMatchNotification(subscribeId1, requestorIdBase + 1, peerMac2, null, null,
NanRangingIndication.INGRESS_MET_MASK, distance);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback1).onMatch(peerIdCaptor.capture(), isNull(),
isNull());
inOrder.verify(mockSessionCallback1).onMatchWithDistance(peerIdCaptor.capture(), isNull(),
isNull(), eq(distance));
int peerId1 = peerIdCaptor.getAllValues().get(0);
int peerId2 = peerIdCaptor.getAllValues().get(1);
mDut.onMatchNotification(subscribeId2, requestorIdBase + 2, peerMac3, null, null, 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback2).onMatch(peerIdCaptor.capture(), isNull(), isNull());
int peerId3 = peerIdCaptor.getAllValues().get(0);
// request MAC addresses
List<Integer> request = new ArrayList<>();
request.add(peerId1);
request.add(peerId2);
request.add(peerId3); // for uid2: so should not be in results
request.add(peerId1 * 20 + peerId2 + peerId3); // garbage values != to any
Mutable<Map> response = new Mutable<>();
mDut.requestMacAddresses(uid1, request, new IWifiAwareMacAddressProvider() {
@Override
public void macAddress(Map peerIdToMacMap) throws RemoteException {
response.value = peerIdToMacMap;
}
@Override
public IBinder asBinder() {
return null;
}
});
mMockLooper.dispatchAll();
assertNotEquals("Non-null result", null, response.value);
assertEquals("Number of results", 2, response.value.size());
assertEquals("Results[peerId1]", peerMac1, response.value.get(peerId1));
assertEquals("Results[peerId2]", peerMac2, response.value.get(peerId2));
}
/**
* Validate that Aware data-path interfaces are brought up and down correctly.
*/
@Test
public void testAwareDataPathInterfaceUpDown() throws Exception {
final int clientId = 12341;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mMockAwareDataPathStatemanager,
mockCallback);
// (1) enable usage
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
// (2) connect (enable Aware)
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrder.verify(mMockAwareDataPathStatemanager).createAllInterfaces();
// (3) disconnect (disable Aware)
mDut.disconnect(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).disable(transactionId.capture());
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
mMockLooper.dispatchAll();
inOrder.verify(mMockAwareDataPathStatemanager).deleteAllInterfaces();
verifyNoMoreInteractions(mMockNative, mMockAwareDataPathStatemanager);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder);
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(transactionId.capture());
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
validateCorrectAwareStatusChangeBroadcast(inOrder);
// (3) try connecting and validate that get failure callback (though app should be aware of
// non-availability through state change broadcast and/or query API)
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectFail(anyInt());
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 String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<SparseArray> sparseArrayCaptor = ArgumentCaptor.forClass(SparseArray.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
InOrder inOrderM = inOrder(mAwareMetricsMock);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder);
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
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, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false),
sparseArrayCaptor.capture());
collector.checkThat("num of clients", sparseArrayCaptor.getValue().size(), equalTo(1));
// (3) disable usage & verify callbacks
mDut.disableUsage();
mMockLooper.dispatchAll();
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
inOrder.verify(mMockNative).disable(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordAttachSessionDuration(anyLong());
inOrderM.verify(mAwareMetricsMock).recordDisableAware();
inOrderM.verify(mAwareMetricsMock).recordDisableUsage();
validateCorrectAwareStatusChangeBroadcast(inOrder);
validateInternalClientInfoCleanedUp(clientId);
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
mMockLooper.dispatchAll();
inOrderM.verify(mAwareMetricsMock).recordDisableAware();
// (4) try connecting again and validate that get a failure
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectFail(anyInt());
inOrderM.verify(mAwareMetricsMock).recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
// (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();
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder);
// (7) connect (should be successful)
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false),
sparseArrayCaptor.capture());
collector.checkThat("num of clients", sparseArrayCaptor.getValue().size(), equalTo(1));
verifyNoMoreInteractions(mMockNative, mockCallback, mAwareMetricsMock);
}
/**
* Validates that a HAL failure on enable and configure results in failed callback.
*/
@Test
public void testHalFailureEnableAndConfigure() throws Exception {
final int clientId = 12341;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
anyBoolean(), eq(true), eq(false), eq(false))).thenReturn(false);
// (1) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (2) connect with HAL failure
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
inOrder.verify(mockCallback).onConnectFail(NanStatusType.INTERNAL_FAILURE);
validateInternalClientInfoCleanedUp(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 testAwareEventsDelivery() 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 String callingFeature = "com.google.someFeature";
final int reason = NanStatusType.INTERNAL_FAILURE;
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final byte[] someMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref)
.build();
IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
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, callingFeature, mockCallback1,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
short transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
mDut.connect(clientId2, uid, pid, callingPackage, callingFeature, mockCallback2,
configRequest, true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
eq(configRequest), eq(true), eq(false), eq(true), eq(false), eq(false));
transactionId = transactionIdCapture.getValue();
mDut.onConfigSuccessResponse(transactionId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onConnectSuccess(clientId2);
// (2) deliver Aware events - without LOCATIONING permission
mDut.onClusterChangeNotification(WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED,
someMac);
mDut.onInterfaceAddressChangeNotification(someMac);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
// (3) deliver new identity - still without LOCATIONING permission (should get an event)
mDut.onInterfaceAddressChangeNotification(someMac2);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
// (4) deliver same identity - still without LOCATIONING permission (should
// not get an event)
mDut.onInterfaceAddressChangeNotification(someMac2);
mMockLooper.dispatchAll();
// (5) deliver new identity - with LOCATIONING permission
when(mWifiPermissionsUtil.checkCallersLocationPermission(eq(callingPackage),
eq(callingFeature), eq(uid), anyBoolean(), any())).thenReturn(true);
mDut.onInterfaceAddressChangeNotification(someMac);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onIdentityChanged(someMac);
// (6) Aware down (no feedback)
mDut.onAwareDownNotification(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 String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect (successfully)
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (2) publish + timeout
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(anyShort(), eq((byte) 0), eq(publishConfig));
assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_COMMAND_TIMEOUT_TAG));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid,
NanStatusType.INTERNAL_FAILURE, true);
validateInternalNoSessions(clientId);
// (3) publish + success
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
eq(publishConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, (byte) 99);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback, mAwareMetricsMock);
}
/**
* Validates publish flow: (1) initial publish (2) fail informed by notification, (3) fail due
* to immediate HAL failure. 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 String callingFeature = "com.google.someFeature";
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
eq(publishConfig));
// (2) publish failure callback (i.e. firmware tried and failed)
mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
validateInternalNoSessions(clientId);
// (3) publish and get immediate failure (i.e. HAL failed)
when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(false);
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
eq(publishConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
validateInternalNoSessions(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final int reasonTerminate = NanStatusType.SUCCESS;
final byte publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
// (3) publish termination (from firmware - not app!)
mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionDuration(anyLong(), eq(true));
// (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, mAwareMetricsMock);
}
/**
* Validate the publish flow: (1) initial publish + (2) success + (3) update + (4) update
* fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
* update failure so second update succeeds (no callbacks) + (7) update + immediate failure from
* HAL + (8) update + failure for invalid ID (which should clean-up state) + (9) another update
* - should get no response.
*/
@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 String callingFeature = "com.google.someFeature";
final byte publishId = 15;
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().setRangingEnabled(true).build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 0),
eq(publishConfig));
// (2) publish success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionWithRanging(eq(uid), eq(false),
eq(-1), eq(-1), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
mMockLooper.dispatchAll();
// Verify reconfigure aware to enable ranging.
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(false), eq(true), eq(false), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (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);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
// (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();
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
// (7) another update + immediate failure
when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(false);
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
eq(publishConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, true);
// (8) an update with bad ID failure
when(mMockNative.publish(anyShort(), anyByte(), any())).thenReturn(true);
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
eq(publishConfig));
mDut.onSessionConfigFailResponse(transactionId.getValue(), true,
NanStatusType.INVALID_SESSION_ID);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INVALID_SESSION_ID);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid,
NanStatusType.INVALID_SESSION_ID, true);
// Verify reconfigure aware to disable ranging.
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(false), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (9) try updating again - do nothing/get nothing
mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
mMockLooper.dispatchAll();
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final byte publishId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
PublishConfig publishConfig = new PublishConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial publish
mDut.publish(clientId, publishConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).publish(transactionId.capture(), eq((byte) 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(anyShort());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, true);
inOrderM.verify(mAwareMetricsMock).recordAttachSessionDuration(anyLong());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionDuration(anyLong(), eq(true));
validateInternalClientInfoCleanedUp(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* Validates subscribe flow: (1) initial subscribe (2) fail (callback from firmware), (3) fail
* due to immeidate HAL failure. 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 String callingFeature = "com.google.someFeature";
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
// (2) subscribe failure
mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, false);
validateInternalNoSessions(clientId);
// (3) subscribe and get immediate failure (i.e. HAL failed)
when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(false);
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, false);
validateInternalNoSessions(clientId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final int reasonTerminate = NanStatusType.SUCCESS;
final byte subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySession(eq(uid), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, false);
// (3) subscribe termination (from firmware - not app!)
mDut.onSessionTerminatedNotification(subscribeId, reasonTerminate, false);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate);
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionDuration(anyLong(), eq(false));
// (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, mAwareMetricsMock);
}
/**
* Validate the subscribe flow: (1) initial subscribe + (2) success + (3) update + (4) update
* fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
* update failure so second update succeeds (no callbacks). + (7) update + immediate failure
* from HAL.
*/
@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 String callingFeature = "com.google.someFeature";
final byte subscribeId = 15;
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
final int rangeMax = 10;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setMaxDistanceMm(
rangeMax).build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) initial subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
// (2) subscribe success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionWithRanging(eq(uid), eq(true),
eq(-1), eq(rangeMax), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, false);
// Verify reconfigure aware to enable ranging.
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(false), eq(true), eq(false), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (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);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, false);
// (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();
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, false);
// (7) another update + immediate failure
when(mMockNative.subscribe(anyShort(), anyByte(), any()))
.thenReturn(false);
mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
eq(subscribeConfig));
inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, reasonFail, false);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, mAwareMetricsMock);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final byte subscribeId = 15;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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(anyShort());
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 callingFeature = "com.google.someFeature";
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final int reasonFail = NanStatusType.INTERNAL_FAILURE;
final byte 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;
final int rangeMin = 0;
final int rangeMax = 55;
final int rangedDistance = 30;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.setServiceSpecificInfo(ssi.getBytes())
.setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
.setMinDistanceMm(rangeMin)
.setMaxDistanceMm(rangeMax)
.build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
InOrder inOrderM = inOrder(mAwareMetricsMock);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
inOrderM.verify(mAwareMetricsMock).recordEnableUsage();
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
inOrderM.verify(mAwareMetricsMock).recordAttachSession(eq(uid), eq(false), any());
// (1) subscribe
mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrderM.verify(mAwareMetricsMock).recordDiscoverySessionWithRanging(eq(uid), eq(true),
eq(rangeMin), eq(rangeMax), any());
inOrderM.verify(mAwareMetricsMock).recordDiscoveryStatus(uid, NanStatusType.SUCCESS, false);
// Verify reconfigure aware to enable ranging.
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(false), eq(true), eq(false), eq(true));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (2) 2 matches : with and w/o range
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
inOrderM.verify(mAwareMetricsMock).recordMatchIndicationForRangeEnabledSubscribe(false);
int peerId1 = peerIdCaptor.getValue();
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), EGRESS_MET_MASK, rangedDistance);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatchWithDistance(peerIdCaptor.capture(),
eq(peerSsi.getBytes()), eq(peerMatchFilter.getBytes()), eq(rangedDistance));
inOrderM.verify(mAwareMetricsMock).recordMatchIndicationForRangeEnabledSubscribe(true);
int peerId2 = peerIdCaptor.getValue();
assertEquals(peerId1, peerId2);
// (3) message Rx
mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.getValue(),
peerMsg.getBytes());
// (4) message Tx successful queuing
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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, mAwareMetricsMock);
}
/**
* 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 String callingFeature = "com.google.someFeature";
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
final byte publishId = 88;
final int requestorId1 = 568;
final int requestorId2 = 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 = NanStatusType.INTERNAL_FAILURE;
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);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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, requestorId1, peerMac1,
msgFromPeer1.getBytes());
mDut.onMessageReceivedNotification(publishId, requestorId2, peerMac2,
msgFromPeer2.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
eq(msgFromPeer1.getBytes()));
int peerId1 = peerIdCaptor.getValue();
inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
eq(msgFromPeer2.getBytes()));
int peerId2 = peerIdCaptor.getValue();
// (4) sending messages back to same peers: one Tx fails, other succeeds
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
msgToPeerId2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
eq(requestorId2), eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
short transactionIdVal = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
mDut.onMessageSendSuccessNotification(transactionIdVal);
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
msgToPeerId1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
eq(requestorId1), 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 String callingFeature = "com.google.someFeature";
final int clusterLow = 7;
final int clusterHigh = 7;
final int masterPref = 0;
final String serviceName = "some-service-name";
final byte publishId = 88;
final int requestorId = 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);
ArgumentCaptor<Integer> peerId = ArgumentCaptor.forClass(Integer.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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, requestorId, peerMacOrig,
msgFromPeer1.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(),
eq(msgFromPeer1.getBytes()));
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId.getValue(),
msgToPeer1.getBytes(), msgToPeerId1, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
eq(requestorId), 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, requestorId, peerMacLater,
msgFromPeer2.getBytes());
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageReceived(peerId.capture(),
eq(msgFromPeer2.getBytes()));
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerId.getValue(),
msgToPeer2.getBytes(), msgToPeerId2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId),
eq(requestorId), 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 callingFeature = "com.google.someFeature";
final String ssi = "some much longer and more arbitrary data";
final byte 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();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
// (3) send message to invalid peer ID
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue() + 5,
ssi.getBytes(), messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
NanStatusType.INTERNAL_FAILURE);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that on send message errors are handled correctly: immediate send error, queue fail
* error (not queue full), and timeout. Behavior: correct callback is dispatched and a later
* firmware notification is ignored. Intersperse with one successfull transmission.
*/
@Test
public void testSendMessageErrorsImmediateQueueTimeout() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final String ssi = "some much longer and more arbitrary data";
final byte 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();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
// (3) send 2 messages and enqueue successfully
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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) send a message and get a queueing failure (not queue full)
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
ssi.getBytes(), messageId + 2, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2));
short transactionId3 = transactionId.getValue();
mDut.onMessageSendQueuedFailResponse(transactionId3, NanStatusType.INTERNAL_FAILURE);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 2,
NanStatusType.INTERNAL_FAILURE);
validateInternalSendMessageQueuesCleanedUp(messageId + 2);
// (5) send a message and get an immediate failure (configure first)
when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenReturn(false);
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
ssi.getBytes(), messageId + 3, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3));
short transactionId4 = transactionId.getValue();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 3,
NanStatusType.INTERNAL_FAILURE);
validateInternalSendMessageQueuesCleanedUp(messageId + 3);
// (6) message send timeout
assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
NanStatusType.INTERNAL_FAILURE);
validateInternalSendMessageQueuesCleanedUp(messageId);
// (7) firmware response (unlikely - but good to check)
mDut.onMessageSendSuccessNotification(transactionId1);
mDut.onMessageSendSuccessNotification(transactionId2);
// bogus: these didn't even go to firmware or weren't queued
mDut.onMessageSendSuccessNotification(transactionId3);
mDut.onMessageSendFailNotification(transactionId4, NanStatusType.INTERNAL_FAILURE);
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 callingFeature = "com.google.someFeature";
final String ssi = "some much longer and more arbitrary data";
final byte 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();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
// (3) send message and enqueue successfully
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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(), NanStatusType.NO_OTA_ACK);
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 callingFeature = "com.google.someFeature";
final String ssi = "some much longer and more arbitrary data";
final byte 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();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(),
peerMatchFilter.getBytes(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
// (3) send message and enqueue successfully
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
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(), NanStatusType.NO_OTA_ACK);
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,
NanStatusType.NO_OTA_ACK);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
/**
* Validate that the host-side message queue functions. Tests the perfect case of queue always
* succeeds and all messages are received on first attempt.
*/
@Test
public void testSendMessageQueueSequence() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final String serviceName = "some-service-name";
final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final int messageIdBase = 6948;
final int numberOfMessages = 30;
final int queueDepth = 6;
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> messageIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null, 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), isNull(), isNull());
// (3) transmit messages
SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
null, null, null);
when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenAnswer(answerObj);
int remainingMessages = numberOfMessages;
for (int i = 0; i < numberOfMessages; ++i) {
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), null,
messageIdBase + i, 0);
mMockLooper.dispatchAll();
// at 1/2 interval have the system simulate transmitting a queued message over-the-air
if (i % 2 == 1) {
assertTrue(answerObj.process());
remainingMessages--;
mMockLooper.dispatchAll();
}
}
for (int i = 0; i < remainingMessages; ++i) {
assertTrue(answerObj.process());
mMockLooper.dispatchAll();
}
assertEquals("queue empty", 0, answerObj.queueSize());
inOrder.verify(mockSessionCallback, times(numberOfMessages)).onMessageSendSuccess(
messageIdCaptor.capture());
for (int i = 0; i < numberOfMessages; ++i) {
assertEquals("message ID: " + i, (long) messageIdBase + i,
(long) messageIdCaptor.getAllValues().get(i));
}
verifyNoMoreInteractions(mockCallback, mockSessionCallback);
}
/**
* Validate that the message queue depth per process function. Tests the case
* with two processes both have message num larger than queue depth. And all messages get
* into the firmware queue are sent out and are received on first attempt.
*/
@Test
public void testSendMessageQueueLimitBlock() throws Exception {
final int clientId1 = 1005;
final int clientId2 = 1006;
final int uid1 = 1000;
final int uid2 = 1500;
final int pid1 = 2000;
final int pid2 = 3000;
final String callingPackage1 = "com.google.somePackage1";
final String callingPackage2 = "com.google.somePackage2";
final String callingFeature = "com.google.someFeature";
final String serviceName1 = "some-service-name1";
final String serviceName2 = "some-service-name2";
final byte subscribeId1 = 15;
final byte subscribeId2 = 16;
final int requestorId1 = 22;
final int requestorId2 = 23;
final byte[] peerMac1 = HexEncoding.decode("060708090A0B".toCharArray(), false);
final byte[] peerMac2 = HexEncoding.decode("060708090A0C".toCharArray(), false);
final int messageIdBase1 = 6948;
final int messageIdBase2 = 7948;
final int numberOfMessages = 70;
final int queueDepth = 6;
final int messageQueueDepthPerUid = 50;
final int numOfReject = numberOfMessages - messageQueueDepthPerUid;
ConfigRequest configRequest1 = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig1 = new SubscribeConfig.Builder()
.setServiceName(serviceName1).build();
ConfigRequest configRequest2 = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig2 = new SubscribeConfig.Builder()
.setServiceName(serviceName2).build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId1 = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> sessionId2 = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> messageIdCaptorFail = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> messageIdCaptorSuccess = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor1 = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor2 = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId1, uid1, pid1, callingPackage1, callingFeature, mockCallback,
configRequest1, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest1), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId1);
mDut.connect(clientId2, uid2, pid2, callingPackage2, callingFeature, mockCallback,
configRequest2, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId2);
// (1) subscribe
mDut.subscribe(clientId1, subscribeConfig1, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig1));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId1);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId1.capture());
mDut.subscribe(clientId2, subscribeConfig2, mockSessionCallback);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq((byte) 0),
eq(subscribeConfig2));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId2);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId2.capture());
// (2) match
mDut.onMatchNotification(subscribeId1, requestorId1, peerMac1, null, null, 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor1.capture(), isNull(), isNull());
mDut.onMatchNotification(subscribeId2, requestorId2, peerMac2, null, null, 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor2.capture(), isNull(), isNull());
// (3) Enqueue messages
SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
null, null, null);
when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenAnswer(answerObj);
for (int i = 0; i < numberOfMessages; ++i) {
mDut.sendMessage(uid1, clientId1, sessionId1.getValue(), peerIdCaptor1.getValue(), null,
messageIdBase1 + i, 0);
}
for (int i = 0; i < numberOfMessages; ++i) {
mDut.sendMessage(uid2, clientId2, sessionId2.getValue(), peerIdCaptor2.getValue(), null,
messageIdBase2 + i, 0);
}
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback, times(numOfReject * 2))
.onMessageSendFail(messageIdCaptorFail.capture(),
eq(NanStatusType.INTERNAL_FAILURE));
// (4) Transmit messages
int successNum = 0;
for (int i = 0; i < numberOfMessages * 2; ++i) {
if (answerObj.process()) {
successNum++;
} else {
break;
}
mMockLooper.dispatchAll();
}
assertEquals("queue empty", 0, answerObj.queueSize());
assertEquals("success message num", messageQueueDepthPerUid * 2, successNum);
inOrder.verify(mockSessionCallback, times(messageQueueDepthPerUid * 2))
.onMessageSendSuccess(messageIdCaptorSuccess.capture());
for (int i = 0; i < numOfReject; ++i) {
assertEquals("message ID: " + i + messageQueueDepthPerUid,
messageIdBase1 + i + messageQueueDepthPerUid,
(int) messageIdCaptorFail.getAllValues().get(i));
assertEquals("message ID: " + i + messageQueueDepthPerUid,
messageIdBase2 + i + messageQueueDepthPerUid,
(int) messageIdCaptorFail.getAllValues().get(i + numOfReject));
}
for (int i = 0; i < messageQueueDepthPerUid; ++i) {
assertEquals("message ID: " + i, messageIdBase1 + i,
(int) messageIdCaptorSuccess.getAllValues().get(i));
assertEquals("message ID: " + i, messageIdBase2 + i,
(int) messageIdCaptorSuccess.getAllValues().get(i + messageQueueDepthPerUid));
}
verifyNoMoreInteractions(mockCallback, mockSessionCallback);
}
/**
* Validate that the host-side message queue functions. A combination of imperfect conditions:
* - Failure to queue: synchronous firmware error
* - Failure to queue: asyncronous firmware error
* - Failure to transmit: OTA (which will be retried)
* - Failure to transmit: other
*/
@Test
public void testSendMessageQueueSequenceImperfect() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final String serviceName = "some-service-name";
final byte subscribeId = 15;
final int requestorId = 22;
final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
final int messageIdBase = 6948;
final int numberOfMessages = 300;
final int queueDepth = 6;
final int retransmitCount = 3; // not the maximum
ConfigRequest configRequest = new ConfigRequest.Builder().build();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
.build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(subscribeConfig));
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
// (2) match
mDut.onMatchNotification(subscribeId, requestorId, peerMac, null, null, 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), isNull(), isNull());
// (3) transmit messages: configure a mix of failures/success
Set<Integer> failQueueCommandImmediately = new HashSet<>();
Set<Integer> failQueueCommandLater = new HashSet<>();
Map<Integer, Integer> numberOfRetries = new HashMap<>();
int numOfSuccesses = 0;
int numOfFailuresInternalFailure = 0;
int numOfFailuresNoOta = 0;
for (int i = 0; i < numberOfMessages; ++i) {
// random results:
// - 0-50: success
// - 51-60: retransmit value (which will fail for >5)
// - 61-70: fail queue later
// - 71-80: fail queue immediately
// - 81-90: fail retransmit with non-OTA failure
int random = mRandomNg.nextInt(90);
if (random <= 50) {
numberOfRetries.put(messageIdBase + i, 0);
numOfSuccesses++;
} else if (random <= 60) {
numberOfRetries.put(messageIdBase + i, random - 51);
if (random - 51 > retransmitCount) {
numOfFailuresNoOta++;
} else {
numOfSuccesses++;
}
} else if (random <= 70) {
failQueueCommandLater.add(messageIdBase + i);
numOfFailuresInternalFailure++;
} else if (random <= 80) {
failQueueCommandImmediately.add(messageIdBase + i);
numOfFailuresInternalFailure++;
} else {
numberOfRetries.put(messageIdBase + i, -1);
numOfFailuresInternalFailure++;
}
}
Log.v("WifiAwareStateManagerTest",
"failQueueCommandImmediately=" + failQueueCommandImmediately
+ ", failQueueCommandLater=" + failQueueCommandLater + ", numberOfRetries="
+ numberOfRetries + ", numOfSuccesses=" + numOfSuccesses
+ ", numOfFailuresInternalFailure=" + numOfFailuresInternalFailure
+ ", numOfFailuresNoOta=" + numOfFailuresNoOta);
SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
failQueueCommandImmediately, failQueueCommandLater, numberOfRetries);
when(mMockNative.sendMessage(anyShort(), anyByte(), anyInt(), any(),
any(), anyInt())).thenAnswer(answerObj);
for (int i = 0; i < numberOfMessages; ++i) {
mDut.sendMessage(uid + i, clientId, sessionId.getValue(), peerIdCaptor.getValue(), null,
messageIdBase + i, retransmitCount);
mMockLooper.dispatchAll();
}
while (answerObj.queueSize() != 0) {
assertTrue(answerObj.process());
mMockLooper.dispatchAll();
}
verify(mockSessionCallback, times(numOfSuccesses)).onMessageSendSuccess(anyInt());
verify(mockSessionCallback, times(numOfFailuresInternalFailure)).onMessageSendFail(anyInt(),
eq(NanStatusType.INTERNAL_FAILURE));
verify(mockSessionCallback, times(numOfFailuresNoOta)).onMessageSendFail(anyInt(),
eq(NanStatusType.NO_OTA_ACK));
verifyNoMoreInteractions(mockCallback, mockSessionCallback);
}
/**
* Validate that can send empty message successfully: null, byte[0], ""
*/
@Test
public void testSendEmptyMessages() throws Exception {
final int clientId = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final String serviceName = "some-service-name";
final String ssi = "some much longer and more arbitrary data";
final byte 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().setServiceName(serviceName)
.setServiceSpecificInfo(ssi.getBytes())
.setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
.build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Integer> peerIdCaptor = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].class);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (0) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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(), 0, 0);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMatch(peerIdCaptor.capture(), eq(peerSsi.getBytes()),
eq(peerMatchFilter.getBytes()));
// (3) message null Tx successful queuing
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
null, messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId));
short tid = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid);
mMockLooper.dispatchAll();
// (4) final Tx results (on-air results)
mDut.onMessageSendSuccessNotification(tid);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId);
// (5) message byte[0] Tx successful queuing
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(), new byte[0],
messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), eq(new byte[0]), eq(messageId));
tid = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid);
mMockLooper.dispatchAll();
// (6) final Tx results (on-air results)
mDut.onMessageSendSuccessNotification(tid);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId);
// (7) message "" Tx successful queuing
mDut.sendMessage(uid, clientId, sessionId.getValue(), peerIdCaptor.getValue(),
"".getBytes(), messageId, 0);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId));
collector.checkThat("Empty message contents", "",
equalTo(new String(byteArrayCaptor.getValue())));
tid = transactionId.getValue();
mDut.onMessageSendQueuedSuccessResponse(tid);
mMockLooper.dispatchAll();
// (8) final Tx results (on-air results)
mDut.onMessageSendSuccessNotification(tid);
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
validateInternalSendMessageQueuesCleanedUp(messageId);
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
}
private class SendMessageQueueModelAnswer extends MockAnswerUtil.AnswerWithArguments {
private final int mMaxQueueDepth;
// keyed by message ID
private final Set<Integer> mFailQueueCommandImmediately; // return a false
private final Set<Integer> mFailQueueCommandLater; // return an error != TX_QUEUE_FULL
// # of times to return NO_OTA_ACK before returning SUCCESS. So a 0 means success on first
// try, a very large number means - never succeed (since max retry is 5).
// a -1 impiles a non-OTA failure: on first attempt
private final Map<Integer, Integer> mRetryLimit;
private final LinkedList<Short> mQueue = new LinkedList<>(); // transaction ID (tid)
private final Map<Short, Integer> mMessageIdsByTid = new HashMap<>(); // tid -> message ID
private final Map<Integer, Integer> mTriesUsedByMid = new HashMap<>(); // mid -> # of retx
SendMessageQueueModelAnswer(int maxQueueDepth, Set<Integer> failQueueCommandImmediately,
Set<Integer> failQueueCommandLater, Map<Integer, Integer> numberOfRetries) {
mMaxQueueDepth = maxQueueDepth;
mFailQueueCommandImmediately = failQueueCommandImmediately;
mFailQueueCommandLater = failQueueCommandLater;
mRetryLimit = numberOfRetries;
if (mRetryLimit != null) {
for (int mid : mRetryLimit.keySet()) {
mTriesUsedByMid.put(mid, 0);
}
}
}
public boolean answer(short transactionId, byte pubSubId, int requestorInstanceId,
byte[] dest, byte[] message, int messageId) throws Exception {
if (mFailQueueCommandImmediately != null && mFailQueueCommandImmediately.contains(
messageId)) {
return false;
}
if (mFailQueueCommandLater != null && mFailQueueCommandLater.contains(messageId)) {
mDut.onMessageSendQueuedFailResponse(transactionId, NanStatusType.INTERNAL_FAILURE);
} else {
if (mQueue.size() <= mMaxQueueDepth) {
mQueue.addLast(transactionId);
mMessageIdsByTid.put(transactionId, messageId);
mDut.onMessageSendQueuedSuccessResponse(transactionId);
} else {
mDut.onMessageSendQueuedFailResponse(transactionId,
NanStatusType.FOLLOWUP_TX_QUEUE_FULL);
}
}
return true;
}
/**
* Processes the first message in the queue: i.e. responds as if sent over-the-air
* (successfully or failed)
*/
boolean process() {
if (mQueue.size() == 0) {
return false;
}
short tid = mQueue.poll();
int mid = mMessageIdsByTid.get(tid);
if (mRetryLimit != null && mRetryLimit.containsKey(mid)) {
int numRetries = mRetryLimit.get(mid);
if (numRetries == -1) {
mDut.onMessageSendFailNotification(tid, NanStatusType.INTERNAL_FAILURE);
} else {
int currentRetries = mTriesUsedByMid.get(mid);
if (currentRetries > numRetries) {
return false; // shouldn't be retrying!?
} else if (currentRetries == numRetries) {
mDut.onMessageSendSuccessNotification(tid);
} else {
mDut.onMessageSendFailNotification(tid, NanStatusType.NO_OTA_ACK);
}
mTriesUsedByMid.put(mid, currentRetries + 1);
}
} else {
mDut.onMessageSendSuccessNotification(tid);
}
return true;
}
/**
* Returns the number of elements in the queue.
*/
int queueSize() {
return mQueue.size();
}
}
/**
* 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 clientId2 = 1001;
final int clientId3 = 1005;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final int masterPref1 = 111;
final int masterPref3 = 115;
final int dwInterval1Band24 = 2;
final int dwInterval3Band24 = 1;
final int dwInterval3Band5 = 0;
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
ConfigRequest configRequest1 = new ConfigRequest.Builder()
.setClusterLow(5).setClusterHigh(100)
.setMasterPreference(masterPref1)
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval1Band24)
.build();
ConfigRequest configRequest2 = new ConfigRequest.Builder()
.setSupport5gBand(true) // compatible
.setSupport6gBand(false)
.setClusterLow(7).setClusterHigh(155) // incompatible!
.setMasterPreference(0) // compatible
.build();
ConfigRequest configRequest3 = new ConfigRequest.Builder()
.setSupport5gBand(true) // compatible (will use true)
.setSupport6gBand(false)
.setClusterLow(5).setClusterHigh(100) // identical (hence compatible)
.setMasterPreference(masterPref3) // compatible (will use max)
// compatible: will use min
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval3Band24)
// compatible: will use interval3 since interval1 not init
.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwInterval3Band5)
.build();
IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
IWifiAwareEventCallback mockCallback3 = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) config1 (valid)
mDut.connect(clientId1, uid, pid, callingPackage, callingFeature, mockCallback1,
configRequest1, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(false), eq(true), eq(true), eq(false), eq(false));
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, callingFeature, mockCallback2,
configRequest2, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback2).onConnectFail(NanStatusType.INTERNAL_FAILURE);
validateInternalClientInfoCleanedUp(clientId2);
// (3) config3 (compatible with config1)
mDut.connect(clientId3, uid, pid, callingPackage, callingFeature, mockCallback3,
configRequest3, true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(true), eq(false), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
collector.checkThat("support 5g: or", true, equalTo(crCapture.getValue().mSupport5gBand));
collector.checkThat("support 6g: or", false, equalTo(crCapture.getValue().mSupport6gBand));
collector.checkThat("master preference: max", Math.max(masterPref1, masterPref3),
equalTo(crCapture.getValue().mMasterPreference));
collector.checkThat("dw interval on 2.4: ~min",
Math.min(dwInterval1Band24, dwInterval3Band24),
equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_24GHZ]));
collector.checkThat("dw interval on 5: ~min", dwInterval3Band5,
equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
.NAN_BAND_5GHZ]));
// (4) disconnect config3: downgrade to config1
mDut.disconnect(clientId3);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId3);
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
crCapture.capture(), eq(false), eq(false), eq(true), eq(false), eq(false));
collector.checkThat("configRequest1", configRequest1, equalTo(crCapture.getValue()));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (5) disconnect config1: disable
mDut.disconnect(clientId1);
mMockLooper.dispatchAll();
validateInternalClientInfoCleanedUp(clientId1);
inOrder.verify(mMockNative).disable(anyShort());
verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
}
/**
* Validate that identical configuration but with different identity callback requirements
* trigger the correct HAL sequence.
* 1. Attach w/o identity -> enable
* 2. Attach w/o identity -> nop
* 3. Attach w/ identity -> re-configure
* 4. Attach w/o identity -> nop
* 5. Attach w/ identity -> nop
*/
@Test
public void testConfigsIdentityCallback() throws Exception {
int clientId = 9999;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
ConfigRequest configRequest = new ConfigRequest.Builder().build();
InOrder inOrder = inOrder(mMockNative, mockCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) attach w/o identity
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
any(ConfigRequest.class), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) attach w/o identity
++clientId;
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (3) attach w/ identity
++clientId;
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
any(ConfigRequest.class), eq(true), eq(false), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (4) attach w/o identity
++clientId;
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (5) attach w/ identity
++clientId;
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, true);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/**
* 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 String callingFeature = "com.google.someFeature";
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 byte publishId = 22;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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(anyShort());
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, 0, 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 String callingFeature = "com.google.someFeature";
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";
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
.setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockPublishSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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";
final String callingFeature = "com.google.someFeature";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect (no response)
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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 byte pubSubId = 125;
final int clientId = 132;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect and succeed
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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], 0, 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 String callingFeature = "com.google.someFeature";
final byte 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);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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(NanStatusType.INTERNAL_FAILURE);
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 String callingFeature = "com.google.someFeature";
final byte 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);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 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(NanStatusType.INTERNAL_FAILURE);
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";
final String callingFeature = "com.google.someFeature";
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);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
IWifiAwareDiscoverySessionCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
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((byte) 0),
eq(publishConfig));
// (3) publish-success
mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, (byte) (i + 1));
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
if (i != 0) {
assertTrue("Session ID incrementing", sessionId.getValue() > prevId);
}
prevId = sessionId.getValue();
}
}
/**
* Validate configuration changes on power state changes when Aware is not disabled on doze.
*/
@Test
public void testConfigOnPowerStateChanges() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(0),
true);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockNative, mockCallback);
mDut.enableUsage();
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) power state change: SCREEN OFF
simulatePowerStateChangeInteractive(false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(false), eq(false), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (3) power state change: DOZE
simulatePowerStateChangeDoze(true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(false), eq(false), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
// (4) restore power state to default
simulatePowerStateChangeInteractive(true); // effectively treated as no-doze
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(false), eq(true), eq(true), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/**
* Validate aware enable/disable during doze transitions.
*/
@Test
public void testEnableDisableOnDoze() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
setSettableParam(WifiAwareStateManager.PARAM_ON_IDLE_DISABLE_AWARE, Integer.toString(1),
true);
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNativeManager, mMockNative, mockCallback);
inOrder.verify(mMockNativeManager).start(any(Handler.class));
mDut.enableUsage();
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).releaseAware();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (3) power state change: DOZE
simulatePowerStateChangeDoze(true);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).disable(transactionId.capture());
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
validateCorrectAwareStatusChangeBroadcast(inOrder);
// (4) power state change: SCREEN ON (but DOZE still on - fakish but expect no changes)
simulatePowerStateChangeInteractive(false);
mMockLooper.dispatchAll();
// and same for other gating changes -> no changes
simulateLocationModeChange(false);
simulateWifiStateChange(false);
mMockLooper.dispatchAll();
// and same for other gating changes -> no changes
simulateLocationModeChange(true);
simulateWifiStateChange(true);
mMockLooper.dispatchAll();
// when WifiAware Native is not available, DOZE OFF -> no change
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false);
simulatePowerStateChangeDoze(false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true);
// (5) power state change: DOZE OFF
simulatePowerStateChangeDoze(false);
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder);
verifyNoMoreInteractions(mMockNativeManager, mMockNative, mockCallback);
}
/**
* Validate aware enable/disable during LOCATION MODE transitions.
*/
@Test
public void testEnableDisableOnLocationModeChanges() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNativeManager, mMockNative, mockCallback);
inOrder.verify(mMockNativeManager).start(any(Handler.class));
mDut.enableUsage();
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).releaseAware();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (3) location mode change: disable
simulateLocationModeChange(false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).disable(transactionId.capture());
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
validateCorrectAwareStatusChangeBroadcast(inOrder);
// disable other gating feature -> no change
simulatePowerStateChangeDoze(true);
simulateWifiStateChange(false);
mMockLooper.dispatchAll();
// enable other gating feature -> no change
simulatePowerStateChangeDoze(false);
simulateWifiStateChange(true);
mMockLooper.dispatchAll();
// when WifiAware Native is not available, enable location -> no change
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false);
simulateLocationModeChange(true);
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true);
// (4) location mode change: enable
simulateLocationModeChange(true);
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder);
verifyNoMoreInteractions(mMockNativeManager, mMockNative, mockCallback);
}
/**
* Validate aware enable/disable during Wi-Fi State transitions.
*/
@Test
public void testEnableDisableOnWifiStateChanges() throws Exception {
final int clientId = 188;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
ConfigRequest configRequest = new ConfigRequest.Builder().build();
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNativeManager, mMockNative, mockCallback);
inOrder.verify(mMockNativeManager).start(any(Handler.class));
mDut.enableUsage();
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).releaseAware();
// (1) connect
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNativeManager).tryToGetAware();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (3) wifi state change: disable
simulateWifiStateChange(false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).disable(transactionId.capture());
mDut.onDisableResponse(transactionId.getValue(), NanStatusType.SUCCESS);
collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
validateCorrectAwareStatusChangeBroadcast(inOrder);
// disable other gating feature -> no change
simulatePowerStateChangeDoze(true);
simulateLocationModeChange(false);
mMockLooper.dispatchAll();
// enable other gating feature -> no change
simulatePowerStateChangeDoze(false);
simulateLocationModeChange(true);
mMockLooper.dispatchAll();
// when WifiAware Native is not available, enable Wifi -> no change
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(false);
simulateWifiStateChange(true);
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
when(mMockNativeManager.isAwareNativeAvailable()).thenReturn(true);
// (4) wifi state change: enable
simulateWifiStateChange(true);
inOrder.verify(mMockNativeManager).isAwareNativeAvailable();
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder);
verifyNoMoreInteractions(mMockNativeManager, mMockNative, mockCallback);
}
/**
* Validate aware state change when get aware down from native
*/
@Test
public void testWifiAwareStateChangeFromNative() throws Exception {
final int clientId = 12314;
final int uid = 1000;
final int pid = 2000;
final String callingPackage = "com.google.somePackage";
final String callingFeature = "com.google.someFeature";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
// (0) check initial state
mDut.enableUsage();
mMockLooper.dispatchAll();
validateCorrectAwareStatusChangeBroadcast(inOrder);
inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
// (1) connect client
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
// (2) Aware down notification from native
mDut.onAwareDownNotification(NanStatusType.UNSUPPORTED_CONCURRENCY_NAN_DISABLED);
mMockLooper.dispatchAll();
collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
validateCorrectAwareStatusChangeBroadcast(inOrder);
// (3) try reconnect client
mDut.connect(clientId, uid, pid, callingPackage, callingFeature, mockCallback,
configRequest, false);
mMockLooper.dispatchAll();
inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
eq(configRequest), eq(false), eq(true), eq(true), eq(false), eq(false));
mDut.onConfigSuccessResponse(transactionId.getValue());
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onConnectSuccess(clientId);
verifyNoMoreInteractions(mMockNative, mockCallback);
}
/*
* Tests of internal state of WifiAwareStateManager: 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 {
WifiAwareClientState 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 {
WifiAwareClientState client = getInternalClientState(mDut, clientId);
collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
WifiAwareDiscoverySessionState 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 {
WifiAwareClientState client = getInternalClientState(mDut, clientId);
collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiAwareDiscoverySessionState> sessions =
(SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
equalTo(0));
}
/**
* Validates that the broadcast sent on Aware status change is correct.
*/
private void validateCorrectAwareStatusChangeBroadcast(InOrder inOrder) {
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL));
collector.checkThat("intent action", intent.getValue().getAction(),
equalTo(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED));
}
/*
* Utilities
*/
private void setSettableParam(String name, String value, boolean expectSuccess) {
PrintWriter pwMock = mock(PrintWriter.class);
WifiAwareShellCommand parentShellMock = mock(WifiAwareShellCommand.class);
when(parentShellMock.getNextArgRequired()).thenReturn("set").thenReturn(name).thenReturn(
value);
when(parentShellMock.getErrPrintWriter()).thenReturn(pwMock);
collector.checkThat(mDut.onCommand(parentShellMock), equalTo(expectSuccess ? 0 : -1));
}
private void dumpDut(String prefix) {
StringWriter sw = new StringWriter();
mDut.dump(null, new PrintWriter(sw), null);
Log.e("WifiAwareStateManagerTest", prefix + sw.toString());
}
private static void installMocksInStateManager(WifiAwareStateManager awareStateManager,
WifiAwareDataPathStateManager mockDpMgr)
throws Exception {
Field field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr");
field.setAccessible(true);
field.set(awareStateManager, mockDpMgr);
}
private static WifiAwareClientState getInternalClientState(WifiAwareStateManager dut,
int clientId) throws Exception {
Field field = WifiAwareStateManager.class.getDeclaredField("mClients");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiAwareClientState> clients = (SparseArray<WifiAwareClientState>) field.get(
dut);
return clients.get(clientId);
}
private static WifiAwareDiscoverySessionState getInternalSessionState(
WifiAwareClientState client, int sessionId) throws Exception {
Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
field.setAccessible(true);
@SuppressWarnings("unchecked")
SparseArray<WifiAwareDiscoverySessionState> sessions =
(SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
return sessions.get(sessionId);
}
private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
Field field = WifiAwareStateManager.class.getDeclaredField("mSm");
field.setAccessible(true);
WifiAwareStateManager.WifiAwareStateMachine sm =
(WifiAwareStateManager.WifiAwareStateMachine) field.get(mDut);
field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
"mHostQueuedSendMessages");
field.setAccessible(true);
SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
field = WifiAwareStateManager.WifiAwareStateMachine.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());
}
}
}
/**
* Simulate power state change due to doze. Changes the power manager return values and
* dispatches a broadcast.
*/
private void simulatePowerStateChangeDoze(boolean isDozeOn) {
when(mMockPowerManager.isDeviceIdleMode()).thenReturn(isDozeOn);
Intent intent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mPowerBcastReceiver.onReceive(mMockContext, intent);
}
/**
* Simulate power state change due to interactive mode change (screen on/off). Changes the power
* manager return values and dispatches a broadcast.
*/
private void simulatePowerStateChangeInteractive(boolean isInteractive) {
when(mMockPowerManager.isInteractive()).thenReturn(isInteractive);
Intent intent = new Intent(
isInteractive ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
mPowerBcastReceiver.onReceive(mMockContext, intent);
}
/**
* Simulate Location Mode change. Changes the location manager return values and dispatches a
* broadcast.
*/
private void simulateLocationModeChange(boolean isLocationModeEnabled) {
when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(isLocationModeEnabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION);
mLocationModeReceiver.onReceive(mMockContext, intent);
}
/**
* Simulate Wi-Fi state change: broadcast state change and modify the API return value.
*/
private void simulateWifiStateChange(boolean isWifiOn) {
when(mMockWifiManager.getWifiState()).thenReturn(
isWifiOn ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_WIFI_STATE,
isWifiOn ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
mWifiStateChangedReceiver.onReceive(mMockContext, intent);
}
private static Capabilities getCapabilities() {
Capabilities cap = new Capabilities();
cap.maxConcurrentAwareClusters = 1;
cap.maxPublishes = 2;
cap.maxSubscribes = 2;
cap.maxServiceNameLen = 255;
cap.maxMatchFilterLen = 255;
cap.maxTotalMatchFilterLen = 255;
cap.maxServiceSpecificInfoLen = 255;
cap.maxExtendedServiceSpecificInfoLen = 255;
cap.maxNdiInterfaces = 1;
cap.maxNdpSessions = 1;
cap.maxAppInfoLen = 255;
cap.maxQueuedTransmitMessages = 6;
return cap;
}
private static class Mutable<E> {
public E value;
Mutable() {
value = null;
}
Mutable(E value) {
this.value = value;
}
}
}