blob: e527d57f7367206e844387ef75b921a56c94a0b1 [file] [log] [blame]
Christopher Wiley497c1472016-10-11 13:26:03 -07001/*
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.connectivity;
18
Erik Klineea9cc482017-03-10 19:35:34 +090019import static org.junit.Assert.assertEquals;
Christopher Wiley497c1472016-10-11 13:26:03 -070020import static org.junit.Assert.assertTrue;
Erik Klineea9cc482017-03-10 19:35:34 +090021import static org.mockito.Mockito.any;
Christopher Wiley497c1472016-10-11 13:26:03 -070022import static org.mockito.Matchers.anyBoolean;
Erik Klineea9cc482017-03-10 19:35:34 +090023import static org.mockito.Matchers.anyInt;
24import static org.mockito.Matchers.anyString;
Christopher Wiley497c1472016-10-11 13:26:03 -070025import static org.mockito.Matchers.eq;
Erik Klineea9cc482017-03-10 19:35:34 +090026import static org.mockito.Mockito.atLeastOnce;
27import static org.mockito.Mockito.times;
28import static org.mockito.Mockito.verify;
29import static org.mockito.Mockito.verifyNoMoreInteractions;
Christopher Wiley497c1472016-10-11 13:26:03 -070030import static org.mockito.Mockito.when;
31
32import android.content.Context;
Erik Klineea9cc482017-03-10 19:35:34 +090033import android.content.ContextWrapper;
34import android.content.Intent;
Christopher Wiley497c1472016-10-11 13:26:03 -070035import android.content.res.Resources;
Erik Klineea9cc482017-03-10 19:35:34 +090036import android.hardware.usb.UsbManager;
37import android.net.ConnectivityManager;
38import android.net.ConnectivityManager.NetworkCallback;
Christopher Wiley497c1472016-10-11 13:26:03 -070039import android.net.INetworkPolicyManager;
40import android.net.INetworkStatsService;
Erik Klineea9cc482017-03-10 19:35:34 +090041import android.net.InterfaceConfiguration;
42import android.net.NetworkRequest;
43import android.net.wifi.WifiConfiguration;
44import android.net.wifi.WifiManager;
45import android.os.Handler;
Christopher Wiley497c1472016-10-11 13:26:03 -070046import android.os.INetworkManagementService;
47import android.os.PersistableBundle;
48import android.os.test.TestLooper;
Erik Klineea9cc482017-03-10 19:35:34 +090049import android.os.UserHandle;
Christopher Wiley497c1472016-10-11 13:26:03 -070050import android.support.test.filters.SmallTest;
51import android.support.test.runner.AndroidJUnit4;
52import android.telephony.CarrierConfigManager;
53
Erik Klineea9cc482017-03-10 19:35:34 +090054import com.android.internal.util.test.BroadcastInterceptingContext;
55
Christopher Wiley497c1472016-10-11 13:26:03 -070056import org.junit.Before;
57import org.junit.Test;
58import org.junit.runner.RunWith;
59import org.mockito.Mock;
60import org.mockito.MockitoAnnotations;
61
62@RunWith(AndroidJUnit4.class)
63@SmallTest
64public class TetheringTest {
65 private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
66
67 @Mock private Context mContext;
Erik Klineea9cc482017-03-10 19:35:34 +090068 @Mock private ConnectivityManager mConnectivityManager;
Christopher Wiley497c1472016-10-11 13:26:03 -070069 @Mock private INetworkManagementService mNMService;
70 @Mock private INetworkStatsService mStatsService;
71 @Mock private INetworkPolicyManager mPolicyManager;
72 @Mock private MockableSystemProperties mSystemProperties;
73 @Mock private Resources mResources;
Erik Klineea9cc482017-03-10 19:35:34 +090074 @Mock private UsbManager mUsbManager;
75 @Mock private WifiManager mWifiManager;
Christopher Wiley497c1472016-10-11 13:26:03 -070076 @Mock private CarrierConfigManager mCarrierConfigManager;
77
78 // Like so many Android system APIs, these cannot be mocked because it is marked final.
79 // We have to use the real versions.
80 private final PersistableBundle mCarrierConfig = new PersistableBundle();
81 private final TestLooper mLooper = new TestLooper();
Erik Klineea9cc482017-03-10 19:35:34 +090082 private final String mTestIfname = "test_wlan0";
Christopher Wiley497c1472016-10-11 13:26:03 -070083
Erik Klineea9cc482017-03-10 19:35:34 +090084 private BroadcastInterceptingContext mServiceContext;
Christopher Wiley497c1472016-10-11 13:26:03 -070085 private Tethering mTethering;
86
Erik Klineea9cc482017-03-10 19:35:34 +090087 private class MockContext extends BroadcastInterceptingContext {
88 MockContext(Context base) {
89 super(base);
90 }
91
92 @Override
93 public Resources getResources() { return mResources; }
94
95 @Override
96 public Object getSystemService(String name) {
97 if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
98 if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
99 return super.getSystemService(name);
100 }
101 }
102
Christopher Wiley497c1472016-10-11 13:26:03 -0700103 @Before public void setUp() throws Exception {
104 MockitoAnnotations.initMocks(this);
Christopher Wiley497c1472016-10-11 13:26:03 -0700105 when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
106 .thenReturn(new String[0]);
107 when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
108 .thenReturn(new String[0]);
109 when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
Erik Klineea9cc482017-03-10 19:35:34 +0900110 .thenReturn(new String[]{ "test_wlan\\d" });
Christopher Wiley497c1472016-10-11 13:26:03 -0700111 when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
112 .thenReturn(new String[0]);
113 when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
114 .thenReturn(new int[0]);
Erik Klineea9cc482017-03-10 19:35:34 +0900115 when(mNMService.listInterfaces())
116 .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
117 when(mNMService.getInterfaceConfig(anyString()))
118 .thenReturn(new InterfaceConfiguration());
119
120 mServiceContext = new MockContext(mContext);
121 mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
Christopher Wiley497c1472016-10-11 13:26:03 -0700122 mLooper.getLooper(), mSystemProperties);
123 }
124
125 private void setupForRequiredProvisioning() {
126 // Produce some acceptable looking provision app setting if requested.
127 when(mResources.getStringArray(
128 com.android.internal.R.array.config_mobile_hotspot_provision_app))
129 .thenReturn(PROVISIONING_APP_NAME);
130 // Don't disable tethering provisioning unless requested.
131 when(mSystemProperties.getBoolean(eq(Tethering.DISABLE_PROVISIONING_SYSPROP_KEY),
132 anyBoolean())).thenReturn(false);
133 // Act like the CarrierConfigManager is present and ready unless told otherwise.
134 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
135 .thenReturn(mCarrierConfigManager);
136 when(mCarrierConfigManager.getConfig()).thenReturn(mCarrierConfig);
137 mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
138 }
139
140 @Test
141 public void canRequireProvisioning() {
142 setupForRequiredProvisioning();
143 assertTrue(mTethering.isTetherProvisioningRequired());
144 }
145
146 @Test
147 public void toleratesCarrierConfigManagerMissing() {
148 setupForRequiredProvisioning();
149 when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
150 .thenReturn(null);
151 // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
152 // We therefore still require provisioning.
153 assertTrue(mTethering.isTetherProvisioningRequired());
154 }
155
156 @Test
157 public void toleratesCarrierConfigMissing() {
158 setupForRequiredProvisioning();
159 when(mCarrierConfigManager.getConfig()).thenReturn(null);
160 // We still have a provisioning app configured, so still require provisioning.
161 assertTrue(mTethering.isTetherProvisioningRequired());
162 }
163
164 @Test
165 public void provisioningNotRequiredWhenAppNotFound() {
166 setupForRequiredProvisioning();
167 when(mResources.getStringArray(
168 com.android.internal.R.array.config_mobile_hotspot_provision_app))
169 .thenReturn(null);
170 assertTrue(!mTethering.isTetherProvisioningRequired());
171 when(mResources.getStringArray(
172 com.android.internal.R.array.config_mobile_hotspot_provision_app))
173 .thenReturn(new String[] {"malformedApp"});
174 assertTrue(!mTethering.isTetherProvisioningRequired());
175 }
Erik Klineea9cc482017-03-10 19:35:34 +0900176
177 private void sendWifiApStateChanged(int state) {
178 final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
179 intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state);
180 mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
181 }
182
183 @Test
184 public void workingLocalOnlyHotspot() throws Exception {
185 when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
186 when(mWifiManager.setWifiApEnabled(any(WifiConfiguration.class), anyBoolean()))
187 .thenReturn(true);
188
189 // Emulate externally-visible WifiManager effects, causing the
190 // per-interface state machine to start up, and telling us that
191 // hotspot mode is to be started.
192 mTethering.interfaceStatusChanged(mTestIfname, true);
193 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED);
194 mLooper.dispatchAll();
195
196 verify(mNMService, times(1)).listInterfaces();
197 verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
198 verify(mNMService, times(1))
199 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
200 verify(mNMService, times(1)).tetherInterface(mTestIfname);
201 verify(mNMService, times(1)).setIpForwardingEnabled(true);
202 verify(mNMService, times(1)).startTethering(any(String[].class));
203 verifyNoMoreInteractions(mNMService);
204 // UpstreamNetworkMonitor will be started, and will register two callbacks:
205 // a "listen all" and a "track default".
206 verify(mConnectivityManager, times(1)).registerNetworkCallback(
207 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
208 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
209 any(NetworkCallback.class), any(Handler.class));
210 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
211 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
212 verifyNoMoreInteractions(mConnectivityManager);
213
214 // Emulate externally-visible WifiManager effects, when hotspot mode
215 // is being torn down.
216 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
217 mTethering.interfaceRemoved(mTestIfname);
218 mLooper.dispatchAll();
219
220 verify(mNMService, times(1)).untetherInterface(mTestIfname);
221 // TODO: Why is {g,s}etInterfaceConfig() called more than once?
222 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
223 verify(mNMService, atLeastOnce())
224 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
225 verify(mNMService, times(1)).stopTethering();
226 verify(mNMService, times(1)).setIpForwardingEnabled(false);
227 verifyNoMoreInteractions(mNMService);
228 // Asking for the last error after the per-interface state machine
229 // has been reaped yields an unknown interface error.
230 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
231 mTethering.getLastTetherError(mTestIfname));
232 }
233
234 @Test
235 public void workingWifiTethering() throws Exception {
236 when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
237 when(mWifiManager.setWifiApEnabled(any(WifiConfiguration.class), anyBoolean()))
238 .thenReturn(true);
239
240 // Emulate pressing the WiFi tethering button.
241 mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
242 mLooper.dispatchAll();
243 verify(mWifiManager, times(1)).setWifiApEnabled(null, true);
244 verifyNoMoreInteractions(mWifiManager);
245 verifyNoMoreInteractions(mConnectivityManager);
246 verifyNoMoreInteractions(mNMService);
247
248 // Emulate externally-visible WifiManager effects, causing the
249 // per-interface state machine to start up, and telling us that
250 // tethering mode is to be started.
251 mTethering.interfaceStatusChanged(mTestIfname, true);
252 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED);
253 mLooper.dispatchAll();
254
255 verify(mNMService, times(1)).listInterfaces();
256 verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
257 verify(mNMService, times(1))
258 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
259 verify(mNMService, times(1)).tetherInterface(mTestIfname);
260 verify(mNMService, times(1)).setIpForwardingEnabled(true);
261 verify(mNMService, times(1)).startTethering(any(String[].class));
262 verifyNoMoreInteractions(mNMService);
263 // UpstreamNetworkMonitor will be started, and will register two callbacks:
264 // a "listen all" and a "track default".
265 verify(mConnectivityManager, times(1)).registerNetworkCallback(
266 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
267 verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
268 any(NetworkCallback.class), any(Handler.class));
269 // In tethering mode, in the default configuration, an explicit request
270 // for a mobile network is also made.
271 verify(mConnectivityManager, atLeastOnce()).getNetworkInfo(anyInt());
272 verify(mConnectivityManager, times(1)).requestNetwork(
273 any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
274 any(Handler.class));
275 // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
276 verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
277 verifyNoMoreInteractions(mConnectivityManager);
278
279 /////
280 // We do not currently emulate any upstream being found.
281 //
282 // This is why there are no calls to verify mNMService.enableNat() or
283 // mNMService.startInterfaceForwarding().
284 /////
285
286 // Emulate pressing the WiFi tethering button.
287 mTethering.stopTethering(ConnectivityManager.TETHERING_WIFI);
288 mLooper.dispatchAll();
289 verify(mWifiManager, times(1)).setWifiApEnabled(null, false);
290 verifyNoMoreInteractions(mWifiManager);
291 verifyNoMoreInteractions(mConnectivityManager);
292 verifyNoMoreInteractions(mNMService);
293
294 // Emulate externally-visible WifiManager effects, when tethering mode
295 // is being torn down.
296 sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
297 mTethering.interfaceRemoved(mTestIfname);
298 mLooper.dispatchAll();
299
300 verify(mNMService, times(1)).untetherInterface(mTestIfname);
301 // TODO: Why is {g,s}etInterfaceConfig() called more than once?
302 verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
303 verify(mNMService, atLeastOnce())
304 .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
305 verify(mNMService, times(1)).stopTethering();
306 verify(mNMService, times(1)).setIpForwardingEnabled(false);
307 verifyNoMoreInteractions(mNMService);
308 // Asking for the last error after the per-interface state machine
309 // has been reaped yields an unknown interface error.
310 assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
311 mTethering.getLastTetherError(mTestIfname));
312 }
313
314 // TODO: Test that a request for hotspot mode doesn't interface with an
315 // already operating tethering mode interface.
Christopher Wiley497c1472016-10-11 13:26:03 -0700316}