| /* |
| * 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); |
| } |
| } |