SupplicantStaIfaceHalTest.java revision 5a1adfdef3025a595544b3d17e1d5d9afca7673b
1/*
2 * Copyright (C) 2017 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 */
16package com.android.server.wifi;
17
18import static org.junit.Assert.*;
19import static org.mockito.Matchers.any;
20import static org.mockito.Matchers.anyBoolean;
21import static org.mockito.Matchers.anyInt;
22import static org.mockito.Matchers.anyLong;
23import static org.mockito.Matchers.anyShort;
24import static org.mockito.Matchers.anyString;
25import static org.mockito.Matchers.eq;
26import static org.mockito.Mockito.doAnswer;
27import static org.mockito.Mockito.doThrow;
28import static org.mockito.Mockito.inOrder;
29import static org.mockito.Mockito.mock;
30import static org.mockito.Mockito.never;
31import static org.mockito.Mockito.times;
32import static org.mockito.Mockito.verify;
33import static org.mockito.Mockito.when;
34
35import android.app.test.MockAnswerUtil;
36import android.content.Context;
37import android.hardware.wifi.supplicant.V1_0.ISupplicant;
38import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
39import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
40import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
41import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
42import android.hardware.wifi.supplicant.V1_0.IfaceType;
43import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
44import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
45import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
46import android.hidl.manager.V1_0.IServiceManager;
47import android.hidl.manager.V1_0.IServiceNotification;
48import android.net.IpConfiguration;
49import android.net.wifi.WifiConfiguration;
50import android.os.IHwBinder;
51import android.os.RemoteException;
52import android.util.SparseArray;
53
54import com.android.server.wifi.hotspot2.AnqpEvent;
55import com.android.server.wifi.hotspot2.IconEvent;
56import com.android.server.wifi.hotspot2.WnmData;
57import com.android.server.wifi.util.NativeUtil;
58
59import org.junit.Before;
60import org.junit.Test;
61import org.mockito.ArgumentCaptor;
62import org.mockito.InOrder;
63import org.mockito.Mock;
64import org.mockito.MockitoAnnotations;
65
66import java.nio.ByteBuffer;
67import java.nio.ByteOrder;
68import java.util.ArrayList;
69import java.util.Arrays;
70import java.util.HashMap;
71import java.util.Map;
72import java.util.Random;
73
74/**
75 * Unit tests for SupplicantStaIfaceHal
76 */
77public class SupplicantStaIfaceHalTest {
78    private static final String TAG = "SupplicantStaIfaceHalTest";
79    private static final Map<Integer, String> NETWORK_ID_TO_SSID = new HashMap<Integer, String>() {{
80            put(1, "ssid1");
81            put(2, "ssid2");
82            put(3, "ssid3");
83        }};
84    private static final int EXISTING_SUPPLICANT_NETWORK_ID = 2;
85    private static final int ROAM_NETWORK_ID = 4;
86    private static final String BSSID = "fa:45:23:23:12:12";
87    private static final String WLAN_IFACE_NAME = "wlan0";
88    private static final String P2P_IFACE_NAME = "p2p0";
89    private static final String ICON_FILE_NAME  = "blahblah";
90    private static final int ICON_FILE_SIZE = 72;
91    private static final String HS20_URL = "http://blahblah";
92
93    @Mock IServiceManager mServiceManagerMock;
94    @Mock ISupplicant mISupplicantMock;
95    @Mock ISupplicantIface mISupplicantIfaceMock;
96    @Mock ISupplicantStaIface mISupplicantStaIfaceMock;
97    @Mock Context mContext;
98    @Mock WifiMonitor mWifiMonitor;
99    @Mock SupplicantStaNetworkHal mSupplicantStaNetworkMock;
100    SupplicantStatus mStatusSuccess;
101    SupplicantStatus mStatusFailure;
102    ISupplicant.IfaceInfo mStaIface;
103    ISupplicant.IfaceInfo mP2pIface;
104    ArrayList<ISupplicant.IfaceInfo> mIfaceInfoList;
105    ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
106    private SupplicantStaIfaceHal mDut;
107    private ArgumentCaptor<IHwBinder.DeathRecipient> mDeathRecipientCaptor =
108            ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
109    private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
110            ArgumentCaptor.forClass(IServiceNotification.Stub.class);
111    private InOrder mInOrder;
112
113    private class SupplicantStaIfaceHalSpy extends SupplicantStaIfaceHal {
114        SupplicantStaIfaceHalSpy(Context context, WifiMonitor monitor) {
115            super(context, monitor);
116        }
117
118        @Override
119        protected IServiceManager getServiceManagerMockable() throws RemoteException {
120            return mServiceManagerMock;
121        }
122
123        @Override
124        protected ISupplicant getSupplicantMockable() throws RemoteException {
125            return mISupplicantMock;
126        }
127
128        @Override
129        protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
130            return mISupplicantStaIfaceMock;
131        }
132
133        @Override
134        protected SupplicantStaNetworkHal getStaNetworkMockable(
135                ISupplicantStaNetwork iSupplicantStaNetwork) {
136            return mSupplicantStaNetworkMock;
137        }
138    }
139
140    @Before
141    public void setUp() throws Exception {
142        MockitoAnnotations.initMocks(this);
143        mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
144        mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
145        mStaIface = createIfaceInfo(IfaceType.STA, WLAN_IFACE_NAME);
146        mP2pIface = createIfaceInfo(IfaceType.P2P, P2P_IFACE_NAME);
147
148        mIfaceInfoList = new ArrayList<>();
149        mIfaceInfoList.add(mStaIface);
150        mIfaceInfoList.add(mP2pIface);
151
152        when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
153                anyLong())).thenReturn(true);
154        when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
155                any(IServiceNotification.Stub.class))).thenReturn(true);
156        mDut = new SupplicantStaIfaceHalSpy(mContext, mWifiMonitor);
157    }
158
159    /**
160     * Sunny day scenario for SupplicantStaIfaceHal initialization
161     * Asserts successful initialization
162     */
163    @Test
164    public void testInitialize_success() throws Exception {
165        executeAndValidateInitializationSequence(false, false, false, false);
166    }
167
168    /**
169     * Tests the initialization flow, with a RemoteException occurring when 'getInterface' is called
170     * Ensures initialization fails.
171     */
172    @Test
173    public void testInitialize_remoteExceptionFailure() throws Exception {
174        executeAndValidateInitializationSequence(true, false, false, false);
175    }
176
177    /**
178     * Tests the initialization flow, with listInterfaces returning 0 interfaces.
179     * Ensures failure
180     */
181    @Test
182    public void testInitialize_zeroInterfacesFailure() throws Exception {
183        executeAndValidateInitializationSequence(false, true, false, false);
184    }
185
186    /**
187     * Tests the initialization flow, with a null interface being returned by getInterface.
188     * Ensures initialization fails.
189     */
190    @Test
191    public void testInitialize_nullInterfaceFailure() throws Exception {
192        executeAndValidateInitializationSequence(false, false, true, false);
193    }
194
195    /**
196     * Tests the initialization flow, with a callback registration failure.
197     * Ensures initialization fails.
198     */
199    @Test
200    public void testInitialize_callbackRegistrationFailure() throws Exception {
201        executeAndValidateInitializationSequence(false, false, false, true);
202    }
203
204    /**
205     * Tests the loading of networks using {@link SupplicantStaNetworkHal}.
206     * Fills up only the SSID field of configs and uses it as a configKey as well.
207     */
208    @Test
209    public void testLoadNetworks() throws Exception {
210        executeAndValidateInitializationSequence();
211        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
212            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
213                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
214            }
215        }).when(mISupplicantStaIfaceMock)
216                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
217        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
218            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
219                // Reset the |mSupplicantStaNetwork| mock for each network.
220                doAnswer(new MockAnswerUtil.AnswerWithArguments() {
221                    public boolean answer(
222                            WifiConfiguration config, Map<String, String> networkExtra) {
223                        config.SSID = NETWORK_ID_TO_SSID.get(networkId);
224                        config.networkId = networkId;
225                        networkExtra.put(
226                                SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY, config.SSID);
227                        return true;
228                    }
229                }).when(mSupplicantStaNetworkMock)
230                        .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
231                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
232                return;
233            }
234        }).when(mISupplicantStaIfaceMock)
235                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
236
237        Map<String, WifiConfiguration> configs = new HashMap<>();
238        SparseArray<Map<String, String>> extras = new SparseArray<>();
239        assertTrue(mDut.loadNetworks(configs, extras));
240
241        assertEquals(3, configs.size());
242        assertEquals(3, extras.size());
243        for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
244            WifiConfiguration config = configs.get(network.getValue());
245            assertTrue(config != null);
246            assertEquals(network.getKey(), Integer.valueOf(config.networkId));
247            assertEquals(network.getValue(), config.SSID);
248            assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
249            assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
250        }
251    }
252
253    /**
254     * Tests the loading of networks using {@link SupplicantStaNetworkHal} removes any networks
255     * with duplicate config key.
256     * Fills up only the SSID field of configs and uses it as a configKey as well.
257     */
258    @Test
259    public void testLoadNetworksRemovesDuplicates() throws Exception {
260        // Network ID which will have the same config key as the previous one.
261        final int duplicateNetworkId = 2;
262        final int toRemoveNetworkId = duplicateNetworkId - 1;
263        executeAndValidateInitializationSequence();
264        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
265            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
266                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
267            }
268        }).when(mISupplicantStaIfaceMock)
269                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
270        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
271            public SupplicantStatus answer(int id) {
272                return mStatusSuccess;
273            }
274        }).when(mISupplicantStaIfaceMock).removeNetwork(eq(toRemoveNetworkId));
275        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
276            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
277                // Reset the |mSupplicantStaNetwork| mock for each network.
278                doAnswer(new MockAnswerUtil.AnswerWithArguments() {
279                    public boolean answer(
280                            WifiConfiguration config, Map<String, String> networkExtra) {
281                        config.SSID = NETWORK_ID_TO_SSID.get(networkId);
282                        config.networkId = networkId;
283                        // Duplicate network gets the same config key as the to removed one.
284                        if (networkId == duplicateNetworkId) {
285                            networkExtra.put(
286                                    SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
287                                    NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
288                        } else {
289                            networkExtra.put(
290                                    SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
291                                    NETWORK_ID_TO_SSID.get(networkId));
292                        }
293                        return true;
294                    }
295                }).when(mSupplicantStaNetworkMock)
296                        .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
297                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
298                return;
299            }
300        }).when(mISupplicantStaIfaceMock)
301                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
302
303        Map<String, WifiConfiguration> configs = new HashMap<>();
304        SparseArray<Map<String, String>> extras = new SparseArray<>();
305        assertTrue(mDut.loadNetworks(configs, extras));
306
307        assertEquals(2, configs.size());
308        assertEquals(2, extras.size());
309        for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
310            if (network.getKey() == toRemoveNetworkId) {
311                continue;
312            }
313            WifiConfiguration config;
314            // Duplicate network gets the same config key as the to removed one. So, use that to
315            // lookup the map.
316            if (network.getKey() == duplicateNetworkId) {
317                config = configs.get(NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
318            } else {
319                config = configs.get(network.getValue());
320            }
321            assertTrue(config != null);
322            assertEquals(network.getKey(), Integer.valueOf(config.networkId));
323            assertEquals(network.getValue(), config.SSID);
324            assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
325            assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
326        }
327    }
328
329    /**
330     * Tests the failure to load networks because of listNetworks failure.
331     */
332    @Test
333    public void testLoadNetworksFailedDueToListNetworks() throws Exception {
334        executeAndValidateInitializationSequence();
335        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
336            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
337                cb.onValues(mStatusFailure, null);
338            }
339        }).when(mISupplicantStaIfaceMock)
340                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
341
342        Map<String, WifiConfiguration> configs = new HashMap<>();
343        SparseArray<Map<String, String>> extras = new SparseArray<>();
344        assertFalse(mDut.loadNetworks(configs, extras));
345    }
346
347    /**
348     * Tests the failure to load networks because of getNetwork failure.
349     */
350    @Test
351    public void testLoadNetworksFailedDueToGetNetwork() throws Exception {
352        executeAndValidateInitializationSequence();
353        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
354            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
355                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
356            }
357        }).when(mISupplicantStaIfaceMock)
358                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
359        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
360            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
361                cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
362                return;
363            }
364        }).when(mISupplicantStaIfaceMock)
365                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
366
367        Map<String, WifiConfiguration> configs = new HashMap<>();
368        SparseArray<Map<String, String>> extras = new SparseArray<>();
369        assertFalse(mDut.loadNetworks(configs, extras));
370    }
371
372    /**
373     * Tests the failure to load networks because of loadWifiConfiguration failure.
374     */
375    @Test
376    public void testLoadNetworksFailedDueToLoadWifiConfiguration() throws Exception {
377        executeAndValidateInitializationSequence();
378        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
379            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
380                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
381            }
382        }).when(mISupplicantStaIfaceMock)
383                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
384        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
385            public boolean answer(WifiConfiguration config, Map<String, String> networkExtra) {
386                return false;
387            }
388        }).when(mSupplicantStaNetworkMock)
389                .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
390
391        Map<String, WifiConfiguration> configs = new HashMap<>();
392        SparseArray<Map<String, String>> extras = new SparseArray<>();
393        assertFalse(mDut.loadNetworks(configs, extras));
394    }
395
396    /**
397     * Tests connection to a specified network without triggering disconnect.
398     */
399    @Test
400    public void testConnectWithNoDisconnectAndEmptyExistingNetworks() throws Exception {
401        executeAndValidateInitializationSequence();
402        executeAndValidateConnectSequence(0, false, false);
403    }
404
405    /**
406     * Tests connection to a specified network without triggering disconnect.
407     */
408    @Test
409    public void testConnectWithNoDisconnectAndSingleExistingNetwork() throws Exception {
410        executeAndValidateInitializationSequence();
411        executeAndValidateConnectSequence(0, true, false);
412    }
413
414    /**
415     * Tests connection to a specified network, with a triggered disconnect.
416     */
417    @Test
418    public void testConnectWithDisconnectAndSingleExistingNetwork() throws Exception {
419        executeAndValidateInitializationSequence();
420        executeAndValidateConnectSequence(0, false, true);
421    }
422
423    /**
424     * Tests connection to a specified network failure due to network add.
425     */
426    @Test
427    public void testConnectFailureDueToNetworkAddFailure() throws Exception {
428        executeAndValidateInitializationSequence();
429        setupMocksForConnectSequence(false);
430        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
431            public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
432                cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
433                return;
434            }
435        }).when(mISupplicantStaIfaceMock).addNetwork(
436                any(ISupplicantStaIface.addNetworkCallback.class));
437
438        assertFalse(mDut.connectToNetwork(new WifiConfiguration(), false));
439    }
440
441    /**
442     * Tests connection to a specified network failure due to network save.
443     */
444    @Test
445    public void testConnectFailureDueToNetworkSaveFailure() throws Exception {
446        executeAndValidateInitializationSequence();
447        setupMocksForConnectSequence(false);
448
449        when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
450                .thenReturn(false);
451
452        assertFalse(mDut.connectToNetwork(new WifiConfiguration(), false));
453    }
454
455    /**
456     * Tests connection to a specified network failure due to network select.
457     */
458    @Test
459    public void testConnectFailureDueToNetworkSelectFailure() throws Exception {
460        executeAndValidateInitializationSequence();
461        setupMocksForConnectSequence(false);
462
463        when(mSupplicantStaNetworkMock.select()).thenReturn(false);
464
465        assertFalse(mDut.connectToNetwork(new WifiConfiguration(), false));
466    }
467
468    /**
469     * Tests roaming to the same network as the currently connected one.
470     */
471    @Test
472    public void testRoamToSameNetwork() throws Exception {
473        executeAndValidateInitializationSequence();
474        executeAndValidateRoamSequence(true);
475    }
476
477    /**
478     * Tests roaming to a different network.
479     */
480    @Test
481    public void testRoamToDifferentNetwork() throws Exception {
482        executeAndValidateInitializationSequence();
483        executeAndValidateRoamSequence(false);
484    }
485
486    /**
487     * Tests roaming failure because of unable to set bssid.
488     */
489    @Test
490    public void testRoamFailureDueToBssidSet() throws Exception {
491        executeAndValidateInitializationSequence();
492        int connectedNetworkId = 5;
493        executeAndValidateConnectSequence(connectedNetworkId, false, false);
494        when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(false);
495
496        WifiConfiguration roamingConfig = new WifiConfiguration();
497        roamingConfig.networkId = connectedNetworkId;
498        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
499        assertFalse(mDut.roamToNetwork(roamingConfig));
500    }
501
502    /**
503     * Tests removal of all configured networks from wpa_supplicant.
504     */
505    @Test
506    public void testRemoveAllNetworks() throws Exception {
507        executeAndValidateInitializationSequence();
508        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
509            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
510                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
511            }
512        }).when(mISupplicantStaIfaceMock)
513                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
514        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
515            public SupplicantStatus answer(int id) {
516                assertTrue(NETWORK_ID_TO_SSID.containsKey(id));
517                return mStatusSuccess;
518            }
519        }).when(mISupplicantStaIfaceMock).removeNetwork(anyInt());
520
521        assertTrue(mDut.removeAllNetworks());
522        verify(mISupplicantStaIfaceMock, times(NETWORK_ID_TO_SSID.size())).removeNetwork(anyInt());
523    }
524
525    /**
526     * Tests roaming failure because of unable to reassociate.
527     */
528    @Test
529    public void testRoamFailureDueToReassociate() throws Exception {
530        executeAndValidateInitializationSequence();
531        int connectedNetworkId = 5;
532        executeAndValidateConnectSequence(connectedNetworkId, false, false);
533
534        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
535            public SupplicantStatus answer() throws RemoteException {
536                return mStatusFailure;
537            }
538        }).when(mISupplicantStaIfaceMock).reassociate();
539        when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(true);
540
541        WifiConfiguration roamingConfig = new WifiConfiguration();
542        roamingConfig.networkId = connectedNetworkId;
543        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
544        assertFalse(mDut.roamToNetwork(roamingConfig));
545    }
546
547    /**
548     * Tests the retrieval of WPS NFC token.
549     */
550    @Test
551    public void testGetCurrentNetworkWpsNfcConfigurationToken() throws Exception {
552        String token = "45adbc1";
553        when(mSupplicantStaNetworkMock.getWpsNfcConfigurationToken()).thenReturn(token);
554
555        executeAndValidateInitializationSequence();
556
557        // Return null when not connected to the network.
558        assertTrue(mDut.getCurrentNetworkWpsNfcConfigurationToken() == null);
559
560        executeAndValidateConnectSequence(4, false, false);
561
562        assertEquals(token, mDut.getCurrentNetworkWpsNfcConfigurationToken());
563    }
564
565    /**
566     * Tests the setting of BSSID.
567     */
568    @Test
569    public void testSetCurrentNetworkBssid() throws Exception {
570        String bssidStr = "34:34:12:12:12:90";
571        when(mSupplicantStaNetworkMock.setBssid(eq(bssidStr))).thenReturn(true);
572
573        executeAndValidateInitializationSequence();
574
575        // Fail when not connected to a network.
576        assertFalse(mDut.setCurrentNetworkBssid(bssidStr));
577
578        executeAndValidateConnectSequence(4, false, false);
579
580        assertTrue(mDut.setCurrentNetworkBssid(bssidStr));
581    }
582
583    /**
584     * Tests the setting of WPS device type.
585     */
586    @Test
587    public void testSetWpsDeviceType() throws Exception {
588        String validDeviceTypeStr = "10-0050F204-5";
589        byte[] expectedDeviceType = { 0x0, 0xa, 0x0, 0x50, (byte) 0xf2, 0x04, 0x0, 0x05};
590        String invalidDeviceType1Str = "10-02050F204-5";
591        String invalidDeviceType2Str = "10-0050F204-534";
592        when(mISupplicantStaIfaceMock.setWpsDeviceType(any(byte[].class)))
593                .thenReturn(mStatusSuccess);
594
595        executeAndValidateInitializationSequence();
596
597        // This should work.
598        assertTrue(mDut.setWpsDeviceType(validDeviceTypeStr));
599        verify(mISupplicantStaIfaceMock).setWpsDeviceType(eq(expectedDeviceType));
600
601        // This should not work
602        assertFalse(mDut.setWpsDeviceType(invalidDeviceType1Str));
603        // This should not work
604        assertFalse(mDut.setWpsDeviceType(invalidDeviceType2Str));
605    }
606
607    /**
608     * Tests the setting of WPS config methods.
609     */
610    @Test
611    public void testSetWpsConfigMethods() throws Exception {
612        String validConfigMethodsStr = "physical_display virtual_push_button";
613        Short expectedConfigMethods =
614                WpsConfigMethods.PHY_DISPLAY | WpsConfigMethods.VIRT_PUSHBUTTON;
615        String invalidConfigMethodsStr = "physical_display virtual_push_button test";
616        when(mISupplicantStaIfaceMock.setWpsConfigMethods(anyShort())).thenReturn(mStatusSuccess);
617
618        executeAndValidateInitializationSequence();
619
620        // This should work.
621        assertTrue(mDut.setWpsConfigMethods(validConfigMethodsStr));
622        verify(mISupplicantStaIfaceMock).setWpsConfigMethods(eq(expectedConfigMethods));
623
624        // This should throw an illegal argument exception.
625        try {
626            assertFalse(mDut.setWpsConfigMethods(invalidConfigMethodsStr));
627        } catch (IllegalArgumentException e) {
628            return;
629        }
630        assertTrue(false);
631    }
632
633    /**
634     * Tests the handling of ANQP done callback.
635     * Note: Since the ANQP element parsing methods are static, this can only test the negative test
636     * where all the parsing fails because the data is empty. It'll be non-trivial and unnecessary
637     * to test out the parsing logic here.
638     */
639    @Test
640    public void testAnqpDoneCallback() throws Exception {
641        executeAndValidateInitializationSequence();
642        assertNotNull(mISupplicantStaIfaceCallback);
643        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
644        mISupplicantStaIfaceCallback.onAnqpQueryDone(
645                bssid, new ISupplicantStaIfaceCallback.AnqpData(),
646                new ISupplicantStaIfaceCallback.Hs20AnqpData());
647
648        ArgumentCaptor<AnqpEvent> anqpEventCaptor = ArgumentCaptor.forClass(AnqpEvent.class);
649        verify(mWifiMonitor).broadcastAnqpDoneEvent(eq(WLAN_IFACE_NAME), anqpEventCaptor.capture());
650        assertEquals(
651                ByteBufferReader.readInteger(
652                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
653                anqpEventCaptor.getValue().getBssid());
654    }
655
656    /**
657     * Tests the handling of Icon done callback.
658     */
659    @Test
660    public void testIconDoneCallback() throws Exception {
661        executeAndValidateInitializationSequence();
662        assertNotNull(mISupplicantStaIfaceCallback);
663
664        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
665        byte[] iconData = new byte[ICON_FILE_SIZE];
666        new Random().nextBytes(iconData);
667        mISupplicantStaIfaceCallback.onHs20IconQueryDone(
668                bssid, ICON_FILE_NAME, NativeUtil.byteArrayToArrayList(iconData));
669
670        ArgumentCaptor<IconEvent> iconEventCaptor = ArgumentCaptor.forClass(IconEvent.class);
671        verify(mWifiMonitor).broadcastIconDoneEvent(eq(WLAN_IFACE_NAME), iconEventCaptor.capture());
672        assertEquals(
673                ByteBufferReader.readInteger(
674                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
675                iconEventCaptor.getValue().getBSSID());
676        assertEquals(ICON_FILE_NAME, iconEventCaptor.getValue().getFileName());
677        assertArrayEquals(iconData, iconEventCaptor.getValue().getData());
678    }
679
680    /**
681     * Tests the handling of HS20 subscription remediation callback.
682     */
683    @Test
684    public void testHs20SubscriptionRemediationCallback() throws Exception {
685        executeAndValidateInitializationSequence();
686        assertNotNull(mISupplicantStaIfaceCallback);
687
688        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
689        byte osuMethod = ISupplicantStaIfaceCallback.OsuMethod.OMA_DM;
690        mISupplicantStaIfaceCallback.onHs20SubscriptionRemediation(
691                bssid, osuMethod, HS20_URL);
692
693        ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
694        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
695        assertEquals(
696                ByteBufferReader.readInteger(
697                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
698                wnmDataCaptor.getValue().getBssid());
699        assertEquals(osuMethod, wnmDataCaptor.getValue().getMethod());
700        assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
701    }
702
703    /**
704     * Tests the handling of HS20 deauth imminent callback.
705     */
706    @Test
707    public void testHs20DeauthImminentCallbackWithEssReasonCode() throws Exception {
708        executeAndValidateHs20DeauthImminentCallback(true);
709    }
710
711    /**
712     * Tests the handling of HS20 deauth imminent callback.
713     */
714    @Test
715    public void testHs20DeauthImminentCallbackWithNonEssReasonCode() throws Exception {
716        executeAndValidateHs20DeauthImminentCallback(false);
717
718    }
719
720    private void executeAndValidateHs20DeauthImminentCallback(boolean isEss) throws Exception {
721        executeAndValidateInitializationSequence();
722        assertNotNull(mISupplicantStaIfaceCallback);
723
724        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
725        int reasonCode = isEss ? WnmData.ESS : WnmData.ESS + 1;
726        int reauthDelay = 5;
727        mISupplicantStaIfaceCallback.onHs20DeauthImminentNotice(
728                bssid, reasonCode, reauthDelay, HS20_URL);
729
730        ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
731        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
732        assertEquals(
733                ByteBufferReader.readInteger(
734                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
735                wnmDataCaptor.getValue().getBssid());
736        assertEquals(isEss, wnmDataCaptor.getValue().isEss());
737        assertEquals(reauthDelay, wnmDataCaptor.getValue().getDelay());
738        assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
739    }
740
741    private void executeAndValidateInitializationSequence() throws  Exception {
742        executeAndValidateInitializationSequence(false, false, false, false);
743    }
744
745    /**
746     * Tests the setting of log level.
747     */
748    @Test
749    public void testSetLogLevel() throws Exception {
750        when(mISupplicantMock.setDebugParams(anyInt(), anyBoolean(), anyBoolean()))
751                .thenReturn(mStatusSuccess);
752
753        // Fail before initialization is performed.
754        assertFalse(mDut.setLogLevel(SupplicantStaIfaceHal.LOG_LEVEL_DEBUG));
755
756        executeAndValidateInitializationSequence();
757
758        // This should work.
759        assertTrue(mDut.setLogLevel(SupplicantStaIfaceHal.LOG_LEVEL_DEBUG));
760        verify(mISupplicantMock)
761                .setDebugParams(eq(ISupplicant.DebugLevel.DEBUG), eq(false), eq(false));
762    }
763
764    /**
765     * Tests the setting of concurrency priority.
766     */
767    @Test
768    public void testConcurrencyPriority() throws Exception {
769        when(mISupplicantMock.setConcurrencyPriority(anyInt())).thenReturn(mStatusSuccess);
770
771        // Fail before initialization is performed.
772        assertFalse(mDut.setConcurrencyPriority(false));
773
774        executeAndValidateInitializationSequence();
775
776        // This should work.
777        assertTrue(mDut.setConcurrencyPriority(false));
778        verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.P2P));
779        assertTrue(mDut.setConcurrencyPriority(true));
780        verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.STA));
781    }
782
783    /**
784     * Calls.initialize(), mocking various call back answers and verifying flow, asserting for the
785     * expected result. Verifies if ISupplicantStaIface manager is initialized or reset.
786     * Each of the arguments will cause a different failure mode when set true.
787     */
788    private void executeAndValidateInitializationSequence(boolean causeRemoteException,
789                                                          boolean getZeroInterfaces,
790                                                          boolean getNullInterface,
791                                                          boolean causeCallbackRegFailure)
792            throws Exception {
793        boolean shouldSucceed =
794                !causeRemoteException && !getZeroInterfaces && !getNullInterface
795                        && !causeCallbackRegFailure;
796        // Setup callback mock answers
797        ArrayList<ISupplicant.IfaceInfo> interfaces;
798        if (getZeroInterfaces) {
799            interfaces = new ArrayList<>();
800        } else {
801            interfaces = mIfaceInfoList;
802        }
803        doAnswer(new GetListInterfacesAnswer(interfaces)).when(mISupplicantMock)
804                .listInterfaces(any(ISupplicant.listInterfacesCallback.class));
805        if (causeRemoteException) {
806            doThrow(new RemoteException("Some error!!!"))
807                    .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
808                    any(ISupplicant.getInterfaceCallback.class));
809        } else {
810            doAnswer(new GetGetInterfaceAnswer(getNullInterface))
811                    .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
812                    any(ISupplicant.getInterfaceCallback.class));
813        }
814        /** Callback registeration */
815        if (causeCallbackRegFailure) {
816            doAnswer(new MockAnswerUtil.AnswerWithArguments() {
817                public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
818                        throws RemoteException {
819                    return mStatusFailure;
820                }
821            }).when(mISupplicantStaIfaceMock)
822                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
823        } else {
824            doAnswer(new MockAnswerUtil.AnswerWithArguments() {
825                public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
826                        throws RemoteException {
827                    mISupplicantStaIfaceCallback = cb;
828                    return mStatusSuccess;
829                }
830            }).when(mISupplicantStaIfaceMock)
831                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
832        }
833
834        mInOrder = inOrder(mServiceManagerMock, mISupplicantMock, mISupplicantStaIfaceMock);
835        // Initialize SupplicantStaIfaceHal, should call serviceManager.registerForNotifications
836        assertTrue(mDut.initialize());
837        // verify: service manager initialization sequence
838        mInOrder.verify(mServiceManagerMock).linkToDeath(any(IHwBinder.DeathRecipient.class),
839                anyLong());
840        mInOrder.verify(mServiceManagerMock).registerForNotifications(
841                eq(ISupplicant.kInterfaceName), eq(""), mServiceNotificationCaptor.capture());
842        // act: cause the onRegistration(...) callback to execute
843        mServiceNotificationCaptor.getValue().onRegistration(ISupplicant.kInterfaceName, "", true);
844
845        assertTrue(mDut.isInitializationComplete() == shouldSucceed);
846        // verify: listInterfaces is called
847        mInOrder.verify(mISupplicantMock).listInterfaces(
848                any(ISupplicant.listInterfacesCallback.class));
849        if (!getZeroInterfaces) {
850            mInOrder.verify(mISupplicantMock)
851                    .getInterface(any(ISupplicant.IfaceInfo.class),
852                            any(ISupplicant.getInterfaceCallback.class));
853        }
854        if (!causeRemoteException && !getZeroInterfaces && !getNullInterface) {
855            mInOrder.verify(mISupplicantStaIfaceMock)
856                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
857        }
858    }
859
860    private SupplicantStatus createSupplicantStatus(int code) {
861        SupplicantStatus status = new SupplicantStatus();
862        status.code = code;
863        return status;
864    }
865
866    /**
867     * Create an IfaceInfo with given type and name
868     */
869    private ISupplicant.IfaceInfo createIfaceInfo(int type, String name) {
870        ISupplicant.IfaceInfo info = new ISupplicant.IfaceInfo();
871        info.type = type;
872        info.name = name;
873        return info;
874    }
875
876    private class GetListInterfacesAnswer extends MockAnswerUtil.AnswerWithArguments {
877        private ArrayList<ISupplicant.IfaceInfo> mInterfaceList;
878
879        GetListInterfacesAnswer(ArrayList<ISupplicant.IfaceInfo> ifaces) {
880            mInterfaceList = ifaces;
881        }
882
883        public void answer(ISupplicant.listInterfacesCallback cb) {
884            cb.onValues(mStatusSuccess, mInterfaceList);
885        }
886    }
887
888    private class GetGetInterfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
889        boolean mGetNullInterface;
890
891        GetGetInterfaceAnswer(boolean getNullInterface) {
892            mGetNullInterface = getNullInterface;
893        }
894
895        public void answer(ISupplicant.IfaceInfo iface, ISupplicant.getInterfaceCallback cb) {
896            if (mGetNullInterface) {
897                cb.onValues(mStatusSuccess, null);
898            } else {
899                cb.onValues(mStatusSuccess, mISupplicantIfaceMock);
900            }
901        }
902    }
903
904    /**
905     * Setup mocks for connect sequence.
906     */
907    private void setupMocksForConnectSequence(final boolean haveExistingNetwork) throws Exception {
908        final int existingNetworkId = EXISTING_SUPPLICANT_NETWORK_ID;
909        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
910            public SupplicantStatus answer() throws RemoteException {
911                return mStatusSuccess;
912            }
913        }).when(mISupplicantStaIfaceMock).disconnect();
914        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
915            public void answer(ISupplicantStaIface.listNetworksCallback cb) throws RemoteException {
916                if (haveExistingNetwork) {
917                    cb.onValues(mStatusSuccess, new ArrayList<>(Arrays.asList(existingNetworkId)));
918                } else {
919                    cb.onValues(mStatusSuccess, new ArrayList<>());
920                }
921            }
922        }).when(mISupplicantStaIfaceMock)
923                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
924        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
925            public SupplicantStatus answer(int id) throws RemoteException {
926                return mStatusSuccess;
927            }
928        }).when(mISupplicantStaIfaceMock).removeNetwork(eq(existingNetworkId));
929        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
930            public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
931                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
932                return;
933            }
934        }).when(mISupplicantStaIfaceMock).addNetwork(
935                any(ISupplicantStaIface.addNetworkCallback.class));
936        when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
937                .thenReturn(true);
938        when(mSupplicantStaNetworkMock.select()).thenReturn(true);
939    }
940
941    /**
942     * Helper function to validate the connect sequence.
943     */
944    private void validateConnectSequence(
945            final boolean haveExistingNetwork, boolean shouldDisconnect, int numNetworkAdditions)
946            throws Exception {
947        if (shouldDisconnect) {
948            verify(mISupplicantStaIfaceMock).disconnect();
949        }
950        if (haveExistingNetwork) {
951            verify(mISupplicantStaIfaceMock).removeNetwork(anyInt());
952        }
953        verify(mISupplicantStaIfaceMock, times(numNetworkAdditions))
954                .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
955        verify(mSupplicantStaNetworkMock, times(numNetworkAdditions))
956                .saveWifiConfiguration(any(WifiConfiguration.class));
957        verify(mSupplicantStaNetworkMock, times(numNetworkAdditions)).select();
958    }
959
960    /**
961     * Helper function to execute all the actions to perform connection to the network.
962     *
963     * @param newFrameworkNetworkId Framework Network Id of the new network to connect.
964     * @param haveExistingNetwork Removes the existing network.
965     * @param shouldDisconnect Should trigger disconnect before connecting.
966     */
967    private void executeAndValidateConnectSequence(
968            final int newFrameworkNetworkId, final boolean haveExistingNetwork,
969            boolean shouldDisconnect) throws Exception {
970        setupMocksForConnectSequence(haveExistingNetwork);
971        WifiConfiguration config = new WifiConfiguration();
972        config.networkId = newFrameworkNetworkId;
973        assertTrue(mDut.connectToNetwork(config, shouldDisconnect));
974        validateConnectSequence(haveExistingNetwork, shouldDisconnect, 1);
975    }
976
977    /**
978     * Setup mocks for roam sequence.
979     */
980    private void setupMocksForRoamSequence(String roamBssid) throws Exception {
981        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
982            public SupplicantStatus answer() throws RemoteException {
983                return mStatusSuccess;
984            }
985        }).when(mISupplicantStaIfaceMock).reassociate();
986        when(mSupplicantStaNetworkMock.setBssid(eq(roamBssid))).thenReturn(true);
987    }
988
989    /**
990     * Helper function to execute all the actions to perform roaming to the network.
991     *
992     * @param sameNetwork Roam to the same network or not.
993     */
994    private void executeAndValidateRoamSequence(boolean sameNetwork) throws Exception {
995        int connectedNetworkId = ROAM_NETWORK_ID;
996        String roamBssid = BSSID;
997        int roamNetworkId;
998        if (sameNetwork) {
999            roamNetworkId = connectedNetworkId;
1000        } else {
1001            roamNetworkId = connectedNetworkId + 1;
1002        }
1003        executeAndValidateConnectSequence(connectedNetworkId, false, true);
1004        setupMocksForRoamSequence(roamBssid);
1005
1006        WifiConfiguration roamingConfig = new WifiConfiguration();
1007        roamingConfig.networkId = roamNetworkId;
1008        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID(roamBssid);
1009        assertTrue(mDut.roamToNetwork(roamingConfig));
1010
1011        if (!sameNetwork) {
1012            validateConnectSequence(false, false, 2);
1013            verify(mSupplicantStaNetworkMock, never()).setBssid(anyString());
1014            verify(mISupplicantStaIfaceMock, never()).reassociate();
1015        } else {
1016            verify(mSupplicantStaNetworkMock).setBssid(eq(roamBssid));
1017            verify(mISupplicantStaIfaceMock).reassociate();
1018        }
1019    }
1020}
1021