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