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