WifiStateMachineTest.java revision f91a96259d1fbdfb9af907dd20cf905a8da3a119
1/*
2 * Copyright (C) 2015 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 org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertTrue;
21import static org.mockito.Mockito.*;
22
23import android.app.ActivityManager;
24import android.app.test.MockAnswerUtil.AnswerWithArguments;
25import android.app.test.TestAlarmManager;
26import android.content.Context;
27import android.content.pm.PackageManager;
28import android.content.pm.UserInfo;
29import android.content.res.Resources;
30import android.net.ConnectivityManager;
31import android.net.DhcpResults;
32import android.net.LinkProperties;
33import android.net.dhcp.DhcpClient;
34import android.net.ip.IpManager;
35import android.net.wifi.IApInterface;
36import android.net.wifi.IClientInterface;
37import android.net.wifi.IWificond;
38import android.net.wifi.ScanResult;
39import android.net.wifi.SupplicantState;
40import android.net.wifi.WifiConfiguration;
41import android.net.wifi.WifiInfo;
42import android.net.wifi.WifiManager;
43import android.net.wifi.WifiScanner;
44import android.net.wifi.WifiSsid;
45import android.net.wifi.p2p.IWifiP2pManager;
46import android.os.BatteryStats;
47import android.os.Binder;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.HandlerThread;
51import android.os.IBinder;
52import android.os.IInterface;
53import android.os.INetworkManagementService;
54import android.os.IPowerManager;
55import android.os.Looper;
56import android.os.Message;
57import android.os.Messenger;
58import android.os.PowerManager;
59import android.os.RemoteException;
60import android.os.UserHandle;
61import android.os.UserManager;
62import android.os.test.TestLooper;
63import android.provider.Settings;
64import android.security.KeyStore;
65import android.test.mock.MockContentProvider;
66import android.test.mock.MockContentResolver;
67import android.test.suitebuilder.annotation.SmallTest;
68import android.util.Log;
69
70import com.android.internal.R;
71import com.android.internal.app.IBatteryStats;
72import com.android.internal.util.AsyncChannel;
73import com.android.internal.util.IState;
74import com.android.internal.util.StateMachine;
75import com.android.server.wifi.hotspot2.NetworkDetail;
76import com.android.server.wifi.p2p.WifiP2pServiceImpl;
77
78import org.junit.After;
79import org.junit.Before;
80import org.junit.Test;
81import org.mockito.ArgumentCaptor;
82import org.mockito.Mock;
83import org.mockito.MockitoAnnotations;
84
85import java.io.ByteArrayOutputStream;
86import java.io.PrintWriter;
87import java.lang.reflect.Field;
88import java.lang.reflect.InvocationTargetException;
89import java.lang.reflect.Method;
90import java.util.ArrayList;
91import java.util.Arrays;
92import java.util.HashSet;
93import java.util.List;
94import java.util.Set;
95import java.util.concurrent.CountDownLatch;
96
97/**
98 * Unit tests for {@link com.android.server.wifi.WifiStateMachine}.
99 */
100@SmallTest
101public class WifiStateMachineTest {
102    public static final String TAG = "WifiStateMachineTest";
103
104    private static final int MANAGED_PROFILE_UID = 1100000;
105    private static final int OTHER_USER_UID = 1200000;
106    private static final int LOG_REC_LIMIT_IN_VERBOSE_MODE =
107            (ActivityManager.isLowRamDeviceStatic()
108                    ? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
109                    : WifiStateMachine.NUM_LOG_RECS_VERBOSE);
110    private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
111
112    private long mBinderToken;
113
114    private static <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) {
115        return mock(class1, withSettings().extraInterfaces(interfaces));
116    }
117
118    private static <T, I> IBinder mockService(Class<T> class1, Class<I> iface) {
119        T tImpl = mockWithInterfaces(class1, iface);
120        IBinder binder = mock(IBinder.class);
121        when(((IInterface) tImpl).asBinder()).thenReturn(binder);
122        when(binder.queryLocalInterface(iface.getCanonicalName()))
123                .thenReturn((IInterface) tImpl);
124        return binder;
125    }
126
127    private void enableDebugLogs() {
128        mWsm.enableVerboseLogging(1);
129    }
130
131    private class TestIpManager extends IpManager {
132        TestIpManager(Context context, String ifname, IpManager.Callback callback) {
133            // Call dependency-injection superclass constructor.
134            super(context, ifname, callback, mock(INetworkManagementService.class));
135        }
136
137        @Override
138        public void startProvisioning(IpManager.ProvisioningConfiguration config) {}
139
140        @Override
141        public void stop() {}
142
143        @Override
144        public void confirmConfiguration() {}
145
146        void injectDhcpSuccess(DhcpResults dhcpResults) {
147            mCallback.onNewDhcpResults(dhcpResults);
148            mCallback.onProvisioningSuccess(new LinkProperties());
149        }
150
151        void injectDhcpFailure() {
152            mCallback.onNewDhcpResults(null);
153            mCallback.onProvisioningFailure(new LinkProperties());
154        }
155    }
156
157    private FrameworkFacade getFrameworkFacade() throws Exception {
158        FrameworkFacade facade = mock(FrameworkFacade.class);
159
160        when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn(
161                mockWithInterfaces(IBinder.class, INetworkManagementService.class));
162
163        IBinder p2pBinder = mockService(WifiP2pServiceImpl.class, IWifiP2pManager.class);
164        when(facade.getService(Context.WIFI_P2P_SERVICE)).thenReturn(p2pBinder);
165
166        WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface(
167                IWifiP2pManager.class.getCanonicalName());
168
169        final CountDownLatch untilDone = new CountDownLatch(1);
170        mP2pThread = new HandlerThread("WifiP2pMockThread") {
171            @Override
172            protected void onLooperPrepared() {
173                untilDone.countDown();
174            }
175        };
176
177        mP2pThread.start();
178        untilDone.await();
179
180        Handler handler = new Handler(mP2pThread.getLooper());
181        when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler));
182
183        IBinder batteryStatsBinder = mockService(BatteryStats.class, IBatteryStats.class);
184        when(facade.getService(BatteryStats.SERVICE_NAME)).thenReturn(batteryStatsBinder);
185
186        when(facade.makeIpManager(any(Context.class), anyString(), any(IpManager.Callback.class)))
187                .then(new AnswerWithArguments() {
188                    public IpManager answer(
189                            Context context, String ifname, IpManager.Callback callback) {
190                        mTestIpManager = new TestIpManager(context, ifname, callback);
191                        return mTestIpManager;
192                    }
193                });
194
195        when(facade.checkUidPermission(eq(android.Manifest.permission.OVERRIDE_WIFI_CONFIG),
196                anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
197        return facade;
198    }
199
200    private Context getContext() throws Exception {
201        PackageManager pkgMgr = mock(PackageManager.class);
202        when(pkgMgr.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true);
203
204        Context context = mock(Context.class);
205        when(context.getPackageManager()).thenReturn(pkgMgr);
206
207        MockResources resources = new com.android.server.wifi.MockResources();
208        when(context.getResources()).thenReturn(resources);
209
210        MockContentResolver mockContentResolver = new MockContentResolver();
211        mockContentResolver.addProvider(Settings.AUTHORITY,
212                new MockContentProvider(context) {
213                    @Override
214                    public Bundle call(String method, String arg, Bundle extras) {
215                        return new Bundle();
216                    }
217                });
218        when(context.getContentResolver()).thenReturn(mockContentResolver);
219
220        when(context.getSystemService(Context.POWER_SERVICE)).thenReturn(
221                new PowerManager(context, mock(IPowerManager.class), new Handler()));
222
223        mAlarmManager = new TestAlarmManager();
224        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
225                mAlarmManager.getAlarmManager());
226
227        when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
228                mock(ConnectivityManager.class));
229
230        return context;
231    }
232
233    private Resources getMockResources() {
234        MockResources resources = new MockResources();
235        resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false);
236        return resources;
237    }
238
239    private IState getCurrentState() throws
240            NoSuchMethodException, InvocationTargetException, IllegalAccessException {
241        Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
242        method.setAccessible(true);
243        return (IState) method.invoke(mWsm);
244    }
245
246    private static HandlerThread getWsmHandlerThread(WifiStateMachine wsm) throws
247            NoSuchFieldException, InvocationTargetException, IllegalAccessException {
248        Field field = StateMachine.class.getDeclaredField("mSmThread");
249        field.setAccessible(true);
250        return (HandlerThread) field.get(wsm);
251    }
252
253    private static void stopLooper(final Looper looper) throws Exception {
254        new Handler(looper).post(new Runnable() {
255            @Override
256            public void run() {
257                looper.quitSafely();
258            }
259        });
260    }
261
262    private void dumpState() {
263        ByteArrayOutputStream stream = new ByteArrayOutputStream();
264        PrintWriter writer = new PrintWriter(stream);
265        mWsm.dump(null, writer, null);
266        writer.flush();
267        Log.d(TAG, "WifiStateMachine state -" + stream.toString());
268    }
269
270    private static ScanDetail getGoogleGuestScanDetail(int rssi) {
271        ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1];
272        ie[0] = ScanResults.generateSsidIe(sSSID);
273        NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq);
274        ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq,
275                Long.MAX_VALUE, /* needed so that scan results aren't rejected because
276                                   there older than scan start */
277                ie, new ArrayList<String>());
278        return detail;
279    }
280
281    private ArrayList<ScanDetail> getMockScanResults() {
282        ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825);
283        ArrayList<ScanDetail> list = sr.getScanDetailArrayList();
284
285        int rssi = -65;
286        list.add(getGoogleGuestScanDetail(rssi));
287        return list;
288    }
289
290    static final String   sSSID = "\"GoogleGuest\"";
291    static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
292    static final String   sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", "");
293    static final String   sBSSID = "01:02:03:04:05:06";
294    static final int      sFreq = 2437;
295
296    WifiStateMachine mWsm;
297    HandlerThread mWsmThread;
298    HandlerThread mP2pThread;
299    HandlerThread mSyncThread;
300    AsyncChannel  mWsmAsyncChannel;
301    TestAlarmManager mAlarmManager;
302    MockWifiMonitor mWifiMonitor;
303    TestIpManager mTestIpManager;
304    TestLooper mLooper;
305
306    @Mock WifiScanner mWifiScanner;
307    @Mock SupplicantStateTracker mSupplicantStateTracker;
308    @Mock WifiMetrics mWifiMetrics;
309    @Mock UserManager mUserManager;
310    @Mock WifiApConfigStore mApConfigStore;
311    @Mock BackupManagerProxy mBackupManagerProxy;
312    @Mock WifiCountryCode mCountryCode;
313    @Mock WifiInjector mWifiInjector;
314    @Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
315    @Mock PropertyService mPropertyService;
316    @Mock BuildProperties mBuildProperties;
317    @Mock IWificond mWificond;
318    @Mock IApInterface mApInterface;
319    @Mock IClientInterface mClientInterface;
320    @Mock IBinder mApInterfaceBinder;
321    @Mock IBinder mClientInterfaceBinder;
322    @Mock WifiConfigManager mWifiConfigManager;
323    @Mock WifiNative mWifiNative;
324    @Mock WifiConnectivityManager mWifiConnectivityManager;
325    @Mock SoftApManager mSoftApManager;
326    @Mock WifiStateTracker mWifiStateTracker;
327
328    public WifiStateMachineTest() throws Exception {
329    }
330
331    @Before
332    public void setUp() throws Exception {
333        Log.d(TAG, "Setting up ...");
334
335        // Ensure looper exists
336        mLooper = new TestLooper();
337
338        MockitoAnnotations.initMocks(this);
339
340        /** uncomment this to enable logs from WifiStateMachines */
341        // enableDebugLogs();
342
343        mWifiMonitor = new MockWifiMonitor();
344        when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
345        when(mWifiInjector.getClock()).thenReturn(new Clock());
346        when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
347        when(mWifiInjector.getPropertyService()).thenReturn(mPropertyService);
348        when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
349        when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
350        when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
351        when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(
352                mock(BaseWifiDiagnostics.class));
353        when(mWifiInjector.makeWificond()).thenReturn(mWificond);
354        when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
355        when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
356        when(mWifiInjector.getWifiNetworkSelector()).thenReturn(mock(WifiNetworkSelector.class));
357        when(mWifiInjector.makeWifiConnectivityManager(any(WifiInfo.class), anyBoolean()))
358                .thenReturn(mWifiConnectivityManager);
359        when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
360                any(SoftApManager.Listener.class), any(IApInterface.class),
361                any(WifiConfiguration.class)))
362                .thenReturn(mSoftApManager);
363
364        when(mWifiNative.setupDriverForClientMode()).thenReturn(mClientInterface);
365        when(mWifiNative.setupDriverForSoftApMode()).thenReturn(mApInterface);
366        when(mWifiInjector.getWifiStateTracker()).thenReturn(mWifiStateTracker);
367        when(mWifiNative.getInterfaceName()).thenReturn("mockWlan");
368        when(mWifiNative.enableSupplicant()).thenReturn(true);
369        when(mWifiNative.disableSupplicant()).thenReturn(true);
370        when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0);
371
372
373        FrameworkFacade factory = getFrameworkFacade();
374        Context context = getContext();
375
376        Resources resources = getMockResources();
377        when(context.getResources()).thenReturn(resources);
378
379        when(factory.getIntegerSetting(context,
380                Settings.Global.WIFI_FREQUENCY_BAND,
381                WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
382                WifiManager.WIFI_FREQUENCY_BAND_AUTO);
383
384        when(factory.makeApConfigStore(eq(context), eq(mBackupManagerProxy)))
385                .thenReturn(mApConfigStore);
386
387        when(factory.makeSupplicantStateTracker(
388                any(Context.class), any(WifiConfigManager.class),
389                any(Handler.class))).thenReturn(mSupplicantStateTracker);
390
391        when(mUserManager.getProfileParent(11))
392                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "owner", 0));
393        when(mUserManager.getProfiles(UserHandle.USER_SYSTEM)).thenReturn(Arrays.asList(
394                new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
395                new UserInfo(11, "managed profile", 0)));
396
397        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
398        when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
399
400        mWsm = new WifiStateMachine(context, factory, mLooper.getLooper(),
401            mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative);
402        mWsmThread = getWsmHandlerThread(mWsm);
403
404        final AsyncChannel channel = new AsyncChannel();
405        Handler handler = new Handler(mLooper.getLooper()) {
406            @Override
407            public void handleMessage(Message msg) {
408                switch (msg.what) {
409                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
410                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
411                            mWsmAsyncChannel = channel;
412                        } else {
413                            Log.d(TAG, "Failed to connect Command channel " + this);
414                        }
415                        break;
416                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
417                        Log.d(TAG, "Command channel disconnected" + this);
418                        break;
419                }
420            }
421        };
422
423        channel.connect(context, handler, mWsm.getMessenger());
424        mLooper.dispatchAll();
425        /* Now channel is supposed to be connected */
426
427        mBinderToken = Binder.clearCallingIdentity();
428    }
429
430    @After
431    public void cleanUp() throws Exception {
432        Binder.restoreCallingIdentity(mBinderToken);
433
434        if (mSyncThread != null) stopLooper(mSyncThread.getLooper());
435        if (mWsmThread != null) stopLooper(mWsmThread.getLooper());
436        if (mP2pThread != null) stopLooper(mP2pThread.getLooper());
437
438        mWsmThread = null;
439        mP2pThread = null;
440        mSyncThread = null;
441        mWsmAsyncChannel = null;
442        mWsm = null;
443    }
444
445    @Test
446    public void createNew() throws Exception {
447        assertEquals("InitialState", getCurrentState().getName());
448
449        mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
450        mLooper.dispatchAll();
451        assertEquals("InitialState", getCurrentState().getName());
452    }
453
454    @Test
455    public void loadComponentsInStaMode() throws Exception {
456        startSupplicantAndDispatchMessages();
457
458        assertEquals("DisconnectedState", getCurrentState().getName());
459    }
460
461    @Test
462    public void loadComponentsInApMode() throws Exception {
463        mWsm.setHostApRunning(new WifiConfiguration(), true);
464        mLooper.dispatchAll();
465
466        assertEquals("SoftApState", getCurrentState().getName());
467
468        verify(mSoftApManager).start();
469    }
470
471    @Test
472    public void shouldRequireSupplicantStartupToLeaveInitialState() throws Exception {
473        when(mWifiNative.enableSupplicant()).thenReturn(false);
474        mWsm.setSupplicantRunning(true);
475        mLooper.dispatchAll();
476        assertEquals("InitialState", getCurrentState().getName());
477    }
478
479    @Test
480    public void shouldRequireWificondToLeaveInitialState() throws Exception {
481        // We start out with valid default values, break them going backwards so that
482        // we test all the bailout cases.
483
484        // ClientInterface dies after creation.
485        doThrow(new RemoteException()).when(mClientInterfaceBinder).linkToDeath(any(), anyInt());
486        mWsm.setSupplicantRunning(true);
487        mLooper.dispatchAll();
488        assertEquals("InitialState", getCurrentState().getName());
489
490        // Failed to even create the client interface.
491        when(mWificond.createClientInterface()).thenReturn(null);
492        mWsm.setSupplicantRunning(true);
493        mLooper.dispatchAll();
494        assertEquals("InitialState", getCurrentState().getName());
495
496        // Failed to get wificond proxy.
497        when(mWifiInjector.makeWificond()).thenReturn(null);
498        mWsm.setSupplicantRunning(true);
499        mLooper.dispatchAll();
500        assertEquals("InitialState", getCurrentState().getName());
501    }
502
503    @Test
504    public void loadComponentsFailure() throws Exception {
505        when(mWifiNative.startHal(anyBoolean())).thenReturn(false);
506        when(mWifiNative.enableSupplicant()).thenReturn(false);
507
508        mWsm.setSupplicantRunning(true);
509        mLooper.dispatchAll();
510        assertEquals("InitialState", getCurrentState().getName());
511
512        when(mWifiNative.startHal(anyBoolean())).thenReturn(true);
513        mWsm.setSupplicantRunning(true);
514        mLooper.dispatchAll();
515        assertEquals("InitialState", getCurrentState().getName());
516    }
517
518    @Test
519    public void checkInitialStateStickyWhenDisabledMode() throws Exception {
520        mLooper.dispatchAll();
521        assertEquals("InitialState", getCurrentState().getName());
522        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
523
524        mWsm.setOperationalMode(WifiStateMachine.DISABLED_MODE);
525        mLooper.dispatchAll();
526        assertEquals(WifiStateMachine.DISABLED_MODE, mWsm.getOperationalModeForTest());
527        assertEquals("InitialState", getCurrentState().getName());
528    }
529
530    @Test
531    public void shouldStartSupplicantWhenConnectModeRequested() throws Exception {
532        when(mWifiNative.startHal(anyBoolean())).thenReturn(true);
533
534        // The first time we start out in InitialState, we sit around here.
535        mLooper.dispatchAll();
536        assertEquals("InitialState", getCurrentState().getName());
537        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
538
539        // But if someone tells us to enter connect mode, we start up supplicant
540        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
541        mLooper.dispatchAll();
542        assertEquals("SupplicantStartingState", getCurrentState().getName());
543    }
544
545    /**
546     *  Test that mode changes accurately reflect the value for isWifiEnabled.
547     */
548    @Test
549    public void checkIsWifiEnabledForModeChanges() throws Exception {
550        when(mWifiNative.startHal(anyBoolean())).thenReturn(true);
551
552        // Check initial state
553        mLooper.dispatchAll();
554        assertEquals("InitialState", getCurrentState().getName());
555        assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
556
557        mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
558        startSupplicantAndDispatchMessages();
559        mWsm.setSupplicantRunning(true);
560        mLooper.dispatchAll();
561        assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
562        assertEquals("ScanModeState", getCurrentState().getName());
563        assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
564
565        // switch to connect mode and verify wifi is reported as enabled
566        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
567        mLooper.dispatchAll();
568        assertEquals("DisconnectedState", getCurrentState().getName());
569        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
570        assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
571
572        // now go back to scan mode with "wifi disabled" to verify the reported wifi state.
573        mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
574        mLooper.dispatchAll();
575        assertEquals(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE,
576                     mWsm.getOperationalModeForTest());
577        assertEquals("ScanModeState", getCurrentState().getName());
578        assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
579
580        // now go to AP mode
581        mWsm.setSupplicantRunning(false);
582        mWsm.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
583        mWsm.sendMessage(WifiMonitor.SUP_DISCONNECTION_EVENT);
584        mWsm.setHostApRunning(new WifiConfiguration(), true);
585        mLooper.dispatchAll();
586        assertEquals("SoftApState", getCurrentState().getName());
587        assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
588    }
589
590
591    /**
592     * Test that mode changes for WifiStateMachine in the InitialState are realized when supplicant
593     * is started.
594     */
595    @Test
596    public void checkStartInCorrectStateAfterChangingInitialState() throws Exception {
597        when(mWifiNative.startHal(anyBoolean())).thenReturn(true);
598
599        // Check initial state
600        mLooper.dispatchAll();
601        assertEquals("InitialState", getCurrentState().getName());
602        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
603
604        // Update the mode
605        mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
606        mLooper.dispatchAll();
607        assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
608
609        // Start supplicant so we move to the next state
610        startSupplicantAndDispatchMessages();
611
612        assertEquals("ScanModeState", getCurrentState().getName());
613    }
614
615    /**
616     * Verifies that configs can be removed when in client mode.
617     */
618    @Test
619    public void canRemoveNetworkConfigInClientMode() throws Exception {
620        boolean result;
621        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
622        addNetworkAndVerifySuccess();
623        mLooper.startAutoDispatch();
624        result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
625        mLooper.stopAutoDispatch();
626        assertTrue(result);
627    }
628
629    /**
630     * Verifies that configs can be removed when not in client mode.
631     */
632    @Test
633    public void canRemoveNetworkConfigWhenWifiDisabled() {
634        boolean result;
635        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
636        mLooper.startAutoDispatch();
637        result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
638        mLooper.stopAutoDispatch();
639
640        assertTrue(result);
641        verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
642    }
643
644    /**
645     * Verifies that configs can be forgotten when in client mode.
646     */
647    @Test
648    public void canForgetNetworkConfigInClientMode() throws Exception {
649        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
650        addNetworkAndVerifySuccess();
651        mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
652        mLooper.dispatchAll();
653        verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
654    }
655
656    /**
657     * Verifies that configs can be removed when not in client mode.
658     */
659    @Test
660    public void canForgetNetworkConfigWhenWifiDisabled() throws Exception {
661        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
662        mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
663        mLooper.dispatchAll();
664        verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
665    }
666
667    /**
668     * Helper method to move through SupplicantStarting and SupplicantStarted states.
669     */
670    private void startSupplicantAndDispatchMessages() throws Exception {
671        mWsm.setSupplicantRunning(true);
672        mLooper.dispatchAll();
673
674        assertEquals("SupplicantStartingState", getCurrentState().getName());
675
676        when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
677        when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
678        when(mWifiNative.setModelName(anyString())).thenReturn(true);
679        when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
680        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
681        when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
682        when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
683        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
684        when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
685
686        mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
687        mLooper.dispatchAll();
688    }
689
690    private void addNetworkAndVerifySuccess() throws Exception {
691        addNetworkAndVerifySuccess(false);
692    }
693
694    private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
695        loadComponentsInStaMode();
696
697        WifiConfiguration config = new WifiConfiguration();
698        config.SSID = sSSID;
699        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
700        config.hiddenSSID = isHidden;
701
702        when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
703                .thenReturn(new NetworkUpdateResult(0));
704        when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
705        when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config);
706
707        mLooper.startAutoDispatch();
708        mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
709        mLooper.stopAutoDispatch();
710
711        verify(mWifiConfigManager).addOrUpdateNetwork(eq(config), anyInt());
712
713        mLooper.startAutoDispatch();
714        List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
715        mLooper.stopAutoDispatch();
716        assertEquals(1, configs.size());
717
718        WifiConfiguration config2 = configs.get(0);
719        assertEquals("\"GoogleGuest\"", config2.SSID);
720        assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
721    }
722
723    /**
724     * Helper method to retrieve WifiConfiguration by SSID.
725     *
726     * Returns the associated WifiConfiguration if it is found, null otherwise.
727     */
728    private WifiConfiguration getWifiConfigurationForNetwork(String ssid) {
729        mLooper.startAutoDispatch();
730        List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
731        mLooper.stopAutoDispatch();
732
733        for (WifiConfiguration checkConfig : configs) {
734            if (checkConfig.SSID.equals(ssid)) {
735                return checkConfig;
736            }
737        }
738        return null;
739    }
740
741    private void verifyScan(int band, int reportEvents, Set<String> hiddenNetworkSSIDSet) {
742        ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor =
743                ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
744        ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor =
745                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
746        verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture(),
747                eq(null));
748        WifiScanner.ScanSettings actualSettings = scanSettingsCaptor.getValue();
749        assertEquals("band", band, actualSettings.band);
750        assertEquals("reportEvents", reportEvents, actualSettings.reportEvents);
751
752        if (hiddenNetworkSSIDSet == null) {
753            hiddenNetworkSSIDSet = new HashSet<>();
754        }
755        Set<String> actualHiddenNetworkSSIDSet = new HashSet<>();
756        if (actualSettings.hiddenNetworks != null) {
757            for (int i = 0; i < actualSettings.hiddenNetworks.length; ++i) {
758                actualHiddenNetworkSSIDSet.add(actualSettings.hiddenNetworks[i].ssid);
759            }
760        }
761        assertEquals("hidden networks", hiddenNetworkSSIDSet, actualHiddenNetworkSSIDSet);
762
763        when(mWifiNative.getScanResults()).thenReturn(getMockScanResults());
764        mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT);
765
766        mLooper.dispatchAll();
767
768        List<ScanResult> reportedResults = mWsm.syncGetScanResultsList();
769        assertEquals(8, reportedResults.size());
770    }
771
772    @Test
773    public void scan() throws Exception {
774        addNetworkAndVerifySuccess();
775
776        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
777        mWsm.startScan(-1, 0, null, null);
778        mLooper.dispatchAll();
779
780        verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
781                WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
782                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, null);
783    }
784
785    @Test
786    public void scanWithHiddenNetwork() throws Exception {
787        addNetworkAndVerifySuccess(true);
788
789        Set<String> hiddenNetworkSet = new HashSet<>();
790        hiddenNetworkSet.add(sSSID);
791        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
792        hiddenNetworkList.add(new WifiScanner.ScanSettings.HiddenNetwork(sSSID));
793        when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(hiddenNetworkList);
794
795        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
796        mWsm.startScan(-1, 0, null, null);
797        mLooper.dispatchAll();
798
799        verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
800                WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
801                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
802                hiddenNetworkSet);
803    }
804
805    @Test
806    public void connect() throws Exception {
807        addNetworkAndVerifySuccess();
808
809        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
810        mLooper.dispatchAll();
811
812        mLooper.startAutoDispatch();
813        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
814        mLooper.stopAutoDispatch();
815
816        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
817
818        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
819        mLooper.dispatchAll();
820
821        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
822                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
823        mLooper.dispatchAll();
824
825        assertEquals("ObtainingIpState", getCurrentState().getName());
826
827        DhcpResults dhcpResults = new DhcpResults();
828        dhcpResults.setGateway("1.2.3.4");
829        dhcpResults.setIpAddress("192.168.1.100", 0);
830        dhcpResults.addDns("8.8.8.8");
831        dhcpResults.setLeaseDuration(3600);
832
833        mTestIpManager.injectDhcpSuccess(dhcpResults);
834        mLooper.dispatchAll();
835
836        assertEquals("ConnectedState", getCurrentState().getName());
837    }
838
839    /**
840     * If caller tries to connect to a network that is already connected, the connection request
841     * should succeed.
842     *
843     * Test: Create and connect to a network, then try to reconnect to the same network. Verify
844     * that connection request returns with CONNECT_NETWORK_SUCCEEDED.
845     */
846    @Test
847    public void reconnectToConnectedNetwork() throws Exception {
848        addNetworkAndVerifySuccess();
849
850        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
851        mLooper.dispatchAll();
852
853        mLooper.startAutoDispatch();
854        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
855        mLooper.stopAutoDispatch();
856
857        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
858
859        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
860        mLooper.dispatchAll();
861
862        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
863                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
864        mLooper.dispatchAll();
865
866        assertEquals("ObtainingIpState", getCurrentState().getName());
867
868        // try to reconnect
869        mLooper.startAutoDispatch();
870        Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.CONNECT_NETWORK, 0);
871        mLooper.stopAutoDispatch();
872
873        assertEquals(WifiManager.CONNECT_NETWORK_SUCCEEDED, reply.what);
874    }
875
876    @Test
877    public void testDhcpFailure() throws Exception {
878        addNetworkAndVerifySuccess();
879
880        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
881        mLooper.dispatchAll();
882
883        mLooper.startAutoDispatch();
884        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
885        mLooper.stopAutoDispatch();
886
887        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
888
889        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
890        mLooper.dispatchAll();
891
892        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
893                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
894        mLooper.dispatchAll();
895
896        assertEquals("ObtainingIpState", getCurrentState().getName());
897
898        mTestIpManager.injectDhcpFailure();
899        mLooper.dispatchAll();
900
901        assertEquals("DisconnectingState", getCurrentState().getName());
902    }
903
904    @Test
905    public void testBadNetworkEvent() throws Exception {
906        addNetworkAndVerifySuccess();
907
908        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
909        mLooper.dispatchAll();
910
911        mLooper.startAutoDispatch();
912        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
913        mLooper.stopAutoDispatch();
914
915        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
916
917        mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID);
918        mLooper.dispatchAll();
919
920        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
921                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
922        mLooper.dispatchAll();
923
924        assertEquals("DisconnectedState", getCurrentState().getName());
925    }
926
927
928    @Test
929    public void smToString() throws Exception {
930        assertEquals("CMD_CHANNEL_HALF_CONNECTED", mWsm.smToString(
931                AsyncChannel.CMD_CHANNEL_HALF_CONNECTED));
932        assertEquals("CMD_PRE_DHCP_ACTION", mWsm.smToString(
933                DhcpClient.CMD_PRE_DHCP_ACTION));
934        assertEquals("CMD_IP_REACHABILITY_LOST", mWsm.smToString(
935                WifiStateMachine.CMD_IP_REACHABILITY_LOST));
936    }
937
938    @Test
939    public void disconnect() throws Exception {
940        connect();
941
942        mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06");
943        mLooper.dispatchAll();
944        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
945                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED));
946        mLooper.dispatchAll();
947
948        assertEquals("DisconnectedState", getCurrentState().getName());
949    }
950
951    /**
952     * Successfully connecting to a network will set WifiConfiguration's value of HasEverConnected
953     * to true.
954     *
955     * Test: Successfully create and connect to a network. Check the config and verify
956     * WifiConfiguration.getHasEverConnected() is true.
957     */
958    @Test
959    public void setHasEverConnectedTrueOnConnect() throws Exception {
960        connect();
961        verify(mWifiConfigManager, atLeastOnce()).updateNetworkAfterConnect(0);
962    }
963
964    /**
965     * Fail network connection attempt and verify HasEverConnected remains false.
966     *
967     * Test: Successfully create a network but fail when connecting. Check the config and verify
968     * WifiConfiguration.getHasEverConnected() is false.
969     */
970    @Test
971    public void connectionFailureDoesNotSetHasEverConnectedTrue() throws Exception {
972        testDhcpFailure();
973        verify(mWifiConfigManager, never()).updateNetworkAfterConnect(0);
974    }
975
976    @Test
977    public void iconQueryTest() throws Exception {
978        // TODO(b/31065385): Passpoint config management.
979    }
980
981    @Test
982    public void verboseLogRecSizeIsGreaterThanNormalSize() {
983        assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
984    }
985
986    /**
987     * Verifies that, by default, we allow only the "normal" number of log records.
988     */
989    @Test
990    public void normalLogRecSizeIsUsedByDefault() {
991        assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
992    }
993
994    /**
995     * Verifies that, in verbose mode, we allow a larger number of log records.
996     */
997    @Test
998    public void enablingVerboseLoggingUpdatesLogRecSize() {
999        mWsm.enableVerboseLogging(1);
1000        assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecMaxSize());
1001    }
1002
1003    @Test
1004    public void disablingVerboseLoggingClearsRecords() {
1005        mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
1006        mLooper.dispatchAll();
1007        assertTrue(mWsm.getLogRecSize() >= 1);
1008
1009        mWsm.enableVerboseLogging(0);
1010        assertEquals(0, mWsm.getLogRecSize());
1011    }
1012
1013    @Test
1014    public void disablingVerboseLoggingUpdatesLogRecSize() {
1015        mWsm.enableVerboseLogging(1);
1016        mWsm.enableVerboseLogging(0);
1017        assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
1018    }
1019
1020    /** Verifies that enabling verbose logging sets the hal log property in eng builds. */
1021    @Test
1022    public void enablingVerboseLoggingSetsHalLogPropertyInEngBuilds() {
1023        reset(mPropertyService);  // Ignore calls made in setUp()
1024        when(mBuildProperties.isEngBuild()).thenReturn(true);
1025        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
1026        when(mBuildProperties.isUserBuild()).thenReturn(false);
1027        mWsm.enableVerboseLogging(1);
1028        verify(mPropertyService).set("log.tag.WifiHAL", "V");
1029    }
1030
1031    /** Verifies that enabling verbose logging sets the hal log property in userdebug builds. */
1032    @Test
1033    public void enablingVerboseLoggingSetsHalLogPropertyInUserdebugBuilds() {
1034        reset(mPropertyService);  // Ignore calls made in setUp()
1035        when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
1036        when(mBuildProperties.isEngBuild()).thenReturn(false);
1037        when(mBuildProperties.isUserBuild()).thenReturn(false);
1038        mWsm.enableVerboseLogging(1);
1039        verify(mPropertyService).set("log.tag.WifiHAL", "V");
1040    }
1041
1042    /** Verifies that enabling verbose logging does NOT set the hal log property in user builds. */
1043    @Test
1044    public void enablingVerboseLoggingDoeNotSetHalLogPropertyInUserBuilds() {
1045        reset(mPropertyService);  // Ignore calls made in setUp()
1046        when(mBuildProperties.isUserBuild()).thenReturn(true);
1047        when(mBuildProperties.isEngBuild()).thenReturn(false);
1048        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
1049        mWsm.enableVerboseLogging(1);
1050        verify(mPropertyService, never()).set(anyString(), anyString());
1051    }
1052
1053    private int testGetSupportedFeaturesCase(int supportedFeatures, boolean rttConfigured) {
1054        AsyncChannel channel = mock(AsyncChannel.class);
1055        Message reply = Message.obtain();
1056        reply.arg1 = supportedFeatures;
1057        reset(mPropertyService);  // Ignore calls made in setUp()
1058        when(channel.sendMessageSynchronously(WifiStateMachine.CMD_GET_SUPPORTED_FEATURES))
1059                .thenReturn(reply);
1060        when(mPropertyService.getBoolean("config.disable_rtt", false))
1061                .thenReturn(rttConfigured);
1062        return mWsm.syncGetSupportedFeatures(channel);
1063    }
1064
1065    /** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */
1066    @Test
1067    public void syncGetSupportedFeatures() {
1068        final int featureAware = WifiManager.WIFI_FEATURE_AWARE;
1069        final int featureInfra = WifiManager.WIFI_FEATURE_INFRA;
1070        final int featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
1071        final int featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
1072
1073        assertEquals(0, testGetSupportedFeaturesCase(0, false));
1074        assertEquals(0, testGetSupportedFeaturesCase(0, true));
1075        assertEquals(featureAware | featureInfra,
1076                testGetSupportedFeaturesCase(featureAware | featureInfra, false));
1077        assertEquals(featureAware | featureInfra,
1078                testGetSupportedFeaturesCase(featureAware | featureInfra, true));
1079        assertEquals(featureInfra | featureD2dRtt,
1080                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, false));
1081        assertEquals(featureInfra,
1082                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, true));
1083        assertEquals(featureInfra | featureD2apRtt,
1084                testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, false));
1085        assertEquals(featureInfra,
1086                testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, true));
1087        assertEquals(featureInfra | featureD2dRtt | featureD2apRtt,
1088                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, false));
1089        assertEquals(featureInfra,
1090                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, true));
1091    }
1092}
1093