blob: 65a0ac13a84b527b6946d14f5e2ae5595dbcd8fe [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;
Christopher Wiley08725a82016-05-18 16:32:44 -070055import android.net.INetworkStatsService;
markchien92b8e7f2020-01-07 14:43:17 +080056import android.net.InterfaceConfigurationParcel;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090057import android.net.IpPrefix;
Erik Klinedd4d5822017-06-12 18:20:08 +090058import android.net.LinkAddress;
Erik Klineab6439b2017-06-06 19:24:21 +090059import android.net.LinkProperties;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090060import android.net.MacAddress;
Erik Klinedd4d5822017-06-12 18:20:08 +090061import android.net.RouteInfo;
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090062import android.net.dhcp.DhcpServingParamsParcel;
63import android.net.dhcp.IDhcpServer;
64import android.net.dhcp.IDhcpServerCallbacks;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090065import android.net.util.InterfaceParams;
Remi NGUYEN VAN25a7e4f2018-03-09 14:07:18 +090066import android.net.util.InterfaceSet;
Erik Kline7747fd42017-05-12 16:52:48 +090067import android.net.util.SharedLog;
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";
markchien92b8e7f2020-01-07 14:43:17 +080092 private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
93 private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +090094 private static final int DHCP_LEASE_TIME_SECS = 3600;
95
96 private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
97 IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
Christopher Wiley08725a82016-05-18 16:32:44 -070098
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090099 private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
100
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900101 @Mock private INetd mNetd;
Christopher Wiley08725a82016-05-18 16:32:44 -0700102 @Mock private INetworkStatsService mStatsService;
Erik Kline7a4ccc62018-08-27 17:26:47 +0900103 @Mock private IpServer.Callback mCallback;
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;
markchien92b8e7f2020-01-07 14:43:17 +0800115 private InterfaceConfigurationParcel mInterfaceConfiguration;
Christopher Wiley08725a82016-05-18 16:32:44 -0700116
Christopher Wileycd0cfbb2016-05-31 14:43:08 -0700117 private void initStateMachine(int interfaceType) throws Exception {
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900118 initStateMachine(interfaceType, false /* usingLegacyDhcp */);
119 }
120
121 private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900122 doAnswer(inv -> {
123 final IDhcpServerCallbacks cb = inv.getArgument(2);
124 new Thread(() -> {
125 try {
126 cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
127 } catch (RemoteException e) {
128 fail(e.getMessage());
129 }
130 }).run();
131 return null;
132 }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900133 when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
134 when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
markchien92b8e7f2020-01-07 14:43:17 +0800135 mInterfaceConfiguration = new InterfaceConfigurationParcel();
136 mInterfaceConfiguration.flags = new String[0];
137 if (interfaceType == TETHERING_BLUETOOTH) {
138 mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
139 mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
140 }
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900141 mIpServer = new IpServer(
markchien92b8e7f2020-01-07 14:43:17 +0800142 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
143 mCallback, usingLegacyDhcp, mDependencies);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900144 mIpServer.start();
145 // Starting the state machine always puts us in a consistent state and notifies
146 // the rest of the world that we've changed from an unknown to available state.
147 mLooper.dispatchAll();
markchien92b8e7f2020-01-07 14:43:17 +0800148 reset(mNetd, mStatsService, mCallback);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900149
150 when(mRaDaemon.start()).thenReturn(true);
Christopher Wiley279eca32016-05-20 13:23:10 -0700151 }
152
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900153 private void initTetheredStateMachine(int interfaceType, String upstreamIface)
154 throws Exception {
155 initTetheredStateMachine(interfaceType, upstreamIface, false);
156 }
157
158 private void initTetheredStateMachine(int interfaceType, String upstreamIface,
159 boolean usingLegacyDhcp) throws Exception {
160 initStateMachine(interfaceType, usingLegacyDhcp);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900161 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
Christopher Wiley279eca32016-05-20 13:23:10 -0700162 if (upstreamIface != null) {
163 dispatchTetherConnectionChanged(upstreamIface);
164 }
markchien92b8e7f2020-01-07 14:43:17 +0800165 reset(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700166 }
167
Christopher Wileye90e0a72016-05-31 10:44:35 -0700168 @Before public void setUp() throws Exception {
Christopher Wiley08725a82016-05-18 16:32:44 -0700169 MockitoAnnotations.initMocks(this);
Erik Kline7747fd42017-05-12 16:52:48 +0900170 when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
Christopher Wiley279eca32016-05-20 13:23:10 -0700171 }
172
173 @Test
174 public void startsOutAvailable() {
markchien92b8e7f2020-01-07 14:43:17 +0800175 mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
176 mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900177 mIpServer.start();
Christopher Wiley279eca32016-05-20 13:23:10 -0700178 mLooper.dispatchAll();
Erik Kline7a4ccc62018-08-27 17:26:47 +0900179 verify(mCallback).updateInterfaceState(
180 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
181 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
markchien92b8e7f2020-01-07 14:43:17 +0800182 verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
Christopher Wiley08725a82016-05-18 16:32:44 -0700183 }
184
185 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700186 public void shouldDoNothingUntilRequested() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900187 initStateMachine(TETHERING_BLUETOOTH);
markchien08cc0302019-09-09 20:50:49 +0800188 final int [] noOp_commands = {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900189 IpServer.CMD_TETHER_UNREQUESTED,
190 IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
191 IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
192 IpServer.CMD_START_TETHERING_ERROR,
193 IpServer.CMD_STOP_TETHERING_ERROR,
194 IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
195 IpServer.CMD_TETHER_CONNECTION_CHANGED
Christopher Wiley08725a82016-05-18 16:32:44 -0700196 };
markchien08cc0302019-09-09 20:50:49 +0800197 for (int command : noOp_commands) {
Christopher Wiley279eca32016-05-20 13:23:10 -0700198 // None of these commands should trigger us to request action from
Christopher Wiley08725a82016-05-18 16:32:44 -0700199 // the rest of the system.
Christopher Wiley279eca32016-05-20 13:23:10 -0700200 dispatchCommand(command);
markchien92b8e7f2020-01-07 14:43:17 +0800201 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley08725a82016-05-18 16:32:44 -0700202 }
203 }
204
Christopher Wiley279eca32016-05-20 13:23:10 -0700205 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700206 public void handlesImmediateInterfaceDown() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900207 initStateMachine(TETHERING_BLUETOOTH);
Christopher Wileye90e0a72016-05-31 10:44:35 -0700208
Erik Kline7a4ccc62018-08-27 17:26:47 +0900209 dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
210 verify(mCallback).updateInterfaceState(
211 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
212 verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
markchien92b8e7f2020-01-07 14:43:17 +0800213 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700214 }
215
216 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700217 public void canBeTethered() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900218 initStateMachine(TETHERING_BLUETOOTH);
Christopher Wileye90e0a72016-05-31 10:44:35 -0700219
Erik Kline7a4ccc62018-08-27 17:26:47 +0900220 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
markchien92b8e7f2020-01-07 14:43:17 +0800221 InOrder inOrder = inOrder(mCallback, mNetd);
222 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
223 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
224 // One for ipv4 route, one for ipv6 link local route.
225 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
226 any(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900227 inOrder.verify(mCallback).updateInterfaceState(
228 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
229 inOrder.verify(mCallback).updateLinkProperties(
230 eq(mIpServer), any(LinkProperties.class));
markchien92b8e7f2020-01-07 14:43:17 +0800231 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700232 }
233
234 @Test
235 public void canUnrequestTethering() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900236 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700237
Erik Kline7a4ccc62018-08-27 17:26:47 +0900238 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
markchien92b8e7f2020-01-07 14:43:17 +0800239 InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
240 inOrder.verify(mNetd).tetherApplyDnsInterfaces();
241 inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
242 inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900243 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900244 inOrder.verify(mCallback).updateInterfaceState(
245 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
246 inOrder.verify(mCallback).updateLinkProperties(
247 eq(mIpServer), any(LinkProperties.class));
markchien92b8e7f2020-01-07 14:43:17 +0800248 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700249 }
250
251 @Test
Christopher Wileyf972edc2016-05-23 16:17:30 -0700252 public void canBeTetheredAsUsb() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900253 initStateMachine(TETHERING_USB);
Christopher Wiley279eca32016-05-20 13:23:10 -0700254
Erik Kline7a4ccc62018-08-27 17:26:47 +0900255 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
markchien92b8e7f2020-01-07 14:43:17 +0800256 InOrder inOrder = inOrder(mCallback, mNetd);
257 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
258 IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
259 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
260 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
261 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
262 any(), any());
Erik Kline7a4ccc62018-08-27 17:26:47 +0900263 inOrder.verify(mCallback).updateInterfaceState(
264 mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
265 inOrder.verify(mCallback).updateLinkProperties(
266 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900267 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
markchien92b8e7f2020-01-07 14:43:17 +0800268 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700269 }
270
271 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800272 public void canBeTetheredAsWifiP2p() throws Exception {
273 initStateMachine(TETHERING_WIFI_P2P);
274
275 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
markchien92b8e7f2020-01-07 14:43:17 +0800276 InOrder inOrder = inOrder(mCallback, mNetd);
277 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
278 IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
279 inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
280 inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
281 inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
282 any(), any());
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800283 inOrder.verify(mCallback).updateInterfaceState(
284 mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
285 inOrder.verify(mCallback).updateLinkProperties(
286 eq(mIpServer), mLinkPropertiesCaptor.capture());
287 assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
markchien92b8e7f2020-01-07 14:43:17 +0800288 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800289 }
290
291 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700292 public void handlesFirstUpstreamChange() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900293 initTetheredStateMachine(TETHERING_BLUETOOTH, null);
Christopher Wiley279eca32016-05-20 13:23:10 -0700294
Erik Klineab6439b2017-06-06 19:24:21 +0900295 // Telling the state machine about its upstream interface triggers
296 // a little more configuration.
Christopher Wiley279eca32016-05-20 13:23:10 -0700297 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
markchien92b8e7f2020-01-07 14:43:17 +0800298 InOrder inOrder = inOrder(mNetd);
299 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
300 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
301 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700302 }
303
304 @Test
305 public void handlesChangingUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900306 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700307
308 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
markchien92b8e7f2020-01-07 14:43:17 +0800309 InOrder inOrder = inOrder(mNetd, mStatsService);
Christopher Wiley279eca32016-05-20 13:23:10 -0700310 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800311 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
312 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
313 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
314 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
315 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700316 }
317
318 @Test
Erik Klinea954be92017-02-13 17:12:02 +0900319 public void handlesChangingUpstreamNatFailure() throws Exception {
320 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
321
markchien92b8e7f2020-01-07 14:43:17 +0800322 doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900323
324 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
markchien92b8e7f2020-01-07 14:43:17 +0800325 InOrder inOrder = inOrder(mNetd, mStatsService);
Erik Klinea954be92017-02-13 17:12:02 +0900326 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800327 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
328 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
329 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Kline8ea45482017-02-13 17:28:53 +0900330 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800331 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
332 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900333 }
334
335 @Test
336 public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
337 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
338
markchien92b8e7f2020-01-07 14:43:17 +0800339 doThrow(RemoteException.class).when(mNetd).ipfwdAddInterfaceForward(
Erik Klinea954be92017-02-13 17:12:02 +0900340 IFACE_NAME, UPSTREAM_IFACE2);
341
342 dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
markchien92b8e7f2020-01-07 14:43:17 +0800343 InOrder inOrder = inOrder(mNetd, mStatsService);
Erik Klinea954be92017-02-13 17:12:02 +0900344 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800345 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
346 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
347 inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
348 inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Kline8ea45482017-02-13 17:28:53 +0900349 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800350 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
351 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
Erik Klinea954be92017-02-13 17:12:02 +0900352 }
353
354 @Test
Christopher Wiley279eca32016-05-20 13:23:10 -0700355 public void canUnrequestTetheringWithUpstream() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900356 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
Christopher Wiley279eca32016-05-20 13:23:10 -0700357
Erik Kline7a4ccc62018-08-27 17:26:47 +0900358 dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
markchien92b8e7f2020-01-07 14:43:17 +0800359 InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700360 inOrder.verify(mStatsService).forceUpdate();
markchien92b8e7f2020-01-07 14:43:17 +0800361 inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
362 inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
363 inOrder.verify(mNetd).tetherApplyDnsInterfaces();
364 inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
365 inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
Remi NGUYEN VANd1b51a32019-01-22 16:13:57 +0900366 inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900367 inOrder.verify(mCallback).updateInterfaceState(
368 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
369 inOrder.verify(mCallback).updateLinkProperties(
370 eq(mIpServer), any(LinkProperties.class));
markchien92b8e7f2020-01-07 14:43:17 +0800371 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Christopher Wiley279eca32016-05-20 13:23:10 -0700372 }
373
Christopher Wileyf972edc2016-05-23 16:17:30 -0700374 @Test
375 public void interfaceDownLeadsToUnavailable() throws Exception {
376 for (boolean shouldThrow : new boolean[]{true, false}) {
Erik Klinea954be92017-02-13 17:12:02 +0900377 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700378
379 if (shouldThrow) {
markchien92b8e7f2020-01-07 14:43:17 +0800380 doThrow(RemoteException.class).when(mNetd).tetherInterfaceRemove(IFACE_NAME);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700381 }
Erik Kline7a4ccc62018-08-27 17:26:47 +0900382 dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
markchien92b8e7f2020-01-07 14:43:17 +0800383 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
384 // Currently IpServer interfaceSetCfg twice to stop IPv4. One just set interface down
385 // Another one is set IPv4 to 0.0.0.0/0 as clearng ipv4 address.
386 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
387 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900388 usbTeardownOrder.verify(mCallback).updateInterfaceState(
389 mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
390 usbTeardownOrder.verify(mCallback).updateLinkProperties(
391 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900392 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700393 }
394 }
395
396 @Test
397 public void usbShouldBeTornDownOnTetherError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900398 initStateMachine(TETHERING_USB);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700399
markchien92b8e7f2020-01-07 14:43:17 +0800400 doThrow(RemoteException.class).when(mNetd).tetherInterfaceAdd(IFACE_NAME);
Erik Kline7a4ccc62018-08-27 17:26:47 +0900401 dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
markchien92b8e7f2020-01-07 14:43:17 +0800402 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
403 usbTeardownOrder.verify(mNetd).interfaceSetCfg(
404 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
405 usbTeardownOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
406
407 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
408 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900409 usbTeardownOrder.verify(mCallback).updateInterfaceState(
410 mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
411 usbTeardownOrder.verify(mCallback).updateLinkProperties(
412 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900413 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700414 }
415
416 @Test
417 public void shouldTearDownUsbOnUpstreamError() throws Exception {
Erik Klinea954be92017-02-13 17:12:02 +0900418 initTetheredStateMachine(TETHERING_USB, null);
Christopher Wileyf972edc2016-05-23 16:17:30 -0700419
markchien92b8e7f2020-01-07 14:43:17 +0800420 doThrow(RemoteException.class).when(mNetd).tetherAddForward(anyString(), anyString());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700421 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
markchien92b8e7f2020-01-07 14:43:17 +0800422 InOrder usbTeardownOrder = inOrder(mNetd, mCallback);
423 usbTeardownOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
424
425 usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
426 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
Erik Kline7a4ccc62018-08-27 17:26:47 +0900427 usbTeardownOrder.verify(mCallback).updateInterfaceState(
428 mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
429 usbTeardownOrder.verify(mCallback).updateLinkProperties(
430 eq(mIpServer), mLinkPropertiesCaptor.capture());
Erik Klinedd4d5822017-06-12 18:20:08 +0900431 assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
Christopher Wileyf972edc2016-05-23 16:17:30 -0700432 }
Christopher Wiley279eca32016-05-20 13:23:10 -0700433
Erik Kline624bf3d2017-02-14 15:55:00 +0900434 @Test
435 public void ignoresDuplicateUpstreamNotifications() throws Exception {
436 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
437
markchien92b8e7f2020-01-07 14:43:17 +0800438 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900439
440 for (int i = 0; i < 5; i++) {
441 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
markchien92b8e7f2020-01-07 14:43:17 +0800442 verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
Erik Kline624bf3d2017-02-14 15:55:00 +0900443 }
444 }
445
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900446 @Test
447 public void startsDhcpServer() throws Exception {
448 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
449 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
450
451 assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
452 }
453
454 @Test
455 public void startsDhcpServerOnBluetooth() throws Exception {
456 initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
457 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
458
459 assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
460 }
461
462 @Test
Jimmy Chenbcd86d02019-07-15 18:03:23 +0800463 public void startsDhcpServerOnWifiP2p() throws Exception {
464 initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
465 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
466
467 assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
468 }
469
470 @Test
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900471 public void doesNotStartDhcpServerIfDisabled() throws Exception {
472 initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
473 dispatchTetherConnectionChanged(UPSTREAM_IFACE);
474
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900475 verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900476 }
477
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900478 private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
479 verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
480 verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
481 final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900482 // Last address byte is random
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900483 assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
484 assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
485 assertEquals(1, params.defaultRouters.length);
486 assertEquals(params.serverAddr, params.defaultRouters[0]);
487 assertEquals(1, params.dnsServers.length);
488 assertEquals(params.serverAddr, params.dnsServers[0]);
Remi NGUYEN VANe3bb5c52018-06-12 15:57:04 +0900489 assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
490 }
491
Christopher Wiley279eca32016-05-20 13:23:10 -0700492 /**
493 * Send a command to the state machine under test, and run the event loop to idle.
494 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900495 * @param command One of the IpServer.CMD_* constants.
Remi NGUYEN VANa911e842018-03-15 11:57:14 +0900496 * @param arg1 An additional argument to pass.
Erik Klineea9cc482017-03-10 19:35:34 +0900497 */
498 private void dispatchCommand(int command, int arg1) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900499 mIpServer.sendMessage(command, arg1);
Erik Klineea9cc482017-03-10 19:35:34 +0900500 mLooper.dispatchAll();
501 }
502
503 /**
504 * Send a command to the state machine under test, and run the event loop to idle.
505 *
Erik Kline7a4ccc62018-08-27 17:26:47 +0900506 * @param command One of the IpServer.CMD_* constants.
Christopher Wiley279eca32016-05-20 13:23:10 -0700507 */
508 private void dispatchCommand(int command) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900509 mIpServer.sendMessage(command);
Christopher Wiley279eca32016-05-20 13:23:10 -0700510 mLooper.dispatchAll();
511 }
512
513 /**
514 * Special override to tell the state machine that the upstream interface has changed.
515 *
516 * @see #dispatchCommand(int)
517 * @param upstreamIface String name of upstream interface (or null)
518 */
519 private void dispatchTetherConnectionChanged(String upstreamIface) {
Erik Kline7a4ccc62018-08-27 17:26:47 +0900520 mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
Remi NGUYEN VAN25a7e4f2018-03-09 14:07:18 +0900521 new InterfaceSet(upstreamIface));
Christopher Wiley279eca32016-05-20 13:23:10 -0700522 mLooper.dispatchAll();
Christopher Wiley08725a82016-05-18 16:32:44 -0700523 }
Erik Klinedd4d5822017-06-12 18:20:08 +0900524
525 private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
526 // Find the first IPv4 LinkAddress.
527 LinkAddress addr4 = null;
528 for (LinkAddress addr : lp.getLinkAddresses()) {
529 if (!(addr.getAddress() instanceof Inet4Address)) continue;
530 addr4 = addr;
531 break;
532 }
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +0900533 assertNotNull("missing IPv4 address", addr4);
Erik Klinedd4d5822017-06-12 18:20:08 +0900534
markchien986750b2019-12-06 15:24:53 +0800535 final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
Erik Klinedd4d5822017-06-12 18:20:08 +0900536 // Assert the presence of the associated directly connected route.
markchien986750b2019-12-06 15:24:53 +0800537 final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
538 RouteInfo.RTN_UNICAST);
Erik Klinedd4d5822017-06-12 18:20:08 +0900539 assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
540 lp.getRoutes().contains(directlyConnected));
541 }
542
543 private void assertNoAddressesNorRoutes(LinkProperties lp) {
544 assertTrue(lp.getLinkAddresses().isEmpty());
545 assertTrue(lp.getRoutes().isEmpty());
546 // We also check that interface name is non-empty, because we should
547 // never see an empty interface name in any LinkProperties update.
548 assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
549 }
markchien92b8e7f2020-01-07 14:43:17 +0800550
551 private boolean assertContainsFlag(String[] flags, String match) {
552 for (String flag : flags) {
553 if (flag.equals(match)) return true;
554 }
555 fail("Missing flag: " + match);
556 return false;
557 }
Lorenzo Colitti5bce5a12016-10-28 17:45:55 +0900558}