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.reset;
32import static org.mockito.Mockito.times;
33import static org.mockito.Mockito.verify;
34import static org.mockito.Mockito.when;
35
36import android.app.test.MockAnswerUtil;
37import android.content.Context;
38import android.hardware.wifi.supplicant.V1_0.ISupplicant;
39import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
40import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
41import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
42import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
43import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
44import android.hardware.wifi.supplicant.V1_0.IfaceType;
45import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
46import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
47import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
48import android.hidl.manager.V1_0.IServiceManager;
49import android.hidl.manager.V1_0.IServiceNotification;
50import android.net.IpConfiguration;
51import android.net.wifi.SupplicantState;
52import android.net.wifi.WifiConfiguration;
53import android.net.wifi.WifiManager;
54import android.net.wifi.WifiSsid;
55import android.os.IHwBinder;
56import android.os.RemoteException;
57import android.util.SparseArray;
58
59import com.android.server.wifi.hotspot2.AnqpEvent;
60import com.android.server.wifi.hotspot2.IconEvent;
61import com.android.server.wifi.hotspot2.WnmData;
62import com.android.server.wifi.util.NativeUtil;
63
64import org.junit.Before;
65import org.junit.Test;
66import org.mockito.ArgumentCaptor;
67import org.mockito.InOrder;
68import org.mockito.Mock;
69import org.mockito.MockitoAnnotations;
70
71import java.nio.ByteBuffer;
72import java.nio.ByteOrder;
73import java.util.ArrayList;
74import java.util.Arrays;
75import java.util.HashMap;
76import java.util.Map;
77import java.util.Random;
78
79/**
80 * Unit tests for SupplicantStaIfaceHal
81 */
82public class SupplicantStaIfaceHalTest {
83    private static final String TAG = "SupplicantStaIfaceHalTest";
84    private static final Map<Integer, String> NETWORK_ID_TO_SSID = new HashMap<Integer, String>() {{
85            put(1, "\"ssid1\"");
86            put(2, "\"ssid2\"");
87            put(3, "\"ssid3\"");
88        }};
89    private static final int SUPPLICANT_NETWORK_ID = 2;
90    private static final String SUPPLICANT_SSID = NETWORK_ID_TO_SSID.get(SUPPLICANT_NETWORK_ID);
91    private static final int ROAM_NETWORK_ID = 4;
92    private static final String BSSID = "fa:45:23:23:12:12";
93    private static final String WLAN_IFACE_NAME = "wlan0";
94    private static final String P2P_IFACE_NAME = "p2p0";
95    private static final String ICON_FILE_NAME  = "blahblah";
96    private static final int ICON_FILE_SIZE = 72;
97    private static final String HS20_URL = "http://blahblah";
98
99    @Mock IServiceManager mServiceManagerMock;
100    @Mock ISupplicant mISupplicantMock;
101    @Mock ISupplicantIface mISupplicantIfaceMock;
102    @Mock ISupplicantStaIface mISupplicantStaIfaceMock;
103    @Mock Context mContext;
104    @Mock WifiMonitor mWifiMonitor;
105    @Mock SupplicantStaNetworkHal mSupplicantStaNetworkMock;
106    SupplicantStatus mStatusSuccess;
107    SupplicantStatus mStatusFailure;
108    ISupplicant.IfaceInfo mStaIface;
109    ISupplicant.IfaceInfo mP2pIface;
110    ArrayList<ISupplicant.IfaceInfo> mIfaceInfoList;
111    ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
112    private SupplicantStaIfaceHal mDut;
113    private ArgumentCaptor<IHwBinder.DeathRecipient> mServiceManagerDeathCaptor =
114            ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
115    private ArgumentCaptor<IHwBinder.DeathRecipient> mSupplicantDeathCaptor =
116            ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
117    private ArgumentCaptor<IHwBinder.DeathRecipient> mSupplicantStaIfaceDeathCaptor =
118            ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
119    private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
120            ArgumentCaptor.forClass(IServiceNotification.Stub.class);
121    private InOrder mInOrder;
122
123    private class SupplicantStaIfaceHalSpy extends SupplicantStaIfaceHal {
124        SupplicantStaIfaceHalSpy(Context context, WifiMonitor monitor) {
125            super(context, monitor);
126        }
127
128        @Override
129        protected IServiceManager getServiceManagerMockable() throws RemoteException {
130            return mServiceManagerMock;
131        }
132
133        @Override
134        protected ISupplicant getSupplicantMockable() throws RemoteException {
135            return mISupplicantMock;
136        }
137
138        @Override
139        protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
140            return mISupplicantStaIfaceMock;
141        }
142
143        @Override
144        protected SupplicantStaNetworkHal getStaNetworkMockable(
145                ISupplicantStaNetwork iSupplicantStaNetwork) {
146            return mSupplicantStaNetworkMock;
147        }
148    }
149
150    @Before
151    public void setUp() throws Exception {
152        MockitoAnnotations.initMocks(this);
153        mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
154        mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
155        mStaIface = createIfaceInfo(IfaceType.STA, WLAN_IFACE_NAME);
156        mP2pIface = createIfaceInfo(IfaceType.P2P, P2P_IFACE_NAME);
157
158        mIfaceInfoList = new ArrayList<>();
159        mIfaceInfoList.add(mStaIface);
160        mIfaceInfoList.add(mP2pIface);
161
162        when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
163                anyLong())).thenReturn(true);
164        when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
165                any(IServiceNotification.Stub.class))).thenReturn(true);
166        when(mISupplicantMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
167                anyLong())).thenReturn(true);
168        when(mISupplicantStaIfaceMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
169                anyLong())).thenReturn(true);
170        mDut = new SupplicantStaIfaceHalSpy(mContext, mWifiMonitor);
171    }
172
173    /**
174     * Sunny day scenario for SupplicantStaIfaceHal initialization
175     * Asserts successful initialization
176     */
177    @Test
178    public void testInitialize_success() throws Exception {
179        executeAndValidateInitializationSequence(false, false, false, false);
180    }
181
182    /**
183     * Tests the initialization flow, with a RemoteException occurring when 'getInterface' is called
184     * Ensures initialization fails.
185     */
186    @Test
187    public void testInitialize_remoteExceptionFailure() throws Exception {
188        executeAndValidateInitializationSequence(true, false, false, false);
189    }
190
191    /**
192     * Tests the initialization flow, with listInterfaces returning 0 interfaces.
193     * Ensures failure
194     */
195    @Test
196    public void testInitialize_zeroInterfacesFailure() throws Exception {
197        executeAndValidateInitializationSequence(false, true, false, false);
198    }
199
200    /**
201     * Tests the initialization flow, with a null interface being returned by getInterface.
202     * Ensures initialization fails.
203     */
204    @Test
205    public void testInitialize_nullInterfaceFailure() throws Exception {
206        executeAndValidateInitializationSequence(false, false, true, false);
207    }
208
209    /**
210     * Tests the initialization flow, with a callback registration failure.
211     * Ensures initialization fails.
212     */
213    @Test
214    public void testInitialize_callbackRegistrationFailure() throws Exception {
215        executeAndValidateInitializationSequence(false, false, false, true);
216    }
217
218    /**
219     * Tests the loading of networks using {@link SupplicantStaNetworkHal}.
220     * Fills up only the SSID field of configs and uses it as a configKey as well.
221     */
222    @Test
223    public void testLoadNetworks() throws Exception {
224        executeAndValidateInitializationSequence();
225        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
226            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
227                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
228            }
229        }).when(mISupplicantStaIfaceMock)
230                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
231        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
232            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
233                // Reset the |mSupplicantStaNetwork| mock for each network.
234                doAnswer(new MockAnswerUtil.AnswerWithArguments() {
235                    public boolean answer(
236                            WifiConfiguration config, Map<String, String> networkExtra) {
237                        config.SSID = NETWORK_ID_TO_SSID.get(networkId);
238                        config.networkId = networkId;
239                        networkExtra.put(
240                                SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY, config.SSID);
241                        return true;
242                    }
243                }).when(mSupplicantStaNetworkMock)
244                        .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
245                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
246                return;
247            }
248        }).when(mISupplicantStaIfaceMock)
249                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
250
251        Map<String, WifiConfiguration> configs = new HashMap<>();
252        SparseArray<Map<String, String>> extras = new SparseArray<>();
253        assertTrue(mDut.loadNetworks(configs, extras));
254
255        assertEquals(3, configs.size());
256        assertEquals(3, extras.size());
257        for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
258            WifiConfiguration config = configs.get(network.getValue());
259            assertTrue(config != null);
260            assertEquals(network.getKey(), Integer.valueOf(config.networkId));
261            assertEquals(network.getValue(), config.SSID);
262            assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
263            assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
264        }
265    }
266
267    /**
268     * Tests the loading of networks using {@link SupplicantStaNetworkHal} removes any networks
269     * with duplicate config key.
270     * Fills up only the SSID field of configs and uses it as a configKey as well.
271     */
272    @Test
273    public void testLoadNetworksRemovesDuplicates() throws Exception {
274        // Network ID which will have the same config key as the previous one.
275        final int duplicateNetworkId = 2;
276        final int toRemoveNetworkId = duplicateNetworkId - 1;
277        executeAndValidateInitializationSequence();
278        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
279            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
280                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
281            }
282        }).when(mISupplicantStaIfaceMock)
283                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
284        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
285            public SupplicantStatus answer(int id) {
286                return mStatusSuccess;
287            }
288        }).when(mISupplicantStaIfaceMock).removeNetwork(eq(toRemoveNetworkId));
289        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
290            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
291                // Reset the |mSupplicantStaNetwork| mock for each network.
292                doAnswer(new MockAnswerUtil.AnswerWithArguments() {
293                    public boolean answer(
294                            WifiConfiguration config, Map<String, String> networkExtra) {
295                        config.SSID = NETWORK_ID_TO_SSID.get(networkId);
296                        config.networkId = networkId;
297                        // Duplicate network gets the same config key as the to removed one.
298                        if (networkId == duplicateNetworkId) {
299                            networkExtra.put(
300                                    SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
301                                    NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
302                        } else {
303                            networkExtra.put(
304                                    SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
305                                    NETWORK_ID_TO_SSID.get(networkId));
306                        }
307                        return true;
308                    }
309                }).when(mSupplicantStaNetworkMock)
310                        .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
311                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
312                return;
313            }
314        }).when(mISupplicantStaIfaceMock)
315                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
316
317        Map<String, WifiConfiguration> configs = new HashMap<>();
318        SparseArray<Map<String, String>> extras = new SparseArray<>();
319        assertTrue(mDut.loadNetworks(configs, extras));
320
321        assertEquals(2, configs.size());
322        assertEquals(2, extras.size());
323        for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
324            if (network.getKey() == toRemoveNetworkId) {
325                continue;
326            }
327            WifiConfiguration config;
328            // Duplicate network gets the same config key as the to removed one. So, use that to
329            // lookup the map.
330            if (network.getKey() == duplicateNetworkId) {
331                config = configs.get(NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
332            } else {
333                config = configs.get(network.getValue());
334            }
335            assertTrue(config != null);
336            assertEquals(network.getKey(), Integer.valueOf(config.networkId));
337            assertEquals(network.getValue(), config.SSID);
338            assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
339            assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
340        }
341    }
342
343    /**
344     * Tests the failure to load networks because of listNetworks failure.
345     */
346    @Test
347    public void testLoadNetworksFailedDueToListNetworks() throws Exception {
348        executeAndValidateInitializationSequence();
349        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
350            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
351                cb.onValues(mStatusFailure, null);
352            }
353        }).when(mISupplicantStaIfaceMock)
354                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
355
356        Map<String, WifiConfiguration> configs = new HashMap<>();
357        SparseArray<Map<String, String>> extras = new SparseArray<>();
358        assertFalse(mDut.loadNetworks(configs, extras));
359    }
360
361    /**
362     * Tests the failure to load networks because of getNetwork failure.
363     */
364    @Test
365    public void testLoadNetworksFailedDueToGetNetwork() throws Exception {
366        executeAndValidateInitializationSequence();
367        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
368            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
369                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
370            }
371        }).when(mISupplicantStaIfaceMock)
372                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
373        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
374            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
375                cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
376                return;
377            }
378        }).when(mISupplicantStaIfaceMock)
379                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
380
381        Map<String, WifiConfiguration> configs = new HashMap<>();
382        SparseArray<Map<String, String>> extras = new SparseArray<>();
383        assertFalse(mDut.loadNetworks(configs, extras));
384    }
385
386    /**
387     * Tests the failure to load networks because of loadWifiConfiguration failure.
388     */
389    @Test
390    public void testLoadNetworksFailedDueToLoadWifiConfiguration() throws Exception {
391        executeAndValidateInitializationSequence();
392        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
393            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
394                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
395            }
396        }).when(mISupplicantStaIfaceMock)
397                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
398        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
399            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
400                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
401                return;
402            }
403        }).when(mISupplicantStaIfaceMock)
404                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
405        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
406            public boolean answer(WifiConfiguration config, Map<String, String> networkExtra) {
407                return false;
408            }
409        }).when(mSupplicantStaNetworkMock)
410                .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
411
412        Map<String, WifiConfiguration> configs = new HashMap<>();
413        SparseArray<Map<String, String>> extras = new SparseArray<>();
414        assertTrue(mDut.loadNetworks(configs, extras));
415        assertTrue(configs.isEmpty());
416    }
417
418    /**
419     * Tests the failure to load networks because of loadWifiConfiguration exception.
420     */
421    @Test
422    public void testLoadNetworksFailedDueToExceptionInLoadWifiConfiguration() throws Exception {
423        executeAndValidateInitializationSequence();
424        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
425            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
426                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
427            }
428        }).when(mISupplicantStaIfaceMock)
429                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
430        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
431            public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
432                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
433                return;
434            }
435        }).when(mISupplicantStaIfaceMock)
436                .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
437        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
438            public boolean answer(WifiConfiguration config, Map<String, String> networkExtra)
439                    throws Exception {
440                throw new IllegalArgumentException();
441            }
442        }).when(mSupplicantStaNetworkMock)
443                .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
444
445        Map<String, WifiConfiguration> configs = new HashMap<>();
446        SparseArray<Map<String, String>> extras = new SparseArray<>();
447        assertTrue(mDut.loadNetworks(configs, extras));
448        assertTrue(configs.isEmpty());
449    }
450
451    /**
452     * Tests connection to a specified network with empty existing network.
453     */
454    @Test
455    public void testConnectWithEmptyExistingNetwork() throws Exception {
456        executeAndValidateInitializationSequence();
457        executeAndValidateConnectSequence(0, false);
458    }
459
460    @Test
461    public void testConnectToNetworkWithDifferentConfigReplacesNetworkInSupplicant()
462            throws Exception {
463        executeAndValidateInitializationSequence();
464        WifiConfiguration config = executeAndValidateConnectSequence(
465                SUPPLICANT_NETWORK_ID, false);
466        // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
467        reset(mISupplicantStaIfaceMock);
468        setupMocksForConnectSequence(true /*haveExistingNetwork*/);
469        // Make this network different by changing SSID.
470        config.SSID = "AnDifferentSSID";
471        assertTrue(mDut.connectToNetwork(config));
472        verify(mISupplicantStaIfaceMock).removeNetwork(SUPPLICANT_NETWORK_ID);
473        verify(mISupplicantStaIfaceMock)
474                .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
475    }
476
477    @Test
478    public void connectToNetworkWithSameNetworkDoesNotRemoveNetworkFromSupplicant()
479            throws Exception {
480        executeAndValidateInitializationSequence();
481        WifiConfiguration config = executeAndValidateConnectSequence(SUPPLICANT_NETWORK_ID, false);
482        // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
483        reset(mISupplicantStaIfaceMock);
484        setupMocksForConnectSequence(true /*haveExistingNetwork*/);
485        assertTrue(mDut.connectToNetwork(config));
486        verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
487        verify(mISupplicantStaIfaceMock, never())
488                .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
489    }
490
491    /**
492     * Tests connection to a specified network failure due to network add.
493     */
494    @Test
495    public void testConnectFailureDueToNetworkAddFailure() throws Exception {
496        executeAndValidateInitializationSequence();
497        setupMocksForConnectSequence(false);
498        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
499            public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
500                cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
501                return;
502            }
503        }).when(mISupplicantStaIfaceMock).addNetwork(
504                any(ISupplicantStaIface.addNetworkCallback.class));
505
506        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
507    }
508
509    /**
510     * Tests connection to a specified network failure due to network save.
511     */
512    @Test
513    public void testConnectFailureDueToNetworkSaveFailure() throws Exception {
514        executeAndValidateInitializationSequence();
515        setupMocksForConnectSequence(true);
516
517        when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
518                .thenReturn(false);
519
520        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
521        // We should have removed the existing network once before connection and once more
522        // on failure to save network configuration.
523        verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
524    }
525
526    /**
527     * Tests connection to a specified network failure due to exception in network save.
528     */
529    @Test
530    public void testConnectFailureDueToNetworkSaveException() throws Exception {
531        executeAndValidateInitializationSequence();
532        setupMocksForConnectSequence(true);
533
534        doThrow(new IllegalArgumentException("Some error!!!"))
535                .when(mSupplicantStaNetworkMock).saveWifiConfiguration(
536                        any(WifiConfiguration.class));
537
538        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
539        // We should have removed the existing network once before connection and once more
540        // on failure to save network configuration.
541        verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
542    }
543
544    /**
545     * Tests connection to a specified network failure due to network select.
546     */
547    @Test
548    public void testConnectFailureDueToNetworkSelectFailure() throws Exception {
549        executeAndValidateInitializationSequence();
550        setupMocksForConnectSequence(false);
551
552        when(mSupplicantStaNetworkMock.select()).thenReturn(false);
553
554        assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
555    }
556
557    /**
558     * Tests roaming to the same network as the currently connected one.
559     */
560    @Test
561    public void testRoamToSameNetwork() throws Exception {
562        executeAndValidateInitializationSequence();
563        executeAndValidateRoamSequence(true);
564        assertTrue(mDut.connectToNetwork(createTestWifiConfiguration()));
565    }
566
567    /**
568     * Tests roaming to a different network.
569     */
570    @Test
571    public void testRoamToDifferentNetwork() throws Exception {
572        executeAndValidateInitializationSequence();
573        executeAndValidateRoamSequence(false);
574    }
575
576    /**
577     * Tests roaming failure because of unable to set bssid.
578     */
579    @Test
580    public void testRoamFailureDueToBssidSet() throws Exception {
581        executeAndValidateInitializationSequence();
582        int connectedNetworkId = 5;
583        executeAndValidateConnectSequence(connectedNetworkId, false);
584        when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(false);
585
586        WifiConfiguration roamingConfig = new WifiConfiguration();
587        roamingConfig.networkId = connectedNetworkId;
588        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
589        assertFalse(mDut.roamToNetwork(roamingConfig));
590    }
591
592    /**
593     * Tests removal of all configured networks from wpa_supplicant.
594     */
595    @Test
596    public void testRemoveAllNetworks() throws Exception {
597        executeAndValidateInitializationSequence();
598        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
599            public void answer(ISupplicantStaIface.listNetworksCallback cb) {
600                cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
601            }
602        }).when(mISupplicantStaIfaceMock)
603                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
604        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
605            public SupplicantStatus answer(int id) {
606                assertTrue(NETWORK_ID_TO_SSID.containsKey(id));
607                return mStatusSuccess;
608            }
609        }).when(mISupplicantStaIfaceMock).removeNetwork(anyInt());
610
611        assertTrue(mDut.removeAllNetworks());
612        verify(mISupplicantStaIfaceMock, times(NETWORK_ID_TO_SSID.size())).removeNetwork(anyInt());
613    }
614
615    /**
616     * Remove all networks while connected, verify that the current network info is resetted.
617     */
618    @Test
619    public void testRemoveAllNetworksWhileConnected() throws Exception {
620        String testBssid = "11:22:33:44:55:66";
621        when(mSupplicantStaNetworkMock.setBssid(eq(testBssid))).thenReturn(true);
622
623        executeAndValidateInitializationSequence();
624
625        // Connect to a network and verify current network is set.
626        executeAndValidateConnectSequence(4, false);
627        assertTrue(mDut.setCurrentNetworkBssid(testBssid));
628        verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
629        reset(mSupplicantStaNetworkMock);
630
631        // Remove all networks and verify current network info is resetted.
632        assertTrue(mDut.removeAllNetworks());
633        assertFalse(mDut.setCurrentNetworkBssid(testBssid));
634        verify(mSupplicantStaNetworkMock, never()).setBssid(eq(testBssid));
635    }
636
637    /**
638     * Tests roaming failure because of unable to reassociate.
639     */
640    @Test
641    public void testRoamFailureDueToReassociate() throws Exception {
642        executeAndValidateInitializationSequence();
643        int connectedNetworkId = 5;
644        executeAndValidateConnectSequence(connectedNetworkId, false);
645
646        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
647            public SupplicantStatus answer() throws RemoteException {
648                return mStatusFailure;
649            }
650        }).when(mISupplicantStaIfaceMock).reassociate();
651        when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(true);
652
653        WifiConfiguration roamingConfig = new WifiConfiguration();
654        roamingConfig.networkId = connectedNetworkId;
655        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
656        assertFalse(mDut.roamToNetwork(roamingConfig));
657    }
658
659    /**
660     * Tests the retrieval of WPS NFC token.
661     */
662    @Test
663    public void testGetCurrentNetworkWpsNfcConfigurationToken() throws Exception {
664        String token = "45adbc1";
665        when(mSupplicantStaNetworkMock.getWpsNfcConfigurationToken()).thenReturn(token);
666
667        executeAndValidateInitializationSequence();
668        // Return null when not connected to the network.
669        assertTrue(mDut.getCurrentNetworkWpsNfcConfigurationToken() == null);
670        verify(mSupplicantStaNetworkMock, never()).getWpsNfcConfigurationToken();
671        executeAndValidateConnectSequence(4, false);
672        assertEquals(token, mDut.getCurrentNetworkWpsNfcConfigurationToken());
673        verify(mSupplicantStaNetworkMock).getWpsNfcConfigurationToken();
674    }
675
676    /**
677     * Tests the setting of BSSID.
678     */
679    @Test
680    public void testSetCurrentNetworkBssid() throws Exception {
681        String bssidStr = "34:34:12:12:12:90";
682        when(mSupplicantStaNetworkMock.setBssid(eq(bssidStr))).thenReturn(true);
683
684        executeAndValidateInitializationSequence();
685        // Fail when not connected to a network.
686        assertFalse(mDut.setCurrentNetworkBssid(bssidStr));
687        verify(mSupplicantStaNetworkMock, never()).setBssid(eq(bssidStr));
688        executeAndValidateConnectSequence(4, false);
689        assertTrue(mDut.setCurrentNetworkBssid(bssidStr));
690        verify(mSupplicantStaNetworkMock).setBssid(eq(bssidStr));
691    }
692
693    /**
694     * Tests the sending identity response for the current network.
695     */
696    @Test
697    public void testSetCurrentNetworkEapIdentityResponse() throws Exception {
698        String identity = "blah@blah.com";
699        when(mSupplicantStaNetworkMock.sendNetworkEapIdentityResponse(eq(identity)))
700                .thenReturn(true);
701
702        executeAndValidateInitializationSequence();
703        // Fail when not connected to a network.
704        assertFalse(mDut.sendCurrentNetworkEapIdentityResponse(identity));
705        verify(mSupplicantStaNetworkMock, never()).sendNetworkEapIdentityResponse(eq(identity));
706        executeAndValidateConnectSequence(4, false);
707        assertTrue(mDut.sendCurrentNetworkEapIdentityResponse(identity));
708        verify(mSupplicantStaNetworkMock).sendNetworkEapIdentityResponse(eq(identity));
709    }
710
711    /**
712     * Tests the getting of anonymous identity for the current network.
713     */
714    @Test
715    public void testGetCurrentNetworkEapAnonymousIdentity() throws Exception {
716        String anonymousIdentity = "aaa@bbb.ccc";
717        when(mSupplicantStaNetworkMock.fetchEapAnonymousIdentity())
718                .thenReturn(anonymousIdentity);
719        executeAndValidateInitializationSequence();
720
721        // Return null when not connected to the network.
722        assertEquals(null, mDut.getCurrentNetworkEapAnonymousIdentity());
723        executeAndValidateConnectSequence(4, false);
724        // Return anonymous identity for the current network.
725        assertEquals(anonymousIdentity, mDut.getCurrentNetworkEapAnonymousIdentity());
726    }
727
728    /**
729     * Tests the sending gsm auth response for the current network.
730     */
731    @Test
732    public void testSetCurrentNetworkEapSimGsmAuthResponse() throws Exception {
733        String params = "test";
734        when(mSupplicantStaNetworkMock.sendNetworkEapSimGsmAuthResponse(eq(params)))
735                .thenReturn(true);
736
737        executeAndValidateInitializationSequence();
738        // Fail when not connected to a network.
739        assertFalse(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
740        verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimGsmAuthResponse(eq(params));
741        executeAndValidateConnectSequence(4, false);
742        assertTrue(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
743        verify(mSupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(eq(params));
744    }
745
746    /**
747     * Tests the sending umts auth response for the current network.
748     */
749    @Test
750    public void testSetCurrentNetworkEapSimUmtsAuthResponse() throws Exception {
751        String params = "test";
752        when(mSupplicantStaNetworkMock.sendNetworkEapSimUmtsAuthResponse(eq(params)))
753                .thenReturn(true);
754
755        executeAndValidateInitializationSequence();
756        // Fail when not connected to a network.
757        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
758        verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAuthResponse(eq(params));
759        executeAndValidateConnectSequence(4, false);
760        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
761        verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAuthResponse(eq(params));
762    }
763
764    /**
765     * Tests the sending umts auts response for the current network.
766     */
767    @Test
768    public void testSetCurrentNetworkEapSimUmtsAutsResponse() throws Exception {
769        String params = "test";
770        when(mSupplicantStaNetworkMock.sendNetworkEapSimUmtsAutsResponse(eq(params)))
771                .thenReturn(true);
772
773        executeAndValidateInitializationSequence();
774        // Fail when not connected to a network.
775        assertFalse(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
776        verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAutsResponse(eq(params));
777        executeAndValidateConnectSequence(4, false);
778        assertTrue(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
779        verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAutsResponse(eq(params));
780    }
781
782    /**
783     * Tests the setting of WPS device type.
784     */
785    @Test
786    public void testSetWpsDeviceType() throws Exception {
787        String validDeviceTypeStr = "10-0050F204-5";
788        byte[] expectedDeviceType = { 0x0, 0xa, 0x0, 0x50, (byte) 0xf2, 0x04, 0x0, 0x05};
789        String invalidDeviceType1Str = "10-02050F204-5";
790        String invalidDeviceType2Str = "10-0050F204-534";
791        when(mISupplicantStaIfaceMock.setWpsDeviceType(any(byte[].class)))
792                .thenReturn(mStatusSuccess);
793
794        executeAndValidateInitializationSequence();
795
796        // This should work.
797        assertTrue(mDut.setWpsDeviceType(validDeviceTypeStr));
798        verify(mISupplicantStaIfaceMock).setWpsDeviceType(eq(expectedDeviceType));
799
800        // This should not work
801        assertFalse(mDut.setWpsDeviceType(invalidDeviceType1Str));
802        // This should not work
803        assertFalse(mDut.setWpsDeviceType(invalidDeviceType2Str));
804    }
805
806    /**
807     * Tests the setting of WPS config methods.
808     */
809    @Test
810    public void testSetWpsConfigMethods() throws Exception {
811        String validConfigMethodsStr = "physical_display virtual_push_button";
812        Short expectedConfigMethods =
813                WpsConfigMethods.PHY_DISPLAY | WpsConfigMethods.VIRT_PUSHBUTTON;
814        String invalidConfigMethodsStr = "physical_display virtual_push_button test";
815        when(mISupplicantStaIfaceMock.setWpsConfigMethods(anyShort())).thenReturn(mStatusSuccess);
816
817        executeAndValidateInitializationSequence();
818
819        // This should work.
820        assertTrue(mDut.setWpsConfigMethods(validConfigMethodsStr));
821        verify(mISupplicantStaIfaceMock).setWpsConfigMethods(eq(expectedConfigMethods));
822
823        // This should throw an illegal argument exception.
824        try {
825            assertFalse(mDut.setWpsConfigMethods(invalidConfigMethodsStr));
826        } catch (IllegalArgumentException e) {
827            return;
828        }
829        assertTrue(false);
830    }
831
832    /**
833     * Tests the handling of ANQP done callback.
834     * Note: Since the ANQP element parsing methods are static, this can only test the negative test
835     * where all the parsing fails because the data is empty. It'll be non-trivial and unnecessary
836     * to test out the parsing logic here.
837     */
838    @Test
839    public void testAnqpDoneCallback() throws Exception {
840        executeAndValidateInitializationSequence();
841        assertNotNull(mISupplicantStaIfaceCallback);
842        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
843        mISupplicantStaIfaceCallback.onAnqpQueryDone(
844                bssid, new ISupplicantStaIfaceCallback.AnqpData(),
845                new ISupplicantStaIfaceCallback.Hs20AnqpData());
846
847        ArgumentCaptor<AnqpEvent> anqpEventCaptor = ArgumentCaptor.forClass(AnqpEvent.class);
848        verify(mWifiMonitor).broadcastAnqpDoneEvent(eq(WLAN_IFACE_NAME), anqpEventCaptor.capture());
849        assertEquals(
850                ByteBufferReader.readInteger(
851                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
852                anqpEventCaptor.getValue().getBssid());
853    }
854
855    /**
856     * Tests the handling of Icon done callback.
857     */
858    @Test
859    public void testIconDoneCallback() throws Exception {
860        executeAndValidateInitializationSequence();
861        assertNotNull(mISupplicantStaIfaceCallback);
862
863        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
864        byte[] iconData = new byte[ICON_FILE_SIZE];
865        new Random().nextBytes(iconData);
866        mISupplicantStaIfaceCallback.onHs20IconQueryDone(
867                bssid, ICON_FILE_NAME, NativeUtil.byteArrayToArrayList(iconData));
868
869        ArgumentCaptor<IconEvent> iconEventCaptor = ArgumentCaptor.forClass(IconEvent.class);
870        verify(mWifiMonitor).broadcastIconDoneEvent(eq(WLAN_IFACE_NAME), iconEventCaptor.capture());
871        assertEquals(
872                ByteBufferReader.readInteger(
873                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
874                iconEventCaptor.getValue().getBSSID());
875        assertEquals(ICON_FILE_NAME, iconEventCaptor.getValue().getFileName());
876        assertArrayEquals(iconData, iconEventCaptor.getValue().getData());
877    }
878
879    /**
880     * Tests the handling of HS20 subscription remediation callback.
881     */
882    @Test
883    public void testHs20SubscriptionRemediationCallback() throws Exception {
884        executeAndValidateInitializationSequence();
885        assertNotNull(mISupplicantStaIfaceCallback);
886
887        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
888        byte osuMethod = ISupplicantStaIfaceCallback.OsuMethod.OMA_DM;
889        mISupplicantStaIfaceCallback.onHs20SubscriptionRemediation(
890                bssid, osuMethod, HS20_URL);
891
892        ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
893        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
894        assertEquals(
895                ByteBufferReader.readInteger(
896                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
897                wnmDataCaptor.getValue().getBssid());
898        assertEquals(osuMethod, wnmDataCaptor.getValue().getMethod());
899        assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
900    }
901
902    /**
903     * Tests the handling of HS20 deauth imminent callback.
904     */
905    @Test
906    public void testHs20DeauthImminentCallbackWithEssReasonCode() throws Exception {
907        executeAndValidateHs20DeauthImminentCallback(true);
908    }
909
910    /**
911     * Tests the handling of HS20 deauth imminent callback.
912     */
913    @Test
914    public void testHs20DeauthImminentCallbackWithNonEssReasonCode() throws Exception {
915        executeAndValidateHs20DeauthImminentCallback(false);
916
917    }
918
919    /**
920     * Tests the handling of state change notification without any configured network.
921     */
922    @Test
923    public void testStateChangeCallbackWithNoConfiguredNetwork() throws Exception {
924        executeAndValidateInitializationSequence();
925        assertNotNull(mISupplicantStaIfaceCallback);
926
927        mISupplicantStaIfaceCallback.onStateChanged(
928                ISupplicantStaIfaceCallback.State.INACTIVE,
929                NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
930                NativeUtil.decodeSsid(SUPPLICANT_SSID));
931
932        // Can't compare WifiSsid instances because they lack an equals.
933        verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
934                eq(WLAN_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID),
935                any(WifiSsid.class), eq(BSSID), eq(SupplicantState.INACTIVE));
936    }
937
938    /**
939     * Tests the handling of state change notification to associated after configuring a network.
940     */
941    @Test
942    public void testStateChangeToAssociatedCallback() throws Exception {
943        executeAndValidateInitializationSequence();
944        int frameworkNetworkId = 6;
945        executeAndValidateConnectSequence(frameworkNetworkId, false);
946        assertNotNull(mISupplicantStaIfaceCallback);
947
948        mISupplicantStaIfaceCallback.onStateChanged(
949                ISupplicantStaIfaceCallback.State.ASSOCIATED,
950                NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
951                NativeUtil.decodeSsid(SUPPLICANT_SSID));
952
953        verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
954                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
955                any(WifiSsid.class), eq(BSSID), eq(SupplicantState.ASSOCIATED));
956    }
957
958    /**
959     * Tests the handling of state change notification to completed after configuring a network.
960     */
961    @Test
962    public void testStateChangeToCompletedCallback() throws Exception {
963        InOrder wifiMonitorInOrder = inOrder(mWifiMonitor);
964        executeAndValidateInitializationSequence();
965        int frameworkNetworkId = 6;
966        executeAndValidateConnectSequence(frameworkNetworkId, false);
967        assertNotNull(mISupplicantStaIfaceCallback);
968
969        mISupplicantStaIfaceCallback.onStateChanged(
970                ISupplicantStaIfaceCallback.State.COMPLETED,
971                NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
972                NativeUtil.decodeSsid(SUPPLICANT_SSID));
973
974        wifiMonitorInOrder.verify(mWifiMonitor).broadcastNetworkConnectionEvent(
975                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID));
976        wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
977                eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
978                any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED));
979    }
980
981    /**
982     * Tests the handling of network disconnected notification.
983     */
984    @Test
985    public void testDisconnectedCallback() throws Exception {
986        executeAndValidateInitializationSequence();
987        assertNotNull(mISupplicantStaIfaceCallback);
988
989        int reasonCode = 5;
990        mISupplicantStaIfaceCallback.onDisconnected(
991                NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
992        verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
993                eq(WLAN_IFACE_NAME), eq(1), eq(reasonCode), eq(BSSID));
994
995        mISupplicantStaIfaceCallback.onDisconnected(
996                NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
997        verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
998                eq(WLAN_IFACE_NAME), eq(0), eq(reasonCode), eq(BSSID));
999    }
1000
1001    /**
1002     * Tests the handling of incorrect network passwords.
1003     */
1004    @Test
1005    public void testAuthFailurePassword() throws Exception {
1006        executeAndValidateInitializationSequence();
1007        assertNotNull(mISupplicantStaIfaceCallback);
1008
1009        int reasonCode = 3;
1010        mISupplicantStaIfaceCallback.onDisconnected(
1011                NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
1012        verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
1013
1014        mISupplicantStaIfaceCallback.onDisconnected(
1015                NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
1016        verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
1017
1018        mISupplicantStaIfaceCallback.onStateChanged(
1019                ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE,
1020                NativeUtil.macAddressToByteArray(BSSID),
1021                SUPPLICANT_NETWORK_ID,
1022                NativeUtil.decodeSsid(SUPPLICANT_SSID));
1023        mISupplicantStaIfaceCallback.onDisconnected(
1024                NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
1025        mISupplicantStaIfaceCallback.onDisconnected(
1026                NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
1027
1028        verify(mWifiMonitor, times(2)).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
1029                eq(WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD));
1030
1031    }
1032
1033     /**
1034      * Tests the handling of incorrect network passwords, edge case.
1035      *
1036      * If the disconnect reason is "IE in 4way differs", do not call it a password mismatch.
1037      */
1038    @Test
1039    public void testIeDiffers() throws Exception {
1040        executeAndValidateInitializationSequence();
1041        assertNotNull(mISupplicantStaIfaceCallback);
1042
1043        int reasonCode = 17; // IEEE 802.11i WLAN_REASON_IE_IN_4WAY_DIFFERS
1044
1045        mISupplicantStaIfaceCallback.onStateChanged(
1046                ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE,
1047                NativeUtil.macAddressToByteArray(BSSID),
1048                SUPPLICANT_NETWORK_ID,
1049                NativeUtil.decodeSsid(SUPPLICANT_SSID));
1050        mISupplicantStaIfaceCallback.onDisconnected(
1051                NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
1052        verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
1053    }
1054
1055
1056    /**
1057     * Tests the handling of association rejection notification.
1058     */
1059    @Test
1060    public void testAssociationRejectionCallback() throws Exception {
1061        executeAndValidateInitializationSequence();
1062        assertNotNull(mISupplicantStaIfaceCallback);
1063
1064        int statusCode = 7;
1065        mISupplicantStaIfaceCallback.onAssociationRejected(
1066                NativeUtil.macAddressToByteArray(BSSID), statusCode, false);
1067        verify(mWifiMonitor).broadcastAssociationRejectionEvent(
1068                eq(WLAN_IFACE_NAME), eq(statusCode), eq(false), eq(BSSID));
1069    }
1070
1071    /**
1072     * Tests the handling of authentification timeout notification.
1073     */
1074    @Test
1075    public void testAuthenticationTimeoutCallback() throws Exception {
1076        executeAndValidateInitializationSequence();
1077        assertNotNull(mISupplicantStaIfaceCallback);
1078
1079        mISupplicantStaIfaceCallback.onAuthenticationTimeout(
1080                NativeUtil.macAddressToByteArray(BSSID));
1081        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
1082                eq(WifiManager.ERROR_AUTH_FAILURE_TIMEOUT));
1083    }
1084
1085    /**
1086     * Tests the handling of bssid change notification.
1087     */
1088    @Test
1089    public void testBssidChangedCallback() throws Exception {
1090        executeAndValidateInitializationSequence();
1091        assertNotNull(mISupplicantStaIfaceCallback);
1092
1093        mISupplicantStaIfaceCallback.onBssidChanged(
1094                BssidChangeReason.ASSOC_START, NativeUtil.macAddressToByteArray(BSSID));
1095        verify(mWifiMonitor).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1096        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1097
1098        reset(mWifiMonitor);
1099        mISupplicantStaIfaceCallback.onBssidChanged(
1100                BssidChangeReason.ASSOC_COMPLETE, NativeUtil.macAddressToByteArray(BSSID));
1101        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1102        verify(mWifiMonitor).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1103
1104        reset(mWifiMonitor);
1105        mISupplicantStaIfaceCallback.onBssidChanged(
1106                BssidChangeReason.DISASSOC, NativeUtil.macAddressToByteArray(BSSID));
1107        verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1108        verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
1109    }
1110
1111    /**
1112     * Tests the handling of EAP failure notification.
1113     */
1114    @Test
1115    public void testEapFailureCallback() throws Exception {
1116        executeAndValidateInitializationSequence();
1117        assertNotNull(mISupplicantStaIfaceCallback);
1118
1119        mISupplicantStaIfaceCallback.onEapFailure();
1120        verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
1121                eq(WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE));
1122    }
1123
1124    /**
1125     * Tests the handling of Wps success notification.
1126     */
1127    @Test
1128    public void testWpsSuccessCallback() throws Exception {
1129        executeAndValidateInitializationSequence();
1130        assertNotNull(mISupplicantStaIfaceCallback);
1131
1132        mISupplicantStaIfaceCallback.onWpsEventSuccess();
1133        verify(mWifiMonitor).broadcastWpsSuccessEvent(eq(WLAN_IFACE_NAME));
1134    }
1135
1136    /**
1137     * Tests the handling of Wps fail notification.
1138     */
1139    @Test
1140    public void testWpsFailureCallback() throws Exception {
1141        executeAndValidateInitializationSequence();
1142        assertNotNull(mISupplicantStaIfaceCallback);
1143
1144        short cfgError = ISupplicantStaIfaceCallback.WpsConfigError.MULTIPLE_PBC_DETECTED;
1145        short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.SECURITY_WEP_PROHIBITED;
1146        mISupplicantStaIfaceCallback.onWpsEventFail(
1147                NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
1148        verify(mWifiMonitor).broadcastWpsFailEvent(eq(WLAN_IFACE_NAME),
1149                eq((int) cfgError), eq((int) errorInd));
1150    }
1151
1152    /**
1153     * Tests the handling of Wps fail notification.
1154     */
1155    @Test
1156    public void testWpsTimeoutCallback() throws Exception {
1157        executeAndValidateInitializationSequence();
1158        assertNotNull(mISupplicantStaIfaceCallback);
1159
1160        short cfgError = ISupplicantStaIfaceCallback.WpsConfigError.MSG_TIMEOUT;
1161        short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.NO_ERROR;
1162        mISupplicantStaIfaceCallback.onWpsEventFail(
1163                NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
1164        verify(mWifiMonitor).broadcastWpsTimeoutEvent(eq(WLAN_IFACE_NAME));
1165    }
1166
1167    /**
1168     * Tests the handling of Wps pbc overlap notification.
1169     */
1170    @Test
1171    public void testWpsPbcOverlapCallback() throws Exception {
1172        executeAndValidateInitializationSequence();
1173        assertNotNull(mISupplicantStaIfaceCallback);
1174
1175        mISupplicantStaIfaceCallback.onWpsEventPbcOverlap();
1176        verify(mWifiMonitor).broadcastWpsOverlapEvent(eq(WLAN_IFACE_NAME));
1177    }
1178
1179    /**
1180     * Tests the handling of service manager death notification.
1181     */
1182    @Test
1183    public void testServiceManagerDeathCallback() throws Exception {
1184        executeAndValidateInitializationSequence();
1185        assertNotNull(mServiceManagerDeathCaptor.getValue());
1186        assertTrue(mDut.isInitializationComplete());
1187
1188        mServiceManagerDeathCaptor.getValue().serviceDied(5L);
1189
1190        assertFalse(mDut.isInitializationComplete());
1191        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
1192    }
1193
1194    /**
1195     * Tests the handling of supplicant death notification.
1196     */
1197    @Test
1198    public void testSupplicantDeathCallback() throws Exception {
1199        executeAndValidateInitializationSequence();
1200        assertNotNull(mSupplicantDeathCaptor.getValue());
1201        assertTrue(mDut.isInitializationComplete());
1202
1203        mSupplicantDeathCaptor.getValue().serviceDied(5L);
1204
1205        assertFalse(mDut.isInitializationComplete());
1206        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
1207    }
1208
1209    /**
1210     * Tests the handling of supplicant sta iface death notification.
1211     */
1212    @Test
1213    public void testSupplicantStaIfaceDeathCallback() throws Exception {
1214        executeAndValidateInitializationSequence();
1215        assertNotNull(mSupplicantStaIfaceDeathCaptor.getValue());
1216        assertTrue(mDut.isInitializationComplete());
1217
1218        mSupplicantStaIfaceDeathCaptor.getValue().serviceDied(5L);
1219
1220        assertFalse(mDut.isInitializationComplete());
1221        verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
1222    }
1223
1224    /**
1225     * Tests the setting of log level.
1226     */
1227    @Test
1228    public void testSetLogLevel() throws Exception {
1229        when(mISupplicantMock.setDebugParams(anyInt(), anyBoolean(), anyBoolean()))
1230                .thenReturn(mStatusSuccess);
1231
1232        // Fail before initialization is performed.
1233        assertFalse(mDut.setLogLevel(true));
1234
1235        executeAndValidateInitializationSequence();
1236
1237        // This should work.
1238        assertTrue(mDut.setLogLevel(true));
1239        verify(mISupplicantMock)
1240                .setDebugParams(eq(ISupplicant.DebugLevel.DEBUG), eq(false), eq(false));
1241    }
1242
1243    /**
1244     * Tests the setting of concurrency priority.
1245     */
1246    @Test
1247    public void testConcurrencyPriority() throws Exception {
1248        when(mISupplicantMock.setConcurrencyPriority(anyInt())).thenReturn(mStatusSuccess);
1249
1250        // Fail before initialization is performed.
1251        assertFalse(mDut.setConcurrencyPriority(false));
1252
1253        executeAndValidateInitializationSequence();
1254
1255        // This should work.
1256        assertTrue(mDut.setConcurrencyPriority(false));
1257        verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.P2P));
1258        assertTrue(mDut.setConcurrencyPriority(true));
1259        verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.STA));
1260    }
1261
1262    /**
1263     * Tests the start of wps registrar.
1264     */
1265    @Test
1266    public void testStartWpsRegistrar() throws Exception {
1267        when(mISupplicantStaIfaceMock.startWpsRegistrar(any(byte[].class), anyString()))
1268                .thenReturn(mStatusSuccess);
1269
1270        // Fail before initialization is performed.
1271        assertFalse(mDut.startWpsRegistrar(null, null));
1272
1273        executeAndValidateInitializationSequence();
1274
1275        assertFalse(mDut.startWpsRegistrar(null, null));
1276        verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
1277
1278        assertFalse(mDut.startWpsRegistrar(new String(), "452233"));
1279        verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
1280
1281        assertTrue(mDut.startWpsRegistrar("45:23:12:12:12:98", "562535"));
1282        verify(mISupplicantStaIfaceMock).startWpsRegistrar(any(byte[].class), anyString());
1283    }
1284
1285    /**
1286     * Tests the start of wps PBC.
1287     */
1288    @Test
1289    public void testStartWpsPbc() throws Exception {
1290        when(mISupplicantStaIfaceMock.startWpsPbc(any(byte[].class))).thenReturn(mStatusSuccess);
1291        String bssid = "45:23:12:12:12:98";
1292        byte[] bssidBytes = {0x45, 0x23, 0x12, 0x12, 0x12, (byte) 0x98};
1293        byte[] anyBssidBytes = {0, 0, 0, 0, 0, 0};
1294
1295        // Fail before initialization is performed.
1296        assertFalse(mDut.startWpsPbc(bssid));
1297        verify(mISupplicantStaIfaceMock, never()).startWpsPbc(any(byte[].class));
1298
1299        executeAndValidateInitializationSequence();
1300
1301        assertTrue(mDut.startWpsPbc(bssid));
1302        verify(mISupplicantStaIfaceMock).startWpsPbc(eq(bssidBytes));
1303
1304        assertTrue(mDut.startWpsPbc(null));
1305        verify(mISupplicantStaIfaceMock).startWpsPbc(eq(anyBssidBytes));
1306    }
1307
1308    private WifiConfiguration createTestWifiConfiguration() {
1309        WifiConfiguration config = new WifiConfiguration();
1310        config.networkId = SUPPLICANT_NETWORK_ID;
1311        return config;
1312    }
1313
1314    private void executeAndValidateHs20DeauthImminentCallback(boolean isEss) throws Exception {
1315        executeAndValidateInitializationSequence();
1316        assertNotNull(mISupplicantStaIfaceCallback);
1317
1318        byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
1319        int reasonCode = isEss ? WnmData.ESS : WnmData.ESS + 1;
1320        int reauthDelay = 5;
1321        mISupplicantStaIfaceCallback.onHs20DeauthImminentNotice(
1322                bssid, reasonCode, reauthDelay, HS20_URL);
1323
1324        ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
1325        verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
1326        assertEquals(
1327                ByteBufferReader.readInteger(
1328                        ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
1329                wnmDataCaptor.getValue().getBssid());
1330        assertEquals(isEss, wnmDataCaptor.getValue().isEss());
1331        assertEquals(reauthDelay, wnmDataCaptor.getValue().getDelay());
1332        assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
1333    }
1334
1335    private void executeAndValidateInitializationSequence() throws  Exception {
1336        executeAndValidateInitializationSequence(false, false, false, false);
1337    }
1338
1339    /**
1340     * Calls.initialize(), mocking various call back answers and verifying flow, asserting for the
1341     * expected result. Verifies if ISupplicantStaIface manager is initialized or reset.
1342     * Each of the arguments will cause a different failure mode when set true.
1343     */
1344    private void executeAndValidateInitializationSequence(boolean causeRemoteException,
1345                                                          boolean getZeroInterfaces,
1346                                                          boolean getNullInterface,
1347                                                          boolean causeCallbackRegFailure)
1348            throws Exception {
1349        boolean shouldSucceed =
1350                !causeRemoteException && !getZeroInterfaces && !getNullInterface
1351                        && !causeCallbackRegFailure;
1352        // Setup callback mock answers
1353        ArrayList<ISupplicant.IfaceInfo> interfaces;
1354        if (getZeroInterfaces) {
1355            interfaces = new ArrayList<>();
1356        } else {
1357            interfaces = mIfaceInfoList;
1358        }
1359        doAnswer(new GetListInterfacesAnswer(interfaces)).when(mISupplicantMock)
1360                .listInterfaces(any(ISupplicant.listInterfacesCallback.class));
1361        if (causeRemoteException) {
1362            doThrow(new RemoteException("Some error!!!"))
1363                    .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
1364                    any(ISupplicant.getInterfaceCallback.class));
1365        } else {
1366            doAnswer(new GetGetInterfaceAnswer(getNullInterface))
1367                    .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
1368                    any(ISupplicant.getInterfaceCallback.class));
1369        }
1370        /** Callback registeration */
1371        if (causeCallbackRegFailure) {
1372            doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1373                public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
1374                        throws RemoteException {
1375                    return mStatusFailure;
1376                }
1377            }).when(mISupplicantStaIfaceMock)
1378                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
1379        } else {
1380            doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1381                public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
1382                        throws RemoteException {
1383                    mISupplicantStaIfaceCallback = cb;
1384                    return mStatusSuccess;
1385                }
1386            }).when(mISupplicantStaIfaceMock)
1387                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
1388        }
1389
1390        mInOrder = inOrder(mServiceManagerMock, mISupplicantMock, mISupplicantStaIfaceMock,
1391                mWifiMonitor);
1392        // Initialize SupplicantStaIfaceHal, should call serviceManager.registerForNotifications
1393        assertTrue(mDut.initialize());
1394        // verify: service manager initialization sequence
1395        mInOrder.verify(mServiceManagerMock).linkToDeath(mServiceManagerDeathCaptor.capture(),
1396                anyLong());
1397        mInOrder.verify(mServiceManagerMock).registerForNotifications(
1398                eq(ISupplicant.kInterfaceName), eq(""), mServiceNotificationCaptor.capture());
1399        // act: cause the onRegistration(...) callback to execute
1400        mServiceNotificationCaptor.getValue().onRegistration(ISupplicant.kInterfaceName, "", true);
1401
1402        assertTrue(mDut.isInitializationComplete() == shouldSucceed);
1403        mInOrder.verify(mISupplicantMock).linkToDeath(mSupplicantDeathCaptor.capture(),
1404                anyLong());
1405        // verify: listInterfaces is called
1406        mInOrder.verify(mISupplicantMock).listInterfaces(
1407                any(ISupplicant.listInterfacesCallback.class));
1408        if (!getZeroInterfaces) {
1409            mInOrder.verify(mISupplicantMock)
1410                    .getInterface(any(ISupplicant.IfaceInfo.class),
1411                            any(ISupplicant.getInterfaceCallback.class));
1412        }
1413        if (causeRemoteException) {
1414            mInOrder.verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(null));
1415        }
1416        if (!causeRemoteException && !getZeroInterfaces && !getNullInterface) {
1417            mInOrder.verify(mISupplicantStaIfaceMock).linkToDeath(
1418                    mSupplicantStaIfaceDeathCaptor.capture(), anyLong());
1419            mInOrder.verify(mISupplicantStaIfaceMock)
1420                    .registerCallback(any(ISupplicantStaIfaceCallback.class));
1421        }
1422    }
1423
1424    private SupplicantStatus createSupplicantStatus(int code) {
1425        SupplicantStatus status = new SupplicantStatus();
1426        status.code = code;
1427        return status;
1428    }
1429
1430    /**
1431     * Create an IfaceInfo with given type and name
1432     */
1433    private ISupplicant.IfaceInfo createIfaceInfo(int type, String name) {
1434        ISupplicant.IfaceInfo info = new ISupplicant.IfaceInfo();
1435        info.type = type;
1436        info.name = name;
1437        return info;
1438    }
1439
1440    private class GetListInterfacesAnswer extends MockAnswerUtil.AnswerWithArguments {
1441        private ArrayList<ISupplicant.IfaceInfo> mInterfaceList;
1442
1443        GetListInterfacesAnswer(ArrayList<ISupplicant.IfaceInfo> ifaces) {
1444            mInterfaceList = ifaces;
1445        }
1446
1447        public void answer(ISupplicant.listInterfacesCallback cb) {
1448            cb.onValues(mStatusSuccess, mInterfaceList);
1449        }
1450    }
1451
1452    private class GetGetInterfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
1453        boolean mGetNullInterface;
1454
1455        GetGetInterfaceAnswer(boolean getNullInterface) {
1456            mGetNullInterface = getNullInterface;
1457        }
1458
1459        public void answer(ISupplicant.IfaceInfo iface, ISupplicant.getInterfaceCallback cb) {
1460            if (mGetNullInterface) {
1461                cb.onValues(mStatusSuccess, null);
1462            } else {
1463                cb.onValues(mStatusSuccess, mISupplicantIfaceMock);
1464            }
1465        }
1466    }
1467
1468    /**
1469     * Setup mocks for connect sequence.
1470     */
1471    private void setupMocksForConnectSequence(final boolean haveExistingNetwork) throws Exception {
1472        final int existingNetworkId = SUPPLICANT_NETWORK_ID;
1473        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1474            public SupplicantStatus answer() throws RemoteException {
1475                return mStatusSuccess;
1476            }
1477        }).when(mISupplicantStaIfaceMock).disconnect();
1478        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1479            public void answer(ISupplicantStaIface.listNetworksCallback cb) throws RemoteException {
1480                if (haveExistingNetwork) {
1481                    cb.onValues(mStatusSuccess, new ArrayList<>(Arrays.asList(existingNetworkId)));
1482                } else {
1483                    cb.onValues(mStatusSuccess, new ArrayList<>());
1484                }
1485            }
1486        }).when(mISupplicantStaIfaceMock)
1487                .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
1488        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1489            public SupplicantStatus answer(int id) throws RemoteException {
1490                return mStatusSuccess;
1491            }
1492        }).when(mISupplicantStaIfaceMock).removeNetwork(eq(existingNetworkId));
1493        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1494            public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
1495                cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
1496                return;
1497            }
1498        }).when(mISupplicantStaIfaceMock).addNetwork(
1499                any(ISupplicantStaIface.addNetworkCallback.class));
1500        when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
1501                .thenReturn(true);
1502        when(mSupplicantStaNetworkMock.select()).thenReturn(true);
1503    }
1504
1505    /**
1506     * Helper function to validate the connect sequence.
1507     */
1508    private void validateConnectSequence(
1509            final boolean haveExistingNetwork, int numNetworkAdditions) throws Exception {
1510        if (haveExistingNetwork) {
1511            verify(mISupplicantStaIfaceMock).removeNetwork(anyInt());
1512        }
1513        verify(mISupplicantStaIfaceMock, times(numNetworkAdditions))
1514                .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
1515        verify(mSupplicantStaNetworkMock, times(numNetworkAdditions))
1516                .saveWifiConfiguration(any(WifiConfiguration.class));
1517        verify(mSupplicantStaNetworkMock, times(numNetworkAdditions)).select();
1518    }
1519
1520    /**
1521     * Helper function to execute all the actions to perform connection to the network.
1522     *
1523     * @param newFrameworkNetworkId Framework Network Id of the new network to connect.
1524     * @param haveExistingNetwork Removes the existing network.
1525     * @return the WifiConfiguration object of the new network to connect.
1526     */
1527    private WifiConfiguration executeAndValidateConnectSequence(
1528            final int newFrameworkNetworkId, final boolean haveExistingNetwork) throws Exception {
1529        setupMocksForConnectSequence(haveExistingNetwork);
1530        WifiConfiguration config = new WifiConfiguration();
1531        config.networkId = newFrameworkNetworkId;
1532        assertTrue(mDut.connectToNetwork(config));
1533        validateConnectSequence(haveExistingNetwork, 1);
1534        return config;
1535    }
1536
1537    /**
1538     * Setup mocks for roam sequence.
1539     */
1540    private void setupMocksForRoamSequence(String roamBssid) throws Exception {
1541        doAnswer(new MockAnswerUtil.AnswerWithArguments() {
1542            public SupplicantStatus answer() throws RemoteException {
1543                return mStatusSuccess;
1544            }
1545        }).when(mISupplicantStaIfaceMock).reassociate();
1546        when(mSupplicantStaNetworkMock.setBssid(eq(roamBssid))).thenReturn(true);
1547    }
1548
1549    /**
1550     * Helper function to execute all the actions to perform roaming to the network.
1551     *
1552     * @param sameNetwork Roam to the same network or not.
1553     */
1554    private void executeAndValidateRoamSequence(boolean sameNetwork) throws Exception {
1555        int connectedNetworkId = ROAM_NETWORK_ID;
1556        String roamBssid = BSSID;
1557        int roamNetworkId;
1558        if (sameNetwork) {
1559            roamNetworkId = connectedNetworkId;
1560        } else {
1561            roamNetworkId = connectedNetworkId + 1;
1562        }
1563        executeAndValidateConnectSequence(connectedNetworkId, false);
1564        setupMocksForRoamSequence(roamBssid);
1565
1566        WifiConfiguration roamingConfig = new WifiConfiguration();
1567        roamingConfig.networkId = roamNetworkId;
1568        roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID(roamBssid);
1569        assertTrue(mDut.roamToNetwork(roamingConfig));
1570
1571        if (!sameNetwork) {
1572            validateConnectSequence(false, 2);
1573            verify(mSupplicantStaNetworkMock, never()).setBssid(anyString());
1574            verify(mISupplicantStaIfaceMock, never()).reassociate();
1575        } else {
1576            verify(mSupplicantStaNetworkMock).setBssid(eq(roamBssid));
1577            verify(mISupplicantStaIfaceMock).reassociate();
1578        }
1579    }
1580}
1581