| /* |
| * 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.HOTSPOT_FAILED; |
| import static android.net.wifi.WifiManager.HOTSPOT_STARTED; |
| import static android.net.wifi.WifiManager.HOTSPOT_STOPPED; |
| import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR; |
| import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; |
| import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; |
| import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC; |
| import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; |
| import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL; |
| import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED; |
| import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; |
| import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL; |
| import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; |
| import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; |
| import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; |
| import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; |
| import static android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; |
| import static android.provider.Settings.Secure.LOCATION_MODE_OFF; |
| |
| import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; |
| import static com.android.server.wifi.WifiController.CMD_SET_AP; |
| 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.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| 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.app.ActivityManager; |
| import android.app.AppOpsManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.net.IpConfiguration; |
| import android.net.wifi.ScanSettings; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiEnterpriseConfig; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; |
| import android.net.wifi.hotspot2.PasspointConfiguration; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.IBinder; |
| import android.os.IPowerManager; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.UserManager; |
| import android.os.WorkSource; |
| import android.os.test.TestLooper; |
| import android.provider.Settings; |
| import android.test.suitebuilder.annotation.SmallTest; |
| |
| import com.android.internal.util.AsyncChannel; |
| import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback; |
| 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.ArgumentCaptor; |
| import org.mockito.ArgumentMatcher; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.Spy; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.List; |
| |
| /** |
| * 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 String SCAN_PACKAGE_NAME = "scanPackage"; |
| private static final String WHITE_LIST_SCAN_PACKAGE_NAME = "whiteListScanPackage"; |
| private static final int DEFAULT_VERBOSE_LOGGING = 0; |
| private static final long WIFI_BACKGROUND_SCAN_INTERVAL = 10000; |
| private static final String ANDROID_SYSTEM_PACKAGE = "android"; |
| private static final String TEST_PACKAGE_NAME = "TestPackage"; |
| private static final String SYSUI_PACKAGE_NAME = "com.android.systemui"; |
| private static final int TEST_PID = 6789; |
| private static final int TEST_PID2 = 9876; |
| private static final String WIFI_IFACE_NAME = "wlan0"; |
| |
| private WifiServiceImpl mWifiServiceImpl; |
| private TestLooper mLooper; |
| private PowerManager mPowerManager; |
| private Handler mHandler; |
| private Messenger mAppMessenger; |
| private int mPid; |
| private int mPid2 = Process.myPid(); |
| |
| final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = |
| ArgumentCaptor.forClass(BroadcastReceiver.class); |
| final ArgumentCaptor<IntentFilter> mIntentFilterCaptor = |
| ArgumentCaptor.forClass(IntentFilter.class); |
| |
| final ArgumentCaptor<Message> mMessageCaptor = ArgumentCaptor.forClass(Message.class); |
| final ArgumentCaptor<SoftApModeConfiguration> mSoftApModeConfigCaptor = |
| ArgumentCaptor.forClass(SoftApModeConfiguration.class); |
| |
| @Mock Context mContext; |
| @Mock WifiInjector mWifiInjector; |
| @Mock Clock mClock; |
| @Mock WifiController mWifiController; |
| @Mock WifiTrafficPoller mWifiTrafficPoller; |
| @Mock WifiStateMachine mWifiStateMachine; |
| @Mock HandlerThread mHandlerThread; |
| @Mock AsyncChannel mAsyncChannel; |
| @Mock Resources mResources; |
| @Mock FrameworkFacade mFrameworkFacade; |
| @Mock WifiLockManager mLockManager; |
| @Mock WifiMulticastLockManager mWifiMulticastLockManager; |
| @Mock WifiLastResortWatchdog mWifiLastResortWatchdog; |
| @Mock WifiBackupRestore mWifiBackupRestore; |
| @Mock WifiMetrics mWifiMetrics; |
| @Mock WifiPermissionsUtil mWifiPermissionsUtil; |
| @Mock WifiSettingsStore mSettingsStore; |
| @Mock ContentResolver mContentResolver; |
| @Mock PackageManager mPackageManager; |
| @Mock UserManager mUserManager; |
| @Mock WifiConfiguration mApConfig; |
| @Mock ActivityManager mActivityManager; |
| @Mock AppOpsManager mAppOpsManager; |
| @Mock IBinder mAppBinder; |
| @Mock LocalOnlyHotspotRequestInfo mRequestInfo; |
| @Mock LocalOnlyHotspotRequestInfo mRequestInfo2; |
| |
| @Spy FakeWifiLog mLog; |
| |
| 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); |
| } |
| |
| private Message sendMessageSynchronously(Message request) { |
| return mChannel.sendMessageSynchronously(request); |
| } |
| |
| private void sendMessage(Message request) { |
| mChannel.sendMessage(request); |
| } |
| } |
| |
| @Before public void setUp() { |
| MockitoAnnotations.initMocks(this); |
| mLooper = new TestLooper(); |
| mHandler = spy(new Handler(mLooper.getLooper())); |
| mAppMessenger = new Messenger(mHandler); |
| |
| when(mRequestInfo.getPid()).thenReturn(mPid); |
| when(mRequestInfo2.getPid()).thenReturn(mPid2); |
| when(mWifiInjector.getUserManager()).thenReturn(mUserManager); |
| 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); |
| when(mContext.getPackageManager()).thenReturn(mPackageManager); |
| doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(), |
| anyBoolean(), any()); |
| when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager); |
| when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); |
| when(mFrameworkFacade.getLongSetting( |
| eq(mContext), |
| eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS), |
| anyLong())) |
| .thenReturn(WIFI_BACKGROUND_SCAN_INTERVAL); |
| when(mFrameworkFacade.getStringSetting( |
| eq(mContext), |
| eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST))) |
| .thenReturn(WHITE_LIST_SCAN_PACKAGE_NAME); |
| 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); |
| when(mWifiInjector.getClock()).thenReturn(mClock); |
| mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel); |
| mWifiServiceImpl.setWifiHandlerLogForTest(mLog); |
| } |
| |
| private WifiAsyncChannelTester verifyAsyncChannelHalfConnected() { |
| 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()); |
| return channelTester; |
| } |
| |
| /** |
| * Verifies that any operations on WifiServiceImpl without setting up the WifiStateMachine |
| * channel would fail. |
| */ |
| @Test |
| public void testRemoveNetworkUnknown() { |
| assertFalse(mWifiServiceImpl.removeNetwork(-1)); |
| verify(mWifiStateMachine, never()).syncRemoveNetwork(any(), anyInt()); |
| } |
| |
| /** |
| * Tests whether we're able to set up an async channel connection with WifiServiceImpl. |
| * This is the path used by some WifiManager public API calls. |
| */ |
| @Test |
| public void testAsyncChannelHalfConnected() { |
| verifyAsyncChannelHalfConnected(); |
| } |
| |
| /** |
| * Tests the isValid() check for StaticIpConfigurations, ensuring that configurations with null |
| * ipAddress are rejected, and configurations with ipAddresses are valid. |
| */ |
| @Test |
| public void testStaticIpConfigurationValidityCheck() { |
| WifiConfiguration conf = WifiConfigurationTestUtil.createOpenNetwork(); |
| IpConfiguration ipConf = |
| WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy(); |
| conf.setIpConfiguration(ipConf); |
| // Ensure staticIpConfiguration with IP Address is valid |
| assertTrue(mWifiServiceImpl.isValid(conf)); |
| ipConf.staticIpConfiguration.ipAddress = null; |
| // Ensure staticIpConfiguration with null IP Address it is not valid |
| conf.setIpConfiguration(ipConf); |
| assertFalse(mWifiServiceImpl.isValid(conf)); |
| } |
| |
| /** |
| * 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)); |
| } |
| |
| |
| /** |
| * Ensure WifiServiceImpl.dump() doesn't throw an NPE when executed with null args |
| */ |
| @Test |
| public void testDumpNullArgs() { |
| mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null); |
| } |
| |
| /** |
| * 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(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); |
| 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(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| 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")); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); |
| mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true); |
| verify(mWifiStateMachine, never()).syncGetWifiApState(); |
| } |
| |
| /** |
| * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we |
| * are in airplane mode. |
| */ |
| @Test |
| public void testSetWifiEnabledFromNetworkSettingsHolderWhenInAirplaneMode() throws Exception { |
| when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); |
| when(mContext.checkPermission( |
| eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) |
| .thenReturn(PackageManager.PERMISSION_GRANTED); |
| assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); |
| verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); |
| } |
| |
| /** |
| * Verify that a caller without the NETWORK_SETTINGS permission can't enable wifi |
| * if we are in airplane mode. |
| */ |
| @Test |
| public void testSetWifiEnabledFromAppFailsWhenInAirplaneMode() throws Exception { |
| when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(true); |
| when(mContext.checkPermission( |
| eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) |
| .thenReturn(PackageManager.PERMISSION_DENIED); |
| assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); |
| verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); |
| } |
| |
| /** |
| * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we |
| * are in softap mode. |
| */ |
| @Test |
| public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception { |
| when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED); |
| when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true); |
| when(mContext.checkPermission( |
| eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) |
| .thenReturn(PackageManager.PERMISSION_GRANTED); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); |
| assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true)); |
| verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED)); |
| } |
| |
| /** |
| * Verify that a call from an app cannot enable wifi if we are in softap mode. |
| */ |
| @Test |
| public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception { |
| when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED); |
| when(mContext.checkPermission( |
| eq(android.Manifest.permission.NETWORK_SETTINGS), anyInt(), anyInt())) |
| .thenReturn(PackageManager.PERMISSION_DENIED); |
| when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); |
| assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true)); |
| verify(mSettingsStore, never()).handleWifiToggled(anyBoolean()); |
| verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED)); |
| } |
| |
| /** |
| * 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(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| 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(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| 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 { |
| when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| 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); |
| when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED); |
| when(mContext.getPackageName()).thenReturn(ANDROID_SYSTEM_PACKAGE); |
| mWifiServiceImpl.checkAndStartWifi(); |
| verify(mWifiController).start(); |
| verify(mWifiController).sendMessage(CMD_WIFI_TOGGLED); |
| } |
| |
| /** |
| * Verify setWifiApEnabled works with the correct permissions and a null config. |
| */ |
| @Test |
| public void testSetWifiApEnabledWithProperPermissionsWithNullConfig() { |
| when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) |
| .thenReturn(false); |
| mWifiServiceImpl.setWifiApEnabled(null, true); |
| verify(mWifiController) |
| .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); |
| assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); |
| } |
| |
| /** |
| * Verify setWifiApEnabled works with correct permissions and a valid config. |
| * |
| * TODO: should really validate that ap configs have a set of basic config settings b/37280779 |
| */ |
| @Test |
| public void testSetWifiApEnabledWithProperPermissionsWithValidConfig() { |
| when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) |
| .thenReturn(false); |
| WifiConfiguration apConfig = new WifiConfiguration(); |
| mWifiServiceImpl.setWifiApEnabled(apConfig, true); |
| verify(mWifiController).sendMessage( |
| eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); |
| assertEquals(apConfig, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); |
| } |
| |
| /** |
| * Verify setWifiApEnabled when disabling softap with correct permissions sends the correct |
| * message to WifiController. |
| */ |
| @Test |
| public void testSetWifiApEnabledFalseWithProperPermissionsWithNullConfig() { |
| when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) |
| .thenReturn(false); |
| mWifiServiceImpl.setWifiApEnabled(null, false); |
| verify(mWifiController) |
| .sendMessage(eq(CMD_SET_AP), eq(0), eq(0), mSoftApModeConfigCaptor.capture()); |
| assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); |
| } |
| |
| /** |
| * setWifiApEnabled should fail if the provided config is not valid. |
| */ |
| @Test |
| public void testSetWifiApEnabledWithProperPermissionInvalidConfigFails() { |
| when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) |
| .thenReturn(false); |
| // mApConfig is a mock and the values are not set - triggering the invalid config. Testing |
| // will be improved when we actually do test softap configs in b/37280779 |
| mWifiServiceImpl.setWifiApEnabled(mApConfig, true); |
| verify(mWifiController, never()) |
| .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class)); |
| } |
| |
| /** |
| * setWifiApEnabled should throw a security exception when the caller does not have the correct |
| * permissions. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testSetWifiApEnabledThrowsSecurityExceptionWithoutConfigOverridePermission() |
| throws Exception { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE), |
| eq("WifiService")); |
| mWifiServiceImpl.setWifiApEnabled(null, true); |
| } |
| |
| /** |
| * setWifiApEnabled should throw a SecurityException when disallow tethering is set for the |
| * user. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testSetWifiApEnabledThrowsSecurityExceptionWithDisallowTethering() |
| throws Exception { |
| when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING))) |
| .thenReturn(true); |
| mWifiServiceImpl.setWifiApEnabled(null, true); |
| |
| } |
| |
| /** |
| * Verify caller with proper permission can call startSoftAp. |
| */ |
| @Test |
| public void testStartSoftApWithPermissionsAndNullConfig() { |
| boolean result = mWifiServiceImpl.startSoftAp(null); |
| assertTrue(result); |
| verify(mWifiController) |
| .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); |
| assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); |
| } |
| |
| /** |
| * Verify caller with proper permissions but an invalid config does not start softap. |
| */ |
| @Test |
| public void testStartSoftApWithPermissionsAndInvalidConfig() { |
| boolean result = mWifiServiceImpl.startSoftAp(mApConfig); |
| assertFalse(result); |
| verifyZeroInteractions(mWifiController); |
| } |
| |
| /** |
| * Verify caller with proper permission and valid config does start softap. |
| */ |
| @Test |
| public void testStartSoftApWithPermissionsAndValidConfig() { |
| WifiConfiguration config = new WifiConfiguration(); |
| boolean result = mWifiServiceImpl.startSoftAp(config); |
| assertTrue(result); |
| verify(mWifiController) |
| .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture()); |
| assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration()); |
| } |
| |
| /** |
| * Verify a SecurityException is thrown when a caller without the correct permission attempts to |
| * start softap. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStartSoftApWithoutPermissionThrowsException() throws Exception { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK), |
| eq("WifiService")); |
| mWifiServiceImpl.startSoftAp(null); |
| } |
| |
| /** |
| * Verify caller with proper permission can call stopSoftAp. |
| */ |
| @Test |
| public void testStopSoftApWithPermissions() { |
| boolean result = mWifiServiceImpl.stopSoftAp(); |
| assertTrue(result); |
| verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); |
| } |
| |
| /** |
| * Verify SecurityException is thrown when a caller without the correct permission attempts to |
| * stop softap. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStopSoftApWithoutPermissionThrowsException() throws Exception { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK), |
| eq("WifiService")); |
| mWifiServiceImpl.stopSoftAp(); |
| } |
| |
| /** |
| * Ensure foreground apps can always do wifi scans. |
| */ |
| @Test |
| public void testWifiScanStartedForeground() { |
| when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( |
| ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); |
| mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine).startScan( |
| anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class)); |
| } |
| |
| /** |
| * Ensure background apps get throttled when the previous scan is too close. |
| */ |
| @Test |
| public void testWifiScanBackgroundThrottled() { |
| when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( |
| ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); |
| long startMs = 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); |
| mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine).startScan( |
| anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class)); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn( |
| startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000); |
| mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine, times(1)).startScan( |
| anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class)); |
| } |
| |
| /** |
| * Ensure background apps can do wifi scan when the throttle interval reached. |
| */ |
| @Test |
| public void testWifiScanBackgroundNotThrottled() { |
| when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn( |
| ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); |
| long startMs = 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); |
| mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine).startScan( |
| anyInt(), eq(0), (ScanSettings) eq(null), any(WorkSource.class)); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn( |
| startMs + WIFI_BACKGROUND_SCAN_INTERVAL + 1000); |
| mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine).startScan( |
| anyInt(), eq(1), (ScanSettings) eq(null), any(WorkSource.class)); |
| } |
| |
| /** |
| * Ensure background apps can do wifi scan when the throttle interval reached. |
| */ |
| @Test |
| public void testWifiScanBackgroundWhiteListed() { |
| when(mActivityManager.getPackageImportance(WHITE_LIST_SCAN_PACKAGE_NAME)).thenReturn( |
| ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); |
| long startMs = 1000; |
| when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs); |
| mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine).startScan( |
| anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class)); |
| |
| when(mClock.getElapsedSinceBootMillis()).thenReturn( |
| startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000); |
| mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME); |
| verify(mWifiStateMachine, times(2)).startScan( |
| anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class)); |
| } |
| |
| private void registerLOHSRequestFull() { |
| // allow test to proceed without a permission check failure |
| when(mSettingsStore.getLocationModeSetting(mContext)) |
| .thenReturn(LOCATION_MODE_HIGH_ACCURACY); |
| try { |
| when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); |
| } catch (RemoteException e) { } |
| when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) |
| .thenReturn(false); |
| int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, |
| TEST_PACKAGE_NAME); |
| assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); |
| } |
| |
| /** |
| * Verify that the call to startLocalOnlyHotspot returns REQUEST_REGISTERED when successfully |
| * called. |
| */ |
| @Test |
| public void testStartLocalOnlyHotspotSingleRegistrationReturnsRequestRegistered() { |
| registerLOHSRequestFull(); |
| } |
| |
| /** |
| * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not |
| * have the CHANGE_WIFI_STATE permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE), |
| eq("WifiService")); |
| mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| } |
| |
| /** |
| * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not |
| * have Location permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationPermission() { |
| when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); |
| doThrow(new SecurityException()) |
| .when(mWifiPermissionsUtil).enforceLocationPermission(eq(TEST_PACKAGE_NAME), |
| anyInt()); |
| mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| } |
| |
| /** |
| * Verify that a call to startLocalOnlyHotspot throws a SecurityException if Location mode is |
| * disabled. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationEnabled() { |
| when(mSettingsStore.getLocationModeSetting(mContext)).thenReturn(LOCATION_MODE_OFF); |
| mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| } |
| |
| /** |
| * Only start LocalOnlyHotspot if the caller is the foreground app at the time of the request. |
| */ |
| @Test |
| public void testStartLocalOnlyHotspotFailsIfRequestorNotForegroundApp() throws Exception { |
| when(mSettingsStore.getLocationModeSetting(mContext)) |
| .thenReturn(LOCATION_MODE_HIGH_ACCURACY); |
| |
| when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false); |
| int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, |
| TEST_PACKAGE_NAME); |
| assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); |
| } |
| |
| /** |
| * Do not register the LocalOnlyHotspot request if the caller app cannot be verified as the |
| * foreground app at the time of the request (ie, throws an exception in the check). |
| */ |
| @Test |
| public void testStartLocalOnlyHotspotFailsIfForegroundAppCheckThrowsRemoteException() |
| throws Exception { |
| when(mSettingsStore.getLocationModeSetting(mContext)) |
| .thenReturn(LOCATION_MODE_HIGH_ACCURACY); |
| |
| when(mFrameworkFacade.isAppForeground(anyInt())).thenThrow(new RemoteException()); |
| int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, |
| TEST_PACKAGE_NAME); |
| assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); |
| } |
| |
| /** |
| * Only start LocalOnlyHotspot if we are not tethering. |
| */ |
| @Test |
| public void testHotspotDoesNotStartWhenAlreadyTethering() throws Exception { |
| when(mSettingsStore.getLocationModeSetting(mContext)) |
| .thenReturn(LOCATION_MODE_HIGH_ACCURACY); |
| when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); |
| mLooper.dispatchAll(); |
| int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( |
| mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| assertEquals(ERROR_INCOMPATIBLE_MODE, returnCode); |
| } |
| |
| /** |
| * Only start LocalOnlyHotspot if admin setting does not disallow tethering. |
| */ |
| @Test |
| public void testHotspotDoesNotStartWhenTetheringDisallowed() throws Exception { |
| when(mSettingsStore.getLocationModeSetting(mContext)) |
| .thenReturn(LOCATION_MODE_HIGH_ACCURACY); |
| when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true); |
| when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) |
| .thenReturn(true); |
| int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( |
| mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| assertEquals(ERROR_TETHERING_DISALLOWED, returnCode); |
| } |
| |
| /** |
| * Verify that callers can only have one registered LOHS request. |
| */ |
| @Test(expected = IllegalStateException.class) |
| public void testStartLocalOnlyHotspotThrowsExceptionWhenCallerAlreadyRegistered() { |
| registerLOHSRequestFull(); |
| |
| // now do the second request that will fail |
| mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME); |
| } |
| |
| /** |
| * Verify that the call to stopLocalOnlyHotspot does not do anything when there aren't any |
| * registered callers. |
| */ |
| @Test |
| public void testStopLocalOnlyHotspotDoesNothingWithoutRegisteredRequests() { |
| // allow test to proceed without a permission check failure |
| mWifiServiceImpl.stopLocalOnlyHotspot(); |
| // there is nothing registered, so this shouldn't do anything |
| verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify that the call to stopLocalOnlyHotspot does not do anything when one caller unregisters |
| * but there is still an active request |
| */ |
| @Test |
| public void testStopLocalOnlyHotspotDoesNothingWithARemainingRegisteredRequest() { |
| // register a request that will remain after the stopLOHS call |
| mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); |
| |
| registerLOHSRequestFull(); |
| |
| // Since we are calling with the same pid, the second register call will be removed |
| mWifiServiceImpl.stopLocalOnlyHotspot(); |
| // there is still a valid registered request - do not tear down LOHS |
| verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); |
| } |
| |
| /** |
| * Verify that the call to stopLocalOnlyHotspot sends a message to WifiController to stop |
| * the softAp when there is one registered caller when that caller is removed. |
| */ |
| @Test |
| public void testStopLocalOnlyHotspotTriggersSoftApStopWithOneRegisteredRequest() { |
| registerLOHSRequestFull(); |
| verify(mWifiController) |
| .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class)); |
| |
| mWifiServiceImpl.stopLocalOnlyHotspot(); |
| // there is was only one request registered, we should tear down softap |
| verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); |
| } |
| |
| /** |
| * Verify that a call to stopLocalOnlyHotspot throws a SecurityException if the caller does not |
| * have the CHANGE_WIFI_STATE permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStopLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE), |
| eq("WifiService")); |
| mWifiServiceImpl.stopLocalOnlyHotspot(); |
| } |
| |
| /** |
| * Verify that WifiServiceImpl does not send the stop ap message if there were no |
| * pending LOHS requests upon a binder death callback. |
| */ |
| @Test |
| public void testServiceImplNotCalledWhenBinderDeathTriggeredNoRequests() { |
| LocalOnlyRequestorCallback binderDeathCallback = |
| mWifiServiceImpl.new LocalOnlyRequestorCallback(); |
| |
| binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo); |
| verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); |
| } |
| |
| /** |
| * Verify that WifiServiceImpl does not send the stop ap message if there are remaining |
| * registered LOHS requests upon a binder death callback. Additionally verify that softap mode |
| * will be stopped if that remaining request is removed (to verify the binder death properly |
| * cleared the requestor that died). |
| */ |
| @Test |
| public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRegisteredRequests() { |
| LocalOnlyRequestorCallback binderDeathCallback = |
| mWifiServiceImpl.new LocalOnlyRequestorCallback(); |
| |
| // registering a request directly from the test will not trigger a message to start |
| // softap mode |
| mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); |
| |
| registerLOHSRequestFull(); |
| |
| binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo); |
| verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt()); |
| |
| reset(mWifiController); |
| |
| // now stop as the second request and confirm CMD_SET_AP will be sent to make sure binder |
| // death requestor was removed |
| mWifiServiceImpl.stopLocalOnlyHotspot(); |
| verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); |
| } |
| |
| private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { |
| @Override |
| public boolean matches(IntentFilter filter) { |
| return filter.hasAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); |
| } |
| } |
| |
| /** |
| * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE |
| * broadcast is received. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnSoftApFailureGeneric() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_GENERIC, message.arg1); |
| } |
| |
| /** |
| * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE |
| * broadcast is received with the SAP_START_FAILURE_NO_CHANNEL error. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnSoftApFailureNoChannel() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_NO_CHANNEL, message.arg1); |
| } |
| |
| /** |
| * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE |
| * broadcast is received with WIFI_AP_STATE_DISABLING and LOHS was active. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| reset(mHandler); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STOPPED, message.what); |
| } |
| |
| |
| /** |
| * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE |
| * broadcast is received with WIFI_AP_STATE_DISABLED and LOHS was enabled. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnSoftApDisabled() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| reset(mHandler); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STOPPED, message.what); |
| } |
| |
| /** |
| * Verify that no callbacks are called for registered LOHS callers when a WIFI_AP_STATE_CHANGE |
| * broadcast is received and the softap started. |
| */ |
| @Test |
| public void testRegisteredCallbacksNotTriggeredOnSoftApStart() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, |
| IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verifyNoMoreInteractions(mHandler); |
| } |
| |
| /** |
| * Verify that onStopped is called only once for registered LOHS callers when |
| * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLING and |
| * WIFI_AP_STATE_DISABLED when LOHS was enabled. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApDisabling() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| reset(mHandler); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STOPPED, message.what); |
| } |
| |
| /** |
| * Verify that onFailed is called only once for registered LOHS callers when |
| * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED twice. |
| */ |
| @Test |
| public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApFailsTwice() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| registerLOHSRequestFull(); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_GENERIC, message.arg1); |
| } |
| |
| /** |
| * Verify that onFailed is called for all registered LOHS callers when |
| * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED. |
| */ |
| @Test |
| public void testAllRegisteredCallbacksTriggeredWhenSoftApFails() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| // make an additional request for this test |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| |
| registerLOHSRequestFull(); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_GENERIC, message.arg1); |
| } |
| |
| /** |
| * Verify that onStopped is called for all registered LOHS callers when |
| * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was |
| * active. |
| */ |
| @Test |
| public void testAllRegisteredCallbacksTriggeredWhenSoftApStops() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mRequestInfo).sendHotspotStartedMessage(any()); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| reset(mHandler); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| verify(mRequestInfo).sendHotspotStoppedMessage(); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STOPPED, message.what); |
| } |
| |
| /** |
| * Verify that onFailed is called for all registered LOHS callers when |
| * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was |
| * not active. |
| */ |
| @Test |
| public void testAllRegisteredCallbacksTriggeredWhenSoftApStopsLOHSNotActive() throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2); |
| |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| |
| verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); |
| verify(mRequestInfo2).sendHotspotFailedMessage(ERROR_GENERIC); |
| } |
| |
| /** |
| * Verify that if we do not have registered LOHS requestors and we receive an update that LOHS |
| * is up and ready for use, we tell WifiController to tear it down. This can happen if softap |
| * mode fails to come up properly and we get an onFailed message for a tethering call and we |
| * had registered callers for LOHS. |
| */ |
| @Test |
| public void testLOHSReadyWithoutRegisteredRequestsStopsSoftApMode() { |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| |
| verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); |
| } |
| |
| /** |
| * Verify that all registered LOHS requestors are notified via a HOTSPOT_STARTED message that |
| * the hotspot is up and ready to use. |
| */ |
| @Test |
| public void testRegisteredLocalOnlyHotspotRequestorsGetOnStartedCallbackWhenReady() |
| throws Exception { |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mRequestInfo).sendHotspotStartedMessage(any(WifiConfiguration.class)); |
| |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| assertNotNull((WifiConfiguration) message.obj); |
| } |
| |
| /** |
| * Verify that if a LOHS is already active, a new call to register a request will trigger the |
| * onStarted callback. |
| */ |
| @Test |
| public void testRegisterLocalOnlyHotspotRequestAfterAlreadyStartedGetsOnStartedCallback() |
| throws Exception { |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| |
| registerLOHSRequestFull(); |
| |
| mLooper.dispatchAll(); |
| |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_STARTED, message.what); |
| // since the first request was registered out of band, the config will be null |
| assertNull((WifiConfiguration) message.obj); |
| } |
| |
| /** |
| * Verify that if a LOHS request is active and we receive an update with an ip mode |
| * configuration error, callers are notified via the onFailed callback with the generic |
| * error and are unregistered. |
| */ |
| @Test |
| public void testCallOnFailedLocalOnlyHotspotRequestWhenIpConfigFails() throws Exception { |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR); |
| mLooper.dispatchAll(); |
| |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_GENERIC, message.arg1); |
| |
| // sendMessage should only happen once since the requestor should be unregistered |
| reset(mHandler); |
| |
| // send HOTSPOT_FAILED message should only happen once since the requestor should be |
| // unregistered |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR); |
| mLooper.dispatchAll(); |
| verify(mHandler, never()).handleMessage(any(Message.class)); |
| } |
| |
| /** |
| * Verify that if a LOHS request is active and tethering starts, callers are notified on the |
| * incompatible mode and are unregistered. |
| */ |
| @Test |
| public void testCallOnFailedLocalOnlyHotspotRequestWhenTetheringStarts() throws Exception { |
| registerLOHSRequestFull(); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); |
| mLooper.dispatchAll(); |
| |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| Message message = mMessageCaptor.getValue(); |
| assertEquals(HOTSPOT_FAILED, message.what); |
| assertEquals(ERROR_INCOMPATIBLE_MODE, message.arg1); |
| |
| // sendMessage should only happen once since the requestor should be unregistered |
| reset(mHandler); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED); |
| mLooper.dispatchAll(); |
| verify(mHandler, never()).handleMessage(any(Message.class)); |
| } |
| |
| /** |
| * Verify that if LOHS is disabled, a new call to register a request will not trigger the |
| * onStopped callback. |
| */ |
| @Test |
| public void testRegisterLocalOnlyHotspotRequestWhenStoppedDoesNotGetOnStoppedCallback() |
| throws Exception { |
| registerLOHSRequestFull(); |
| mLooper.dispatchAll(); |
| |
| verify(mHandler, never()).handleMessage(any(Message.class)); |
| } |
| |
| /** |
| * Verify that if a LOHS was active and then stopped, a new call to register a request will |
| * not trigger the onStarted callback. |
| */ |
| @Test |
| public void testRegisterLocalOnlyHotspotRequestAfterStoppedNoOnStartedCallback() |
| throws Exception { |
| when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); |
| when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); |
| mWifiServiceImpl.checkAndStartWifi(); |
| verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), |
| (IntentFilter) argThat(new IntentFilterMatcher())); |
| |
| // register a request so we don't drop the LOHS interface ip update |
| mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); |
| |
| mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| |
| registerLOHSRequestFull(); |
| mLooper.dispatchAll(); |
| |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| assertEquals(HOTSPOT_STARTED, mMessageCaptor.getValue().what); |
| |
| reset(mHandler); |
| |
| // now stop the hotspot |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, |
| WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, |
| WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); |
| mLooper.dispatchAll(); |
| verify(mHandler).handleMessage(mMessageCaptor.capture()); |
| assertEquals(HOTSPOT_STOPPED, mMessageCaptor.getValue().what); |
| |
| reset(mHandler); |
| |
| // now register a new caller - they should not get the onStarted callback |
| Messenger messenger2 = new Messenger(mHandler); |
| IBinder binder2 = mock(IBinder.class); |
| |
| int result = mWifiServiceImpl.startLocalOnlyHotspot(messenger2, binder2, TEST_PACKAGE_NAME); |
| assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); |
| mLooper.dispatchAll(); |
| |
| verify(mHandler, never()).handleMessage(any(Message.class)); |
| } |
| |
| /** |
| * Verify that a call to startWatchLocalOnlyHotspot is only allowed from callers with the |
| * signature only NETWORK_SETTINGS permission. |
| * |
| * This test is expecting the permission check to enforce the permission and throw a |
| * SecurityException for callers without the permission. This exception should be bubbled up to |
| * the caller of startLocalOnlyHotspot. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStartWatchLocalOnlyHotspotNotApprovedCaller() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), |
| eq("WifiService")); |
| mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder); |
| } |
| |
| /** |
| * Verify that the call to startWatchLocalOnlyHotspot throws the UnsupportedOperationException |
| * when called until the implementation is complete. |
| */ |
| @Test(expected = UnsupportedOperationException.class) |
| public void testStartWatchLocalOnlyHotspotNotSupported() { |
| mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder); |
| } |
| |
| /** |
| * Verify that a call to stopWatchLocalOnlyHotspot is only allowed from callers with the |
| * signature only NETWORK_SETTINGS permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testStopWatchLocalOnlyHotspotNotApprovedCaller() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), |
| eq("WifiService")); |
| mWifiServiceImpl.stopWatchLocalOnlyHotspot(); |
| } |
| |
| /** |
| * Verify that the call to stopWatchLocalOnlyHotspot throws the UnsupportedOperationException |
| * until the implementation is complete. |
| */ |
| @Test(expected = UnsupportedOperationException.class) |
| public void testStopWatchLocalOnlyHotspotNotSupported() { |
| mWifiServiceImpl.stopWatchLocalOnlyHotspot(); |
| } |
| |
| /** |
| * Verify that the call to addOrUpdateNetwork for installing Passpoint profile is redirected |
| * to the Passpoint specific API addOrUpdatePasspointConfiguration. |
| */ |
| @Test |
| public void testAddPasspointProfileViaAddNetwork() throws Exception { |
| WifiConfiguration config = WifiConfigurationTestUtil.createPasspointNetwork(); |
| config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); |
| |
| PackageManager pm = mock(PackageManager.class); |
| when(pm.hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT)).thenReturn(true); |
| when(mContext.getPackageManager()).thenReturn(pm); |
| |
| when(mWifiStateMachine.syncAddOrUpdatePasspointConfig(any(), |
| any(PasspointConfiguration.class), anyInt())).thenReturn(true); |
| assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config)); |
| verify(mWifiStateMachine).syncAddOrUpdatePasspointConfig(any(), |
| any(PasspointConfiguration.class), anyInt()); |
| reset(mWifiStateMachine); |
| |
| when(mWifiStateMachine.syncAddOrUpdatePasspointConfig(any(), |
| any(PasspointConfiguration.class), anyInt())).thenReturn(false); |
| assertEquals(-1, mWifiServiceImpl.addOrUpdateNetwork(config)); |
| verify(mWifiStateMachine).syncAddOrUpdatePasspointConfig(any(), |
| any(PasspointConfiguration.class), anyInt()); |
| } |
| |
| /** |
| * Verify that a call to {@link WifiServiceImpl#restoreBackupData(byte[])} is only allowed from |
| * callers with the signature only NETWORK_SETTINGS permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testRestoreBackupDataNotApprovedCaller() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), |
| eq("WifiService")); |
| mWifiServiceImpl.restoreBackupData(null); |
| verify(mWifiBackupRestore, never()).retrieveConfigurationsFromBackupData(any(byte[].class)); |
| } |
| |
| /** |
| * Verify that a call to {@link WifiServiceImpl#restoreSupplicantBackupData(byte[], byte[])} is |
| * only allowed from callers with the signature only NETWORK_SETTINGS permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testRestoreSupplicantBackupDataNotApprovedCaller() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), |
| eq("WifiService")); |
| mWifiServiceImpl.restoreSupplicantBackupData(null, null); |
| verify(mWifiBackupRestore, never()).retrieveConfigurationsFromSupplicantBackupData( |
| any(byte[].class), any(byte[].class)); |
| } |
| |
| /** |
| * Verify that a call to {@link WifiServiceImpl#retrieveBackupData()} is only allowed from |
| * callers with the signature only NETWORK_SETTINGS permission. |
| */ |
| @Test(expected = SecurityException.class) |
| public void testRetrieveBackupDataNotApprovedCaller() { |
| doThrow(new SecurityException()).when(mContext) |
| .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS), |
| eq("WifiService")); |
| mWifiServiceImpl.retrieveBackupData(); |
| verify(mWifiBackupRestore, never()).retrieveBackupDataFromConfigurations(any(List.class)); |
| } |
| |
| /** |
| * Helper to test handling of async messages by wifi service when the message comes from an |
| * app without {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission. |
| */ |
| private void verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| int requestMsgWhat, int expectedReplyMsgwhat) { |
| WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); |
| |
| int uidWithoutPermission = 5; |
| when(mWifiPermissionsUtil.checkChangePermission(eq(uidWithoutPermission))) |
| .thenReturn(false); |
| |
| Message request = Message.obtain(); |
| request.what = requestMsgWhat; |
| request.sendingUid = uidWithoutPermission; |
| |
| mLooper.startAutoDispatch(); |
| Message reply = tester.sendMessageSynchronously(request); |
| mLooper.stopAutoDispatch(); |
| |
| verify(mWifiStateMachine, never()).sendMessage(any(Message.class)); |
| assertEquals(expectedReplyMsgwhat, reply.what); |
| assertEquals(WifiManager.NOT_AUTHORIZED, reply.arg1); |
| } |
| |
| /** |
| * Verify that the CONNECT_NETWORK message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testConnectNetworkWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.CONNECT_NETWORK, WifiManager.CONNECT_NETWORK_FAILED); |
| } |
| |
| /** |
| * Verify that the FORGET_NETWORK message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testForgetNetworkWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.SAVE_NETWORK, WifiManager.SAVE_NETWORK_FAILED); |
| } |
| |
| /** |
| * Verify that the START_WPS message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testStartWpsWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.START_WPS, WifiManager.WPS_FAILED); |
| } |
| |
| /** |
| * Verify that the CANCEL_WPS message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testCancelWpsWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.CANCEL_WPS, WifiManager.CANCEL_WPS_FAILED); |
| } |
| |
| /** |
| * Verify that the DISABLE_NETWORK message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testDisableNetworkWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.DISABLE_NETWORK, WifiManager.DISABLE_NETWORK_FAILED); |
| } |
| |
| /** |
| * Verify that the RSSI_PKTCNT_FETCH message received from an app without |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is rejected with the correct |
| * error code. |
| */ |
| @Test |
| public void testRssiPktcntFetchWithoutChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithoutChangePermisson( |
| WifiManager.RSSI_PKTCNT_FETCH, WifiManager.RSSI_PKTCNT_FETCH_FAILED); |
| } |
| |
| /** |
| * Helper to test handling of async messages by wifi service when the message comes from an |
| * app with {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission. |
| */ |
| private void verifyAsyncChannelMessageHandlingWithChangePermisson( |
| int requestMsgWhat, Object requestMsgObj) { |
| WifiAsyncChannelTester tester = verifyAsyncChannelHalfConnected(); |
| |
| when(mWifiPermissionsUtil.checkChangePermission(anyInt())).thenReturn(true); |
| |
| Message request = Message.obtain(); |
| request.what = requestMsgWhat; |
| request.obj = requestMsgObj; |
| |
| tester.sendMessage(request); |
| mLooper.dispatchAll(); |
| |
| ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class); |
| verify(mWifiStateMachine).sendMessage(messageArgumentCaptor.capture()); |
| assertEquals(requestMsgWhat, messageArgumentCaptor.getValue().what); |
| } |
| |
| /** |
| * Verify that the CONNECT_NETWORK message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testConnectNetworkWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.CONNECT_NETWORK, new WifiConfiguration()); |
| } |
| |
| /** |
| * Verify that the SAVE_NETWORK message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testSaveNetworkWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.SAVE_NETWORK, new WifiConfiguration()); |
| } |
| |
| /** |
| * Verify that the START_WPS message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testStartWpsWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.START_WPS, new Object()); |
| } |
| |
| /** |
| * Verify that the CANCEL_WPS message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testCancelWpsWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.CANCEL_WPS, new Object()); |
| } |
| |
| /** |
| * Verify that the DISABLE_NETWORK message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testDisableNetworkWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.DISABLE_NETWORK, new Object()); |
| } |
| |
| /** |
| * Verify that the RSSI_PKTCNT_FETCH message received from an app with |
| * {@link android.Manifest.permission#CHANGE_WIFI_STATE} permission is forwarded to |
| * WifiStateMachine. |
| */ |
| @Test |
| public void testRssiPktcntFetchWithChangePermission() throws Exception { |
| verifyAsyncChannelMessageHandlingWithChangePermisson( |
| WifiManager.RSSI_PKTCNT_FETCH, new Object()); |
| } |
| } |