| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi.nan; |
| |
| import static org.hamcrest.core.IsEqual.equalTo; |
| import static org.hamcrest.core.IsNull.notNullValue; |
| import static org.hamcrest.core.IsNull.nullValue; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Matchers.anyBoolean; |
| import static org.mockito.Matchers.anyInt; |
| import static org.mockito.Matchers.anyShort; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.inOrder; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.net.wifi.RttManager; |
| import android.net.wifi.nan.ConfigRequest; |
| import android.net.wifi.nan.IWifiNanEventCallback; |
| import android.net.wifi.nan.IWifiNanSessionCallback; |
| import android.net.wifi.nan.PublishConfig; |
| import android.net.wifi.nan.SubscribeConfig; |
| import android.net.wifi.nan.WifiNanEventCallback; |
| import android.net.wifi.nan.WifiNanManager; |
| import android.net.wifi.nan.WifiNanSessionCallback; |
| import android.os.UserHandle; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.util.SparseArray; |
| |
| import com.android.server.wifi.MockAlarmManager; |
| import com.android.server.wifi.MockLooper; |
| |
| import libcore.util.HexEncoding; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.ErrorCollector; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.InOrder; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| |
| /** |
| * Unit test harness for WifiNanStateManager. |
| */ |
| @SmallTest |
| public class WifiNanStateManagerTest { |
| private MockLooper mMockLooper; |
| private WifiNanStateManager mDut; |
| @Mock private WifiNanNative mMockNative; |
| @Mock private Context mMockContext; |
| @Mock private WifiNanRttStateManager mMockNanRttStateManager; |
| MockAlarmManager mAlarmManager; |
| |
| @Rule |
| public ErrorCollector collector = new ErrorCollector(); |
| |
| /** |
| * Pre-test configuration. Initialize and install mocks. |
| */ |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| |
| mAlarmManager = new MockAlarmManager(); |
| when(mMockContext.getSystemService(Context.ALARM_SERVICE)) |
| .thenReturn(mAlarmManager.getAlarmManager()); |
| |
| mMockLooper = new MockLooper(); |
| |
| mDut = installNewNanStateManagerAndResetState(mMockNanRttStateManager); |
| mDut.start(mMockContext, mMockLooper.getLooper()); |
| |
| when(mMockNative.enableAndConfigure(anyShort(), any(ConfigRequest.class), anyBoolean())) |
| .thenReturn(true); |
| when(mMockNative.disable(anyShort())).thenReturn(true); |
| when(mMockNative.publish(anyShort(), anyInt(), any(PublishConfig.class))).thenReturn(true); |
| when(mMockNative.subscribe(anyShort(), anyInt(), any(SubscribeConfig.class))) |
| .thenReturn(true); |
| when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(byte[].class), |
| any(byte[].class), anyInt())).thenReturn(true); |
| when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true); |
| when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true); |
| |
| installMockWifiNanNative(mMockNative); |
| } |
| |
| /** |
| * Validate that APIs aren't functional when usage is disabled. |
| */ |
| @Test |
| public void testDisableUsageDisablesApis() throws Exception { |
| final int clientId = 12314; |
| final ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| |
| // (1) check initial state |
| validateCorrectNanStatusChangeBroadcast(inOrder, true); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); |
| |
| // (2) disable usage and validate state |
| mDut.disableUsage(); |
| mMockLooper.dispatchAll(); |
| collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false)); |
| inOrder.verify(mMockNative).disable((short) 0); |
| inOrder.verify(mMockNative).deInitNan(); |
| validateCorrectNanStatusChangeBroadcast(inOrder, false); |
| |
| // (3) try connecting and validate that get nothing (app should be aware of non-availability |
| // through state change broadcast and/or query API) |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback); |
| } |
| |
| /** |
| * Validate that when API usage is disabled while in the middle of a connection that internal |
| * state is cleaned-up, and that all subsequent operations are NOP. Then enable usage again and |
| * validate that operates correctly. |
| */ |
| @Test |
| public void testDisableUsageFlow() throws Exception { |
| final int clientId = 12341; |
| final ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| |
| // (1) check initial state |
| validateCorrectNanStatusChangeBroadcast(inOrder, true); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); |
| |
| // (2) connect (successfully) |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (3) disable usage & verify callbacks |
| mDut.disableUsage(); |
| mMockLooper.dispatchAll(); |
| collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false)); |
| inOrder.verify(mMockNative).disable((short) 0); |
| inOrder.verify(mMockNative).deInitNan(); |
| validateCorrectNanStatusChangeBroadcast(inOrder, false); |
| validateInternalClientInfoCleanedUp(clientId); |
| |
| // (4) try connecting again and validate that just get an onNanDown |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| |
| // (5) disable usage again and validate that not much happens |
| mDut.disableUsage(); |
| mMockLooper.dispatchAll(); |
| collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false)); |
| |
| // (6) enable usage |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true)); |
| validateCorrectNanStatusChangeBroadcast(inOrder, true); |
| |
| // note: this is called a second time (which it shouldn't if capabilties were obtained |
| // at first) because this test case does not provide a capabilities response. |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (7) connect (should be successful) |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback); |
| } |
| |
| /** |
| * Validates that all events are delivered with correct arguments. Validates |
| * that IdentityChanged not delivered if configuration disables delivery. |
| */ |
| @Test |
| public void testNanEventsDelivery() throws Exception { |
| final int clientId1 = 1005; |
| final int clientId2 = 1007; |
| final int clusterLow = 5; |
| final int clusterHigh = 100; |
| final int masterPref = 111; |
| final int reason = WifiNanEventCallback.REASON_OTHER; |
| final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false); |
| |
| ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref) |
| .setEnableIdentityChangeCallback(false).build(); |
| |
| ConfigRequest configRequest2 = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref) |
| .setEnableIdentityChangeCallback(true).build(); |
| |
| IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class); |
| IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class); |
| ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect 1st and 2nd clients |
| mDut.connect(clientId1, mockCallback1, configRequest1); |
| mDut.connect(clientId2, mockCallback2, configRequest2); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(), |
| eq(configRequest1), eq(true)); |
| short transactionId = transactionIdCapture.getValue(); |
| mDut.onConfigSuccessResponse(transactionId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback1).onConnectSuccess(); |
| |
| // (2) finish connection of 2nd client |
| inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(), |
| eq(configRequest2), eq(false)); |
| transactionId = transactionIdCapture.getValue(); |
| mDut.onConfigSuccessResponse(transactionId); |
| |
| // (3) deliver NAN events |
| mDut.onClusterChangeNotification(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac); |
| mDut.onInterfaceAddressChangeNotification(someMac); |
| mDut.onNanDownNotification(reason); |
| mMockLooper.dispatchAll(); |
| |
| inOrder.verify(mockCallback2).onConnectSuccess(); |
| inOrder.verify(mockCallback2, times(2)).onIdentityChanged(); |
| |
| 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 ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| final PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect (successfully) |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish + timeout |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig)); |
| assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_COMMAND_TIMEOUT_TAG)); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback) |
| .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER); |
| validateInternalNoSessions(clientId); |
| |
| // (3) publish + success |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, 9999); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(anyInt()); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback); |
| } |
| |
| /** |
| * Validates publish flow: (1) initial publish (2) fail. Expected: get a |
| * failure callback. |
| */ |
| @Test |
| public void testPublishFail() throws Exception { |
| final int clientId = 1005; |
| final int reasonFail = WifiNanSessionCallback.REASON_NO_RESOURCES; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| eq(configRequest), eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (2) publish failure |
| mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail); |
| validateInternalNoSessions(clientId); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validates the publish flow: (1) initial publish (2) success (3) |
| * termination (e.g. DONE) (4) update session attempt (5) terminateSession |
| * (6) update session attempt. Expected: session ID callback + session |
| * cleaned-up. |
| */ |
| @Test |
| public void testPublishSuccessTerminated() throws Exception { |
| final int clientId = 2005; |
| final int reasonTerminate = WifiNanSessionCallback.TERMINATE_REASON_DONE; |
| final int publishId = 15; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| eq(configRequest), eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (2) publish success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) publish termination (from firmware - not app!) |
| mDut.onSessionTerminatedNotification(publishId, reasonTerminate, true); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate); |
| |
| // (4) app update session (race condition: app didn't get termination |
| // yet) |
| mDut.updatePublish(clientId, sessionId.getValue(), publishConfig); |
| mMockLooper.dispatchAll(); |
| |
| // (5) app terminates session |
| mDut.terminateSession(clientId, sessionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| // (6) app updates session (app already knows that terminated - will get |
| // a local FAIL). |
| mDut.updatePublish(clientId, sessionId.getValue(), publishConfig); |
| mMockLooper.dispatchAll(); |
| |
| validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue()); |
| |
| verifyNoMoreInteractions(mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate the publish flow: (1) initial publish + (2) success + (3) update |
| * + (4) update fails + (5) update + (6). Expected: session is still alive |
| * after update failure so second update succeeds (no callbacks). |
| */ |
| @Test |
| public void testPublishUpdateFail() throws Exception { |
| final int clientId = 2005; |
| final int publishId = 15; |
| final int reasonFail = WifiNanSessionCallback.REASON_INVALID_ARGS; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (2) publish success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) update publish |
| mDut.updatePublish(clientId, sessionId.getValue(), publishConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId), |
| eq(publishConfig)); |
| |
| // (4) update fails |
| mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail); |
| |
| // (5) another update publish |
| mDut.updatePublish(clientId, sessionId.getValue(), publishConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId), |
| eq(publishConfig)); |
| |
| // (6) update succeeds |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigSuccess(); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate race condition: publish pending but session terminated (due to |
| * disconnect - can't terminate such a session directly from app). Need to |
| * make sure that once publish succeeds (failure isn't a problem) the |
| * session is immediately terminated since no-one is listening for it. |
| */ |
| @Test |
| public void testDisconnectWhilePublishPending() throws Exception { |
| final int clientId = 2005; |
| final int publishId = 15; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (2) disconnect (but doesn't get executed until get response for |
| // publish command) |
| mDut.disconnect(clientId); |
| mMockLooper.dispatchAll(); |
| |
| // (3) publish success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(anyInt()); |
| inOrder.verify(mMockNative).stopPublish(transactionId.capture(), eq(publishId)); |
| inOrder.verify(mMockNative).disable((short) 0); |
| |
| validateInternalClientInfoCleanedUp(clientId); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validates subscribe flow: (1) initial subscribe (2) fail. Expected: get a |
| * failure callback. |
| */ |
| @Test |
| public void testSubscribeFail() throws Exception { |
| final int clientId = 1005; |
| final int reasonFail = WifiNanSessionCallback.REASON_NO_RESOURCES; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| |
| // (2) subscribe failure |
| mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail); |
| validateInternalNoSessions(clientId); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validates the subscribe flow: (1) initial subscribe (2) success (3) |
| * termination (e.g. DONE) (4) update session attempt (5) terminateSession |
| * (6) update session attempt. Expected: session ID callback + session |
| * cleaned-up |
| */ |
| @Test |
| public void testSubscribeSuccessTerminated() throws Exception { |
| final int clientId = 2005; |
| final int reasonTerminate = WifiNanSessionCallback.TERMINATE_REASON_DONE; |
| final int subscribeId = 15; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| |
| // (2) subscribe success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) subscribe termination (from firmware - not app!) |
| mDut.onSessionTerminatedNotification(subscribeId, reasonTerminate, false); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionTerminated(reasonTerminate); |
| |
| // (4) app update session (race condition: app didn't get termination |
| // yet) |
| mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig); |
| mMockLooper.dispatchAll(); |
| |
| // (5) app terminates session |
| mDut.terminateSession(clientId, sessionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| // (6) app updates session |
| mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig); |
| mMockLooper.dispatchAll(); |
| |
| validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue()); |
| |
| verifyNoMoreInteractions(mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate the subscribe flow: (1) initial subscribe + (2) success + (3) |
| * update + (4) update fails + (5) update + (6). Expected: session is still |
| * alive after update failure so second update succeeds (no callbacks). |
| */ |
| @Test |
| public void testSubscribeUpdateFail() throws Exception { |
| final int clientId = 2005; |
| final int subscribeId = 15; |
| final int reasonFail = WifiNanSessionCallback.REASON_INVALID_ARGS; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| |
| // (2) subscribe success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) update subscribe |
| mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId), |
| eq(subscribeConfig)); |
| |
| // (4) update fails |
| mDut.onSessionConfigFailResponse(transactionId.getValue(), false, reasonFail); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail); |
| |
| // (5) another update subscribe |
| mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId), |
| eq(subscribeConfig)); |
| |
| // (6) update succeeds |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionConfigSuccess(); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate race condition: subscribe pending but session terminated (due to |
| * disconnect - can't terminate such a session directly from app). Need to |
| * make sure that once subscribe succeeds (failure isn't a problem) the |
| * session is immediately terminated since no-one is listening for it. |
| */ |
| @Test |
| public void testDisconnectWhileSubscribePending() throws Exception { |
| final int clientId = 2005; |
| final int subscribeId = 15; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) initial subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| |
| // (2) disconnect (but doesn't get executed until get response for |
| // subscribe command) |
| mDut.disconnect(clientId); |
| mMockLooper.dispatchAll(); |
| |
| // (3) subscribe success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(anyInt()); |
| inOrder.verify(mMockNative).stopSubscribe((short) 0, subscribeId); |
| inOrder.verify(mMockNative).disable((short) 0); |
| |
| validateInternalClientInfoCleanedUp(clientId); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate (1) subscribe (success), (2) match (i.e. discovery), (3) message reception, (4) |
| * message queuing failed, (5) message transmission failed (after ok queuing), (6) message |
| * transmission success. |
| */ |
| @Test |
| public void testMatchAndMessages() throws Exception { |
| final int clientId = 1005; |
| final String serviceName = "some-service-name"; |
| final String ssi = "some much longer and more arbitrary data"; |
| final int subscribeCount = 7; |
| final int reasonFail = WifiNanSessionCallback.REASON_TX_FAIL; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final String peerMsg = "some message from peer"; |
| final int messageId = 6948; |
| final int messageId2 = 6949; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName) |
| .setServiceSpecificInfo(ssi) |
| .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE) |
| .setSubscribeCount(subscribeCount).build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (0) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| eq(configRequest), eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (1) subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (2) match |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) message Rx |
| mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes(), |
| peerMsg.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes(), |
| peerMsg.length()); |
| |
| // (4) message Tx queuing fail |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), ssi.length(), |
| messageId, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| mDut.onMessageSendQueuedFailResponse(transactionId.getValue(), reasonFail); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, reasonFail); |
| |
| // (5) message Tx successful queuing |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), ssi.length(), |
| messageId, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| short tid1 = transactionId.getValue(); |
| mDut.onMessageSendQueuedSuccessResponse(tid1); |
| mMockLooper.dispatchAll(); |
| |
| // (6) message Tx successful queuing |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), ssi.length(), |
| messageId2, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| short tid2 = transactionId.getValue(); |
| mDut.onMessageSendQueuedSuccessResponse(tid2); |
| mMockLooper.dispatchAll(); |
| |
| // (5) and (6) 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); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Summary: in a single publish session interact with multiple peers |
| * (different MAC addresses). |
| */ |
| @Test |
| public void testMultipleMessageSources() throws Exception { |
| final int clientId = 300; |
| final int clusterLow = 7; |
| final int clusterHigh = 7; |
| final int masterPref = 0; |
| final String serviceName = "some-service-name"; |
| final int publishId = 88; |
| final int peerId1 = 568; |
| final int peerId2 = 873; |
| final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false); |
| final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String msgFromPeer1 = "hey from 000102..."; |
| final String msgFromPeer2 = "hey from 0607..."; |
| final String msgToPeer1 = "hey there 000102..."; |
| final String msgToPeer2 = "hey there 0506..."; |
| final int msgToPeerId1 = 546; |
| final int msgToPeerId2 = 9654; |
| final int reason = WifiNanSessionCallback.REASON_OTHER; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build(); |
| |
| PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) |
| .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) message received from peers 1 & 2 |
| mDut.onMessageReceivedNotification(publishId, peerId1, peerMac1, msgFromPeer1.getBytes(), |
| msgFromPeer1.length()); |
| mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes(), |
| msgFromPeer2.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes(), |
| msgFromPeer1.length()); |
| inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes(), |
| msgFromPeer2.length()); |
| |
| // (4) sending messages back to same peers: one Tx fails, other succeeds |
| mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(), |
| msgToPeer2.length(), msgToPeerId2, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2), |
| eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length())); |
| short transactionIdVal = transactionId.getValue(); |
| mDut.onMessageSendQueuedSuccessResponse(transactionIdVal); |
| mDut.onMessageSendSuccessNotification(transactionIdVal); |
| |
| mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(), |
| msgToPeer1.length(), msgToPeerId1, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1), |
| eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length())); |
| transactionIdVal = transactionId.getValue(); |
| mDut.onMessageSendQueuedSuccessResponse(transactionIdVal); |
| mDut.onMessageSendFailNotification(transactionIdVal, reason); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason); |
| |
| 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 clusterLow = 7; |
| final int clusterHigh = 7; |
| final int masterPref = 0; |
| final String serviceName = "some-service-name"; |
| final int publishId = 88; |
| final int peerId = 568; |
| final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false); |
| final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String msgFromPeer1 = "hey from 000102..."; |
| final String msgFromPeer2 = "hey from 0607..."; |
| final String msgToPeer1 = "hey there 000102..."; |
| final String msgToPeer2 = "hey there 0506..."; |
| final int msgToPeerId1 = 546; |
| final int msgToPeerId2 = 9654; |
| ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build(); |
| |
| PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) |
| .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) message received & responded to |
| mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes(), |
| msgFromPeer1.length()); |
| mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(), |
| msgToPeer1.length(), msgToPeerId1, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes(), |
| msgFromPeer1.length()); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId), |
| eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeer1.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mDut.onMessageSendSuccessNotification(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1); |
| |
| // (4) message received with same peer ID but different MAC |
| mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater, msgFromPeer2.getBytes(), |
| msgFromPeer2.length()); |
| mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(), |
| msgToPeer2.length(), msgToPeerId2, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes(), |
| msgFromPeer2.length()); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId), |
| eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeer2.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mDut.onMessageSendSuccessNotification(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendSuccess(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 String ssi = "some much longer and more arbitrary data"; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final int messageId = 6948; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe & match |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) send message to invalid peer ID |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(), |
| ssi.length(), messageId, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, |
| WifiNanSessionCallback.REASON_NO_MATCH_SESSION); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate that on send message timeout correct callback is dispatched and that a later |
| * firmware notification is ignored. |
| */ |
| @Test |
| public void testSendMessageTimeout() throws Exception { |
| final int clientId = 1005; |
| final String ssi = "some much longer and more arbitrary data"; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final int messageId = 6948; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe & match |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) send message and enqueue successfully |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), |
| ssi.length(), messageId, 0); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| // (4) message send timeout |
| assertTrue(mAlarmManager.dispatch(WifiNanStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG)); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, |
| WifiNanSessionCallback.REASON_TX_FAIL); |
| |
| // (5) firmware response (unlikely - but good to check) |
| mDut.onMessageSendSuccessNotification(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| 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 String ssi = "some much longer and more arbitrary data"; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final int messageId = 6948; |
| final int retryCount = 3; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe & match |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) send message and enqueue successfully |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), |
| ssi.length(), messageId, retryCount); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| // (4) loop and fail until reach retryCount |
| for (int i = 0; i < retryCount; ++i) { |
| mDut.onMessageSendFailNotification(transactionId.getValue(), |
| WifiNanSessionCallback.REASON_TX_FAIL); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| } |
| |
| // (5) succeed on last retry |
| mDut.onMessageSendSuccessNotification(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| inOrder.verify(mockSessionCallback).onMessageSendSuccess(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 String ssi = "some much longer and more arbitrary data"; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final int messageId = 6948; |
| final int retryCount = 3; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe & match |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) send message and enqueue successfully |
| mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), |
| ssi.length(), messageId, retryCount); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| 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(), |
| WifiNanSessionCallback.REASON_TX_FAIL); |
| mMockLooper.dispatchAll(); |
| |
| if (i != retryCount) { |
| inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId), |
| eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length())); |
| mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| } |
| } |
| |
| inOrder.verify(mockSessionCallback).onMessageSendFail(messageId, |
| WifiNanSessionCallback.REASON_TX_FAIL); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative); |
| } |
| |
| /** |
| * Validate that start ranging function fills-in correct MAC addresses for peer IDs and |
| * passed along to RTT module. |
| */ |
| @Test |
| public void testStartRanging() throws Exception { |
| final int clientId = 1005; |
| final int subscribeId = 15; |
| final int requestorId = 22; |
| final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false); |
| final String peerSsi = "some peer ssi data"; |
| final String peerMatchFilter = "filter binary array represented as string"; |
| final int rangingId = 18423; |
| final RttManager.RttParams[] params = new RttManager.RttParams[2]; |
| params[0] = new RttManager.RttParams(); |
| params[0].bssid = Integer.toString(requestorId); |
| params[1] = new RttManager.RttParams(); |
| params[1].bssid = Integer.toString(requestorId + 5); |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| ArgumentCaptor<WifiNanClientState> clientCaptor = |
| ArgumentCaptor.forClass(WifiNanClientState.class); |
| ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor = |
| ArgumentCaptor.forClass(RttManager.RttParams[].class); |
| |
| InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative, |
| mMockNanRttStateManager); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe & match |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mDut.onMatchNotification(subscribeId, requestorId, peerMac, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(), |
| peerSsi.length(), peerMatchFilter.getBytes(), peerMatchFilter.length()); |
| |
| // (3) start ranging: pass along a valid peer ID and an invalid one |
| mDut.startRanging(clientId, sessionId.getValue(), params, rangingId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNanRttStateManager).startRanging(eq(rangingId), clientCaptor.capture(), |
| rttParamsCaptor.capture()); |
| collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B", |
| equalTo(rttParamsCaptor.getValue()[0].bssid)); |
| collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid)); |
| |
| verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative, |
| mMockNanRttStateManager); |
| } |
| |
| /** |
| * Test sequence of configuration: (1) config1, (2) config2 - incompatible, |
| * (3) config3 - compatible with config1 (requiring upgrade), (4) disconnect |
| * config3 (should get a downgrade), (5) disconnect config1 (should get a |
| * disable). |
| */ |
| @Test |
| public void testConfigs() throws Exception { |
| final int clientId1 = 9999; |
| final int clusterLow1 = 5; |
| final int clusterHigh1 = 100; |
| final int masterPref1 = 111; |
| final int clientId2 = 1001; |
| final boolean support5g2 = true; |
| final int clusterLow2 = 7; |
| final int clusterHigh2 = 155; |
| final int masterPref2 = 0; |
| final int clientId3 = 55; |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class); |
| |
| ConfigRequest configRequest1 = new ConfigRequest.Builder().setClusterLow(clusterLow1) |
| .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1) |
| .setEnableIdentityChangeCallback(false).build(); |
| |
| ConfigRequest configRequest2 = new ConfigRequest.Builder().setSupport5gBand(support5g2) |
| .setClusterLow(clusterLow2).setClusterHigh(clusterHigh2) |
| .setMasterPreference(masterPref2).build(); |
| |
| ConfigRequest configRequest3 = new ConfigRequest.Builder().setClusterLow(clusterLow1) |
| .setClusterHigh(clusterHigh1).setMasterPreference(masterPref1) |
| .setEnableIdentityChangeCallback(true).build(); |
| |
| IWifiNanEventCallback mockCallback1 = mock(IWifiNanEventCallback.class); |
| IWifiNanEventCallback mockCallback2 = mock(IWifiNanEventCallback.class); |
| IWifiNanEventCallback mockCallback3 = mock(IWifiNanEventCallback.class); |
| |
| InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) config1 (valid) |
| mDut.connect(clientId1, mockCallback1, configRequest1); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| crCapture.capture(), eq(true)); |
| collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback1).onConnectSuccess(); |
| |
| // (2) config2 (incompatible with config1) |
| mDut.connect(clientId2, mockCallback2, configRequest2); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback2) |
| .onConnectFail(WifiNanEventCallback.REASON_ALREADY_CONNECTED_INCOMPAT_CONFIG); |
| validateInternalClientInfoCleanedUp(clientId2); |
| |
| // (3) config3 (compatible with config1 but requires upgrade - i.e. no |
| // OTA changes) |
| mDut.connect(clientId3, mockCallback3, configRequest3); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| crCapture.capture(), eq(false)); |
| collector.checkThat("merge: stage 3: support 5g", crCapture.getValue().mSupport5gBand, |
| equalTo(false)); |
| collector.checkThat("merge: stage 3: master pref", crCapture.getValue().mMasterPreference, |
| equalTo(masterPref1)); |
| collector.checkThat("merge: stage 3: cluster low", crCapture.getValue().mClusterLow, |
| equalTo(clusterLow1)); |
| collector.checkThat("merge: stage 3: cluster high", crCapture.getValue().mClusterHigh, |
| equalTo(clusterHigh1)); |
| collector.checkThat("merge: stage 3: enable identity change callback", |
| crCapture.getValue().mEnableIdentityChangeCallback, equalTo(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback3).onConnectSuccess(); |
| |
| // (4) disconnect config3: want a downgrade |
| mDut.disconnect(clientId3); |
| mMockLooper.dispatchAll(); |
| validateInternalClientInfoCleanedUp(clientId3); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| crCapture.capture(), eq(false)); |
| collector.checkThat("merge: stage 4", crCapture.getValue(), equalTo(configRequest1)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| |
| // (5) disconnect config1: disable |
| mDut.disconnect(clientId1); |
| mMockLooper.dispatchAll(); |
| validateInternalClientInfoCleanedUp(clientId1); |
| inOrder.verify(mMockNative).disable((short) 0); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3); |
| } |
| |
| /** |
| * Summary: disconnect a client while there are pending transactions. |
| */ |
| @Test |
| public void testDisconnectWithPendingTransactions() throws Exception { |
| final int clientId = 125; |
| final int clusterLow = 5; |
| final int clusterHigh = 100; |
| final int masterPref = 111; |
| final String serviceName = "some-service-name"; |
| final String ssi = "some much longer and more arbitrary data"; |
| final int publishCount = 7; |
| final int reason = WifiNanSessionCallback.TERMINATE_REASON_DONE; |
| final int publishId = 22; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build(); |
| |
| PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) |
| .setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED) |
| .setPublishCount(publishCount).build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish (no response yet) |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (3) disconnect (but doesn't get executed until get a RESPONSE to the |
| // previous publish) |
| mDut.disconnect(clientId); |
| mMockLooper.dispatchAll(); |
| |
| // (4) get successful response to the publish |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(anyInt()); |
| inOrder.verify(mMockNative).stopPublish((short) 0, publishId); |
| inOrder.verify(mMockNative).disable((short) 0); |
| |
| validateInternalClientInfoCleanedUp(clientId); |
| |
| // (5) trying to publish on the same client: NOP |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| |
| // (6) got some callback on original publishId - should be ignored |
| mDut.onSessionTerminatedNotification(publishId, reason, true); |
| mMockLooper.dispatchAll(); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback); |
| } |
| |
| /** |
| * Validate that an unknown transaction (i.e. a callback from HAL with an |
| * unknown type) is simply ignored - but also cleans up its state. |
| */ |
| @Test |
| public void testUnknownTransactionType() throws Exception { |
| final int clientId = 129; |
| final int clusterLow = 15; |
| final int clusterHigh = 192; |
| final int masterPref = 234; |
| final String serviceName = "some-service-name"; |
| final String ssi = "some much longer and more arbitrary data"; |
| final int publishCount = 15; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow) |
| .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build(); |
| |
| PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) |
| .setServiceSpecificInfo(ssi).setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED) |
| .setPublishCount(publishCount).build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockPublishSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish - no response |
| mDut.publish(clientId, publishConfig, mockPublishSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockPublishSessionCallback); |
| } |
| |
| /** |
| * Validate that a NoOp transaction (i.e. a callback from HAL which doesn't |
| * require any action except clearing up state) actually cleans up its state |
| * (and does nothing else). |
| */ |
| @Test |
| public void testNoOpTransaction() throws Exception { |
| final int clientId = 1294; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect (no response) |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback); |
| } |
| |
| /** |
| * Validate that getting callbacks from HAL with unknown (expired) |
| * transaction ID or invalid publish/subscribe ID session doesn't have any |
| * impact. |
| */ |
| @Test |
| public void testInvalidCallbackIdParameters() throws Exception { |
| final int pubSubId = 1235; |
| final int clientId = 132; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect and succeed |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| short transactionIdConfig = transactionId.getValue(); |
| mDut.onConfigSuccessResponse(transactionIdConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (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], 0, new byte[0], 0); |
| mDut.onSessionTerminatedNotification(-1, -1, true); |
| mDut.onSessionTerminatedNotification(-1, -1, false); |
| mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[0], 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 publishId = 25; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest), |
| eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, publishId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) update-subscribe -> failure |
| mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback) |
| .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback); |
| } |
| |
| /** |
| * Validate that trying to (re)subscribe on a publish session or (re)publish |
| * on a subscribe session fails. |
| */ |
| @Test |
| public void testPublishOnSubscribeSessionType() throws Exception { |
| final int clientId = 188; |
| final int subscribeId = 25; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| eq(configRequest), eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| // (2) subscribe |
| mDut.subscribe(clientId, subscribeConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig)); |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), false, subscribeId); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| // (3) update-publish -> error |
| mDut.updatePublish(clientId, sessionId.getValue(), publishConfig); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback) |
| .onSessionConfigFail(WifiNanSessionCallback.REASON_OTHER); |
| |
| verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback); |
| } |
| |
| /** |
| * Validate that the session ID increments monotonically |
| */ |
| @Test |
| public void testSessionIdIncrement() throws Exception { |
| final int clientId = 188; |
| int loopCount = 100; |
| |
| ConfigRequest configRequest = new ConfigRequest.Builder().build(); |
| PublishConfig publishConfig = new PublishConfig.Builder().build(); |
| |
| ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class); |
| ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class); |
| IWifiNanEventCallback mockCallback = mock(IWifiNanEventCallback.class); |
| IWifiNanSessionCallback mockSessionCallback = mock(IWifiNanSessionCallback.class); |
| InOrder inOrder = inOrder(mMockNative, mockCallback, mockSessionCallback); |
| |
| mDut.enableUsage(); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).getCapabilities(anyShort()); |
| |
| // (1) connect |
| mDut.connect(clientId, mockCallback, configRequest); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), |
| eq(configRequest), eq(true)); |
| mDut.onConfigSuccessResponse(transactionId.getValue()); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockCallback).onConnectSuccess(); |
| |
| int prevId = 0; |
| for (int i = 0; i < loopCount; ++i) { |
| // (2) publish |
| mDut.publish(clientId, publishConfig, mockSessionCallback); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig)); |
| |
| // (3) publish-success |
| mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, i + 1); |
| mMockLooper.dispatchAll(); |
| inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture()); |
| |
| if (i != 0) { |
| assertTrue("Session ID incrementing", sessionId.getValue() > prevId); |
| } |
| prevId = sessionId.getValue(); |
| } |
| } |
| |
| /* |
| * Tests of internal state of WifiNanStateManager: very limited (not usually |
| * a good idea). However, these test that the internal state is cleaned-up |
| * appropriately. Alternatively would cause issues with memory leaks or |
| * information leak between sessions. |
| */ |
| |
| /** |
| * Utility routine used to validate that the internal state is cleaned-up |
| * after a client is disconnected. To be used in every test which terminates |
| * a client. |
| * |
| * @param clientId The ID of the client which should be deleted. |
| */ |
| private void validateInternalClientInfoCleanedUp(int clientId) throws Exception { |
| WifiNanClientState client = getInternalClientState(mDut, clientId); |
| collector.checkThat("Client record not cleared up for clientId=" + clientId, client, |
| nullValue()); |
| } |
| |
| /** |
| * Utility routine used to validate that the internal state is cleaned-up |
| * (deleted) after a session is terminated through API (not callback!). To |
| * be used in every test which terminates a session. |
| * |
| * @param clientId The ID of the client containing the session. |
| * @param sessionId The ID of the terminated session. |
| */ |
| private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId) |
| throws Exception { |
| WifiNanClientState client = getInternalClientState(mDut, clientId); |
| collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue()); |
| WifiNanSessionState session = getInternalSessionState(client, sessionId); |
| collector.checkThat("Client record not cleaned-up for sessionId=" + sessionId, session, |
| nullValue()); |
| } |
| |
| /** |
| * Utility routine used to validate that the internal state is cleaned-up |
| * (deleted) correctly. Checks that a specific client has no sessions |
| * attached to it. |
| * |
| * @param clientId The ID of the client which we want to check. |
| */ |
| private void validateInternalNoSessions(int clientId) throws Exception { |
| WifiNanClientState client = getInternalClientState(mDut, clientId); |
| collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue()); |
| |
| Field field = WifiNanClientState.class.getDeclaredField("mSessions"); |
| field.setAccessible(true); |
| @SuppressWarnings("unchecked") |
| SparseArray<WifiNanSessionState> sessions = (SparseArray<WifiNanSessionState>) field |
| .get(client); |
| |
| collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(), |
| equalTo(0)); |
| } |
| |
| /** |
| * Validates that the broadcast sent on NAN status change is correct. |
| * |
| * @param expectedEnabled The expected change status - i.e. are we expected |
| * to announce that NAN is enabled (true) or disabled (false). |
| */ |
| private void validateCorrectNanStatusChangeBroadcast(InOrder inOrder, boolean expectedEnabled) { |
| ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class); |
| |
| inOrder.verify(mMockContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL)); |
| |
| collector.checkThat("intent action", intent.getValue().getAction(), |
| equalTo(WifiNanManager.WIFI_NAN_STATE_CHANGED_ACTION)); |
| collector.checkThat("intent contains wifi status key", |
| intent.getValue().getExtras().containsKey(WifiNanManager.EXTRA_WIFI_STATE), |
| equalTo(true)); |
| collector.checkThat("intnent wifi status key value", |
| intent.getValue().getExtras().getInt(WifiNanManager.EXTRA_WIFI_STATE), |
| equalTo(expectedEnabled ? WifiNanManager.WIFI_NAN_STATE_ENABLED |
| : WifiNanManager.WIFI_NAN_STATE_DISABLED)); |
| } |
| |
| /* |
| * Utilities |
| */ |
| |
| private static WifiNanStateManager installNewNanStateManagerAndResetState( |
| WifiNanRttStateManager mockRtt) throws Exception { |
| Constructor<WifiNanStateManager> ctr = WifiNanStateManager.class.getDeclaredConstructor(); |
| ctr.setAccessible(true); |
| WifiNanStateManager nanStateManager = ctr.newInstance(); |
| |
| Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton"); |
| field.setAccessible(true); |
| field.set(null, nanStateManager); |
| |
| field = WifiNanStateManager.class.getDeclaredField("mRtt"); |
| field.setAccessible(true); |
| field.set(nanStateManager, mockRtt); |
| |
| return WifiNanStateManager.getInstance(); |
| } |
| |
| private static void installMockWifiNanNative(WifiNanNative obj) throws Exception { |
| Field field = WifiNanNative.class.getDeclaredField("sWifiNanNativeSingleton"); |
| field.setAccessible(true); |
| field.set(null, obj); |
| } |
| |
| private static WifiNanClientState getInternalClientState(WifiNanStateManager dut, int clientId) |
| throws Exception { |
| Field field = WifiNanStateManager.class.getDeclaredField("mClients"); |
| field.setAccessible(true); |
| @SuppressWarnings("unchecked") |
| SparseArray<WifiNanClientState> clients = (SparseArray<WifiNanClientState>) field.get(dut); |
| |
| return clients.get(clientId); |
| } |
| |
| private static WifiNanSessionState getInternalSessionState(WifiNanClientState client, |
| int sessionId) throws Exception { |
| Field field = WifiNanClientState.class.getDeclaredField("mSessions"); |
| field.setAccessible(true); |
| @SuppressWarnings("unchecked") |
| SparseArray<WifiNanSessionState> sessions = (SparseArray<WifiNanSessionState>) field |
| .get(client); |
| |
| return sessions.get(sessionId); |
| } |
| } |
| |