blob: 5a2116242699d941e174e36ea32febc23023f2a2 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.AsyncChannel;
import com.android.server.wifi.util.WifiAsyncChannel;
import com.android.server.wifi.util.WifiPermissionsUtil;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* Unit tests for {@link WifiServiceImpl}.
*
* Note: this is intended to build up over time and will not immediately cover the entire file.
*/
@SmallTest
public class WifiServiceImplTest {
private static final String TAG = "WifiServiceImplTest";
private static final int DEFAULT_VERBOSE_LOGGING = 0;
private static final String TEST_PACKAGE_NAME = "TestPackage";
@Mock Context mContext;
@Mock WifiInjector mWifiInjector;
WifiServiceImpl mWifiServiceImpl;
@Mock WifiController mWifiController;
@Mock WifiTrafficPoller mWifiTrafficPoller;
@Mock WifiStateMachine mWifiStateMachine;
@Mock HandlerThread mHandlerThread;
TestLooper mLooper;
@Mock AsyncChannel mAsyncChannel;
@Mock Resources mResources;
@Mock FrameworkFacade mFrameworkFacade;
@Mock WifiLockManager mLockManager;
@Mock WifiMulticastLockManager mWifiMulticastLockManager;
@Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
@Mock WifiBackupRestore mWifiBackupRestore;
@Mock WifiMetrics mWifiMetrics;
@Spy FakeWifiLog mLog;
@Mock WifiPermissionsUtil mWifiPermissionsUtil;
@Mock WifiSettingsStore mSettingsStore;
@Mock ContentResolver mContentResolver;
PowerManager mPowerManager;
private class WifiAsyncChannelTester {
private static final String TAG = "WifiAsyncChannelTester";
public static final int CHANNEL_STATE_FAILURE = -1;
public static final int CHANNEL_STATE_DISCONNECTED = 0;
public static final int CHANNEL_STATE_HALF_CONNECTED = 1;
public static final int CHANNEL_STATE_FULLY_CONNECTED = 2;
private int mState = CHANNEL_STATE_DISCONNECTED;
private WifiAsyncChannel mChannel;
private WifiLog mAsyncTestLog;
WifiAsyncChannelTester(WifiInjector wifiInjector) {
mAsyncTestLog = wifiInjector.makeLog(TAG);
}
public int getChannelState() {
return mState;
}
public void connect(final Looper looper, final Messenger messenger,
final Handler incomingMessageHandler) {
assertEquals("AsyncChannel must be in disconnected state",
CHANNEL_STATE_DISCONNECTED, mState);
mChannel = new WifiAsyncChannel(TAG);
mChannel.setWifiLog(mLog);
Handler handler = new Handler(mLooper.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
mState = CHANNEL_STATE_HALF_CONNECTED;
} else {
mState = CHANNEL_STATE_FAILURE;
}
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
mState = CHANNEL_STATE_FULLY_CONNECTED;
break;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
mState = CHANNEL_STATE_DISCONNECTED;
break;
default:
incomingMessageHandler.handleMessage(msg);
break;
}
}
};
mChannel.connect(null, handler, messenger);
}
}
@Before public void setUp() {
MockitoAnnotations.initMocks(this);
mLooper = new TestLooper();
when(mWifiInjector.getWifiController()).thenReturn(mWifiController);
when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
when(mWifiInjector.getWifiStateMachine()).thenReturn(mWifiStateMachine);
when(mWifiStateMachine.syncInitialize(any())).thenReturn(true);
when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mHandlerThread);
when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(),
anyBoolean(), any());
IPowerManager powerManagerService = mock(IPowerManager.class);
mPowerManager = new PowerManager(mContext, powerManagerService, new Handler());
when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
WifiAsyncChannel wifiAsyncChannel = new WifiAsyncChannel("WifiServiceImplTest");
wifiAsyncChannel.setWifiLog(mLog);
when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(wifiAsyncChannel);
when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade);
when(mWifiInjector.getWifiLockManager()).thenReturn(mLockManager);
when(mWifiInjector.getWifiMulticastLockManager()).thenReturn(mWifiMulticastLockManager);
when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
when(mWifiInjector.getWifiBackupRestore()).thenReturn(mWifiBackupRestore);
when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
WifiTrafficPoller wifiTrafficPoller = new WifiTrafficPoller(mContext,
mLooper.getLooper(), "mockWlan");
when(mWifiInjector.getWifiTrafficPoller()).thenReturn(wifiTrafficPoller);
when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
when(mWifiInjector.getWifiSettingsStore()).thenReturn(mSettingsStore);
mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
}
@Test
public void testRemoveNetworkUnknown() {
assertFalse(mWifiServiceImpl.removeNetwork(-1));
}
@Test
public void testAsyncChannelHalfConnected() {
WifiAsyncChannelTester channelTester = new WifiAsyncChannelTester(mWifiInjector);
Handler handler = mock(Handler.class);
TestLooper looper = new TestLooper();
channelTester.connect(looper.getLooper(), mWifiServiceImpl.getWifiServiceMessenger(),
handler);
mLooper.dispatchAll();
assertEquals("AsyncChannel must be half connected",
WifiAsyncChannelTester.CHANNEL_STATE_HALF_CONNECTED,
channelTester.getChannelState());
}
/**
* Ensure WifiMetrics.dump() is the only dump called when 'dumpsys wifi WifiMetricsProto' is
* called. This is required to support simple metrics collection via dumpsys
*/
@Test
public void testWifiMetricsDump() {
mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()),
new String[]{mWifiMetrics.PROTO_DUMP_ARG});
verify(mWifiMetrics)
.dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
verify(mWifiStateMachine, never())
.dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
}
/**
* Verify that wifi can be enabled by a caller with WIFI_STATE_CHANGE permission when wifi is
* off (no hotspot, no airplane mode).
*/
@Test
public void testSetWifiEnabledSuccess() throws Exception {
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
}
/**
* Verify that the CMD_TOGGLE_WIFI message won't be sent if wifi is already on.
*/
@Test
public void testSetWifiEnabledNoToggle() throws Exception {
when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(false);
assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
}
/**
* Verify a SecurityException is thrown if a caller does not have the correct permission to
* toggle wifi.
*/
@Test(expected = SecurityException.class)
public void testSetWifiEnableWithoutPermission() throws Exception {
doThrow(new SecurityException()).when(mContext)
.enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
eq("WifiService"));
mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
}
/**
* Verify that wifi can be disabled by a caller with WIFI_STATE_CHANGE permission when wifi is
* on.
*/
@Test
public void testSetWifiDisabledSuccess() throws Exception {
when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
}
/**
* Verify that CMD_TOGGLE_WIFI message won't be sent if wifi is already off.
*/
@Test
public void testSetWifiDisabledNoToggle() throws Exception {
when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false);
assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
}
/**
* Verify a SecurityException is thrown if a caller does not have the correct permission to
* toggle wifi.
*/
@Test(expected = SecurityException.class)
public void testSetWifiDisabledWithoutPermission() throws Exception {
doThrow(new SecurityException()).when(mContext)
.enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
eq("WifiService"));
mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false);
}
/**
* Ensure unpermitted callers cannot write the SoftApConfiguration.
*
* @throws SecurityException
*/
@Test(expected = SecurityException.class)
public void testSetWifiApConfigurationNotSavedWithoutPermission() {
when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
WifiConfiguration apConfig = new WifiConfiguration();
mWifiServiceImpl.setWifiApConfiguration(apConfig);
verify(mWifiStateMachine, never()).setWifiApConfiguration(eq(apConfig));
}
/**
* Ensure softap config is written when the caller has the correct permission.
*/
@Test
public void testSetWifiApConfigurationSuccess() {
when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
WifiConfiguration apConfig = new WifiConfiguration();
mWifiServiceImpl.setWifiApConfiguration(apConfig);
verify(mWifiStateMachine).setWifiApConfiguration(eq(apConfig));
}
/**
* Ensure that a null config does not overwrite the saved ap config.
*/
@Test
public void testSetWifiApConfigurationNullConfigNotSaved() {
when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
mWifiServiceImpl.setWifiApConfiguration(null);
verify(mWifiStateMachine, never()).setWifiApConfiguration(isNull(WifiConfiguration.class));
}
/**
* Ensure unpermitted callers are not able to retrieve the softap config.
*
* @throws SecurityException
*/
@Test(expected = SecurityException.class)
public void testGetWifiApConfigurationNotReturnedWithoutPermission() {
when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
mWifiServiceImpl.getWifiApConfiguration();
verify(mWifiStateMachine, never()).syncGetWifiApConfiguration();
}
/**
* Ensure permitted callers are able to retrieve the softap config.
*/
@Test
public void testGetWifiApConfigurationSuccess() {
when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
WifiConfiguration apConfig = new WifiConfiguration();
when(mWifiStateMachine.syncGetWifiApConfiguration()).thenReturn(apConfig);
assertEquals(apConfig, mWifiServiceImpl.getWifiApConfiguration());
}
/**
* Make sure we do not start wifi if System services have to be restarted to decrypt the device.
*/
@Test
public void testWifiControllerDoesNotStartWhenDeviceTriggerResetMainAtBoot() {
when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
mWifiServiceImpl.checkAndStartWifi();
verify(mWifiController, never()).start();
}
/**
* Make sure we do start WifiController (wifi disabled) if the device is already decrypted.
*/
@Test
public void testWifiControllerStartsWhenDeviceIsDecryptedAtBootWithWifiDisabled() {
when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
mWifiServiceImpl.checkAndStartWifi();
verify(mWifiController).start();
verify(mWifiController, never()).sendMessage(CMD_WIFI_TOGGLED);
}
/**
* Make sure we do start WifiController (wifi enabled) if the device is already decrypted.
*/
@Test
public void testWifiFullyStartsWhenDeviceIsDecryptedAtBootWithWifiEnabled() {
when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
when(mSettingsStore.handleWifiToggled(true)).thenReturn(true);
when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
when(mWifiStateMachine.syncGetWifiState()).thenReturn(WIFI_STATE_DISABLED);
mWifiServiceImpl.checkAndStartWifi();
verify(mWifiController).start();
verify(mWifiController).sendMessage(CMD_WIFI_TOGGLED);
}
}