blob: fd2f708aea302ef9a9f532b1cd7d1b329a88179e [file] [log] [blame]
Christopher Wiley08725a82016-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
Erik Kline7a4ccc62018-08-27 17:26:47 +090017package android.net.ip;
Christopher Wiley08725a82016-05-18 16:32:44 -070018
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090019import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
20import static android.net.ConnectivityManager.TETHERING_USB;
21import static android.net.ConnectivityManager.TETHERING_WIFI;
Jimmy Chenbcd86d02019-07-15 18:03:23 +080022import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090023import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
24import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
25import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090026import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090027import static android.net.ip.IpServer.STATE_AVAILABLE;
Jimmy Chenbcd86d02019-07-15 18:03:23 +080028import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090029import static android.net.ip.IpServer.STATE_TETHERED;
30import static android.net.ip.IpServer.STATE_UNAVAILABLE;
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090031import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090032
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090033import static org.junit.Assert.assertEquals;
Erik Klinedd4d5822017-06-12 18:20:08 +090034import static org.junit.Assert.assertFalse;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090035import static org.junit.Assert.assertNotNull;
Erik Klinedd4d5822017-06-12 18:20:08 +090036import static org.junit.Assert.assertTrue;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090037import static org.junit.Assert.fail;
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +090038import static org.mockito.ArgumentMatchers.argThat;
Erik Klineab6439b2017-06-06 19:24:21 +090039import static org.mockito.Matchers.any;
Christopher Wileyf972edc2016-05-23 16:17:30 -070040import static org.mockito.Matchers.anyString;
Erik Klineab6439b2017-06-06 19:24:21 +090041import static org.mockito.Matchers.eq;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090042import static org.mockito.Mockito.doAnswer;
Christopher Wileyf972edc2016-05-23 16:17:30 -070043import static org.mockito.Mockito.doThrow;
Christopher Wiley279eca32016-05-20 13:23:10 -070044import static org.mockito.Mockito.inOrder;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090045import static org.mockito.Mockito.never;
Christopher Wiley279eca32016-05-20 13:23:10 -070046import static org.mockito.Mockito.reset;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090047import static org.mockito.Mockito.timeout;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090048import static org.mockito.Mockito.times;
Christopher Wiley279eca32016-05-20 13:23:10 -070049import static org.mockito.Mockito.verify;
Christopher Wiley08725a82016-05-18 16:32:44 -070050import static org.mockito.Mockito.verifyNoMoreInteractions;
Christopher Wiley279eca32016-05-20 13:23:10 -070051import static org.mockito.Mockito.when;
Christopher Wiley08725a82016-05-18 16:32:44 -070052
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +090053import android.net.INetd;
Christopher Wiley08725a82016-05-18 16:32:44 -070054import android.net.INetworkStatsService;
Christopher Wiley279eca32016-05-20 13:23:10 -070055import android.net.InterfaceConfiguration;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090056import android.net.IpPrefix;
Erik Klinedd4d5822017-06-12 18:20:08 +090057import android.net.LinkAddress;
Erik Klineab6439b2017-06-06 19:24:21 +090058import android.net.LinkProperties;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090059import android.net.MacAddress;
Erik Klinedd4d5822017-06-12 18:20:08 +090060import android.net.RouteInfo;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090061import android.net.dhcp.DhcpServingParamsParcel;
62import android.net.dhcp.IDhcpServer;
63import android.net.dhcp.IDhcpServerCallbacks;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090064import android.net.util.InterfaceParams;
Remi NGUYEN VAN25a7e4f2018-03-09 14:07:18 +090065import android.net.util.InterfaceSet;
Erik Kline7747fd42017-05-12 16:52:48 +090066import android.net.util.SharedLog;
Christopher Wiley08725a82016-05-18 16:32:44 -070067import android.os.INetworkManagementService;
Christopher Wiley279eca32016-05-20 13:23:10 -070068import android.os.RemoteException;
Christopher Wiley08725a82016-05-18 16:32:44 -070069import android.os.test.TestLooper;
Erik Klinedd4d5822017-06-12 18:20:08 +090070import android.text.TextUtils;
71
Brett Chabot1ae2aa62019-03-04 14:14:56 -080072import androidx.test.filters.SmallTest;
73import androidx.test.runner.AndroidJUnit4;
74
Christopher Wiley08725a82016-05-18 16:32:44 -070075import org.junit.Before;
76import org.junit.Test;
Christopher Wiley9bc0df22016-05-25 13:57:27 -070077import org.junit.runner.RunWith;
Erik Klinedd4d5822017-06-12 18:20:08 +090078import org.mockito.ArgumentCaptor;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090079import org.mockito.Captor;
Christopher Wiley279eca32016-05-20 13:23:10 -070080import org.mockito.InOrder;
Christopher Wiley08725a82016-05-18 16:32:44 -070081import org.mockito.Mock;
82import org.mockito.MockitoAnnotations;
83
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090084import java.net.Inet4Address;
85
Christopher Wiley9bc0df22016-05-25 13:57:27 -070086@RunWith(AndroidJUnit4.class)
87@SmallTest
Erik Kline7a4ccc62018-08-27 17:26:47 +090088public class IpServerTest {
Christopher Wiley08725a82016-05-18 16:32:44 -070089 private static final String IFACE_NAME = "testnet1";
Christopher Wiley279eca32016-05-20 13:23:10 -070090 private static final String UPSTREAM_IFACE = "upstream0";
91 private static final String UPSTREAM_IFACE2 = "upstream1";
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090092 private static final int DHCP_LEASE_TIME_SECS = 3600;
93
94 private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
95 IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
Christopher Wiley08725a82016-05-18 16:32:44 -070096
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090097 private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
98
Christopher Wiley08725a82016-05-18 16:32:44 -070099 @Mock private INetworkManagementService mNMService;
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900100 @Mock private INetd mNetd;
Christopher Wiley08725a82016-05-18 16:32:44 -0700101 @Mock private INetworkStatsService mStatsService;
Erik Kline7a4ccc62018-08-27 17:26:47 +0900102 @Mock private IpServer.Callback mCallback;
Christopher Wiley279eca32016-05-20 13:23:10 -0700103 @Mock private InterfaceConfiguration mInterfaceConfiguration;
Erik Kline7747fd42017-05-12 16:52:48 +0900104 @Mock private SharedLog mSharedLog;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900105 @Mock private IDhcpServer mDhcpServer;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900106 @Mock private RouterAdvertisementDaemon mRaDaemon;
Erik Kline7a4ccc62018-08-27 17:26:47 +0900107 @Mock private IpServer.Dependencies mDependencies;
Christopher Wiley08725a82016-05-18 16:32:44 -0700108
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900109 @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900110
Christopher Wiley08725a82016-05-18 16:32:44 -0700111 private final TestLooper mLooper = new TestLooper();
Erik Klinedd4d5822017-06-12 18:20:08 +0900112 private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
113 ArgumentCaptor.forClass(LinkProperties.class);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900114 private IpServer mIpServer;
Christopher Wiley08725a82016-05-18 16:32:44 -0700115
Christopher Wileycd0cfbb2016-05-31 14:43:08 -0700116 private void initStateMachine(int interfaceType) throws Exception {
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900117 initStateMachine(interfaceType, false /* usingLegacyDhcp */);
118 }
119
120 private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900121 doAnswer(inv -> {
122 final IDhcpServerCallbacks cb = inv.getArgument(2);
123 new Thread(() -> {
124 try {
125 cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
126 } catch (RemoteException e) {
127 fail(e.getMessage());
128 }
129 }).run();
130 return null;
131 }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900132 when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
133 when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900134 when(mDependencies.getNetdService()).thenReturn(mNetd);
135
136 mIpServer = new IpServer(
137 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
138 mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
139 mIpServer.start();
140 // Starting the state machine always puts us in a consistent state and notifies
141 // the rest of the world that we've changed from an unknown to available state.
142 mLooper.dispatchAll();
143 reset(mNMService, mStatsService, mCallback);
144 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900145
146 when(mRaDaemon.start()).thenReturn(true);
Christopher Wiley279eca32016-05-20 13:23:10 -0700147 }
148
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900149 private void initTetheredStateMachine(int interfaceType, String upstreamIface)
150 throws Exception {
151 initTetheredStateMachine(interfaceType, upstreamIface, false);
152 }
153
154 private void initTetheredStateMachine(int interfaceType, String upstreamIface,
155 boolean usingLegacyDhcp) throws Exception {
156 initStateMachine(interfaceType, usingLegacyDhcp);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900157 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
Christopher Wiley279eca32016-05-20 13:23:10 -0700158 if (upstreamIface != null) {
159 dispatchTetherConnectionChanged(upstreamIface);
160 }
Erik Kline7a4ccc62018-08-27 17:26:47 +0900161 reset(mNMService, mStatsService, mCallback);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700162 when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
Christopher Wiley279eca32016-05-20 13:23:10 -0700163 }
164
Christopher Wileye90e0a72016-05-31 10:44:35 -0700165 @Before public void setUp() throws Exception {
Christopher Wiley08725a82016-05-18 16:32:44 -0700166 MockitoAnnotations.initMocks(this);
Erik Kline7747fd42017-05-12 16:52:48 +0900167 when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
Christopher Wiley279eca32016-05-20 13:23:10 -0700168 }
169
170 @Test
171 public void startsOutAvailable() {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900172 mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
173 TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
174 false /* usingLegacyDhcp */, mDependencies);
175 mIpServer.start();
Christopher Wiley279eca32016-05-20 13:23:10 -0700176 mLooper.dispatchAll();
Erik Kline7a4ccc62018-08-27 17:26:47 +0900177 verify(mCallback).updateInterfaceState(
178 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
179 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
180 verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
Christopher Wiley08725a82016-05-18 16:32:44 -0700181 }
182
183 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700184 public void shouldDoNothingUntilRequested() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900185 initStateMachine(TETHERING_BLUETOOTH);
markchien08cc0302019-09-09 20:50:49 +0800186 final int [] noOp_commands = {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900187 IpServer.CMD_TETHER_UNREQUESTED,
188 IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
189 IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
190 IpServer.CMD_START_TETHERING_ERROR,
191 IpServer.CMD_STOP_TETHERING_ERROR,
192 IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
193 IpServer.CMD_TETHER_CONNECTION_CHANGED
Christopher Wiley08725a82016-05-18 16:32:44 -0700194 };
markchien08cc0302019-09-09 20:50:49 +0800195 for (int command : noOp_commands) {
Christopher Wiley279eca32016-05-20 13:23:10 -0700196 // None of these commands should trigger us to request action from
Christopher Wiley08725a82016-05-18 16:32:44 -0700197 // the rest of the system.
Christopher Wiley279eca32016-05-20 13:23:10 -0700198 dispatchCommand(command);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900199 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley08725a82016-05-18 16:32:44 -0700200 }
201 }
202
Christopher Wiley279eca32016-05-20 13:23:10 -0700203 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700204 public void handlesImmediateInterfaceDown() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900205 initStateMachine(TETHERING_BLUETOOTH);
Christopher Wileye90e0a72016-05-31 10:44:35 -0700206
Erik Kline7a4ccc62018-08-27 17:26:47 +0900207 dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
208 verify(mCallback).updateInterfaceState(
209 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
210 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
211 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700212 }
213
214 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700215 public void canBeTethered() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900216 initStateMachine(TETHERING_BLUETOOTH);
Christopher Wileye90e0a72016-05-31 10:44:35 -0700217
Erik Kline7a4ccc62018-08-27 17:26:47 +0900218 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
219 InOrder inOrder = inOrder(mCallback, mNMService);
Christopher Wiley279eca32016-05-20 13:23:10 -0700220 inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900221 inOrder.verify(mCallback).updateInterfaceState(
222 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
223 inOrder.verify(mCallback).updateLinkProperties(
224 eq(mIpServer), any(LinkProperties.class));
225 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700226 }
227
228 @Test
229 public void canUnrequestTethering() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900230 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700231
Erik Kline7a4ccc62018-08-27 17:26:47 +0900232 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900233 InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700234 inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900235 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900236 inOrder.verify(mCallback).updateInterfaceState(
237 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
238 inOrder.verify(mCallback).updateLinkProperties(
239 eq(mIpServer), any(LinkProperties.class));
240 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700241 }
242
243 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700244 public void canBeTetheredAsUsb() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900245 initStateMachine(TETHERING_USB);
Christopher Wiley279eca32016-05-20 13:23:10 -0700246
Erik Kline7a4ccc62018-08-27 17:26:47 +0900247 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
248 InOrder inOrder = inOrder(mCallback, mNMService);
Christopher Wiley279eca32016-05-20 13:23:10 -0700249 inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
250 inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
Christopher Wiley279eca32016-05-20 13:23:10 -0700251 inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900252 inOrder.verify(mCallback).updateInterfaceState(
253 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
254 inOrder.verify(mCallback).updateLinkProperties(
255 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900256 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900257 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700258 }
259
260 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800261 public void canBeTetheredAsWifiP2p() throws Exception {
262 initStateMachine(TETHERING_WIFI_P2P);
263
264 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
265 InOrder inOrder = inOrder(mCallback, mNMService);
266 inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
267 inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
268 inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
269 inOrder.verify(mCallback).updateInterfaceState(
270 mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
271 inOrder.verify(mCallback).updateLinkProperties(
272 eq(mIpServer), mLinkPropertiesCaptor.capture());
273 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
274 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
275 }
276
277 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700278 public void handlesFirstUpstreamChange() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900279 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700280
Erik Klineab6439b2017-06-06 19:24:21 +0900281 // Telling the state machine about its upstream interface triggers
282 // a little more configuration.
Christopher Wiley279eca32016-05-20 13:23:10 -0700283 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
284 InOrder inOrder = inOrder(mNMService);
285 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
286 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900287 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700288 }
289
290 @Test
291 public void handlesChangingUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900292 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700293
294 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
295 InOrder inOrder = inOrder(mNMService, mStatsService);
296 inOrder.verify(mStatsService).forceUpdate();
297 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
298 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
299 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
300 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900301 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700302 }
303
304 @Test
Erik Klinea954be92017-02-13 17:12:02 +0900305 public void handlesChangingUpstreamNatFailure() throws Exception {
306 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
307
308 doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
309
310 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
311 InOrder inOrder = inOrder(mNMService, mStatsService);
312 inOrder.verify(mStatsService).forceUpdate();
313 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
314 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
315 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
Erik Kline8ea45482017-02-13 17:28:53 +0900316 inOrder.verify(mStatsService).forceUpdate();
317 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
318 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900319 }
320
321 @Test
322 public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
323 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
324
325 doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding(
326 IFACE_NAME, UPSTREAM_IFACE2);
327
328 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
329 InOrder inOrder = inOrder(mNMService, mStatsService);
330 inOrder.verify(mStatsService).forceUpdate();
331 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
332 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
333 inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
334 inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
Erik Kline8ea45482017-02-13 17:28:53 +0900335 inOrder.verify(mStatsService).forceUpdate();
336 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
337 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900338 }
339
340 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700341 public void canUnrequestTetheringWithUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900342 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700343
Erik Kline7a4ccc62018-08-27 17:26:47 +0900344 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900345 InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700346 inOrder.verify(mStatsService).forceUpdate();
347 inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
348 inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
349 inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900350 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900351 inOrder.verify(mCallback).updateInterfaceState(
352 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
353 inOrder.verify(mCallback).updateLinkProperties(
354 eq(mIpServer), any(LinkProperties.class));
355 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700356 }
357
Christopher Wileyf972edc2016-05-23 16:17:30 -0700358 @Test
359 public void interfaceDownLeadsToUnavailable() throws Exception {
360 for (boolean shouldThrow : new boolean[]{true, false}) {
Erik Klinea954be92017-02-13 17:12:02 +0900361 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700362
363 if (shouldThrow) {
364 doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
365 }
Erik Kline7a4ccc62018-08-27 17:26:47 +0900366 dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
367 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700368 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
369 usbTeardownOrder.verify(mNMService).setInterfaceConfig(
370 IFACE_NAME, mInterfaceConfiguration);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900371 usbTeardownOrder.verify(mCallback).updateInterfaceState(
372 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
373 usbTeardownOrder.verify(mCallback).updateLinkProperties(
374 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900375 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700376 }
377 }
378
379 @Test
380 public void usbShouldBeTornDownOnTetherError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900381 initStateMachine(TETHERING_USB);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700382
383 doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900384 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
385 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700386 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
387 usbTeardownOrder.verify(mNMService).setInterfaceConfig(
388 IFACE_NAME, mInterfaceConfiguration);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900389 usbTeardownOrder.verify(mCallback).updateInterfaceState(
390 mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
391 usbTeardownOrder.verify(mCallback).updateLinkProperties(
392 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900393 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700394 }
395
396 @Test
397 public void shouldTearDownUsbOnUpstreamError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900398 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700399
400 doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
401 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900402 InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700403 usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
404 usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900405 usbTeardownOrder.verify(mCallback).updateInterfaceState(
406 mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
407 usbTeardownOrder.verify(mCallback).updateLinkProperties(
408 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900409 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700410 }
Christopher Wiley279eca32016-05-20 13:23:10 -0700411
Erik Kline624bf3d2017-02-14 15:55:00 +0900412 @Test
413 public void ignoresDuplicateUpstreamNotifications() throws Exception {
414 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
415
Erik Kline7a4ccc62018-08-27 17:26:47 +0900416 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900417
418 for (int i = 0; i < 5; i++) {
419 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900420 verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900421 }
422 }
423
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900424 @Test
425 public void startsDhcpServer() throws Exception {
426 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
427 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
428
429 assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
430 }
431
432 @Test
433 public void startsDhcpServerOnBluetooth() throws Exception {
434 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
435 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
436
437 assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
438 }
439
440 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800441 public void startsDhcpServerOnWifiP2p() throws Exception {
442 initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
443 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
444
445 assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
446 }
447
448 @Test
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900449 public void doesNotStartDhcpServerIfDisabled() throws Exception {
450 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
451 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
452
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900453 verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900454 }
455
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900456 private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
457 verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
458 verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
459 final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900460 // Last address byte is random
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900461 assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
462 assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
463 assertEquals(1, params.defaultRouters.length);
464 assertEquals(params.serverAddr, params.defaultRouters[0]);
465 assertEquals(1, params.dnsServers.length);
466 assertEquals(params.serverAddr, params.dnsServers[0]);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900467 assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
468 }
469
Christopher Wiley279eca32016-05-20 13:23:10 -0700470 /**
471 * Send a command to the state machine under test, and run the event loop to idle.
472 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900473 * @param command One of the IpServer.CMD_* constants.
Remi NGUYEN VANa911e842018-03-15 11:57:14 +0900474 * @param arg1 An additional argument to pass.
Erik Klineea9cc482017-03-10 19:35:34 +0900475 */
476 private void dispatchCommand(int command, int arg1) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900477 mIpServer.sendMessage(command, arg1);
Erik Klineea9cc482017-03-10 19:35:34 +0900478 mLooper.dispatchAll();
479 }
480
481 /**
482 * Send a command to the state machine under test, and run the event loop to idle.
483 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900484 * @param command One of the IpServer.CMD_* constants.
Christopher Wiley279eca32016-05-20 13:23:10 -0700485 */
486 private void dispatchCommand(int command) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900487 mIpServer.sendMessage(command);
Christopher Wiley279eca32016-05-20 13:23:10 -0700488 mLooper.dispatchAll();
489 }
490
491 /**
492 * Special override to tell the state machine that the upstream interface has changed.
493 *
494 * @see #dispatchCommand(int)
495 * @param upstreamIface String name of upstream interface (or null)
496 */
497 private void dispatchTetherConnectionChanged(String upstreamIface) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900498 mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
Remi NGUYEN VAN25a7e4f2018-03-09 14:07:18 +0900499 new InterfaceSet(upstreamIface));
Christopher Wiley279eca32016-05-20 13:23:10 -0700500 mLooper.dispatchAll();
Christopher Wiley08725a82016-05-18 16:32:44 -0700501 }
Erik Klinedd4d5822017-06-12 18:20:08 +0900502
503 private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
504 // Find the first IPv4 LinkAddress.
505 LinkAddress addr4 = null;
506 for (LinkAddress addr : lp.getLinkAddresses()) {
507 if (!(addr.getAddress() instanceof Inet4Address)) continue;
508 addr4 = addr;
509 break;
510 }
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900511 assertNotNull("missing IPv4 address", addr4);
Erik Klinedd4d5822017-06-12 18:20:08 +0900512
markchien986750b2019-12-06 15:24:53 +0800513 final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
Erik Klinedd4d5822017-06-12 18:20:08 +0900514 // Assert the presence of the associated directly connected route.
markchien986750b2019-12-06 15:24:53 +0800515 final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
516 RouteInfo.RTN_UNICAST);
Erik Klinedd4d5822017-06-12 18:20:08 +0900517 assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
518 lp.getRoutes().contains(directlyConnected));
519 }
520
521 private void assertNoAddressesNorRoutes(LinkProperties lp) {
522 assertTrue(lp.getLinkAddresses().isEmpty());
523 assertTrue(lp.getRoutes().isEmpty());
524 // We also check that interface name is non-empty, because we should
525 // never see an empty interface name in any LinkProperties update.
526 assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
527 }
Lorenzo Colitti5bce5a12016-10-28 17:45:55 +0900528}