blob: 1f50b6bf7fb3240588bd439298774d7b249a9c3b [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
markchien92b8e7f2020-01-07 14:43:17 +080019import static android.net.INetd.IF_STATE_UP;
markchien9e44cde2019-12-25 19:40:32 +080020import static android.net.TetheringManager.TETHERING_BLUETOOTH;
21import static android.net.TetheringManager.TETHERING_USB;
22import static android.net.TetheringManager.TETHERING_WIFI;
23import static android.net.TetheringManager.TETHERING_WIFI_P2P;
24import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
25import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
26import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090027import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090028import static android.net.ip.IpServer.STATE_AVAILABLE;
Jimmy Chenbcd86d02019-07-15 18:03:23 +080029import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090030import static android.net.ip.IpServer.STATE_TETHERED;
31import static android.net.ip.IpServer.STATE_UNAVAILABLE;
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090032import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090033
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090034import static org.junit.Assert.assertEquals;
Erik Klinedd4d5822017-06-12 18:20:08 +090035import static org.junit.Assert.assertFalse;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090036import static org.junit.Assert.assertNotNull;
Erik Klinedd4d5822017-06-12 18:20:08 +090037import static org.junit.Assert.assertTrue;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090038import static org.junit.Assert.fail;
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +090039import static org.mockito.ArgumentMatchers.argThat;
Erik Klineab6439b2017-06-06 19:24:21 +090040import static org.mockito.Matchers.any;
Christopher Wileyf972edc2016-05-23 16:17:30 -070041import static org.mockito.Matchers.anyString;
Erik Klineab6439b2017-06-06 19:24:21 +090042import static org.mockito.Matchers.eq;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090043import static org.mockito.Mockito.doAnswer;
Christopher Wileyf972edc2016-05-23 16:17:30 -070044import static org.mockito.Mockito.doThrow;
Christopher Wiley279eca32016-05-20 13:23:10 -070045import static org.mockito.Mockito.inOrder;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090046import static org.mockito.Mockito.never;
Christopher Wiley279eca32016-05-20 13:23:10 -070047import static org.mockito.Mockito.reset;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090048import static org.mockito.Mockito.timeout;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090049import static org.mockito.Mockito.times;
Christopher Wiley279eca32016-05-20 13:23:10 -070050import static org.mockito.Mockito.verify;
Christopher Wiley08725a82016-05-18 16:32:44 -070051import static org.mockito.Mockito.verifyNoMoreInteractions;
Christopher Wiley279eca32016-05-20 13:23:10 -070052import static org.mockito.Mockito.when;
Christopher Wiley08725a82016-05-18 16:32:44 -070053
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +090054import android.net.INetd;
markchien92b8e7f2020-01-07 14:43:17 +080055import android.net.InterfaceConfigurationParcel;
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 Wiley279eca32016-05-20 13:23:10 -070067import android.os.RemoteException;
Christopher Wiley08725a82016-05-18 16:32:44 -070068import android.os.test.TestLooper;
Erik Klinedd4d5822017-06-12 18:20:08 +090069import android.text.TextUtils;
70
Brett Chabot1ae2aa62019-03-04 14:14:56 -080071import androidx.test.filters.SmallTest;
72import androidx.test.runner.AndroidJUnit4;
73
Christopher Wiley08725a82016-05-18 16:32:44 -070074import org.junit.Before;
75import org.junit.Test;
Christopher Wiley9bc0df22016-05-25 13:57:27 -070076import org.junit.runner.RunWith;
Erik Klinedd4d5822017-06-12 18:20:08 +090077import org.mockito.ArgumentCaptor;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090078import org.mockito.Captor;
Christopher Wiley279eca32016-05-20 13:23:10 -070079import org.mockito.InOrder;
Christopher Wiley08725a82016-05-18 16:32:44 -070080import org.mockito.Mock;
81import org.mockito.MockitoAnnotations;
82
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090083import java.net.Inet4Address;
84
Christopher Wiley9bc0df22016-05-25 13:57:27 -070085@RunWith(AndroidJUnit4.class)
86@SmallTest
Erik Kline7a4ccc62018-08-27 17:26:47 +090087public class IpServerTest {
Christopher Wiley08725a82016-05-18 16:32:44 -070088 private static final String IFACE_NAME = "testnet1";
Christopher Wiley279eca32016-05-20 13:23:10 -070089 private static final String UPSTREAM_IFACE = "upstream0";
90 private static final String UPSTREAM_IFACE2 = "upstream1";
markchien92b8e7f2020-01-07 14:43:17 +080091 private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
92 private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090093 private static final int DHCP_LEASE_TIME_SECS = 3600;
94
95 private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
96 IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
Christopher Wiley08725a82016-05-18 16:32:44 -070097
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090098 private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
99
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900100 @Mock private INetd mNetd;
Erik Kline7a4ccc62018-08-27 17:26:47 +0900101 @Mock private IpServer.Callback mCallback;
Erik Kline7747fd42017-05-12 16:52:48 +0900102 @Mock private SharedLog mSharedLog;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900103 @Mock private IDhcpServer mDhcpServer;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900104 @Mock private RouterAdvertisementDaemon mRaDaemon;
Erik Kline7a4ccc62018-08-27 17:26:47 +0900105 @Mock private IpServer.Dependencies mDependencies;
Christopher Wiley08725a82016-05-18 16:32:44 -0700106
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900107 @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900108
Christopher Wiley08725a82016-05-18 16:32:44 -0700109 private final TestLooper mLooper = new TestLooper();
Erik Klinedd4d5822017-06-12 18:20:08 +0900110 private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
111 ArgumentCaptor.forClass(LinkProperties.class);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900112 private IpServer mIpServer;
markchien92b8e7f2020-01-07 14:43:17 +0800113 private InterfaceConfigurationParcel mInterfaceConfiguration;
Christopher Wiley08725a82016-05-18 16:32:44 -0700114
Christopher Wileycd0cfbb2016-05-31 14:43:08 -0700115 private void initStateMachine(int interfaceType) throws Exception {
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900116 initStateMachine(interfaceType, false /* usingLegacyDhcp */);
117 }
118
119 private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900120 doAnswer(inv -> {
121 final IDhcpServerCallbacks cb = inv.getArgument(2);
122 new Thread(() -> {
123 try {
124 cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
125 } catch (RemoteException e) {
126 fail(e.getMessage());
127 }
128 }).run();
129 return null;
130 }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900131 when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
132 when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
markchien92b8e7f2020-01-07 14:43:17 +0800133 mInterfaceConfiguration = new InterfaceConfigurationParcel();
134 mInterfaceConfiguration.flags = new String[0];
135 if (interfaceType == TETHERING_BLUETOOTH) {
136 mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
137 mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
138 }
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900139 mIpServer = new IpServer(
junyulaie3f41432019-12-03 14:34:13 +0800140 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
markchien92b8e7f2020-01-07 14:43:17 +0800141 mCallback, usingLegacyDhcp, mDependencies);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900142 mIpServer.start();
143 // Starting the state machine always puts us in a consistent state and notifies
144 // the rest of the world that we've changed from an unknown to available state.
145 mLooper.dispatchAll();
junyulaie3f41432019-12-03 14:34:13 +0800146 reset(mNetd, mCallback);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900147
148 when(mRaDaemon.start()).thenReturn(true);
Christopher Wiley279eca32016-05-20 13:23:10 -0700149 }
150
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900151 private void initTetheredStateMachine(int interfaceType, String upstreamIface)
152 throws Exception {
153 initTetheredStateMachine(interfaceType, upstreamIface, false);
154 }
155
156 private void initTetheredStateMachine(int interfaceType, String upstreamIface,
157 boolean usingLegacyDhcp) throws Exception {
158 initStateMachine(interfaceType, usingLegacyDhcp);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900159 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
Christopher Wiley279eca32016-05-20 13:23:10 -0700160 if (upstreamIface != null) {
161 dispatchTetherConnectionChanged(upstreamIface);
162 }
junyulaie3f41432019-12-03 14:34:13 +0800163 reset(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700164 }
165
Christopher Wileye90e0a72016-05-31 10:44:35 -0700166 @Before public void setUp() throws Exception {
Christopher Wiley08725a82016-05-18 16:32:44 -0700167 MockitoAnnotations.initMocks(this);
Erik Kline7747fd42017-05-12 16:52:48 +0900168 when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
Christopher Wiley279eca32016-05-20 13:23:10 -0700169 }
170
171 @Test
172 public void startsOutAvailable() {
markchien92b8e7f2020-01-07 14:43:17 +0800173 mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
junyulaie3f41432019-12-03 14:34:13 +0800174 mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900175 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));
junyulaie3f41432019-12-03 14:34:13 +0800180 verifyNoMoreInteractions(mCallback, mNetd);
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);
junyulaie3f41432019-12-03 14:34:13 +0800199 verifyNoMoreInteractions(mNetd, 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));
junyulaie3f41432019-12-03 14:34:13 +0800211 verifyNoMoreInteractions(mNetd, 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);
markchien92b8e7f2020-01-07 14:43:17 +0800219 InOrder inOrder = inOrder(mCallback, mNetd);
220 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
221 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
222 // One for ipv4 route, one for ipv6 link local route.
223 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
224 any(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900225 inOrder.verify(mCallback).updateInterfaceState(
226 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
227 inOrder.verify(mCallback).updateLinkProperties(
228 eq(mIpServer), any(LinkProperties.class));
junyulaie3f41432019-12-03 14:34:13 +0800229 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700230 }
231
232 @Test
233 public void canUnrequestTethering() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900234 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700235
Erik Kline7a4ccc62018-08-27 17:26:47 +0900236 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
junyulaie3f41432019-12-03 14:34:13 +0800237 InOrder inOrder = inOrder(mNetd, mCallback);
markchien92b8e7f2020-01-07 14:43:17 +0800238 inOrder.verify(mNetd).tetherApplyDnsInterfaces();
239 inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
240 inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900241 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900242 inOrder.verify(mCallback).updateInterfaceState(
243 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
244 inOrder.verify(mCallback).updateLinkProperties(
245 eq(mIpServer), any(LinkProperties.class));
junyulaie3f41432019-12-03 14:34:13 +0800246 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700247 }
248
249 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700250 public void canBeTetheredAsUsb() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900251 initStateMachine(TETHERING_USB);
Christopher Wiley279eca32016-05-20 13:23:10 -0700252
Erik Kline7a4ccc62018-08-27 17:26:47 +0900253 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
markchien92b8e7f2020-01-07 14:43:17 +0800254 InOrder inOrder = inOrder(mCallback, mNetd);
255 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
256 IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
257 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
258 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
259 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
260 any(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900261 inOrder.verify(mCallback).updateInterfaceState(
262 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
263 inOrder.verify(mCallback).updateLinkProperties(
264 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900265 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
junyulaie3f41432019-12-03 14:34:13 +0800266 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700267 }
268
269 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800270 public void canBeTetheredAsWifiP2p() throws Exception {
271 initStateMachine(TETHERING_WIFI_P2P);
272
273 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
markchien92b8e7f2020-01-07 14:43:17 +0800274 InOrder inOrder = inOrder(mCallback, mNetd);
275 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
276 IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
277 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
278 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
279 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
280 any(), any());
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800281 inOrder.verify(mCallback).updateInterfaceState(
282 mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
283 inOrder.verify(mCallback).updateLinkProperties(
284 eq(mIpServer), mLinkPropertiesCaptor.capture());
285 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
junyulaie3f41432019-12-03 14:34:13 +0800286 verifyNoMoreInteractions(mNetd, mCallback);
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800287 }
288
289 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700290 public void handlesFirstUpstreamChange() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900291 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700292
Erik Klineab6439b2017-06-06 19:24:21 +0900293 // Telling the state machine about its upstream interface triggers
294 // a little more configuration.
Christopher Wiley279eca32016-05-20 13:23:10 -0700295 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
markchien92b8e7f2020-01-07 14:43:17 +0800296 InOrder inOrder = inOrder(mNetd);
297 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
298 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
junyulaie3f41432019-12-03 14:34:13 +0800299 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700300 }
301
302 @Test
303 public void handlesChangingUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900304 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700305
306 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
junyulaie3f41432019-12-03 14:34:13 +0800307 InOrder inOrder = inOrder(mNetd);
markchien92b8e7f2020-01-07 14:43:17 +0800308 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
309 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
310 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
311 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
junyulaie3f41432019-12-03 14:34:13 +0800312 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700313 }
314
315 @Test
Erik Klinea954be92017-02-13 17:12:02 +0900316 public void handlesChangingUpstreamNatFailure() throws Exception {
317 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
318
markchien92b8e7f2020-01-07 14:43:17 +0800319 doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900320
321 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
junyulaie3f41432019-12-03 14:34:13 +0800322 InOrder inOrder = inOrder(mNetd);
markchien92b8e7f2020-01-07 14:43:17 +0800323 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
324 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
325 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
markchien92b8e7f2020-01-07 14:43:17 +0800326 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
327 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900328 }
329
330 @Test
331 public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
332 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
333
markchien92b8e7f2020-01-07 14:43:17 +0800334 doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward(
Erik Klinea954be92017-02-13 17:12:02 +0900335 IFACE_NAME, UPSTREAM_IFACE2);
336
337 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
junyulaie3f41432019-12-03 14:34:13 +0800338 InOrder inOrder = inOrder(mNetd);
markchien92b8e7f2020-01-07 14:43:17 +0800339 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
340 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
341 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
342 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
markchien92b8e7f2020-01-07 14:43:17 +0800343 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
344 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900345 }
346
347 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700348 public void canUnrequestTetheringWithUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900349 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700350
Erik Kline7a4ccc62018-08-27 17:26:47 +0900351 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
junyulaie3f41432019-12-03 14:34:13 +0800352 InOrder inOrder = inOrder(mNetd, mCallback);
markchien92b8e7f2020-01-07 14:43:17 +0800353 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
354 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
355 inOrder.verify(mNetd).tetherApplyDnsInterfaces();
356 inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
357 inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900358 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900359 inOrder.verify(mCallback).updateInterfaceState(
360 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
361 inOrder.verify(mCallback).updateLinkProperties(
362 eq(mIpServer), any(LinkProperties.class));
junyulaie3f41432019-12-03 14:34:13 +0800363 verifyNoMoreInteractions(mNetd, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700364 }
365
Christopher Wileyf972edc2016-05-23 16:17:30 -0700366 @Test
367 public void interfaceDownLeadsToUnavailable() throws Exception {
368 for (boolean shouldThrow : new boolean[]{true, false}) {
Erik Klinea954be92017-02-13 17:12:02 +0900369 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700370
371 if (shouldThrow) {
markchien92b8e7f2020-01-07 14:43:17 +0800372 doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700373 }
Erik Kline7a4ccc62018-08-27 17:26:47 +0900374 dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
markchien92b8e7f2020-01-07 14:43:17 +0800375 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
376 // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down
377 // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address.
378 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
379 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900380 usbTeardownOrder.verify(mCallback).updateInterfaceState(
381 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
382 usbTeardownOrder.verify(mCallback).updateLinkProperties(
383 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900384 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700385 }
386 }
387
388 @Test
389 public void usbShouldBeTornDownOnTetherError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900390 initStateMachine(TETHERING_USB);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700391
markchien92b8e7f2020-01-07 14:43:17 +0800392 doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900393 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
markchien92b8e7f2020-01-07 14:43:17 +0800394 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
395 usbTeardownOrder.verify(mNetd).interfaceSetCfg(
396 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
397 usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
398
399 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
400 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900401 usbTeardownOrder.verify(mCallback).updateInterfaceState(
402 mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
403 usbTeardownOrder.verify(mCallback).updateLinkProperties(
404 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900405 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700406 }
407
408 @Test
409 public void shouldTearDownUsbOnUpstreamError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900410 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700411
markchien92b8e7f2020-01-07 14:43:17 +0800412 doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700413 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
markchien92b8e7f2020-01-07 14:43:17 +0800414 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
415 usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
416
417 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
418 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900419 usbTeardownOrder.verify(mCallback).updateInterfaceState(
420 mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
421 usbTeardownOrder.verify(mCallback).updateLinkProperties(
422 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900423 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700424 }
Christopher Wiley279eca32016-05-20 13:23:10 -0700425
Erik Kline624bf3d2017-02-14 15:55:00 +0900426 @Test
427 public void ignoresDuplicateUpstreamNotifications() throws Exception {
428 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
429
junyulaie3f41432019-12-03 14:34:13 +0800430 verifyNoMoreInteractions(mNetd, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900431
432 for (int i = 0; i < 5; i++) {
433 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
junyulaie3f41432019-12-03 14:34:13 +0800434 verifyNoMoreInteractions(mNetd, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900435 }
436 }
437
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900438 @Test
439 public void startsDhcpServer() throws Exception {
440 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
441 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
442
443 assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
444 }
445
446 @Test
447 public void startsDhcpServerOnBluetooth() throws Exception {
448 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
449 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
450
451 assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
452 }
453
454 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800455 public void startsDhcpServerOnWifiP2p() throws Exception {
456 initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
457 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
458
459 assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
460 }
461
462 @Test
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900463 public void doesNotStartDhcpServerIfDisabled() throws Exception {
464 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
465 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
466
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900467 verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900468 }
469
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900470 private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
471 verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
472 verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
473 final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900474 // Last address byte is random
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900475 assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
476 assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
477 assertEquals(1, params.defaultRouters.length);
478 assertEquals(params.serverAddr, params.defaultRouters[0]);
479 assertEquals(1, params.dnsServers.length);
480 assertEquals(params.serverAddr, params.dnsServers[0]);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900481 assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
482 }
483
Christopher Wiley279eca32016-05-20 13:23:10 -0700484 /**
485 * Send a command to the state machine under test, and run the event loop to idle.
486 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900487 * @param command One of the IpServer.CMD_* constants.
Remi NGUYEN VANa911e842018-03-15 11:57:14 +0900488 * @param arg1 An additional argument to pass.
Erik Klineea9cc482017-03-10 19:35:34 +0900489 */
490 private void dispatchCommand(int command, int arg1) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900491 mIpServer.sendMessage(command, arg1);
Erik Klineea9cc482017-03-10 19:35:34 +0900492 mLooper.dispatchAll();
493 }
494
495 /**
496 * Send a command to the state machine under test, and run the event loop to idle.
497 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900498 * @param command One of the IpServer.CMD_* constants.
Christopher Wiley279eca32016-05-20 13:23:10 -0700499 */
500 private void dispatchCommand(int command) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900501 mIpServer.sendMessage(command);
Christopher Wiley279eca32016-05-20 13:23:10 -0700502 mLooper.dispatchAll();
503 }
504
505 /**
506 * Special override to tell the state machine that the upstream interface has changed.
507 *
508 * @see #dispatchCommand(int)
509 * @param upstreamIface String name of upstream interface (or null)
510 */
511 private void dispatchTetherConnectionChanged(String upstreamIface) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900512 mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
Remi NGUYEN VAN25a7e4f2018-03-09 14:07:18 +0900513 new InterfaceSet(upstreamIface));
Christopher Wiley279eca32016-05-20 13:23:10 -0700514 mLooper.dispatchAll();
Christopher Wiley08725a82016-05-18 16:32:44 -0700515 }
Erik Klinedd4d5822017-06-12 18:20:08 +0900516
517 private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
518 // Find the first IPv4 LinkAddress.
519 LinkAddress addr4 = null;
520 for (LinkAddress addr : lp.getLinkAddresses()) {
521 if (!(addr.getAddress() instanceof Inet4Address)) continue;
522 addr4 = addr;
523 break;
524 }
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900525 assertNotNull("missing IPv4 address", addr4);
Erik Klinedd4d5822017-06-12 18:20:08 +0900526
markchien986750b2019-12-06 15:24:53 +0800527 final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
Erik Klinedd4d5822017-06-12 18:20:08 +0900528 // Assert the presence of the associated directly connected route.
markchien986750b2019-12-06 15:24:53 +0800529 final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
530 RouteInfo.RTN_UNICAST);
Erik Klinedd4d5822017-06-12 18:20:08 +0900531 assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
532 lp.getRoutes().contains(directlyConnected));
533 }
534
535 private void assertNoAddressesNorRoutes(LinkProperties lp) {
536 assertTrue(lp.getLinkAddresses().isEmpty());
537 assertTrue(lp.getRoutes().isEmpty());
538 // We also check that interface name is non-empty, because we should
539 // never see an empty interface name in any LinkProperties update.
540 assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
541 }
markchien92b8e7f2020-01-07 14:43:17 +0800542
543 private boolean assertContainsFlag(String[] flags, String match) {
544 for (String flag : flags) {
545 if (flag.equals(match)) return true;
546 }
547 fail("Missing flag: " + match);
548 return false;
549 }
Lorenzo Colitti5bce5a12016-10-28 17:45:55 +0900550}