WifiServiceImplTest.java revision de6ff4c1fc87364efc5fa15de31325b4450548b0
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static android.net.wifi.WifiManager.HOTSPOT_FAILED;
20import static android.net.wifi.WifiManager.HOTSPOT_STARTED;
21import static android.net.wifi.WifiManager.HOTSPOT_STOPPED;
22import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR;
23import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
24import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
25import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
26import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
27import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
28import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
29import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
30import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
31import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
32import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
33import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
34import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
35import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
36import static android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
37import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
38
39import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
40import static com.android.server.wifi.WifiController.CMD_SET_AP;
41import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
42
43import static org.junit.Assert.assertEquals;
44import static org.junit.Assert.assertFalse;
45import static org.junit.Assert.assertNotNull;
46import static org.junit.Assert.assertNull;
47import static org.junit.Assert.assertTrue;
48import static org.mockito.Matchers.any;
49import static org.mockito.Matchers.anyString;
50import static org.mockito.Matchers.eq;
51import static org.mockito.Mockito.*;
52
53import android.app.ActivityManager;
54import android.app.AppOpsManager;
55import android.content.BroadcastReceiver;
56import android.content.ContentResolver;
57import android.content.Context;
58import android.content.IntentFilter;
59import android.content.pm.PackageManager;
60import android.content.res.Resources;
61import android.net.IpConfiguration;
62import android.net.wifi.ScanSettings;
63import android.net.wifi.WifiConfiguration;
64import android.net.wifi.WifiManager;
65import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
66import android.os.Handler;
67import android.os.HandlerThread;
68import android.os.IBinder;
69import android.os.IPowerManager;
70import android.os.Looper;
71import android.os.Message;
72import android.os.Messenger;
73import android.os.PowerManager;
74import android.os.Process;
75import android.os.UserManager;
76import android.os.WorkSource;
77import android.os.test.TestLooper;
78import android.provider.Settings;
79import android.test.suitebuilder.annotation.SmallTest;
80
81import com.android.internal.util.AsyncChannel;
82import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback;
83import com.android.server.wifi.util.WifiAsyncChannel;
84import com.android.server.wifi.util.WifiPermissionsUtil;
85
86
87import org.junit.Before;
88import org.junit.Test;
89import org.mockito.ArgumentCaptor;
90import org.mockito.ArgumentMatcher;
91import org.mockito.Mock;
92import org.mockito.MockitoAnnotations;
93import org.mockito.Spy;
94
95import java.io.FileDescriptor;
96import java.io.PrintWriter;
97import java.io.StringWriter;
98
99/**
100 * Unit tests for {@link WifiServiceImpl}.
101 *
102 * Note: this is intended to build up over time and will not immediately cover the entire file.
103 */
104@SmallTest
105public class WifiServiceImplTest {
106
107    private static final String TAG = "WifiServiceImplTest";
108    private static final String SCAN_PACKAGE_NAME = "scanPackage";
109    private static final String WHITE_LIST_SCAN_PACKAGE_NAME = "whiteListScanPackage";
110    private static final int DEFAULT_VERBOSE_LOGGING = 0;
111    private static final long WIFI_BACKGROUND_SCAN_INTERVAL = 10000;
112    private static final String ANDROID_SYSTEM_PACKAGE = "android";
113    private static final String TEST_PACKAGE_NAME = "TestPackage";
114    private static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
115    private static final int TEST_PID = 6789;
116    private static final int TEST_PID2 = 9876;
117    private static final String WIFI_IFACE_NAME = "wlan0";
118
119    private WifiServiceImpl mWifiServiceImpl;
120    private TestLooper mLooper;
121    private PowerManager mPowerManager;
122    private Handler mHandler;
123    private Messenger mAppMessenger;
124    private int mPid;
125    private int mPid2 = Process.myPid();
126
127    final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor =
128            ArgumentCaptor.forClass(BroadcastReceiver.class);
129    final ArgumentCaptor<IntentFilter> mIntentFilterCaptor =
130            ArgumentCaptor.forClass(IntentFilter.class);
131
132    final ArgumentCaptor<Message> mMessageCaptor = ArgumentCaptor.forClass(Message.class);
133
134    @Mock Context mContext;
135    @Mock WifiInjector mWifiInjector;
136    @Mock Clock mClock;
137    @Mock WifiController mWifiController;
138    @Mock WifiTrafficPoller mWifiTrafficPoller;
139    @Mock WifiStateMachine mWifiStateMachine;
140    @Mock HandlerThread mHandlerThread;
141    @Mock AsyncChannel mAsyncChannel;
142    @Mock Resources mResources;
143    @Mock FrameworkFacade mFrameworkFacade;
144    @Mock WifiLockManager mLockManager;
145    @Mock WifiMulticastLockManager mWifiMulticastLockManager;
146    @Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
147    @Mock WifiBackupRestore mWifiBackupRestore;
148    @Mock WifiMetrics mWifiMetrics;
149    @Mock WifiPermissionsUtil mWifiPermissionsUtil;
150    @Mock WifiSettingsStore mSettingsStore;
151    @Mock ContentResolver mContentResolver;
152    @Mock UserManager mUserManager;
153    @Mock WifiConfiguration mApConfig;
154    @Mock ActivityManager mActivityManager;
155    @Mock AppOpsManager mAppOpsManager;
156    @Mock IBinder mAppBinder;
157    @Mock WifiNotificationController mWifiNotificationController;
158    @Mock LocalOnlyHotspotRequestInfo mRequestInfo;
159    @Mock LocalOnlyHotspotRequestInfo mRequestInfo2;
160
161    @Spy FakeWifiLog mLog;
162
163    private class WifiAsyncChannelTester {
164        private static final String TAG = "WifiAsyncChannelTester";
165        public static final int CHANNEL_STATE_FAILURE = -1;
166        public static final int CHANNEL_STATE_DISCONNECTED = 0;
167        public static final int CHANNEL_STATE_HALF_CONNECTED = 1;
168        public static final int CHANNEL_STATE_FULLY_CONNECTED = 2;
169
170        private int mState = CHANNEL_STATE_DISCONNECTED;
171        private WifiAsyncChannel mChannel;
172        private WifiLog mAsyncTestLog;
173
174        WifiAsyncChannelTester(WifiInjector wifiInjector) {
175            mAsyncTestLog = wifiInjector.makeLog(TAG);
176        }
177
178        public int getChannelState() {
179            return mState;
180        }
181
182        public void connect(final Looper looper, final Messenger messenger,
183                final Handler incomingMessageHandler) {
184            assertEquals("AsyncChannel must be in disconnected state",
185                    CHANNEL_STATE_DISCONNECTED, mState);
186            mChannel = new WifiAsyncChannel(TAG);
187            mChannel.setWifiLog(mLog);
188            Handler handler = new Handler(mLooper.getLooper()) {
189                @Override
190                public void handleMessage(Message msg) {
191                    switch (msg.what) {
192                        case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
193                            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
194                                mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
195                                mState = CHANNEL_STATE_HALF_CONNECTED;
196                            } else {
197                                mState = CHANNEL_STATE_FAILURE;
198                            }
199                            break;
200                        case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
201                            mState = CHANNEL_STATE_FULLY_CONNECTED;
202                            break;
203                        case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
204                            mState = CHANNEL_STATE_DISCONNECTED;
205                            break;
206                        default:
207                            incomingMessageHandler.handleMessage(msg);
208                            break;
209                    }
210                }
211            };
212            mChannel.connect(null, handler, messenger);
213        }
214    }
215
216    @Before public void setUp() {
217        MockitoAnnotations.initMocks(this);
218        mLooper = new TestLooper();
219        mHandler = spy(new Handler(mLooper.getLooper()));
220        mAppMessenger = new Messenger(mHandler);
221
222        when(mRequestInfo.getPid()).thenReturn(mPid);
223        when(mRequestInfo2.getPid()).thenReturn(mPid2);
224        when(mWifiInjector.getUserManager()).thenReturn(mUserManager);
225        when(mWifiInjector.getWifiController()).thenReturn(mWifiController);
226        when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
227        when(mWifiInjector.getWifiStateMachine()).thenReturn(mWifiStateMachine);
228        when(mWifiStateMachine.syncInitialize(any())).thenReturn(true);
229        when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mHandlerThread);
230        when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
231        when(mContext.getResources()).thenReturn(mResources);
232        when(mContext.getContentResolver()).thenReturn(mContentResolver);
233        doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(),
234                anyBoolean(), any());
235        when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
236        when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
237        when(mFrameworkFacade.getLongSetting(
238                eq(mContext),
239                eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
240                anyLong()))
241                .thenReturn(WIFI_BACKGROUND_SCAN_INTERVAL);
242        when(mFrameworkFacade.getStringSetting(
243                eq(mContext),
244                eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST)))
245                .thenReturn(WHITE_LIST_SCAN_PACKAGE_NAME);
246        IPowerManager powerManagerService = mock(IPowerManager.class);
247        mPowerManager = new PowerManager(mContext, powerManagerService, new Handler());
248        when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
249        when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
250        WifiAsyncChannel wifiAsyncChannel = new WifiAsyncChannel("WifiServiceImplTest");
251        wifiAsyncChannel.setWifiLog(mLog);
252        when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(wifiAsyncChannel);
253        when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade);
254        when(mWifiInjector.getWifiLockManager()).thenReturn(mLockManager);
255        when(mWifiInjector.getWifiMulticastLockManager()).thenReturn(mWifiMulticastLockManager);
256        when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
257        when(mWifiInjector.getWifiBackupRestore()).thenReturn(mWifiBackupRestore);
258        when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
259        WifiTrafficPoller wifiTrafficPoller = new WifiTrafficPoller(mContext,
260                mLooper.getLooper(), "mockWlan");
261        when(mWifiInjector.getWifiTrafficPoller()).thenReturn(wifiTrafficPoller);
262        when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
263        when(mWifiInjector.getWifiSettingsStore()).thenReturn(mSettingsStore);
264        when(mWifiInjector.getClock()).thenReturn(mClock);
265        when(mWifiInjector.getWifiNotificationController()).thenReturn(mWifiNotificationController);
266        mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
267        mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
268    }
269
270    @Test
271    public void testRemoveNetworkUnknown() {
272        assertFalse(mWifiServiceImpl.removeNetwork(-1));
273    }
274
275    @Test
276    public void testAsyncChannelHalfConnected() {
277        WifiAsyncChannelTester channelTester = new WifiAsyncChannelTester(mWifiInjector);
278        Handler handler = mock(Handler.class);
279        TestLooper looper = new TestLooper();
280        channelTester.connect(looper.getLooper(), mWifiServiceImpl.getWifiServiceMessenger(),
281                handler);
282        mLooper.dispatchAll();
283        assertEquals("AsyncChannel must be half connected",
284                WifiAsyncChannelTester.CHANNEL_STATE_HALF_CONNECTED,
285                channelTester.getChannelState());
286    }
287
288    /**
289     * Tests the isValid() check for StaticIpConfigurations, ensuring that configurations with null
290     * ipAddress are rejected, and configurations with ipAddresses are valid.
291     */
292    @Test
293    public void testStaticIpConfigurationValidityCheck() {
294        WifiConfiguration conf = WifiConfigurationTestUtil.createOpenNetwork();
295        IpConfiguration ipConf =
296                WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy();
297        conf.setIpConfiguration(ipConf);
298        // Ensure staticIpConfiguration with IP Address is valid
299        assertTrue(mWifiServiceImpl.isValid(conf));
300        ipConf.staticIpConfiguration.ipAddress = null;
301        // Ensure staticIpConfiguration with null IP Address it is not valid
302        conf.setIpConfiguration(ipConf);
303        assertFalse(mWifiServiceImpl.isValid(conf));
304    }
305
306    /**
307     * Ensure WifiMetrics.dump() is the only dump called when 'dumpsys wifi WifiMetricsProto' is
308     * called. This is required to support simple metrics collection via dumpsys
309     */
310    @Test
311    public void testWifiMetricsDump() {
312        mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()),
313                new String[]{mWifiMetrics.PROTO_DUMP_ARG});
314        verify(mWifiMetrics)
315                .dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
316        verify(mWifiStateMachine, never())
317                .dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
318    }
319
320
321    /**
322     * Ensure WifiServiceImpl.dump() doesn't throw an NPE when executed with null args
323     */
324    @Test
325    public void testDumpNullArgs() {
326        mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
327    }
328
329    /**
330     * Verify that wifi can be enabled by a caller with WIFI_STATE_CHANGE permission when wifi is
331     * off (no hotspot, no airplane mode).
332     */
333    @Test
334    public void testSetWifiEnabledSuccess() throws Exception {
335        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
336        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
337        assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
338        verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
339    }
340
341    /**
342     * Verify that the CMD_TOGGLE_WIFI message won't be sent if wifi is already on.
343     */
344    @Test
345    public void testSetWifiEnabledNoToggle() throws Exception {
346        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
347        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(false);
348        assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
349        verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
350    }
351
352    /**
353     * Verify a SecurityException is thrown if a caller does not have the correct permission to
354     * toggle wifi.
355     */
356    @Test(expected = SecurityException.class)
357    public void testSetWifiEnableWithoutPermission() throws Exception {
358        doThrow(new SecurityException()).when(mContext)
359                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
360                                                eq("WifiService"));
361        mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
362        verify(mWifiStateMachine, never()).syncGetWifiApState();
363    }
364
365    /**
366     * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
367     * are in softap mode.
368     */
369    @Test
370    public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception {
371        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
372        when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
373        when(mContext.checkCallingOrSelfPermission(
374                eq(android.Manifest.permission.NETWORK_SETTINGS)))
375                        .thenReturn(PackageManager.PERMISSION_GRANTED);
376        assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
377        verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
378    }
379
380    /**
381     * Verify that a call from an app cannot enable wifi if we are in softap mode.
382     */
383    @Test
384    public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
385        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
386        when(mContext.checkCallingOrSelfPermission(
387                eq(android.Manifest.permission.NETWORK_SETTINGS)))
388                        .thenReturn(PackageManager.PERMISSION_DENIED);
389        assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
390        verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
391        verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
392    }
393
394    /**
395     * Verify that wifi can be disabled by a caller with WIFI_STATE_CHANGE permission when wifi is
396     * on.
397     */
398    @Test
399    public void testSetWifiDisabledSuccess() throws Exception {
400        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
401        when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
402        assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
403        verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
404    }
405
406    /**
407     * Verify that CMD_TOGGLE_WIFI message won't be sent if wifi is already off.
408     */
409    @Test
410    public void testSetWifiDisabledNoToggle() throws Exception {
411        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
412        when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false);
413        assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
414        verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
415    }
416
417    /**
418     * Verify a SecurityException is thrown if a caller does not have the correct permission to
419     * toggle wifi.
420     */
421    @Test(expected = SecurityException.class)
422    public void testSetWifiDisabledWithoutPermission() throws Exception {
423        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
424        doThrow(new SecurityException()).when(mContext)
425                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
426                                                eq("WifiService"));
427        mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false);
428    }
429
430    /**
431     * Ensure unpermitted callers cannot write the SoftApConfiguration.
432     *
433     * @throws SecurityException
434     */
435    @Test(expected = SecurityException.class)
436    public void testSetWifiApConfigurationNotSavedWithoutPermission() {
437        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
438        WifiConfiguration apConfig = new WifiConfiguration();
439        mWifiServiceImpl.setWifiApConfiguration(apConfig);
440        verify(mWifiStateMachine, never()).setWifiApConfiguration(eq(apConfig));
441    }
442
443    /**
444     * Ensure softap config is written when the caller has the correct permission.
445     */
446    @Test
447    public void testSetWifiApConfigurationSuccess() {
448        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
449        WifiConfiguration apConfig = new WifiConfiguration();
450        mWifiServiceImpl.setWifiApConfiguration(apConfig);
451        verify(mWifiStateMachine).setWifiApConfiguration(eq(apConfig));
452    }
453
454    /**
455     * Ensure that a null config does not overwrite the saved ap config.
456     */
457    @Test
458    public void testSetWifiApConfigurationNullConfigNotSaved() {
459        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
460        mWifiServiceImpl.setWifiApConfiguration(null);
461        verify(mWifiStateMachine, never()).setWifiApConfiguration(isNull(WifiConfiguration.class));
462    }
463
464    /**
465     * Ensure unpermitted callers are not able to retrieve the softap config.
466     *
467     * @throws SecurityException
468     */
469    @Test(expected = SecurityException.class)
470    public void testGetWifiApConfigurationNotReturnedWithoutPermission() {
471        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
472        mWifiServiceImpl.getWifiApConfiguration();
473        verify(mWifiStateMachine, never()).syncGetWifiApConfiguration();
474    }
475
476    /**
477     * Ensure permitted callers are able to retrieve the softap config.
478     */
479    @Test
480    public void testGetWifiApConfigurationSuccess() {
481        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
482        WifiConfiguration apConfig = new WifiConfiguration();
483        when(mWifiStateMachine.syncGetWifiApConfiguration()).thenReturn(apConfig);
484        assertEquals(apConfig, mWifiServiceImpl.getWifiApConfiguration());
485    }
486
487    /**
488     * Make sure we do not start wifi if System services have to be restarted to decrypt the device.
489     */
490    @Test
491    public void testWifiControllerDoesNotStartWhenDeviceTriggerResetMainAtBoot() {
492        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
493        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
494        mWifiServiceImpl.checkAndStartWifi();
495        verify(mWifiController, never()).start();
496    }
497
498    /**
499     * Make sure we do start WifiController (wifi disabled) if the device is already decrypted.
500     */
501    @Test
502    public void testWifiControllerStartsWhenDeviceIsDecryptedAtBootWithWifiDisabled() {
503        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
504        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
505        mWifiServiceImpl.checkAndStartWifi();
506        verify(mWifiController).start();
507        verify(mWifiController, never()).sendMessage(CMD_WIFI_TOGGLED);
508    }
509
510    /**
511     * Make sure we do start WifiController (wifi enabled) if the device is already decrypted.
512     */
513    @Test
514    public void testWifiFullyStartsWhenDeviceIsDecryptedAtBootWithWifiEnabled() {
515        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
516        when(mSettingsStore.handleWifiToggled(true)).thenReturn(true);
517        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
518        when(mWifiStateMachine.syncGetWifiState()).thenReturn(WIFI_STATE_DISABLED);
519        when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
520        when(mContext.getPackageName()).thenReturn(ANDROID_SYSTEM_PACKAGE);
521        mWifiServiceImpl.checkAndStartWifi();
522        verify(mWifiController).start();
523        verify(mWifiController).sendMessage(CMD_WIFI_TOGGLED);
524    }
525
526    /**
527     * Verify setWifiApEnabled works with the correct permissions and a null config.
528     */
529    @Test
530    public void testSetWifiApEnabledWithProperPermissionsWithNullConfig() {
531        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
532        when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
533                .thenReturn(false);
534        mWifiServiceImpl.setWifiApEnabled(null, true);
535        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(null));
536    }
537
538    /**
539     * Verify setWifiApEnabled works with correct permissions and a valid config.
540     *
541     * TODO: should really validate that ap configs have a set of basic config settings b/37280779
542     */
543    @Test
544    public void testSetWifiApEnabledWithProperPermissionsWithValidConfig() {
545        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
546        when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
547                .thenReturn(false);
548        WifiConfiguration apConfig = new WifiConfiguration();
549        mWifiServiceImpl.setWifiApEnabled(apConfig, true);
550        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(apConfig));
551    }
552
553    /**
554     * Verify setWifiApEnabled when disabling softap with correct permissions sends the correct
555     * message to WifiController.
556     */
557    @Test
558    public void testSetWifiApEnabledFalseWithProperPermissionsWithNullConfig() {
559        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
560        when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
561                .thenReturn(false);
562        mWifiServiceImpl.setWifiApEnabled(null, false);
563        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0), eq(null));
564    }
565
566    /**
567     * setWifiApEnabled should fail if the provided config is not valid.
568     */
569    @Test
570    public void testSetWifiApEnabledWithProperPermissionInvalidConfigFails() {
571        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
572        when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
573                .thenReturn(false);
574        // mApConfig is a mock and the values are not set - triggering the invalid config.  Testing
575        // will be improved when we actually do test softap configs in b/37280779
576        mWifiServiceImpl.setWifiApEnabled(mApConfig, true);
577        verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(mApConfig));
578    }
579
580    /**
581     * setWifiApEnabled should throw a security exception when the caller does not have the correct
582     * permissions.
583     */
584    @Test(expected = SecurityException.class)
585    public void testSetWifiApEnabledThrowsSecurityExceptionWithoutConfigOverridePermission()
586            throws Exception {
587        doThrow(new SecurityException()).when(mContext)
588                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
589                        eq("WifiService"));
590        mWifiServiceImpl.setWifiApEnabled(null, true);
591    }
592
593    /**
594     * setWifiApEnabled should throw a SecurityException when disallow tethering is set for the
595     * user.
596     */
597    @Test(expected = SecurityException.class)
598    public void testSetWifiApEnabledThrowsSecurityExceptionWithDisallowTethering()
599            throws Exception {
600        when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
601        when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
602                .thenReturn(true);
603        mWifiServiceImpl.setWifiApEnabled(null, true);
604
605    }
606
607    /**
608     * Verify caller with proper permission can call startSoftAp.
609     */
610    @Test
611    public void testStartSoftApWithPermissionsAndNullConfig() {
612        boolean result = mWifiServiceImpl.startSoftAp(null);
613        assertTrue(result);
614        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(null));
615    }
616
617    /**
618     * Verify caller with proper permissions but an invalid config does not start softap.
619     */
620    @Test
621    public void testStartSoftApWithPermissionsAndInvalidConfig() {
622        boolean result = mWifiServiceImpl.startSoftAp(mApConfig);
623        assertFalse(result);
624        verifyZeroInteractions(mWifiController);
625    }
626
627    /**
628     * Verify caller with proper permission and valid config does start softap.
629     */
630    @Test
631    public void testStartSoftApWithPermissionsAndValidConfig() {
632        WifiConfiguration config = new WifiConfiguration();
633        boolean result = mWifiServiceImpl.startSoftAp(config);
634        assertTrue(result);
635        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(1), eq(0), eq(config));
636    }
637
638    /**
639     * Verify a SecurityException is thrown when a caller without the correct permission attempts to
640     * start softap.
641     */
642    @Test(expected = SecurityException.class)
643    public void testStartSoftApWithoutPermissionThrowsException() throws Exception {
644        doThrow(new SecurityException()).when(mContext)
645                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK),
646                                                eq("WifiService"));
647        mWifiServiceImpl.startSoftAp(null);
648    }
649
650    /**
651     * Verify caller with proper permission can call stopSoftAp.
652     */
653    @Test
654    public void testStopSoftApWithPermissions() {
655        boolean result = mWifiServiceImpl.stopSoftAp();
656        assertTrue(result);
657        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
658    }
659
660    /**
661     * Verify SecurityException is thrown when a caller without the correct permission attempts to
662     * stop softap.
663     */
664    @Test(expected = SecurityException.class)
665    public void testStopSoftApWithoutPermissionThrowsException() throws Exception {
666        doThrow(new SecurityException()).when(mContext)
667                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK),
668                                                eq("WifiService"));
669        mWifiServiceImpl.stopSoftAp();
670    }
671
672    /**
673     * Ensure foreground apps can always do wifi scans.
674     */
675    @Test
676    public void testWifiScanStartedForeground() {
677        when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
678                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
679        mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
680        verify(mWifiStateMachine).startScan(
681                anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
682    }
683
684    /**
685     * Ensure background apps get throttled when the previous scan is too close.
686     */
687    @Test
688    public void testWifiScanBackgroundThrottled() {
689        when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
690                ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
691        long startMs = 1000;
692        when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
693        mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
694        verify(mWifiStateMachine).startScan(
695                anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
696
697        when(mClock.getElapsedSinceBootMillis()).thenReturn(
698                startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
699        mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
700        verify(mWifiStateMachine, times(1)).startScan(
701                anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
702    }
703
704    /**
705     * Ensure background apps can do wifi scan when the throttle interval reached.
706     */
707    @Test
708    public void testWifiScanBackgroundNotThrottled() {
709        when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
710                ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
711        long startMs = 1000;
712        when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
713        mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
714        verify(mWifiStateMachine).startScan(
715                anyInt(), eq(0), (ScanSettings) eq(null), any(WorkSource.class));
716
717        when(mClock.getElapsedSinceBootMillis()).thenReturn(
718                startMs + WIFI_BACKGROUND_SCAN_INTERVAL + 1000);
719        mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
720        verify(mWifiStateMachine).startScan(
721                anyInt(), eq(1), (ScanSettings) eq(null), any(WorkSource.class));
722    }
723
724    /**
725     * Ensure background apps can do wifi scan when the throttle interval reached.
726     */
727    @Test
728    public void testWifiScanBackgroundWhiteListed() {
729        when(mActivityManager.getPackageImportance(WHITE_LIST_SCAN_PACKAGE_NAME)).thenReturn(
730                ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
731        long startMs = 1000;
732        when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
733        mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
734        verify(mWifiStateMachine).startScan(
735                anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
736
737        when(mClock.getElapsedSinceBootMillis()).thenReturn(
738                startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
739        mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
740        verify(mWifiStateMachine, times(2)).startScan(
741                anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
742    }
743
744    private void registerLOHSRequestFull() {
745        // allow test to proceed without a permission check failure
746        when(mSettingsStore.getLocationModeSetting(mContext))
747                .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
748        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING))
749                .thenReturn(false);
750        int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder,
751                TEST_PACKAGE_NAME);
752        assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
753    }
754
755    /**
756     * Verify that the call to startLocalOnlyHotspot returns REQUEST_REGISTERED when successfully
757     * called.
758     */
759    @Test
760    public void testStartLocalOnlyHotspotSingleRegistrationReturnsRequestRegistered() {
761        registerLOHSRequestFull();
762    }
763
764    /**
765     * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not
766     * have the CHANGE_WIFI_STATE permission.
767     */
768    @Test(expected = SecurityException.class)
769    public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() {
770        doThrow(new SecurityException()).when(mContext)
771                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
772                                                eq("WifiService"));
773        mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
774    }
775
776    /**
777     * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not
778     * have Location permission.
779     */
780    @Test(expected = SecurityException.class)
781    public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationPermission() {
782        when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME);
783        doThrow(new SecurityException())
784                .when(mWifiPermissionsUtil).enforceLocationPermission(eq(TEST_PACKAGE_NAME),
785                                                                      anyInt());
786        mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
787    }
788
789    /**
790     * Verify that a call to startLocalOnlyHotspot throws a SecurityException if Location mode is
791     * disabled.
792     */
793    @Test(expected = SecurityException.class)
794    public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationEnabled() {
795        when(mSettingsStore.getLocationModeSetting(mContext)).thenReturn(LOCATION_MODE_OFF);
796        mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
797    }
798
799    /**
800     * Only start LocalOnlyHotspot if we are not tethering.
801     */
802    @Test
803    public void testHotspotDoesNotStartWhenAlreadyTethering() {
804        when(mSettingsStore.getLocationModeSetting(mContext))
805                            .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
806        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
807        mLooper.dispatchAll();
808        int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(
809                mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
810        assertEquals(ERROR_INCOMPATIBLE_MODE, returnCode);
811    }
812
813    /**
814     * Only start LocalOnlyHotspot if admin setting does not disallow tethering.
815     */
816    @Test
817    public void testHotspotDoesNotStartWhenTetheringDisallowed() {
818        when(mSettingsStore.getLocationModeSetting(mContext))
819                .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
820        when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING))
821                .thenReturn(true);
822        int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(
823                mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
824        assertEquals(ERROR_TETHERING_DISALLOWED, returnCode);
825    }
826
827    /**
828     * Verify that callers can only have one registered LOHS request.
829     */
830    @Test(expected = IllegalStateException.class)
831    public void testStartLocalOnlyHotspotThrowsExceptionWhenCallerAlreadyRegistered() {
832        registerLOHSRequestFull();
833
834        // now do the second request that will fail
835        mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
836    }
837
838    /**
839     * Verify that the call to stopLocalOnlyHotspot does not do anything when there aren't any
840     * registered callers.
841     */
842    @Test
843    public void testStopLocalOnlyHotspotDoesNothingWithoutRegisteredRequests() {
844        // allow test to proceed without a permission check failure
845        mWifiServiceImpl.stopLocalOnlyHotspot();
846        // there is nothing registered, so this shouldn't do anything
847        verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
848    }
849
850    /**
851     * Verify that the call to stopLocalOnlyHotspot does not do anything when one caller unregisters
852     * but there is still an active request
853     */
854    @Test
855    public void testStopLocalOnlyHotspotDoesNothingWithARemainingRegisteredRequest() {
856        // register a request that will remain after the stopLOHS call
857        mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
858
859        registerLOHSRequestFull();
860
861        // Since we are calling with the same pid, the second register call will be removed
862        mWifiServiceImpl.stopLocalOnlyHotspot();
863        // there is still a valid registered request - do not tear down LOHS
864        verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
865    }
866
867    /**
868     * Verify that the call to stopLocalOnlyHotspot sends a message to WifiController to stop
869     * the softAp when there is one registered caller when that caller is removed.
870     */
871    @Test
872    public void testStopLocalOnlyHotspotTriggersSoftApStopWithOneRegisteredRequest() {
873        registerLOHSRequestFull();
874        verify(mWifiController)
875                .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(WifiConfiguration.class));
876
877        mWifiServiceImpl.stopLocalOnlyHotspot();
878        // there is was only one request registered, we should tear down softap
879        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
880    }
881
882    /**
883     * Verify that a call to stopLocalOnlyHotspot throws a SecurityException if the caller does not
884     * have the CHANGE_WIFI_STATE permission.
885     */
886    @Test(expected = SecurityException.class)
887    public void testStopLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() {
888        doThrow(new SecurityException()).when(mContext)
889                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
890                                                eq("WifiService"));
891        mWifiServiceImpl.stopLocalOnlyHotspot();
892    }
893
894    /**
895     * Verify that WifiServiceImpl does not send the stop ap message if there were no
896     * pending LOHS requests upon a binder death callback.
897     */
898    @Test
899    public void testServiceImplNotCalledWhenBinderDeathTriggeredNoRequests() {
900        LocalOnlyRequestorCallback binderDeathCallback =
901                mWifiServiceImpl.new LocalOnlyRequestorCallback();
902
903        binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo);
904        verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
905    }
906
907    /**
908     * Verify that WifiServiceImpl does not send the stop ap message if there are remaining
909     * registered LOHS requests upon a binder death callback.  Additionally verify that softap mode
910     * will be stopped if that remaining request is removed (to verify the binder death properly
911     * cleared the requestor that died).
912     */
913    @Test
914    public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRegisteredRequests() {
915        LocalOnlyRequestorCallback binderDeathCallback =
916                mWifiServiceImpl.new LocalOnlyRequestorCallback();
917
918        // registering a request directly from the test will not trigger a message to start
919        // softap mode
920        mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
921
922        registerLOHSRequestFull();
923
924        binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo);
925        verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
926
927        reset(mWifiController);
928
929        // now stop as the second request and confirm CMD_SET_AP will be sent to make sure binder
930        // death requestor was removed
931        mWifiServiceImpl.stopLocalOnlyHotspot();
932        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
933    }
934
935    private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
936        @Override
937        public boolean matches(IntentFilter filter) {
938            return filter.hasAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
939        }
940    }
941
942    /**
943     * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
944     * broadcast is received.
945     */
946    @Test
947    public void testRegisteredCallbacksTriggeredOnSoftApFailureGeneric() throws Exception {
948        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
949        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
950        mWifiServiceImpl.checkAndStartWifi();
951
952        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
953                (IntentFilter) argThat(new IntentFilterMatcher()));
954
955        registerLOHSRequestFull();
956
957        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
958                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL);
959        mLooper.dispatchAll();
960        verify(mHandler).handleMessage(mMessageCaptor.capture());
961        Message message = mMessageCaptor.getValue();
962        assertEquals(HOTSPOT_FAILED, message.what);
963        assertEquals(ERROR_GENERIC, message.arg1);
964    }
965
966    /**
967     * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
968     * broadcast is received with the SAP_START_FAILURE_NO_CHANNEL error.
969     */
970    @Test
971    public void testRegisteredCallbacksTriggeredOnSoftApFailureNoChannel() throws Exception {
972        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
973        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
974        mWifiServiceImpl.checkAndStartWifi();
975
976        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
977                (IntentFilter) argThat(new IntentFilterMatcher()));
978
979        registerLOHSRequestFull();
980
981        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
982                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL);
983
984        mLooper.dispatchAll();
985        verify(mHandler).handleMessage(mMessageCaptor.capture());
986        Message message = mMessageCaptor.getValue();
987        assertEquals(HOTSPOT_FAILED, message.what);
988        assertEquals(ERROR_NO_CHANNEL, message.arg1);
989    }
990
991    /**
992     * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
993     * broadcast is received with WIFI_AP_STATE_DISABLING and LOHS was active.
994     */
995    @Test
996    public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception {
997        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
998        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
999        mWifiServiceImpl.checkAndStartWifi();
1000
1001        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1002                (IntentFilter) argThat(new IntentFilterMatcher()));
1003
1004        registerLOHSRequestFull();
1005
1006        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1007        mLooper.dispatchAll();
1008        verify(mHandler).handleMessage(mMessageCaptor.capture());
1009        Message message = mMessageCaptor.getValue();
1010        assertEquals(HOTSPOT_STARTED, message.what);
1011        reset(mHandler);
1012
1013        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1014                WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR);
1015
1016        mLooper.dispatchAll();
1017        verify(mHandler).handleMessage(mMessageCaptor.capture());
1018        message = mMessageCaptor.getValue();
1019        assertEquals(HOTSPOT_STOPPED, message.what);
1020    }
1021
1022
1023    /**
1024     * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
1025     * broadcast is received with WIFI_AP_STATE_DISABLED and LOHS was enabled.
1026     */
1027    @Test
1028    public void testRegisteredCallbacksTriggeredOnSoftApDisabled() throws Exception {
1029        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1030        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1031        mWifiServiceImpl.checkAndStartWifi();
1032
1033        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1034                (IntentFilter) argThat(new IntentFilterMatcher()));
1035
1036        registerLOHSRequestFull();
1037
1038        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1039        mLooper.dispatchAll();
1040        verify(mHandler).handleMessage(mMessageCaptor.capture());
1041        Message message = mMessageCaptor.getValue();
1042        assertEquals(HOTSPOT_STARTED, message.what);
1043        reset(mHandler);
1044
1045        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1046                WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR);
1047
1048        mLooper.dispatchAll();
1049        verify(mHandler).handleMessage(mMessageCaptor.capture());
1050        message = mMessageCaptor.getValue();
1051        assertEquals(HOTSPOT_STOPPED, message.what);
1052    }
1053
1054    /**
1055     * Verify that no callbacks are called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
1056     * broadcast is received and the softap started.
1057     */
1058    @Test
1059    public void testRegisteredCallbacksNotTriggeredOnSoftApStart() throws Exception {
1060        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1061        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1062        mWifiServiceImpl.checkAndStartWifi();
1063
1064        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1065                (IntentFilter) argThat(new IntentFilterMatcher()));
1066
1067        registerLOHSRequestFull();
1068
1069        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1070                WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR);
1071
1072        mLooper.dispatchAll();
1073        verifyNoMoreInteractions(mHandler);
1074    }
1075
1076    /**
1077     * Verify that onStopped is called only once for registered LOHS callers when
1078     * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLING and
1079     * WIFI_AP_STATE_DISABLED when LOHS was enabled.
1080     */
1081    @Test
1082    public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApDisabling() throws Exception {
1083        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1084        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1085        mWifiServiceImpl.checkAndStartWifi();
1086
1087        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1088                (IntentFilter) argThat(new IntentFilterMatcher()));
1089
1090        registerLOHSRequestFull();
1091
1092        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1093        mLooper.dispatchAll();
1094        verify(mHandler).handleMessage(mMessageCaptor.capture());
1095        Message message = mMessageCaptor.getValue();
1096        assertEquals(HOTSPOT_STARTED, message.what);
1097        reset(mHandler);
1098
1099        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1100                WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR);
1101        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1102                WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR);
1103
1104        mLooper.dispatchAll();
1105        verify(mHandler).handleMessage(mMessageCaptor.capture());
1106        message = mMessageCaptor.getValue();
1107        assertEquals(HOTSPOT_STOPPED, message.what);
1108    }
1109
1110    /**
1111     * Verify that onFailed is called only once for registered LOHS callers when
1112     * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED twice.
1113     */
1114    @Test
1115    public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApFailsTwice() throws Exception {
1116        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1117        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1118        mWifiServiceImpl.checkAndStartWifi();
1119
1120        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1121                (IntentFilter) argThat(new IntentFilterMatcher()));
1122
1123        registerLOHSRequestFull();
1124
1125        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1126                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC);
1127        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1128                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC);
1129
1130        mLooper.dispatchAll();
1131        verify(mHandler).handleMessage(mMessageCaptor.capture());
1132        Message message = mMessageCaptor.getValue();
1133        assertEquals(HOTSPOT_FAILED, message.what);
1134        assertEquals(ERROR_GENERIC, message.arg1);
1135    }
1136
1137    /**
1138     * Verify that onFailed is called for all registered LOHS callers when
1139     * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED.
1140     */
1141    @Test
1142    public void testAllRegisteredCallbacksTriggeredWhenSoftApFails() throws Exception {
1143        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1144        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1145        mWifiServiceImpl.checkAndStartWifi();
1146
1147        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1148                (IntentFilter) argThat(new IntentFilterMatcher()));
1149
1150        // make an additional request for this test
1151        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1152
1153        registerLOHSRequestFull();
1154
1155        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1156                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC);
1157        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1158                WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC);
1159
1160        verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC);
1161        mLooper.dispatchAll();
1162        verify(mHandler).handleMessage(mMessageCaptor.capture());
1163        Message message = mMessageCaptor.getValue();
1164        assertEquals(HOTSPOT_FAILED, message.what);
1165        assertEquals(ERROR_GENERIC, message.arg1);
1166    }
1167
1168    /**
1169     * Verify that onStopped is called for all registered LOHS callers when
1170     * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was
1171     * active.
1172     */
1173    @Test
1174    public void testAllRegisteredCallbacksTriggeredWhenSoftApStops() throws Exception {
1175        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1176        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1177        mWifiServiceImpl.checkAndStartWifi();
1178
1179        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1180                (IntentFilter) argThat(new IntentFilterMatcher()));
1181
1182        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1183
1184        registerLOHSRequestFull();
1185
1186        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1187        mLooper.dispatchAll();
1188        verify(mRequestInfo).sendHotspotStartedMessage(any());
1189        verify(mHandler).handleMessage(mMessageCaptor.capture());
1190        Message message = mMessageCaptor.getValue();
1191        assertEquals(HOTSPOT_STARTED, message.what);
1192        reset(mHandler);
1193
1194        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1195                WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR);
1196        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1197                WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR);
1198
1199        verify(mRequestInfo).sendHotspotStoppedMessage();
1200        mLooper.dispatchAll();
1201        verify(mHandler).handleMessage(mMessageCaptor.capture());
1202        message = mMessageCaptor.getValue();
1203        assertEquals(HOTSPOT_STOPPED, message.what);
1204    }
1205
1206    /**
1207     * Verify that onFailed is called for all registered LOHS callers when
1208     * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was
1209     * not active.
1210     */
1211    @Test
1212    public void testAllRegisteredCallbacksTriggeredWhenSoftApStopsLOHSNotActive() throws Exception {
1213        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1214        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1215        mWifiServiceImpl.checkAndStartWifi();
1216
1217        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1218                (IntentFilter) argThat(new IntentFilterMatcher()));
1219
1220        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1221        mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2);
1222
1223        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1224                WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR);
1225        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1226                WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR);
1227
1228        verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC);
1229        verify(mRequestInfo2).sendHotspotFailedMessage(ERROR_GENERIC);
1230    }
1231
1232    /**
1233     * Verify that if we do not have registered LOHS requestors and we receive an update that LOHS
1234     * is up and ready for use, we tell WifiController to tear it down.  This can happen if softap
1235     * mode fails to come up properly and we get an onFailed message for a tethering call and we
1236     * had registered callers for LOHS.
1237     */
1238    @Test
1239    public void testLOHSReadyWithoutRegisteredRequestsStopsSoftApMode() {
1240        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1241        mLooper.dispatchAll();
1242
1243        verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
1244    }
1245
1246    /**
1247     * Verify that all registered LOHS requestors are notified via a HOTSPOT_STARTED message that
1248     * the hotspot is up and ready to use.
1249     */
1250    @Test
1251    public void testRegisteredLocalOnlyHotspotRequestorsGetOnStartedCallbackWhenReady()
1252            throws Exception {
1253        registerLOHSRequestFull();
1254
1255        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1256
1257        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1258        mLooper.dispatchAll();
1259        verify(mRequestInfo).sendHotspotStartedMessage(any(WifiConfiguration.class));
1260
1261        mLooper.dispatchAll();
1262        verify(mHandler).handleMessage(mMessageCaptor.capture());
1263        Message message = mMessageCaptor.getValue();
1264        assertEquals(HOTSPOT_STARTED, message.what);
1265        assertNotNull((WifiConfiguration) message.obj);
1266    }
1267
1268    /**
1269     * Verify that if a LOHS is already active, a new call to register a request will trigger the
1270     * onStarted callback.
1271     */
1272    @Test
1273    public void testRegisterLocalOnlyHotspotRequestAfterAlreadyStartedGetsOnStartedCallback()
1274            throws Exception {
1275        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1276
1277        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1278        mLooper.dispatchAll();
1279
1280        registerLOHSRequestFull();
1281
1282        mLooper.dispatchAll();
1283
1284        verify(mHandler).handleMessage(mMessageCaptor.capture());
1285        Message message = mMessageCaptor.getValue();
1286        assertEquals(HOTSPOT_STARTED, message.what);
1287        // since the first request was registered out of band, the config will be null
1288        assertNull((WifiConfiguration) message.obj);
1289    }
1290
1291    /**
1292     * Verify that if a LOHS request is active and we receive an update with an ip mode
1293     * configuration error, callers are notified via the onFailed callback with the generic
1294     * error and are unregistered.
1295     */
1296    @Test
1297    public void testCallOnFailedLocalOnlyHotspotRequestWhenIpConfigFails() throws Exception {
1298        registerLOHSRequestFull();
1299
1300        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR);
1301        mLooper.dispatchAll();
1302
1303        verify(mHandler).handleMessage(mMessageCaptor.capture());
1304        Message message = mMessageCaptor.getValue();
1305        assertEquals(HOTSPOT_FAILED, message.what);
1306        assertEquals(ERROR_GENERIC, message.arg1);
1307
1308        // sendMessage should only happen once since the requestor should be unregistered
1309        reset(mHandler);
1310
1311        // send HOTSPOT_FAILED message should only happen once since the requestor should be
1312        // unregistered
1313        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR);
1314        mLooper.dispatchAll();
1315        verify(mHandler, never()).handleMessage(any(Message.class));
1316    }
1317
1318    /**
1319     * Verify that if a LOHS request is active and tethering starts, callers are notified on the
1320     * incompatible mode and are unregistered.
1321     */
1322    @Test
1323    public void testCallOnFailedLocalOnlyHotspotRequestWhenTetheringStarts() throws Exception {
1324        registerLOHSRequestFull();
1325
1326        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
1327        mLooper.dispatchAll();
1328
1329        verify(mHandler).handleMessage(mMessageCaptor.capture());
1330        Message message = mMessageCaptor.getValue();
1331        assertEquals(HOTSPOT_FAILED, message.what);
1332        assertEquals(ERROR_INCOMPATIBLE_MODE, message.arg1);
1333
1334        // sendMessage should only happen once since the requestor should be unregistered
1335        reset(mHandler);
1336
1337        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
1338        mLooper.dispatchAll();
1339        verify(mHandler, never()).handleMessage(any(Message.class));
1340    }
1341
1342    /**
1343     * Verify that if LOHS is disabled, a new call to register a request will not trigger the
1344     * onStopped callback.
1345     */
1346    @Test
1347    public void testRegisterLocalOnlyHotspotRequestWhenStoppedDoesNotGetOnStoppedCallback()
1348            throws Exception {
1349        registerLOHSRequestFull();
1350        mLooper.dispatchAll();
1351
1352        verify(mHandler, never()).handleMessage(any(Message.class));
1353    }
1354
1355    /**
1356     * Verify that if a LOHS was active and then stopped, a new call to register a request will
1357     * not trigger the onStarted callback.
1358     */
1359    @Test
1360    public void testRegisterLocalOnlyHotspotRequestAfterStoppedNoOnStartedCallback()
1361            throws Exception {
1362        when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
1363        when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
1364        mWifiServiceImpl.checkAndStartWifi();
1365        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
1366                (IntentFilter) argThat(new IntentFilterMatcher()));
1367
1368        // register a request so we don't drop the LOHS interface ip update
1369        mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
1370
1371        mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
1372        mLooper.dispatchAll();
1373
1374        registerLOHSRequestFull();
1375        mLooper.dispatchAll();
1376
1377        verify(mHandler).handleMessage(mMessageCaptor.capture());
1378        assertEquals(HOTSPOT_STARTED, mMessageCaptor.getValue().what);
1379
1380        reset(mHandler);
1381
1382        // now stop the hotspot
1383        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1384                WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR);
1385        TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
1386                WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR);
1387        mLooper.dispatchAll();
1388        verify(mHandler).handleMessage(mMessageCaptor.capture());
1389        assertEquals(HOTSPOT_STOPPED, mMessageCaptor.getValue().what);
1390
1391        reset(mHandler);
1392
1393        // now register a new caller - they should not get the onStarted callback
1394        Messenger messenger2 = new Messenger(mHandler);
1395        IBinder binder2 = mock(IBinder.class);
1396
1397        int result = mWifiServiceImpl.startLocalOnlyHotspot(messenger2, binder2, TEST_PACKAGE_NAME);
1398        assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
1399        mLooper.dispatchAll();
1400
1401        verify(mHandler, never()).handleMessage(any(Message.class));
1402    }
1403
1404    /**
1405     * Verify that a call to startWatchLocalOnlyHotspot is only allowed from callers with the
1406     * signature only NETWORK_SETTINGS permission.
1407     *
1408     * This test is expecting the permission check to enforce the permission and throw a
1409     * SecurityException for callers without the permission.  This exception should be bubbled up to
1410     * the caller of startLocalOnlyHotspot.
1411     */
1412    @Test(expected = SecurityException.class)
1413    public void testStartWatchLocalOnlyHotspotNotApprovedCaller() {
1414        doThrow(new SecurityException()).when(mContext)
1415                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
1416                                                eq("WifiService"));
1417        mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder);
1418    }
1419
1420    /**
1421     * Verify that the call to startWatchLocalOnlyHotspot throws the UnsupportedOperationException
1422     * when called until the implementation is complete.
1423     */
1424    @Test(expected = UnsupportedOperationException.class)
1425    public void testStartWatchLocalOnlyHotspotNotSupported() {
1426        mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder);
1427    }
1428
1429    /**
1430     * Verify that a call to stopWatchLocalOnlyHotspot is only allowed from callers with the
1431     * signature only NETWORK_SETTINGS permission.
1432     */
1433    @Test(expected = SecurityException.class)
1434    public void testStopWatchLocalOnlyHotspotNotApprovedCaller() {
1435        doThrow(new SecurityException()).when(mContext)
1436                .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
1437                                                eq("WifiService"));
1438        mWifiServiceImpl.stopWatchLocalOnlyHotspot();
1439    }
1440
1441    /**
1442     * Verify that the call to stopWatchLocalOnlyHotspot throws the UnsupportedOperationException
1443     * until the implementation is complete.
1444     */
1445    @Test(expected = UnsupportedOperationException.class)
1446    public void testStopWatchLocalOnlyHotspotNotSupported() {
1447        mWifiServiceImpl.stopWatchLocalOnlyHotspot();
1448    }
1449}
1450