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