blob: 9f7261dc6019d6f8f5882da60c829daa22b77ba2 [file] [log] [blame]
Christopher Wiley1ff75bd2016-05-18 16:32:44 -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.tethering;
18
Christopher Wileye10bfc02016-05-23 16:17:30 -070019import static org.mockito.Matchers.anyString;
20import static org.mockito.Mockito.doThrow;
Christopher Wiley7b61d712016-05-20 13:23:10 -070021import static org.mockito.Mockito.inOrder;
22import static org.mockito.Mockito.reset;
23import static org.mockito.Mockito.verify;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070024import static org.mockito.Mockito.verifyNoMoreInteractions;
Christopher Wiley7b61d712016-05-20 13:23:10 -070025import static org.mockito.Mockito.when;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070026
Christopher Wileyd985dde2016-05-31 10:44:35 -070027import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
28import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
29import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
30import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
31import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
32import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
33import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
34
Christopher Wiley5c0b10a2016-05-31 14:43:08 -070035import android.net.ConnectivityManager;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070036import android.net.INetworkStatsService;
Christopher Wiley7b61d712016-05-20 13:23:10 -070037import android.net.InterfaceConfiguration;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070038import android.os.INetworkManagementService;
Christopher Wiley7b61d712016-05-20 13:23:10 -070039import android.os.RemoteException;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070040import android.os.test.TestLooper;
Christopher Wiley0ab0dd32016-05-25 13:57:27 -070041import android.support.test.filters.SmallTest;
42import android.support.test.runner.AndroidJUnit4;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070043
44import org.junit.Before;
45import org.junit.Test;
Christopher Wiley0ab0dd32016-05-25 13:57:27 -070046import org.junit.runner.RunWith;
Christopher Wiley7b61d712016-05-20 13:23:10 -070047import org.mockito.InOrder;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070048import org.mockito.Mock;
49import org.mockito.MockitoAnnotations;
50
Christopher Wiley0ab0dd32016-05-25 13:57:27 -070051@RunWith(AndroidJUnit4.class)
52@SmallTest
Mitchell Wills7040b4e2016-05-23 16:40:10 -070053public class TetherInterfaceStateMachineTest {
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070054 private static final String IFACE_NAME = "testnet1";
Christopher Wiley7b61d712016-05-20 13:23:10 -070055 private static final String UPSTREAM_IFACE = "upstream0";
56 private static final String UPSTREAM_IFACE2 = "upstream1";
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070057
58 @Mock private INetworkManagementService mNMService;
59 @Mock private INetworkStatsService mStatsService;
60 @Mock private IControlsTethering mTetherHelper;
Christopher Wiley7b61d712016-05-20 13:23:10 -070061 @Mock private InterfaceConfiguration mInterfaceConfiguration;
Lorenzo Colitti14422332016-10-28 17:45:55 +090062 @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070063
64 private final TestLooper mLooper = new TestLooper();
Mitchell Wills7040b4e2016-05-23 16:40:10 -070065 private TetherInterfaceStateMachine mTestedSm;
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070066
Christopher Wiley5c0b10a2016-05-31 14:43:08 -070067 private void initStateMachine(int interfaceType) throws Exception {
68 mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType,
Lorenzo Colitti14422332016-10-28 17:45:55 +090069 mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices);
Christopher Wiley7b61d712016-05-20 13:23:10 -070070 mTestedSm.start();
71 // Starting the state machine always puts us in a consistent state and notifies
72 // the test of the world that we've changed from an unknown to available state.
73 mLooper.dispatchAll();
74 reset(mNMService, mStatsService, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -070075 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
Christopher Wiley7b61d712016-05-20 13:23:10 -070076 }
77
Christopher Wiley5c0b10a2016-05-31 14:43:08 -070078 private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception {
79 initStateMachine(interfaceType);
Mitchell Wills7040b4e2016-05-23 16:40:10 -070080 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
Christopher Wiley7b61d712016-05-20 13:23:10 -070081 if (upstreamIface != null) {
82 dispatchTetherConnectionChanged(upstreamIface);
83 }
84 reset(mNMService, mStatsService, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -070085 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
Christopher Wiley7b61d712016-05-20 13:23:10 -070086 }
87
Christopher Wileyd985dde2016-05-31 10:44:35 -070088 @Before public void setUp() throws Exception {
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070089 MockitoAnnotations.initMocks(this);
Christopher Wiley7b61d712016-05-20 13:23:10 -070090 }
91
92 @Test
93 public void startsOutAvailable() {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -070094 mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
Lorenzo Colitti14422332016-10-28 17:45:55 +090095 ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper,
96 mIPv6TetheringInterfaceServices);
Christopher Wiley1ff75bd2016-05-18 16:32:44 -070097 mTestedSm.start();
Christopher Wiley7b61d712016-05-20 13:23:10 -070098 mLooper.dispatchAll();
Christopher Wileyd985dde2016-05-31 10:44:35 -070099 verify(mTetherHelper).notifyInterfaceStateChange(
100 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700101 verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700102 }
103
104 @Test
Christopher Wileye10bfc02016-05-23 16:17:30 -0700105 public void shouldDoNothingUntilRequested() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700106 initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700107 final int [] NOOP_COMMANDS = {
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700108 TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
109 TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
110 TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
111 TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
112 TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
113 TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
114 TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700115 };
116 for (int command : NOOP_COMMANDS) {
Christopher Wiley7b61d712016-05-20 13:23:10 -0700117 // None of these commands should trigger us to request action from
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700118 // the rest of the system.
Christopher Wiley7b61d712016-05-20 13:23:10 -0700119 dispatchCommand(command);
120 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700121 }
122 }
123
Christopher Wiley7b61d712016-05-20 13:23:10 -0700124 @Test
Christopher Wileye10bfc02016-05-23 16:17:30 -0700125 public void handlesImmediateInterfaceDown() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700126 initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700127
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700128 dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700129 verify(mTetherHelper).notifyInterfaceStateChange(
130 IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700131 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700132 }
133
134 @Test
Christopher Wileye10bfc02016-05-23 16:17:30 -0700135 public void canBeTethered() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700136 initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700137
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700138 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700139 InOrder inOrder = inOrder(mTetherHelper, mNMService);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700140 inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700141 inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
142 IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700143 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700144 }
145
146 @Test
147 public void canUnrequestTethering() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700148 initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700149
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700150 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700151 InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700152 inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700153 inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
154 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700155 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700156 }
157
158 @Test
Christopher Wileye10bfc02016-05-23 16:17:30 -0700159 public void canBeTetheredAsUsb() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700160 initStateMachine(ConnectivityManager.TETHERING_USB);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700161
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700162 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700163 InOrder inOrder = inOrder(mTetherHelper, mNMService);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700164 inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
165 inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700166 inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700167 inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
168 IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700169 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700170 }
171
172 @Test
173 public void handlesFirstUpstreamChange() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700174 initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700175
176 // Telling the state machine about its upstream interface triggers a little more configuration.
177 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
178 InOrder inOrder = inOrder(mNMService);
179 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
180 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
181 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700182 }
183
184 @Test
185 public void handlesChangingUpstream() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700186 initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700187
188 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
189 InOrder inOrder = inOrder(mNMService, mStatsService);
190 inOrder.verify(mStatsService).forceUpdate();
191 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
192 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
193 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
194 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
195 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700196 }
197
198 @Test
199 public void canUnrequestTetheringWithUpstream() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700200 initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700201
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700202 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700203 InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
204 inOrder.verify(mStatsService).forceUpdate();
205 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
206 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
207 inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700208 inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
209 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700210 verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700211 }
212
Christopher Wileye10bfc02016-05-23 16:17:30 -0700213 @Test
214 public void interfaceDownLeadsToUnavailable() throws Exception {
215 for (boolean shouldThrow : new boolean[]{true, false}) {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700216 initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700217
218 if (shouldThrow) {
219 doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
220 }
221 dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700222 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700223 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
224 usbTeardownOrder.verify(mNMService).setInterfaceConfig(
225 IFACE_NAME, mInterfaceConfiguration);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700226 usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
227 IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700228 }
229 }
230
231 @Test
232 public void usbShouldBeTornDownOnTetherError() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700233 initStateMachine(ConnectivityManager.TETHERING_USB);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700234
235 doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
236 dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700237 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700238 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
239 usbTeardownOrder.verify(mNMService).setInterfaceConfig(
240 IFACE_NAME, mInterfaceConfiguration);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700241 usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
242 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700243 }
244
245 @Test
246 public void shouldTearDownUsbOnUpstreamError() throws Exception {
Christopher Wiley5c0b10a2016-05-31 14:43:08 -0700247 initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700248
249 doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
250 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700251 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700252 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
253 usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
Christopher Wileyd985dde2016-05-31 10:44:35 -0700254 usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
255 IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
Christopher Wileye10bfc02016-05-23 16:17:30 -0700256 }
Christopher Wiley7b61d712016-05-20 13:23:10 -0700257
258 /**
259 * Send a command to the state machine under test, and run the event loop to idle.
260 *
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700261 * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
Christopher Wiley7b61d712016-05-20 13:23:10 -0700262 */
263 private void dispatchCommand(int command) {
264 mTestedSm.sendMessage(command);
265 mLooper.dispatchAll();
266 }
267
268 /**
269 * Special override to tell the state machine that the upstream interface has changed.
270 *
271 * @see #dispatchCommand(int)
272 * @param upstreamIface String name of upstream interface (or null)
273 */
274 private void dispatchTetherConnectionChanged(String upstreamIface) {
Mitchell Wills7040b4e2016-05-23 16:40:10 -0700275 mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
276 upstreamIface);
Christopher Wiley7b61d712016-05-20 13:23:10 -0700277 mLooper.dispatchAll();
Christopher Wiley1ff75bd2016-05-18 16:32:44 -0700278 }
Lorenzo Colitti14422332016-10-28 17:45:55 +0900279}