1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import static org.mockito.Mockito.any;
20import static org.mockito.Mockito.anyBoolean;
21import static org.mockito.Mockito.anyInt;
22import static org.mockito.Mockito.eq;
23import static org.mockito.Mockito.inOrder;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
28import android.net.InterfaceConfiguration;
29import android.net.wifi.IApInterface;
30import android.net.wifi.WifiConfiguration;
31import android.net.wifi.WifiManager;
32import android.os.IBinder;
33import android.os.IBinder.DeathRecipient;
34import android.os.INetworkManagementService;
35import android.os.test.TestLooper;
36import android.test.suitebuilder.annotation.SmallTest;
37
38import com.android.server.net.BaseNetworkObserver;
39
40import org.junit.Before;
41import org.junit.Test;
42import org.mockito.ArgumentCaptor;
43import org.mockito.InOrder;
44import org.mockito.Mock;
45import org.mockito.MockitoAnnotations;
46
47import java.nio.charset.StandardCharsets;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.Locale;
51
52/** Unit tests for {@link SoftApManager}. */
53@SmallTest
54public class SoftApManagerTest {
55
56    private static final String TAG = "SoftApManagerTest";
57
58    private static final String DEFAULT_SSID = "DefaultTestSSID";
59    private static final String TEST_SSID = "TestSSID";
60    private static final String TEST_COUNTRY_CODE = "TestCountry";
61    private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4};
62    private static final String TEST_INTERFACE_NAME = "testif0";
63
64    private final ArrayList<Integer> mAllowed2GChannels =
65            new ArrayList<>(Arrays.asList(ALLOWED_2G_CHANNELS));
66
67    private final WifiConfiguration mDefaultApConfig = createDefaultApConfig();
68
69    TestLooper mLooper;
70    @Mock WifiNative mWifiNative;
71    @Mock SoftApManager.Listener mListener;
72    @Mock InterfaceConfiguration mInterfaceConfiguration;
73    @Mock IBinder mApInterfaceBinder;
74    @Mock IApInterface mApInterface;
75    @Mock INetworkManagementService mNmService;
76    @Mock WifiApConfigStore mWifiApConfigStore;
77    @Mock WifiMetrics mWifiMetrics;
78    final ArgumentCaptor<DeathRecipient> mDeathListenerCaptor =
79            ArgumentCaptor.forClass(DeathRecipient.class);
80    final ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor =
81            ArgumentCaptor.forClass(BaseNetworkObserver.class);
82
83    SoftApManager mSoftApManager;
84
85    /** Sets up test. */
86    @Before
87    public void setUp() throws Exception {
88        MockitoAnnotations.initMocks(this);
89        mLooper = new TestLooper();
90
91        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
92        when(mApInterface.startHostapd()).thenReturn(true);
93        when(mApInterface.stopHostapd()).thenReturn(true);
94        when(mApInterface.writeHostapdConfig(
95                any(), anyBoolean(), anyInt(), anyInt(), any())).thenReturn(true);
96        when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
97    }
98
99    private WifiConfiguration createDefaultApConfig() {
100        WifiConfiguration defaultConfig = new WifiConfiguration();
101        defaultConfig.SSID = DEFAULT_SSID;
102        return defaultConfig;
103    }
104
105    private SoftApManager createSoftApManager(WifiConfiguration config) throws Exception {
106        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
107        when(mApInterface.startHostapd()).thenReturn(true);
108        when(mApInterface.stopHostapd()).thenReturn(true);
109        if (config == null) {
110            when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
111        }
112        SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(),
113                                                           mWifiNative,
114                                                           TEST_COUNTRY_CODE,
115                                                           mListener,
116                                                           mApInterface,
117                                                           mNmService,
118                                                           mWifiApConfigStore,
119                                                           config,
120                                                           mWifiMetrics);
121        mLooper.dispatchAll();
122        return newSoftApManager;
123    }
124
125    /** Verifies startSoftAp will use default config if AP configuration is not provided. */
126    @Test
127    public void startSoftApWithoutConfig() throws Exception {
128        startSoftApAndVerifyEnabled(null);
129    }
130
131    /** Verifies startSoftAp will use provided config and start AP. */
132    @Test
133    public void startSoftApWithConfig() throws Exception {
134        WifiConfiguration config = new WifiConfiguration();
135        config.apBand = WifiConfiguration.AP_BAND_2GHZ;
136        config.SSID = TEST_SSID;
137        startSoftApAndVerifyEnabled(config);
138    }
139
140
141    /**
142     * Verifies startSoftAp will start with the hiddenSSID param set when it is set to true in the
143     * supplied config.
144     */
145    @Test
146    public void startSoftApWithHiddenSsidTrueInConfig() throws Exception {
147        WifiConfiguration config = new WifiConfiguration();
148        config.apBand = WifiConfiguration.AP_BAND_2GHZ;
149        config.SSID = TEST_SSID;
150        config.hiddenSSID = true;
151        startSoftApAndVerifyEnabled(config);
152    }
153
154    /** Tests softap startup if default config fails to load. **/
155    @Test
156    public void startSoftApDefaultConfigFailedToLoad() throws Exception {
157        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
158        when(mApInterface.startHostapd()).thenReturn(true);
159        when(mApInterface.stopHostapd()).thenReturn(true);
160        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
161        SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(),
162                                                           mWifiNative,
163                                                           TEST_COUNTRY_CODE,
164                                                           mListener,
165                                                           mApInterface,
166                                                           mNmService,
167                                                           mWifiApConfigStore,
168                                                           null,
169                                                           mWifiMetrics);
170        mLooper.dispatchAll();
171        newSoftApManager.start();
172        mLooper.dispatchAll();
173        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
174                WifiManager.SAP_START_FAILURE_GENERAL);
175    }
176
177    /** Tests the handling of stop command when soft AP is not started. */
178    @Test
179    public void stopWhenNotStarted() throws Exception {
180        mSoftApManager = createSoftApManager(null);
181        mSoftApManager.stop();
182        mLooper.dispatchAll();
183        /* Verify no state changes. */
184        verify(mListener, never()).onStateChanged(anyInt(), anyInt());
185    }
186
187    /** Tests the handling of stop command when soft AP is started. */
188    @Test
189    public void stopWhenStarted() throws Exception {
190        startSoftApAndVerifyEnabled(null);
191
192        InOrder order = inOrder(mListener);
193
194        mSoftApManager.stop();
195        mLooper.dispatchAll();
196
197        verify(mApInterface).stopHostapd();
198        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
199        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
200    }
201
202    @Test
203    public void handlesWificondInterfaceDeath() throws Exception {
204        startSoftApAndVerifyEnabled(null);
205
206        mDeathListenerCaptor.getValue().binderDied();
207        mLooper.dispatchAll();
208        InOrder order = inOrder(mListener);
209        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
210        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
211                WifiManager.SAP_START_FAILURE_GENERAL);
212    }
213
214    /** Starts soft AP and verifies that it is enabled successfully. */
215    protected void startSoftApAndVerifyEnabled(WifiConfiguration config) throws Exception {
216        String expectedSSID;
217        boolean expectedHiddenSsid;
218        InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService);
219
220        when(mWifiNative.isHalStarted()).thenReturn(false);
221        when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
222                .thenReturn(true);
223
224        mSoftApManager = createSoftApManager(config);
225        if (config == null) {
226            when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
227            expectedSSID = mDefaultApConfig.SSID;
228            expectedHiddenSsid = mDefaultApConfig.hiddenSSID;
229        } else {
230            expectedSSID = config.SSID;
231            expectedHiddenSsid = config.hiddenSSID;
232        }
233
234        mSoftApManager.start();
235        mLooper.dispatchAll();
236        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
237        order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0));
238        order.verify(mNmService).registerObserver(mNetworkObserverCaptor.capture());
239        order.verify(mApInterface).writeHostapdConfig(
240                eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), eq(expectedHiddenSsid),
241                anyInt(), anyInt(), any());
242        order.verify(mApInterface).startHostapd();
243        mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true);
244        mLooper.dispatchAll();
245        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
246    }
247
248    /** Verifies that soft AP was not disabled. */
249    protected void verifySoftApNotDisabled() throws Exception {
250        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
251        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
252    }
253}
254