WifiStateMachineTest.java revision 458d8dceedd28a99084d17a5d3118f2c03b3c2d9
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
352        when(mWifiNative.getInterfaceName()).thenReturn("mockWlan");
353        when(mWifiSupplicantControl.getFrameworkNetworkId(anyInt())).thenReturn(0);
354
355
356        FrameworkFacade factory = getFrameworkFacade();
357        Context context = getContext();
358
359        Resources resources = getMockResources();
360        when(context.getResources()).thenReturn(resources);
361
362        when(factory.getIntegerSetting(context,
363                Settings.Global.WIFI_FREQUENCY_BAND,
364                WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
365                WifiManager.WIFI_FREQUENCY_BAND_AUTO);
366
367        when(factory.makeApConfigStore(eq(context), eq(mBackupManagerProxy)))
368                .thenReturn(mApConfigStore);
369
370        when(factory.makeSupplicantStateTracker(
371                any(Context.class), any(WifiConfigManager.class),
372                any(Handler.class))).thenReturn(mSupplicantStateTracker);
373
374        when(mUserManager.getProfileParent(11))
375                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "owner", 0));
376        when(mUserManager.getProfiles(UserHandle.USER_SYSTEM)).thenReturn(Arrays.asList(
377                new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
378                new UserInfo(11, "managed profile", 0)));
379
380        when(mWificond.createClientInterface()).thenReturn(mClientInterface);
381        when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
382        when(mClientInterface.enableSupplicant()).thenReturn(true);
383        when(mClientInterface.disableSupplicant()).thenReturn(true);
384
385        mWsm = new WifiStateMachine(context, factory, mLooper.getLooper(),
386            mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative);
387        mWsmThread = getWsmHandlerThread(mWsm);
388
389        final AsyncChannel channel = new AsyncChannel();
390        Handler handler = new Handler(mLooper.getLooper()) {
391            @Override
392            public void handleMessage(Message msg) {
393                switch (msg.what) {
394                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
395                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
396                            mWsmAsyncChannel = channel;
397                        } else {
398                            Log.d(TAG, "Failed to connect Command channel " + this);
399                        }
400                        break;
401                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
402                        Log.d(TAG, "Command channel disconnected" + this);
403                        break;
404                }
405            }
406        };
407
408        channel.connect(context, handler, mWsm.getMessenger());
409        mLooper.dispatchAll();
410        /* Now channel is supposed to be connected */
411
412        mBinderToken = Binder.clearCallingIdentity();
413    }
414
415    @After
416    public void cleanUp() throws Exception {
417        Binder.restoreCallingIdentity(mBinderToken);
418
419        if (mSyncThread != null) stopLooper(mSyncThread.getLooper());
420        if (mWsmThread != null) stopLooper(mWsmThread.getLooper());
421        if (mP2pThread != null) stopLooper(mP2pThread.getLooper());
422
423        mWsmThread = null;
424        mP2pThread = null;
425        mSyncThread = null;
426        mWsmAsyncChannel = null;
427        mWsm = null;
428    }
429
430    @Test
431    public void createNew() throws Exception {
432        assertEquals("InitialState", getCurrentState().getName());
433
434        mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
435        mLooper.dispatchAll();
436        assertEquals("InitialState", getCurrentState().getName());
437    }
438
439    @Test
440    public void loadComponents() throws Exception {
441        when(mWifiNative.startHal()).thenReturn(true);
442        mWsm.setSupplicantRunning(true);
443        mLooper.dispatchAll();
444
445        assertEquals("SupplicantStartingState", getCurrentState().getName());
446
447        when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
448        when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
449        when(mWifiNative.setModelName(anyString())).thenReturn(true);
450        when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
451        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
452        when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
453        when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
454        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
455        when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
456
457        mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
458        mLooper.dispatchAll();
459
460        assertEquals("DisconnectedState", getCurrentState().getName());
461    }
462
463    @Test
464    public void shouldRequireSupplicantStartupToLeaveInitialState() throws Exception {
465        when(mClientInterface.enableSupplicant()).thenReturn(false);
466        mWsm.setSupplicantRunning(true);
467        mLooper.dispatchAll();
468        assertEquals("InitialState", getCurrentState().getName());
469    }
470
471    @Test
472    public void shouldRequireWificondToLeaveInitialState() throws Exception {
473        // We start out with valid default values, break them going backwards so that
474        // we test all the bailout cases.
475
476        // ClientInterface dies after creation.
477        doThrow(new RemoteException()).when(mClientInterfaceBinder).linkToDeath(any(), anyInt());
478        mWsm.setSupplicantRunning(true);
479        mLooper.dispatchAll();
480        assertEquals("InitialState", getCurrentState().getName());
481
482        // Failed to even create the client interface.
483        when(mWificond.createClientInterface()).thenReturn(null);
484        mWsm.setSupplicantRunning(true);
485        mLooper.dispatchAll();
486        assertEquals("InitialState", getCurrentState().getName());
487
488        // Failed to get wificond proxy.
489        when(mWifiInjector.makeWificond()).thenReturn(null);
490        mWsm.setSupplicantRunning(true);
491        mLooper.dispatchAll();
492        assertEquals("InitialState", getCurrentState().getName());
493    }
494
495    @Test
496    public void loadComponentsFailure() throws Exception {
497        when(mWifiNative.startHal()).thenReturn(false);
498        when(mClientInterface.enableSupplicant()).thenReturn(false);
499
500        mWsm.setSupplicantRunning(true);
501        mLooper.dispatchAll();
502        assertEquals("InitialState", getCurrentState().getName());
503
504        when(mWifiNative.startHal()).thenReturn(true);
505        mWsm.setSupplicantRunning(true);
506        mLooper.dispatchAll();
507        assertEquals("InitialState", getCurrentState().getName());
508    }
509
510    @Test
511    public void checkInitialStateStickyWhenDisabledMode() throws Exception {
512        mLooper.dispatchAll();
513        assertEquals("InitialState", getCurrentState().getName());
514        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
515
516        mWsm.setOperationalMode(WifiStateMachine.DISABLED_MODE);
517        mLooper.dispatchAll();
518        assertEquals(WifiStateMachine.DISABLED_MODE, mWsm.getOperationalModeForTest());
519        assertEquals("InitialState", getCurrentState().getName());
520    }
521
522    @Test
523    public void shouldStartSupplicantWhenConnectModeRequested() throws Exception {
524        when(mWifiNative.startHal()).thenReturn(true);
525
526        // The first time we start out in InitialState, we sit around here.
527        mLooper.dispatchAll();
528        assertEquals("InitialState", getCurrentState().getName());
529        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
530
531        // But if someone tells us to enter connect mode, we start up supplicant
532        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
533        mLooper.dispatchAll();
534        assertEquals("SupplicantStartingState", getCurrentState().getName());
535    }
536
537    /**
538     * Test that mode changes for WifiStateMachine in the InitialState are realized when supplicant
539     * is started.
540     */
541    @Test
542    public void checkStartInCorrectStateAfterChangingInitialState() throws Exception {
543        when(mWifiNative.startHal()).thenReturn(true);
544
545        // Check initial state
546        mLooper.dispatchAll();
547        assertEquals("InitialState", getCurrentState().getName());
548        assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
549
550        // Update the mode
551        mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
552        mLooper.dispatchAll();
553        assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
554
555        // Start supplicant so we move to the next state
556        mWsm.setSupplicantRunning(true);
557        mLooper.dispatchAll();
558        assertEquals("SupplicantStartingState", getCurrentState().getName());
559        when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
560        when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
561        when(mWifiNative.setModelName(anyString())).thenReturn(true);
562        when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
563        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
564        when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
565        when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
566        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
567        when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
568
569        mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
570        mLooper.dispatchAll();
571
572        assertEquals("ScanModeState", getCurrentState().getName());
573    }
574
575    private void addNetworkAndVerifySuccess() throws Exception {
576        addNetworkAndVerifySuccess(false);
577    }
578
579    private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
580        loadComponents();
581
582        WifiConfiguration config = new WifiConfiguration();
583        config.SSID = sSSID;
584        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
585        config.hiddenSSID = isHidden;
586
587        when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
588                .thenReturn(new NetworkUpdateResult(0));
589        when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
590        when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config);
591
592        mLooper.startAutoDispatch();
593        mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
594        mLooper.stopAutoDispatch();
595
596        verify(mWifiConfigManager).addOrUpdateNetwork(eq(config), anyInt());
597
598        mLooper.startAutoDispatch();
599        List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
600        mLooper.stopAutoDispatch();
601        assertEquals(1, configs.size());
602
603        WifiConfiguration config2 = configs.get(0);
604        assertEquals("\"GoogleGuest\"", config2.SSID);
605        assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
606    }
607
608    /**
609     * Helper method to retrieve WifiConfiguration by SSID.
610     *
611     * Returns the associated WifiConfiguration if it is found, null otherwise.
612     */
613    private WifiConfiguration getWifiConfigurationForNetwork(String ssid) {
614        mLooper.startAutoDispatch();
615        List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
616        mLooper.stopAutoDispatch();
617
618        for (WifiConfiguration checkConfig : configs) {
619            if (checkConfig.SSID.equals(ssid)) {
620                return checkConfig;
621            }
622        }
623        return null;
624    }
625
626    private void verifyScan(int band, int reportEvents, Set<String> hiddenNetworkSSIDSet) {
627        ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor =
628                ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
629        ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor =
630                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
631        verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture(),
632                eq(null));
633        WifiScanner.ScanSettings actualSettings = scanSettingsCaptor.getValue();
634        assertEquals("band", band, actualSettings.band);
635        assertEquals("reportEvents", reportEvents, actualSettings.reportEvents);
636
637        if (hiddenNetworkSSIDSet == null) {
638            hiddenNetworkSSIDSet = new HashSet<>();
639        }
640        Set<String> actualHiddenNetworkSSIDSet = new HashSet<>();
641        if (actualSettings.hiddenNetworks != null) {
642            for (int i = 0; i < actualSettings.hiddenNetworks.length; ++i) {
643                actualHiddenNetworkSSIDSet.add(actualSettings.hiddenNetworks[i].ssid);
644            }
645        }
646        assertEquals("hidden networks", hiddenNetworkSSIDSet, actualHiddenNetworkSSIDSet);
647
648        when(mWifiNative.getScanResults()).thenReturn(getMockScanResults());
649        mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT);
650
651        mLooper.dispatchAll();
652
653        List<ScanResult> reportedResults = mWsm.syncGetScanResultsList();
654        assertEquals(8, reportedResults.size());
655    }
656
657    @Test
658    public void scan() throws Exception {
659        addNetworkAndVerifySuccess();
660
661        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
662        mWsm.startScan(-1, 0, null, null);
663        mLooper.dispatchAll();
664
665        verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
666                WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
667                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT, null);
668    }
669
670    @Test
671    public void scanWithHiddenNetwork() throws Exception {
672        addNetworkAndVerifySuccess(true);
673
674        Set<String> hiddenNetworkSet = new HashSet<>();
675        hiddenNetworkSet.add(sSSID);
676        List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
677        hiddenNetworkList.add(new WifiScanner.ScanSettings.HiddenNetwork(sSSID));
678        when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(hiddenNetworkList);
679
680        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
681        mWsm.startScan(-1, 0, null, null);
682        mLooper.dispatchAll();
683
684        verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
685                WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
686                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
687                hiddenNetworkSet);
688    }
689
690    @Test
691    public void connect() throws Exception {
692        addNetworkAndVerifySuccess();
693
694        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
695        mLooper.dispatchAll();
696
697        mLooper.startAutoDispatch();
698        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
699        mLooper.stopAutoDispatch();
700
701        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
702
703        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
704        mLooper.dispatchAll();
705
706        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
707                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
708        mLooper.dispatchAll();
709
710        assertEquals("ObtainingIpState", getCurrentState().getName());
711
712        DhcpResults dhcpResults = new DhcpResults();
713        dhcpResults.setGateway("1.2.3.4");
714        dhcpResults.setIpAddress("192.168.1.100", 0);
715        dhcpResults.addDns("8.8.8.8");
716        dhcpResults.setLeaseDuration(3600);
717
718        mTestIpManager.injectDhcpSuccess(dhcpResults);
719        mLooper.dispatchAll();
720
721        assertEquals("ConnectedState", getCurrentState().getName());
722    }
723
724    @Test
725    public void testDhcpFailure() throws Exception {
726        addNetworkAndVerifySuccess();
727
728        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
729        mLooper.dispatchAll();
730
731        mLooper.startAutoDispatch();
732        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
733        mLooper.stopAutoDispatch();
734
735        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
736
737        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
738        mLooper.dispatchAll();
739
740        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
741                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
742        mLooper.dispatchAll();
743
744        assertEquals("ObtainingIpState", getCurrentState().getName());
745
746        mTestIpManager.injectDhcpFailure();
747        mLooper.dispatchAll();
748
749        assertEquals("DisconnectingState", getCurrentState().getName());
750    }
751
752    @Test
753    public void testBadNetworkEvent() throws Exception {
754        addNetworkAndVerifySuccess();
755
756        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
757        mLooper.dispatchAll();
758
759        mLooper.startAutoDispatch();
760        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
761        mLooper.stopAutoDispatch();
762
763        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
764
765        mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID);
766        mLooper.dispatchAll();
767
768        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
769                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
770        mLooper.dispatchAll();
771
772        assertEquals("DisconnectedState", getCurrentState().getName());
773    }
774
775
776    @Test
777    public void smToString() throws Exception {
778        assertEquals("CMD_CHANNEL_HALF_CONNECTED", mWsm.smToString(
779                AsyncChannel.CMD_CHANNEL_HALF_CONNECTED));
780        assertEquals("CMD_PRE_DHCP_ACTION", mWsm.smToString(
781                DhcpClient.CMD_PRE_DHCP_ACTION));
782        assertEquals("CMD_IP_REACHABILITY_LOST", mWsm.smToString(
783                WifiStateMachine.CMD_IP_REACHABILITY_LOST));
784    }
785
786    @Test
787    public void disconnect() throws Exception {
788        connect();
789
790        mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06");
791        mLooper.dispatchAll();
792        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
793                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED));
794        mLooper.dispatchAll();
795
796        assertEquals("DisconnectedState", getCurrentState().getName());
797    }
798
799    /**
800     * Successfully connecting to a network will set WifiConfiguration's value of HasEverConnected
801     * to true.
802     *
803     * Test: Successfully create and connect to a network. Check the config and verify
804     * WifiConfiguration.getHasEverConnected() is true.
805     */
806    @Test
807    public void setHasEverConnectedTrueOnConnect() throws Exception {
808        connect();
809        verify(mWifiConfigManager, atLeastOnce()).updateNetworkAfterConnect(0);
810    }
811
812    /**
813     * Fail network connection attempt and verify HasEverConnected remains false.
814     *
815     * Test: Successfully create a network but fail when connecting. Check the config and verify
816     * WifiConfiguration.getHasEverConnected() is false.
817     */
818    @Test
819    public void connectionFailureDoesNotSetHasEverConnectedTrue() throws Exception {
820        testDhcpFailure();
821        verify(mWifiConfigManager, never()).updateNetworkAfterConnect(0);
822    }
823
824    @Test
825    public void iconQueryTest() throws Exception {
826        // TODO(b/31065385): Passpoint config management.
827    }
828
829    /**
830     * Verifies that, by default, we allow only the "normal" number of log records.
831     */
832    @Test
833    public void normalLogRecSizeIsUsedByDefault() {
834        for (int i = 0; i < WifiStateMachine.NUM_LOG_RECS_NORMAL * 2; i++) {
835            mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
836        }
837        mLooper.dispatchAll();
838        assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
839    }
840
841    /**
842     * Verifies that, in verbose mode, we allow a larger number of log records.
843     */
844    @Test
845    public void enablingVerboseLoggingIncreasesLogRecSize() {
846        assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
847        mWsm.enableVerboseLogging(1);
848        for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE * 2; i++) {
849            mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
850        }
851        mLooper.dispatchAll();
852        assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
853    }
854
855    /**
856     * Verifies that moving from verbose mode to normal mode resets the buffer, and limits new
857     * records to a small number of entries.
858     */
859    @Test
860    public void disablingVerboseLoggingClearsRecordsAndDecreasesLogRecSize() {
861        mWsm.enableVerboseLogging(1);
862        for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
863            mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
864        }
865        mLooper.dispatchAll();
866        assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
867
868        mWsm.enableVerboseLogging(0);
869        assertEquals(0, mWsm.getLogRecSize());
870        for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
871            mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
872        }
873        mLooper.dispatchAll();
874        assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
875    }
876
877    /** Verifies that enabling verbose logging sets the hal log property in eng builds. */
878    @Test
879    public void enablingVerboseLoggingSetsHalLogPropertyInEngBuilds() {
880        reset(mPropertyService);  // Ignore calls made in setUp()
881        when(mBuildProperties.isEngBuild()).thenReturn(true);
882        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
883        when(mBuildProperties.isUserBuild()).thenReturn(false);
884        mWsm.enableVerboseLogging(1);
885        verify(mPropertyService).set("log.tag.WifiHAL", "V");
886    }
887
888    /** Verifies that enabling verbose logging sets the hal log property in userdebug builds. */
889    @Test
890    public void enablingVerboseLoggingSetsHalLogPropertyInUserdebugBuilds() {
891        reset(mPropertyService);  // Ignore calls made in setUp()
892        when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
893        when(mBuildProperties.isEngBuild()).thenReturn(false);
894        when(mBuildProperties.isUserBuild()).thenReturn(false);
895        mWsm.enableVerboseLogging(1);
896        verify(mPropertyService).set("log.tag.WifiHAL", "V");
897    }
898
899    /** Verifies that enabling verbose logging does NOT set the hal log property in user builds. */
900    @Test
901    public void enablingVerboseLoggingDoeNotSetHalLogPropertyInUserBuilds() {
902        reset(mPropertyService);  // Ignore calls made in setUp()
903        when(mBuildProperties.isUserBuild()).thenReturn(true);
904        when(mBuildProperties.isEngBuild()).thenReturn(false);
905        when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
906        mWsm.enableVerboseLogging(1);
907        verify(mPropertyService, never()).set(anyString(), anyString());
908    }
909
910    private int testGetSupportedFeaturesCase(int supportedFeatures, boolean rttConfigured) {
911        AsyncChannel channel = mock(AsyncChannel.class);
912        Message reply = Message.obtain();
913        reply.arg1 = supportedFeatures;
914        reset(mPropertyService);  // Ignore calls made in setUp()
915        when(channel.sendMessageSynchronously(WifiStateMachine.CMD_GET_SUPPORTED_FEATURES))
916                .thenReturn(reply);
917        when(mPropertyService.getBoolean("config.disable_rtt", false))
918                .thenReturn(rttConfigured);
919        return mWsm.syncGetSupportedFeatures(channel);
920    }
921
922    /** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */
923    @Test
924    public void syncGetSupportedFeatures() {
925        final int featureNan = WifiManager.WIFI_FEATURE_NAN;
926        final int featureInfra = WifiManager.WIFI_FEATURE_INFRA;
927        final int featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
928        final int featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
929
930        assertEquals(0, testGetSupportedFeaturesCase(0, false));
931        assertEquals(0, testGetSupportedFeaturesCase(0, true));
932        assertEquals(featureNan | featureInfra,
933                testGetSupportedFeaturesCase(featureNan | featureInfra, false));
934        assertEquals(featureNan | featureInfra,
935                testGetSupportedFeaturesCase(featureNan | featureInfra, true));
936        assertEquals(featureInfra | featureD2dRtt,
937                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, false));
938        assertEquals(featureInfra,
939                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, true));
940        assertEquals(featureInfra | featureD2apRtt,
941                testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, false));
942        assertEquals(featureInfra,
943                testGetSupportedFeaturesCase(featureInfra | featureD2apRtt, true));
944        assertEquals(featureInfra | featureD2dRtt | featureD2apRtt,
945                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, false));
946        assertEquals(featureInfra,
947                testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, true));
948    }
949}
950