WifiStateMachineTest.java revision 772124d1f1ddb2b9537de5efc748943808dafe80
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.any;
22import static org.mockito.Mockito.anyBoolean;
23import static org.mockito.Mockito.anyInt;
24import static org.mockito.Mockito.anyObject;
25import static org.mockito.Mockito.anyString;
26import static org.mockito.Mockito.mock;
27import static org.mockito.Mockito.verify;
28import static org.mockito.Mockito.when;
29import static org.mockito.Mockito.withSettings;
30
31import android.content.ContentResolver;
32import android.content.Context;
33import android.content.pm.PackageManager;
34import android.content.res.Resources;
35import android.net.BaseDhcpStateMachine;
36import android.net.ConnectivityManager;
37import android.net.DhcpResults;
38import android.net.DhcpStateMachine;
39import android.net.ip.IpReachabilityMonitor;
40import android.net.wifi.ScanResult;
41import android.net.wifi.SupplicantState;
42import android.net.wifi.WifiConfiguration;
43import android.net.wifi.WifiManager;
44import android.net.wifi.WifiScanner;
45import android.net.wifi.WifiSsid;
46import android.net.wifi.p2p.IWifiP2pManager;
47import android.os.BatteryStats;
48import android.os.Handler;
49import android.os.HandlerThread;
50import android.os.IBinder;
51import android.os.IInterface;
52import android.os.INetworkManagementService;
53import android.os.IPowerManager;
54import android.os.Looper;
55import android.os.Message;
56import android.os.Messenger;
57import android.os.PowerManager;
58import android.provider.Settings;
59import android.test.suitebuilder.annotation.SmallTest;
60import android.util.Log;
61
62import com.android.internal.R;
63import com.android.internal.app.IBatteryStats;
64import com.android.internal.util.AsyncChannel;
65import com.android.internal.util.IState;
66import com.android.internal.util.StateMachine;
67import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
68import com.android.server.wifi.hotspot2.NetworkDetail;
69import com.android.server.wifi.hotspot2.SupplicantBridge;
70import com.android.server.wifi.hotspot2.omadm.MOManager;
71import com.android.server.wifi.hotspot2.osu.OSUManager;
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.Mockito;
80import org.mockito.MockitoAnnotations;
81
82import java.io.ByteArrayOutputStream;
83import java.io.PrintWriter;
84import java.lang.reflect.Field;
85import java.lang.reflect.InvocationTargetException;
86import java.lang.reflect.Method;
87import java.util.ArrayList;
88import java.util.Arrays;
89import java.util.HashMap;
90import java.util.List;
91import java.util.Map;
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 <T> T mockWithInterfaces(Class<T> class1, Class<?>... interfaces) {
101        return mock(class1, withSettings().extraInterfaces(interfaces));
102    }
103
104    private static <T, I> IBinder mockService(Class<T> class1, Class<I> iface) {
105        T tImpl = mockWithInterfaces(class1, iface);
106        IBinder binder = mock(IBinder.class);
107        when(((IInterface) tImpl).asBinder()).thenReturn(binder);
108        when(binder.queryLocalInterface(iface.getCanonicalName()))
109                .thenReturn((IInterface) tImpl);
110        return binder;
111    }
112
113    private void enableDebugLogs() {
114        mWsm.enableVerboseLogging(1);
115    }
116
117    private static void installWlanWifiNative(WifiNative wifiNative) throws Exception {
118        Field field = WifiNative.class.getDeclaredField("wlanNativeInterface");
119        field.setAccessible(true);
120        field.set(null, wifiNative);
121
122        when(wifiNative.getInterfaceName()).thenReturn("wlan0");
123    }
124
125    private FrameworkFacade getFrameworkFacade() throws InterruptedException {
126        FrameworkFacade facade = mock(FrameworkFacade.class);
127
128        when(facade.makeBaseLogger()).thenReturn(mock(BaseWifiLogger.class));
129        when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn(
130                mockWithInterfaces(IBinder.class, INetworkManagementService.class));
131
132        IBinder p2pBinder = mockService(WifiP2pServiceImpl.class, IWifiP2pManager.class);
133        when(facade.getService(Context.WIFI_P2P_SERVICE)).thenReturn(p2pBinder);
134
135        WifiP2pServiceImpl p2pm = (WifiP2pServiceImpl) p2pBinder.queryLocalInterface(
136                IWifiP2pManager.class.getCanonicalName());
137
138        final Object sync = new Object();
139        synchronized (sync) {
140            mP2pThread = new HandlerThread("WifiP2pMockThread") {
141                @Override
142                protected void onLooperPrepared() {
143                    synchronized (sync) {
144                        sync.notifyAll();
145                    }
146                }
147            };
148
149            mP2pThread.start();
150            sync.wait();
151        }
152
153        Handler handler = new Handler(mP2pThread.getLooper());
154        when(p2pm.getP2pStateMachineMessenger()).thenReturn(new Messenger(handler));
155
156        IBinder batteryStatsBinder = mockService(BatteryStats.class, IBatteryStats.class);
157        when(facade.getService(BatteryStats.SERVICE_NAME)).thenReturn(batteryStatsBinder);
158
159        when(facade.makeOsuManager(any(WifiConfigStore.class), any(Context.class), any(
160                SupplicantBridge.class), any(MOManager.class), any(WifiStateMachine.class)))
161                .thenReturn(mock(OSUManager.class));
162
163        when(facade.makeDhcpStateMachine(
164                any(Context.class), any(StateMachine.class), any(String.class))).thenReturn(
165                mock(BaseDhcpStateMachine.class));
166
167        when(facade.makeIpReachabilityMonitor(any(Context.class), anyString(),
168                any(IpReachabilityMonitor.Callback.class))).thenReturn(
169                mock(IpReachabilityMonitor.class));
170
171        return facade;
172    }
173
174    private Context getContext() throws Exception {
175        PackageManager pkgMgr = mock(PackageManager.class);
176        when(pkgMgr.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)).thenReturn(true);
177
178        Context context = mock(Context.class);
179        when(context.getPackageManager()).thenReturn(pkgMgr);
180        when(context.getContentResolver()).thenReturn(mock(ContentResolver.class));
181
182        MockResources resources = new com.android.server.wifi.MockResources();
183        when(context.getResources()).thenReturn(resources);
184
185        ContentResolver cr = mock(ContentResolver.class);
186        when(context.getContentResolver()).thenReturn(cr);
187
188        when(context.getSystemService(Context.POWER_SERVICE)).thenReturn(
189                new PowerManager(context, mock(IPowerManager.class), new Handler()));
190
191        mAlarmManager = new MockAlarmManager();
192        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
193                mAlarmManager.getAlarmManager());
194
195        when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
196                mock(ConnectivityManager.class));
197
198        when(context.getSystemService(Context.WIFI_SCANNING_SERVICE)).thenReturn(mWifiScanner);
199
200        return context;
201    }
202
203    private Resources getMockResources() {
204        MockResources resources = new MockResources();
205        resources.setBoolean(R.bool.config_wifi_enable_wifi_firmware_debugging, false);
206        return resources;
207    }
208
209    private IState getCurrentState() throws
210            NoSuchMethodException, InvocationTargetException, IllegalAccessException {
211        Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
212        method.setAccessible(true);
213        return (IState) method.invoke(mWsm);
214    }
215
216    private static HandlerThread getWsmHandlerThread(WifiStateMachine wsm) throws
217            NoSuchFieldException, InvocationTargetException, IllegalAccessException {
218        Field field = StateMachine.class.getDeclaredField("mSmThread");
219        field.setAccessible(true);
220        return (HandlerThread) field.get(wsm);
221    }
222
223    private static void stopLooper(final Looper looper) throws Exception {
224        new Handler(looper).post(new Runnable() {
225            @Override
226            public void run() {
227                looper.quitSafely();
228            }
229        });
230    }
231
232    private void wait(int delayInMs) throws InterruptedException {
233        Looper looper = mWsmThread.getLooper();
234        final Handler handler = new Handler(looper);
235        synchronized (handler) {
236            handler.postDelayed(new Runnable() {
237                @Override
238                public void run() {
239                    synchronized (handler) {
240                        handler.notifyAll();
241                    }
242                }
243            }, delayInMs);
244
245            handler.wait();
246        }
247    }
248
249    private void dumpState() {
250        ByteArrayOutputStream stream = new ByteArrayOutputStream();
251        PrintWriter writer = new PrintWriter(stream);
252        mWsm.dump(null, writer, null);
253        writer.flush();
254        Log.d(TAG, "WifiStateMachine state -" + stream.toString());
255    }
256
257    private static ScanDetail getGoogleGuestScanDetail(int rssi) {
258        ScanResult.InformationElement ie[] = new ScanResult.InformationElement[1];
259        ie[0] = ScanResults.generateSsidIe(sSSID);
260        NetworkDetail nd = new NetworkDetail(sBSSID, ie, new ArrayList<String>(), sFreq);
261        ScanDetail detail = new ScanDetail(nd, sWifiSsid, sBSSID, "", rssi, sFreq,
262                Long.MAX_VALUE /* needed so that scan results aren't rejected because
263                                  there older than scan start */);
264        detail.getScanResult().informationElements = ie;
265        detail.getScanResult().anqpLines = new ArrayList<String>();
266        return detail;
267    }
268
269    private ScanResult[] getMockScanResults() {
270        ScanResults sr = ScanResults.create(0, 2412, 2437, 2462, 5180, 5220, 5745, 5825);
271        // copy generated results and add space on the end for one more
272        ScanResult[] results = Arrays.copyOf(sr.getScanData().getResults(),
273                sr.getScanData().getResults().length + 1);
274
275        int rssi = -65;
276        results[results.length - 1] = getGoogleGuestScanDetail(rssi).getScanResult();
277        return results;
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    MockAlarmManager mAlarmManager;
292    MockWifiMonitor mWifiMonitor;
293
294    @Mock WifiNative mWifiNative;
295    @Mock WifiScanner mWifiScanner;
296    @Mock SupplicantStateTracker mSupplicantStateTracker;
297    @Mock WifiMetrics mWifiMetrics;
298
299    public WifiStateMachineTest() throws Exception {
300    }
301
302    @Before
303    public void setUp() throws Exception {
304        Log.d(TAG, "Setting up ...");
305
306        // Ensure looper exists
307        MockLooper looper = new MockLooper();
308
309        MockitoAnnotations.initMocks(this);
310
311        /** uncomment this to enable logs from WifiStateMachines */
312        // enableDebugLogs();
313
314        installWlanWifiNative(mWifiNative);
315        mWifiMonitor = new MockWifiMonitor();
316        mWifiMetrics = mock(WifiMetrics.class);
317        FrameworkFacade factory = getFrameworkFacade();
318        Context context = getContext();
319
320        Resources resources = getMockResources();
321        when(context.getResources()).thenReturn(resources);
322
323        when(factory.getIntegerSetting(context,
324                Settings.Global.WIFI_FREQUENCY_BAND,
325                WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
326                WifiManager.WIFI_FREQUENCY_BAND_AUTO);
327
328        when(factory.makeApConfigStore(Mockito.eq(context), any(Handler.class)))
329                .thenCallRealMethod();
330
331        when(factory.makeSupplicantStateTracker(
332                any(Context.class), any(WifiStateMachine.class), any(WifiConfigStore.class),
333                any(Handler.class))).thenReturn(mSupplicantStateTracker);
334
335        mWsm = new WifiStateMachine(context, null, factory, mWifiMetrics);
336        mWsmThread = getWsmHandlerThread(mWsm);
337
338        final Object sync = new Object();
339        synchronized (sync) {
340            mSyncThread = new HandlerThread("SynchronizationThread");
341            final AsyncChannel channel = new AsyncChannel();
342            mSyncThread.start();
343            Handler handler = new Handler(mSyncThread.getLooper()) {
344                @Override
345                public void handleMessage(Message msg) {
346                    switch (msg.what) {
347                        case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
348                            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
349                                mWsmAsyncChannel = channel;
350                                synchronized (sync) {
351                                    sync.notifyAll();
352                                    Log.d(TAG, "Successfully connected " + this);
353                                }
354                            } else {
355                                Log.d(TAG, "Failed to connect Command channel " + this);
356                            }
357                            break;
358                        case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
359                            Log.d(TAG, "Command channel disconnected" + this);
360                            break;
361                    }
362                }
363            };
364
365            channel.connect(context, handler, mWsm.getMessenger());
366            sync.wait();
367        }
368
369        /* Now channel is supposed to be connected */
370    }
371
372    @After
373    public void cleanUp() throws Exception {
374
375        if (mSyncThread != null) stopLooper(mSyncThread.getLooper());
376        if (mWsmThread != null) stopLooper(mWsmThread.getLooper());
377        if (mP2pThread != null) stopLooper(mP2pThread.getLooper());
378
379        mWsmThread = null;
380        mP2pThread = null;
381        mSyncThread = null;
382        mWsmAsyncChannel = null;
383        mWsm = null;
384    }
385
386    @Test
387    public void createNew() throws Exception {
388        assertEquals("InitialState", getCurrentState().getName());
389
390        mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
391        wait(200);
392        assertEquals("InitialState", getCurrentState().getName());
393    }
394
395    @Test
396    public void loadComponents() throws Exception {
397
398        when(mWifiNative.loadDriver()).thenReturn(true);
399        when(mWifiNative.startHal()).thenReturn(true);
400        when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true);
401
402        mWsm.setSupplicantRunning(true);
403        wait(200);
404        assertEquals("SupplicantStartingState", getCurrentState().getName());
405
406        when(mWifiNative.setBand(anyInt())).thenReturn(true);
407        when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
408        when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
409        when(mWifiNative.setModelName(anyString())).thenReturn(true);
410        when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
411        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
412        when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
413        when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
414        when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
415        when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
416        when(mWifiNative.enableBackgroundScan(anyBoolean())).thenReturn(true);
417
418        mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
419        wait(200);
420        assertEquals("DisconnectedState", getCurrentState().getName());
421    }
422
423    @Test
424    public void loadComponentsFailure() throws Exception {
425        when(mWifiNative.loadDriver()).thenReturn(false);
426        when(mWifiNative.startHal()).thenReturn(false);
427        when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(false);
428
429        mWsm.setSupplicantRunning(true);
430        wait(200);
431        assertEquals("InitialState", getCurrentState().getName());
432
433        when(mWifiNative.loadDriver()).thenReturn(true);
434        mWsm.setSupplicantRunning(true);
435        wait(200);
436        assertEquals("InitialState", getCurrentState().getName());
437
438        when(mWifiNative.startHal()).thenReturn(true);
439        mWsm.setSupplicantRunning(true);
440        wait(200);
441        assertEquals("InitialState", getCurrentState().getName());
442    }
443
444    @Test
445    public void addNetwork() throws Exception {
446
447        loadComponents();
448
449        final HashMap<String, String> nameToValue = new HashMap<String, String>();
450
451        when(mWifiNative.addNetwork()).thenReturn(0);
452        when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString()))
453                .then(new AnswerWithArguments<Boolean>() {
454                    public boolean answer(int netId, String name, String value) {
455                        if (netId != 0) {
456                            Log.d(TAG, "Can't set var " + name + " for " + netId);
457                            return false;
458                        }
459
460                        Log.d(TAG, "Setting var " + name + " to " + value + " for " + netId);
461                        nameToValue.put(name, value);
462                        return true;
463                    }
464                });
465
466        when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject()))
467                .then(new AnswerWithArguments<Boolean>() {
468                        public boolean answer(int netId, String name, Map<String, String> values) {
469                            if (netId != 0) {
470                                Log.d(TAG, "Can't set extra " + name + " for " + netId);
471                                return false;
472                            }
473
474                            Log.d(TAG, "Setting extra for " + netId);
475                            return true;
476                        }
477                    });
478
479        when(mWifiNative.getNetworkVariable(anyInt(), anyString()))
480                .then(new AnswerWithArguments<String>() {
481                    public String answer(int netId, String name) throws Throwable {
482                        if (netId != 0) {
483                            Log.d(TAG, "Can't find var " + name + " for " + netId);
484                            return null;
485                        }
486                        String value = nameToValue.get(name);
487                        if (value != null) {
488                            Log.d(TAG, "Returning var " + name + " to " + value + " for " + netId);
489                        } else {
490                            Log.d(TAG, "Can't find var " + name + " for " + netId);
491                        }
492                        return value;
493                    }
494                });
495
496        WifiConfiguration config = new WifiConfiguration();
497        config.SSID = sSSID;
498        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
499        mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
500        wait(200);
501
502        verify(mWifiNative).addNetwork();
503        verify(mWifiNative).setNetworkVariable(0, "ssid", sHexSSID);
504
505        List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
506        assertEquals(1, configs.size());
507
508        WifiConfiguration config2 = configs.get(0);
509        assertEquals("\"GoogleGuest\"", config2.SSID);
510        assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
511    }
512
513    @Test
514    public void scan() throws Exception {
515
516        addNetwork();
517
518        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
519
520        mWsm.startScan(-1, 0, null, null);
521        wait(200);
522
523        ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor =
524                ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
525        ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor =
526                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
527        verify(mWifiScanner).startScan(scanSettingsCaptor.capture(), scanListenerCaptor.capture());
528        assertEquals("band", WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
529                scanSettingsCaptor.getValue().band);
530        assertEquals("reportEvents", WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
531                | WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
532                scanSettingsCaptor.getValue().reportEvents);
533
534        ScanResult[] results = getMockScanResults();
535        for (ScanResult result : results) {
536            scanListenerCaptor.getValue().onFullResult(result);
537        }
538        scanListenerCaptor.getValue().onResults(
539                new WifiScanner.ScanData[] {new WifiScanner.ScanData(0, 0, results)});
540
541        wait(200);
542        List<ScanResult> reportedResults = mWsm.syncGetScanResultsList();
543        assertEquals(8, reportedResults.size());
544    }
545
546    @Test
547    public void connect() throws Exception {
548
549        addNetwork();
550
551        mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
552        mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
553        wait(200);
554
555        verify(mWifiNative).enableNetwork(0, true);
556
557        mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
558        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
559                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
560        wait(200);
561
562        assertEquals("ObtainingIpState", getCurrentState().getName());
563
564        DhcpResults dhcpResults = new DhcpResults();
565        dhcpResults.setGateway("1.2.3.4");
566        dhcpResults.setIpAddress("192.168.1.100", 0);
567        dhcpResults.addDns("8.8.8.8");
568        dhcpResults.setLeaseDuration(3600);
569
570        mWsm.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION, DhcpStateMachine.DHCP_SUCCESS, 0,
571                dhcpResults);
572        wait(200);
573
574        assertEquals("ConnectedState", getCurrentState().getName());
575    }
576
577    @Test
578    public void disconnect() throws Exception {
579        connect();
580
581        mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, -1, 3, "01:02:03:04:05:06");
582        mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
583                new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED));
584        wait(200);
585
586        assertEquals("DisconnectedState", getCurrentState().getName());
587    }
588}
589