/* * 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.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 mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); final ArgumentCaptor mIntentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); final ArgumentCaptor mMessageCaptor = ArgumentCaptor.forClass(Message.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 UserManager mUserManager; @Mock WifiConfiguration mApConfig; @Mock ActivityManager mActivityManager; @Mock AppOpsManager mAppOpsManager; @Mock IBinder mAppBinder; @Mock WifiNotificationController mWifiNotificationController; @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); } } @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); 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); when(mWifiInjector.getWifiNotificationController()).thenReturn(mWifiNotificationController); 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()); } /** * 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); 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")); 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 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.checkCallingOrSelfPermission( eq(android.Manifest.permission.NETWORK_SETTINGS))) .thenReturn(PackageManager.PERMISSION_GRANTED); 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.checkCallingOrSelfPermission( eq(android.Manifest.permission.NETWORK_SETTINGS))) .thenReturn(PackageManager.PERMISSION_DENIED); 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), eq(null)); } /** * 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), eq(apConfig)); } /** * 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), eq(null)); } /** * 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), eq(mApConfig)); } /** * 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), eq(null)); } /** * 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), eq(config)); } /** * 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); 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 we are not tethering. */ @Test public void testHotspotDoesNotStartWhenAlreadyTethering() { when(mSettingsStore.getLocationModeSetting(mContext)) .thenReturn(LOCATION_MODE_HIGH_ACCURACY); 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() { when(mSettingsStore.getLocationModeSetting(mContext)) .thenReturn(LOCATION_MODE_HIGH_ACCURACY); 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(WifiConfiguration.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 { @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); 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); 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); 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); 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); 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, 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 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); 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); TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); 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); when(mResources.getBoolean(com.android.internal.R.bool.config_wifi_hotspot2_enabled)) .thenReturn(true); 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)); } }