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.anyInt;
21import static org.mockito.Mockito.eq;
22import static org.mockito.Mockito.inOrder;
23import static org.mockito.Mockito.mock;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
27
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.IntentFilter;
31import android.net.ConnectivityManager;
32import android.net.InterfaceConfiguration;
33import android.net.LinkAddress;
34import android.net.wifi.WifiConfiguration;
35import android.net.wifi.WifiManager;
36import android.os.INetworkManagementService;
37import android.test.suitebuilder.annotation.SmallTest;
38
39import org.junit.Before;
40import org.junit.Test;
41import org.mockito.ArgumentCaptor;
42import org.mockito.InOrder;
43import org.mockito.Mock;
44import org.mockito.MockitoAnnotations;
45
46import java.util.ArrayList;
47import java.util.Arrays;
48import java.util.Locale;
49
50/** Unit tests for {@link SoftApManager}. */
51@SmallTest
52public class SoftApManagerTest {
53
54    private static final String TAG = "SoftApManagerTest";
55
56    private static final String TEST_INTERFACE_NAME = "TestInterface";
57    private static final String TEST_COUNTRY_CODE = "TestCountry";
58    private static final Integer[] ALLOWED_2G_CHANNELS = {1, 2, 3, 4};
59    private static final String[] AVAILABLE_DEVICES = { TEST_INTERFACE_NAME };
60
61    private final ArrayList<Integer> mAllowed2GChannels =
62            new ArrayList<Integer>(Arrays.asList(ALLOWED_2G_CHANNELS));
63
64    MockLooper mLooper;
65    @Mock Context mContext;
66    @Mock WifiNative mWifiNative;
67    @Mock INetworkManagementService mNmService;
68    @Mock ConnectivityManager mConnectivityManager;
69    @Mock SoftApManager.Listener mListener;
70    @Mock InterfaceConfiguration mInterfaceConfiguration;
71
72    /**
73     * Internal BroadcastReceiver that SoftApManager uses to listen for tethering
74     * events from ConnectivityManager.
75     */
76    BroadcastReceiver mBroadcastReceiver;
77
78    SoftApManager mSoftApManager;
79
80    /** Sets up test. */
81    @Before
82    public void setUp() throws Exception {
83        MockitoAnnotations.initMocks(this);
84        mLooper = new MockLooper();
85
86        when(mWifiNative.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
87        when(mNmService.getInterfaceConfig(TEST_INTERFACE_NAME))
88                .thenReturn(mInterfaceConfiguration);
89        when(mConnectivityManager.getTetherableWifiRegexs())
90                .thenReturn(AVAILABLE_DEVICES);
91
92        mSoftApManager = new SoftApManager(mContext,
93                                           mLooper.getLooper(),
94                                           mWifiNative,
95                                           mNmService,
96                                           mConnectivityManager,
97                                           TEST_COUNTRY_CODE,
98                                           mAllowed2GChannels,
99                                           mListener);
100        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
101                ArgumentCaptor.forClass(BroadcastReceiver.class);
102        verify(mContext).registerReceiver(
103                broadcastReceiverCaptor.capture(), any(IntentFilter.class));
104        mBroadcastReceiver = broadcastReceiverCaptor.getValue();
105
106        mLooper.dispatchAll();
107    }
108
109    /** Verifies startSoftAp will fail if AP configuration is not provided. */
110    @Test
111    public void startSoftApWithoutConfig() throws Exception {
112        InOrder order = inOrder(mListener);
113
114        mSoftApManager.start(null);
115        mLooper.dispatchAll();
116
117        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
118        order.verify(mListener).onStateChanged(
119                WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL);
120    }
121
122    /** Tests the handling of timeout after tethering is started. */
123    @Test
124    public void tetheringTimedOut() throws Exception {
125        startSoftApAndVerifyEnabled();
126        announceAvailableForTethering();
127        verifyTetheringRequested();
128
129        InOrder order = inOrder(mListener);
130
131        /* Move the time forward to simulate notification timeout. */
132        mLooper.moveTimeForward(5000);
133        mLooper.dispatchAll();
134
135        /* Verify soft ap is disabled. */
136        verify(mNmService).stopAccessPoint(eq(TEST_INTERFACE_NAME));
137        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
138        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
139    }
140
141    /** Tests the handling of tethered notification after tethering is started. */
142    @Test
143    public void tetherCompleted() throws Exception {
144        startSoftApAndVerifyEnabled();
145        announceAvailableForTethering();
146        verifyTetheringRequested();
147        announceTethered();
148        verifySoftApNotDisabled();
149    }
150
151    /** Tests the handling of stop command when soft AP is not started. */
152    @Test
153    public void stopWhenNotStarted() throws Exception {
154        mSoftApManager.stop();
155        mLooper.dispatchAll();
156        /* Verify no state changes. */
157        verify(mListener, never()).onStateChanged(anyInt(), anyInt());
158    }
159
160    /** Tests the handling of stop command when soft AP is started. */
161    @Test
162    public void stopWhenStarted() throws Exception {
163        startSoftApAndVerifyEnabled();
164
165        InOrder order = inOrder(mListener);
166
167        mSoftApManager.stop();
168        mLooper.dispatchAll();
169
170        verify(mNmService).stopAccessPoint(TEST_INTERFACE_NAME);
171        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
172        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
173    }
174
175    /** Starts soft AP and verifies that it is enabled successfully. */
176    protected void startSoftApAndVerifyEnabled() throws Exception {
177        InOrder order = inOrder(mListener);
178
179        /**
180         *  Only test the default configuration. Testing for different configurations
181         *  are taken care of by ApConfigUtilTest.
182         */
183        WifiConfiguration config = new WifiConfiguration();
184        config.apBand = WifiConfiguration.AP_BAND_2GHZ;
185        when(mWifiNative.isHalStarted()).thenReturn(false);
186        when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
187                .thenReturn(true);
188        mSoftApManager.start(config);
189        mLooper.dispatchAll();
190        verify(mNmService).startAccessPoint(
191                any(WifiConfiguration.class), eq(TEST_INTERFACE_NAME));
192        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
193        order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
194    }
195
196    /** Verifies that soft AP was not disabled. */
197    protected void verifySoftApNotDisabled() throws Exception {
198        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
199        verify(mListener, never()).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
200    }
201
202    /** Sends a broadcast intent indicating that the interface is available for tethering. */
203    protected void announceAvailableForTethering() throws Exception {
204        when(mConnectivityManager.tether(TEST_INTERFACE_NAME))
205                .thenReturn(ConnectivityManager.TETHER_ERROR_NO_ERROR);
206        ArrayList<String> availableList =
207                new ArrayList<String>(Arrays.asList(AVAILABLE_DEVICES));
208        TestUtil.sendTetherStateChanged(
209                mBroadcastReceiver, mContext, availableList, new ArrayList<String>());
210        mLooper.dispatchAll();
211    }
212
213    /** Verifies that tethering was requested. */
214    protected void verifyTetheringRequested() throws Exception {
215        verify(mInterfaceConfiguration).setLinkAddress(any(LinkAddress.class));
216        verify(mInterfaceConfiguration).setInterfaceUp();
217        verify(mNmService).setInterfaceConfig(eq(TEST_INTERFACE_NAME), eq(mInterfaceConfiguration));
218    }
219
220    /** Sends a broadcast intent indicating that the interface is tethered. */
221    protected void announceTethered() throws Exception {
222        ArrayList<String> deviceList =
223                new ArrayList<String>(Arrays.asList(AVAILABLE_DEVICES));
224        TestUtil.sendTetherStateChanged(
225                mBroadcastReceiver, mContext, deviceList, deviceList);
226        mLooper.dispatchAll();
227    }
228}
229