blob: 8c244459281c41080699b465b9f5da96ccdf1d0d [file] [log] [blame]
/*
* Copyright 2018 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;
import static android.net.wifi.WifiManager.EXTRA_SCAN_AVAILABLE;
import static android.net.wifi.WifiManager.WIFI_SCAN_AVAILABLE;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
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.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
/**
* Unit tests for {@link ScanOnlyModeManager}.
*/
@SmallTest
public class ScanOnlyModeManagerTest {
private static final String TAG = "ScanOnlyModeManagerTest";
private static final String TEST_INTERFACE_NAME = "testif0";
private static final String OTHER_INTERFACE_NAME = "notTestIf";
TestLooper mLooper;
ScanOnlyModeManager mScanOnlyModeManager;
@Mock Context mContext;
@Mock WifiMetrics mWifiMetrics;
@Mock WifiNative mWifiNative;
@Mock ScanOnlyModeManager.Listener mListener;
@Mock WifiMonitor mWifiMonitor;
@Mock ScanRequestProxy mScanRequestProxy;
@Mock WakeupController mWakeupController;
@Mock SarManager mSarManager;
final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor =
ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mLooper = new TestLooper();
mScanOnlyModeManager = createScanOnlyModeManager();
mLooper.dispatchAll();
}
private ScanOnlyModeManager createScanOnlyModeManager() {
return new ScanOnlyModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener,
mWifiMetrics, mScanRequestProxy, mWakeupController, mSarManager);
}
private void startScanOnlyModeAndVerifyEnabled() throws Exception {
when(mWifiNative.setupInterfaceForClientMode(anyBoolean(), any())).thenReturn(
TEST_INTERFACE_NAME);
mScanOnlyModeManager.start();
mLooper.dispatchAll();
verify(mWifiNative).setupInterfaceForClientMode(eq(true),
mInterfaceCallbackCaptor.capture());
// now mark the interface as up
mInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(),
eq(UserHandle.ALL));
List<Intent> intents = intentCaptor.getAllValues();
checkWifiScanStateChangedBroadcast(intents.get(0), WIFI_STATE_DISABLED);
checkWifiScanStateChangedBroadcast(intents.get(1), WIFI_STATE_ENABLED);
checkWifiStateChangeListenerUpdate(WIFI_STATE_ENABLED);
verify(mScanRequestProxy, atLeastOnce()).clearScanResults();
verify(mScanRequestProxy, atLeastOnce()).enableScanningForHiddenNetworks(false);
verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_ENABLED));
}
private void checkWifiScanStateChangedBroadcast(Intent intent, int expectedCurrentState) {
String action = intent.getAction();
assertEquals(WIFI_SCAN_AVAILABLE, action);
int currentState = intent.getIntExtra(EXTRA_SCAN_AVAILABLE, WIFI_STATE_UNKNOWN);
assertEquals(expectedCurrentState, currentState);
}
private void checkWifiStateChangeListenerUpdate(int expectedCurrentState) {
verify(mListener).onStateChanged(eq(expectedCurrentState));
}
/**
* ScanMode start sets up an interface in ClientMode for scanning.
*/
@Test
public void scanModeStartAndVerifyEnabled() throws Exception {
startScanOnlyModeAndVerifyEnabled();
}
/**
* ScanMode start does not indicate scanning is available when the interface name is empty.
*/
@Test
public void scanModeStartDoesNotSendScanningActiveWhenClientInterfaceNameIsEmpty()
throws Exception {
when(mWifiNative.setupInterfaceForClientMode(anyBoolean(), any())).thenReturn("");
mScanOnlyModeManager.start();
mLooper.dispatchAll();
verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL));
checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN);
}
/**
* Calling ScanOnlyModeManager.start twice does not crash or restart scan mode.
*/
@Test
public void scanOnlyModeStartCalledTwice() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mWifiNative, mContext);
mScanOnlyModeManager.start();
mLooper.dispatchAll();
verifyNoMoreInteractions(mWifiNative, mContext);
}
/**
* ScanMode stop properly cleans up state
*/
@Test
public void scanModeStopCleansUpState() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mContext, mListener);
mScanOnlyModeManager.stop();
mLooper.dispatchAll();
verify(mWifiNative).teardownInterface(TEST_INTERFACE_NAME);
verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL));
verify(mSarManager).setScanOnlyWifiState(eq(WIFI_STATE_DISABLED));
verifyNoMoreInteractions(mListener);
}
/**
* ScanMode properly stops when underlying interface is destroyed.
*/
@Test
public void scanModeStopsOnInterfaceDestroyed() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mContext);
mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL));
}
/**
* Calling stop when ScanMode is not started should not send scan state updates
*/
@Test
public void scanModeStopWhenNotStartedDoesNotUpdateScanStateUpdates() throws Exception {
startScanOnlyModeAndVerifyEnabled();
mScanOnlyModeManager.stop();
mLooper.dispatchAll();
reset(mContext, mListener);
// now call stop again
mScanOnlyModeManager.stop();
mLooper.dispatchAll();
verify(mContext, never()).sendStickyBroadcastAsUser(any(), any());
verify(mListener, never()).onStateChanged(anyInt());
}
/**
* Triggering interface down when ScanOnlyMode is active properly exits the active state and
* reports an error.
*/
@Test
public void scanModeStartedStopsWhenInterfaceDown() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mContext);
mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
verify(mContext, never()).sendStickyBroadcastAsUser(any(), eq(UserHandle.ALL));
checkWifiStateChangeListenerUpdate(WIFI_STATE_UNKNOWN);
verify(mScanRequestProxy).clearScanResults();
}
/**
* Triggering an interface down for a different interface will not exit scan mode.
*/
@Test
public void scanModeStartedDoesNotStopOnDownForDifferentIface() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mContext, mListener, mScanRequestProxy);
mInterfaceCallbackCaptor.getValue().onDown(OTHER_INTERFACE_NAME);
mLooper.dispatchAll();
verifyNoMoreInteractions(mContext, mListener);
verify(mScanRequestProxy, never()).clearScanResults();
}
/**
* Verify that onDestroyed after scan mode is stopped doesn't trigger a callback.
*/
@Test
public void noCallbackOnInterfaceDestroyedWhenAlreadyStopped() throws Exception {
startScanOnlyModeAndVerifyEnabled();
reset(mListener);
mScanOnlyModeManager.stop();
mLooper.dispatchAll();
// now trigger interface destroyed and make sure callback doesn't get called
mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
verifyNoMoreInteractions(mListener);
}
/**
* Entering StartedState starts the WakeupController.
*/
@Test
public void scanModeEnterStartsWakeupController() throws Exception {
startScanOnlyModeAndVerifyEnabled();
verify(mWakeupController).start();
}
/**
* Exiting StartedState stops the WakeupController.
*/
@Test
public void scanModeExitStopsWakeupController() throws Exception {
startScanOnlyModeAndVerifyEnabled();
mScanOnlyModeManager.stop();
mLooper.dispatchAll();
InOrder inOrder = inOrder(mWakeupController, mWifiNative, mListener);
inOrder.verify(mWakeupController).start();
inOrder.verify(mWakeupController).stop();
inOrder.verify(mWifiNative).teardownInterface(eq(TEST_INTERFACE_NAME));
}
}