108725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley/*
208725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * Copyright (C) 2016 The Android Open Source Project
308725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley *
408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * Licensed under the Apache License, Version 2.0 (the "License");
508725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * you may not use this file except in compliance with the License.
608725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * You may obtain a copy of the License at
708725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley *
808725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley *      http://www.apache.org/licenses/LICENSE-2.0
908725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley *
1008725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * Unless required by applicable law or agreed to in writing, software
1108725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * distributed under the License is distributed on an "AS IS" BASIS,
1208725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1308725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * See the License for the specific language governing permissions and
1408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley * limitations under the License.
1508725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley */
1608725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
1708725a8c4042aee177774f6275811aa88f26ef30Christopher Wileypackage com.android.server.connectivity.tethering;
1808725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
19f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wileyimport static org.mockito.Matchers.anyString;
20f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wileyimport static org.mockito.Mockito.doThrow;
21279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport static org.mockito.Mockito.inOrder;
22279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport static org.mockito.Mockito.reset;
23279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport static org.mockito.Mockito.verify;
2408725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport static org.mockito.Mockito.verifyNoMoreInteractions;
25279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport static org.mockito.Mockito.when;
2608725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
27e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
28e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
29e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
30e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
31a954be956315e6d25a63e961fc9befe9916e1cbdErik Klineimport static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
32a954be956315e6d25a63e961fc9befe9916e1cbdErik Klineimport static android.net.ConnectivityManager.TETHERING_USB;
33a954be956315e6d25a63e961fc9befe9916e1cbdErik Klineimport static android.net.ConnectivityManager.TETHERING_WIFI;
34e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
35e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
36e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wileyimport static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
37e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley
38cd0cfbbc61168b61360a6247b4a826494deb3912Christopher Wileyimport android.net.ConnectivityManager;
3908725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport android.net.INetworkStatsService;
40279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport android.net.InterfaceConfiguration;
41292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Klineimport android.net.util.SharedLog;
4208725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport android.os.INetworkManagementService;
43279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport android.os.RemoteException;
4408725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport android.os.test.TestLooper;
459bc0df24827336889d3100542536595c2809c3ceChristopher Wileyimport android.support.test.filters.SmallTest;
469bc0df24827336889d3100542536595c2809c3ceChristopher Wileyimport android.support.test.runner.AndroidJUnit4;
4708725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
4808725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport org.junit.Before;
4908725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport org.junit.Test;
509bc0df24827336889d3100542536595c2809c3ceChristopher Wileyimport org.junit.runner.RunWith;
51279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wileyimport org.mockito.InOrder;
5208725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport org.mockito.Mock;
5308725a8c4042aee177774f6275811aa88f26ef30Christopher Wileyimport org.mockito.MockitoAnnotations;
5408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
559bc0df24827336889d3100542536595c2809c3ceChristopher Wiley@RunWith(AndroidJUnit4.class)
569bc0df24827336889d3100542536595c2809c3ceChristopher Wiley@SmallTest
574622c2d8b65e267ede15bffd903faaf917642d7eMitchell Willspublic class TetherInterfaceStateMachineTest {
5808725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    private static final String IFACE_NAME = "testnet1";
59279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    private static final String UPSTREAM_IFACE = "upstream0";
60279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    private static final String UPSTREAM_IFACE2 = "upstream1";
6108725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
6208725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    @Mock private INetworkManagementService mNMService;
6308725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    @Mock private INetworkStatsService mStatsService;
6408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    @Mock private IControlsTethering mTetherHelper;
65279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Mock private InterfaceConfiguration mInterfaceConfiguration;
665bce5a16b17498981253d7a2d1a490667cd71798Lorenzo Colitti    @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices;
67292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Kline    @Mock private SharedLog mSharedLog;
6808725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
6908725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    private final TestLooper mLooper = new TestLooper();
704622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills    private TetherInterfaceStateMachine mTestedSm;
7108725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
72cd0cfbbc61168b61360a6247b4a826494deb3912Christopher Wiley    private void initStateMachine(int interfaceType) throws Exception {
73292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Kline        mTestedSm = new TetherInterfaceStateMachine(
74292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Kline                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
755bce5a16b17498981253d7a2d1a490667cd71798Lorenzo Colitti                mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices);
76279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mTestedSm.start();
77279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        // Starting the state machine always puts us in a consistent state and notifies
78279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        // the test of the world that we've changed from an unknown to available state.
79279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mLooper.dispatchAll();
80279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        reset(mNMService, mStatsService, mTetherHelper);
81f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
82279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
83279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
84cd0cfbbc61168b61360a6247b4a826494deb3912Christopher Wiley    private void initTetheredStateMachine(int interfaceType, String upstreamIface) throws Exception {
85cd0cfbbc61168b61360a6247b4a826494deb3912Christopher Wiley        initStateMachine(interfaceType);
865f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
87279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        if (upstreamIface != null) {
88279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley            dispatchTetherConnectionChanged(upstreamIface);
89279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        }
90279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        reset(mNMService, mStatsService, mTetherHelper);
91f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
92279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
93279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
94e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley    @Before public void setUp() throws Exception {
9508725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        MockitoAnnotations.initMocks(this);
96292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Kline        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
97279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
98279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
99279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
100279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    public void startsOutAvailable() {
101cd0cfbbc61168b61360a6247b4a826494deb3912Christopher Wiley        mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
102292b65a6f11a37b5cbb354fab5587484c2ef4d2eErik Kline                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
1035bce5a16b17498981253d7a2d1a490667cd71798Lorenzo Colitti                mIPv6TetheringInterfaceServices);
10408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        mTestedSm.start();
105279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mLooper.dispatchAll();
106e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        verify(mTetherHelper).notifyInterfaceStateChange(
107e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
108279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
10908725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    }
11008725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
11108725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    @Test
112f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void shouldDoNothingUntilRequested() throws Exception {
113a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initStateMachine(TETHERING_BLUETOOTH);
11408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        final int [] NOOP_COMMANDS = {
1154622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
1164622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
1174622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
1184622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
1194622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
1204622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
1214622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills            TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
12208725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        };
12308725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        for (int command : NOOP_COMMANDS) {
124279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley            // None of these commands should trigger us to request action from
12508725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley            // the rest of the system.
126279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley            dispatchCommand(command);
127279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley            verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
12808725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley        }
12908725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    }
13008725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley
131279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
132f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void handlesImmediateInterfaceDown() throws Exception {
133a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initStateMachine(TETHERING_BLUETOOTH);
134e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley
1354622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills        dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
136e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        verify(mTetherHelper).notifyInterfaceStateChange(
137e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
138279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
139279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
140279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
141279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
142f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void canBeTethered() throws Exception {
143a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initStateMachine(TETHERING_BLUETOOTH);
144e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley
1455f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
146279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mTetherHelper, mNMService);
147279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
148e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
149e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
150279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
151279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
152279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
153279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
154279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    public void canUnrequestTethering() throws Exception {
155a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_BLUETOOTH, null);
156279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
1574622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
158279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
159f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
160e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
161e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
162279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
163279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
164279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
165279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
166f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void canBeTetheredAsUsb() throws Exception {
167a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initStateMachine(TETHERING_USB);
168279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
1695f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
170279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mTetherHelper, mNMService);
171279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
172279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
173279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
174e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
175e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
176279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
177279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
178279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
179279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
180279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    public void handlesFirstUpstreamChange() throws Exception {
181a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_BLUETOOTH, null);
182279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
183279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        // Telling the state machine about its upstream interface triggers a little more configuration.
184279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
185279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mNMService);
186279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
187279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
188279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
189279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
190279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
191279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
192279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    public void handlesChangingUpstream() throws Exception {
193a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
194279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
195279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
196279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mNMService, mStatsService);
197279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mStatsService).forceUpdate();
198279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
199279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
200279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
201279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
202279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
203279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
204279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
205279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    @Test
206a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    public void handlesChangingUpstreamNatFailure() throws Exception {
207a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
208a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
209a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
210a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
211a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
212a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        InOrder inOrder = inOrder(mNMService, mStatsService);
213a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mStatsService).forceUpdate();
214a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
215a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
216a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
2178ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mStatsService).forceUpdate();
2188ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
2198ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
220a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    }
221a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
222a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    @Test
223a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception {
224a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
225a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
226a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding(
227a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline                IFACE_NAME, UPSTREAM_IFACE2);
228a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
229a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
230a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        InOrder inOrder = inOrder(mNMService, mStatsService);
231a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mStatsService).forceUpdate();
232a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
233a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
234a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
235a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
2368ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mStatsService).forceUpdate();
2378ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
2388ea45483fc59ef63851a64640ed1bb18c09f7ea9Erik Kline        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2);
239a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    }
240a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline
241a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline    @Test
242279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    public void canUnrequestTetheringWithUpstream() throws Exception {
243a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
244279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
2454622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
246279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
247279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mStatsService).forceUpdate();
248279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
249279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
250279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
251e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        inOrder.verify(mTetherHelper).notifyInterfaceStateChange(
252e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
253279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
254279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
255279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
256f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    @Test
257f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void interfaceDownLeadsToUnavailable() throws Exception {
258f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        for (boolean shouldThrow : new boolean[]{true, false}) {
259a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline            initTetheredStateMachine(TETHERING_USB, null);
260f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley
261f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley            if (shouldThrow) {
262f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley                doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
263f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley            }
264f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley            dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
265e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley            InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
266f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley            usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
267f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley            usbTeardownOrder.verify(mNMService).setInterfaceConfig(
268f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley                    IFACE_NAME, mInterfaceConfiguration);
269e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley            usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
270e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                    IFACE_NAME, mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
271f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        }
272f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    }
273f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley
274f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    @Test
275f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void usbShouldBeTornDownOnTetherError() throws Exception {
276a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initStateMachine(TETHERING_USB);
277f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley
278f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
2795f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
280e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
281f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
282f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        usbTeardownOrder.verify(mNMService).setInterfaceConfig(
283f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley                IFACE_NAME, mInterfaceConfiguration);
284e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
285e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
286f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    }
287f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley
288f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    @Test
289f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    public void shouldTearDownUsbOnUpstreamError() throws Exception {
290a954be956315e6d25a63e961fc9befe9916e1cbdErik Kline        initTetheredStateMachine(TETHERING_USB, null);
291f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley
292f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
293f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
294e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
295f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
296f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley        usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
297e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley        usbTeardownOrder.verify(mTetherHelper).notifyInterfaceStateChange(
298e90e0a74adf33f3d0ed0f2617520ab72b3b5d624Christopher Wiley                IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
299f972edcae7386d0dfc596aaa01e97d039dd1e0e2Christopher Wiley    }
300279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
301624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline    @Test
302624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline    public void ignoresDuplicateUpstreamNotifications() throws Exception {
303624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
304624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline
305624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
306624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline
307624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline        for (int i = 0; i < 5; i++) {
308624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline            dispatchTetherConnectionChanged(UPSTREAM_IFACE);
309624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline            verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
310624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline        }
311624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline    }
312624bf3d7f66e496952c5ae8917dc785676921c99Erik Kline
313279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    /**
314279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     * Send a command to the state machine under test, and run the event loop to idle.
315279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     *
3164622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills     * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
3175f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline     * @param obj An additional argument to pass.
3185f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline     */
3195f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline    private void dispatchCommand(int command, int arg1) {
3205f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        mTestedSm.sendMessage(command, arg1);
3215f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline        mLooper.dispatchAll();
3225f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline    }
3235f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline
3245f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline    /**
3255f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline     * Send a command to the state machine under test, and run the event loop to idle.
3265f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline     *
3275f2b7992cfe1149de115c6f7056d0da128b8afbaErik Kline     * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
328279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     */
329279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    private void dispatchCommand(int command) {
330279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mTestedSm.sendMessage(command);
331279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mLooper.dispatchAll();
332279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    }
333279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley
334279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    /**
335279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     * Special override to tell the state machine that the upstream interface has changed.
336279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     *
337279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     * @see #dispatchCommand(int)
338279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     * @param upstreamIface String name of upstream interface (or null)
339279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley     */
340279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley    private void dispatchTetherConnectionChanged(String upstreamIface) {
3414622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills        mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
3424622c2d8b65e267ede15bffd903faaf917642d7eMitchell Wills                upstreamIface);
343279eca3de6f0eb3cd9a947991fe9d1d5d2cd13c1Christopher Wiley        mLooper.dispatchAll();
34408725a8c4042aee177774f6275811aa88f26ef30Christopher Wiley    }
3455bce5a16b17498981253d7a2d1a490667cd71798Lorenzo Colitti}
346