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    /** Tests softap startup if default config fails to load. **/
141    @Test
142    public void startSoftApDefaultConfigFailedToLoad() throws Exception {
143        when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
144        when(mApInterface.startHostapd()).thenReturn(true);
145        when(mApInterface.stopHostapd()).thenReturn(true);
146        when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
147        SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(),
148                                                           mWifiNative,
149                                                           TEST_COUNTRY_CODE,
150                                                           mListener,
151                                                           mApInterface,
152                                                           mNmService,
153                                                           mWifiApConfigStore,
154                                                           null,
155                                                           mWifiMetrics);
156        mLooper.dispatchAll();
157        newSoftApManager.start();
158        mLooper.dispatchAll();
159        verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
160                WifiManager.SAP_START_FAILURE_GENERAL);
161    }
162
163    /** Tests the handling of stop command when soft AP is not started. */
164    @Test
165    public void stopWhenNotStarted() throws Exception {
166        mSoftApManager = createSoftApManager(null);
167        mSoftApManager.stop();
168        mLooper.dispatchAll();
169        /* Verify no state changes. */
170        verify(mListener, never()).onStateChanged(anyInt(), anyInt());
171    }
172
173    /** Tests the handling of stop command when soft AP is started. */
174    @Test
175    public void stopWhenStarted() throws Exception {
176        startSoftApAndVerifyEnabled(null);
177
178        InOrder order = inOrder(mListener);
179
180        mSoftApManager.stop();
181        mLooper.dispatchAll();
182
183        verify(mApInterface).stopHostapd();
184        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
185        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
186    }
187
188    @Test
189    public void handlesWificondInterfaceDeath() throws Exception {
190        startSoftApAndVerifyEnabled(null);
191
192        mDeathListenerCaptor.getValue().binderDied();
193        mLooper.dispatchAll();
194        InOrder order = inOrder(mListener);
195        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
196        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
197                WifiManager.SAP_START_FAILURE_GENERAL);
198    }
199
200    /** Starts soft AP and verifies that it is enabled successfully. */
201    protected void startSoftApAndVerifyEnabled(WifiConfiguration config) throws Exception {
202        String expectedSSID;
203        InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService);
204
205        when(mWifiNative.isHalStarted()).thenReturn(false);
206        when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
207                .thenReturn(true);
208
209        mSoftApManager = createSoftApManager(config);
210        if (config == null) {
211            when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
212            expectedSSID = mDefaultApConfig.SSID;
213        } else {
214            expectedSSID = config.SSID;
215        }
216        mSoftApManager.start();
217        mLooper.dispatchAll();
218        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
219        order.verify(mApInterfaceBinder).linkToDeath(mDeathListenerCaptor.capture(), eq(0));
220        order.verify(mNmService).registerObserver(mNetworkObserverCaptor.capture());
221        order.verify(mApInterface).writeHostapdConfig(
222                eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), anyBoolean(),
223                anyInt(), anyInt(), any());
224        order.verify(mApInterface).startHostapd();
225        mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true);
226        mLooper.dispatchAll();
227        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
228    }
229
230    /** Verifies that soft AP was not disabled. */
231    protected void verifySoftApNotDisabled() throws Exception {
232        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
233        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
234    }
235}
236