1 /*
2  * Copyright (C) 2020 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 package android.tethering.mts;
17 
18 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
19 import static android.Manifest.permission.READ_DEVICE_CONFIG;
20 import static android.Manifest.permission.WRITE_SETTINGS;
21 import static android.net.TetheringManager.TETHERING_WIFI;
22 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
23 
24 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
25 import static com.android.testutils.TestPermissionUtil.runAsShell;
26 
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.fail;
30 
31 import android.content.Context;
32 import android.net.IpPrefix;
33 import android.net.LinkAddress;
34 import android.net.TetheringInterface;
35 import android.net.cts.util.CtsTetheringUtils;
36 import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
37 import android.provider.DeviceConfig;
38 
39 import androidx.annotation.NonNull;
40 import androidx.test.InstrumentationRegistry;
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import com.android.testutils.TestNetworkTracker;
44 
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.net.InterfaceAddress;
50 import java.net.NetworkInterface;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.List;
54 
55 @RunWith(AndroidJUnit4.class)
56 public class TetheringModuleTest {
57     private Context mContext;
58     private CtsTetheringUtils mCtsTetheringUtils;
59     private final long mRestartTimeOutMs = 5_000;
60 
61     @Before
setUp()62     public void setUp() throws Exception {
63         mContext = InstrumentationRegistry.getContext();
64         mCtsTetheringUtils = new CtsTetheringUtils(mContext);
65     }
66 
67     @Test
testSwitchBasePrefixRangeWhenConflict()68     public void testSwitchBasePrefixRangeWhenConflict() throws Exception {
69         addressConflictTest(true);
70     }
71 
72     @Test
testSwitchPrefixRangeWhenConflict()73     public void testSwitchPrefixRangeWhenConflict() throws Exception {
74         addressConflictTest(false);
75     }
76 
addressConflictTest(final boolean wholeRangeConflict)77     private void addressConflictTest(final boolean wholeRangeConflict) throws Exception {
78         final TestTetheringEventCallback tetherEventCallback =
79                 mCtsTetheringUtils.registerTetheringEventCallback();
80 
81         TestNetworkTracker tnt = null;
82         try {
83             tetherEventCallback.assumeWifiTetheringSupported(mContext);
84             tetherEventCallback.expectNoTetheringActive();
85 
86             final TetheringInterface tetheredIface =
87                     mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
88 
89             assertNotNull(tetheredIface);
90             final String wifiTetheringIface = tetheredIface.getInterface();
91 
92             NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface);
93             // Tethering downstream only have one ipv4 address.
94             final LinkAddress hotspotAddr = getFirstIpv4Address(nif);
95             assertNotNull(hotspotAddr);
96 
97             final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict);
98             assertNotNull(testPrefix);
99 
100             tnt = setUpTestNetwork(
101                     new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength()));
102 
103             tetherEventCallback.expectNoTetheringActive();
104             final List<String> wifiRegexs =
105                     tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
106 
107             final TetheringInterface restartedIface =
108                     tetherEventCallback.pollTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI,
109                     mRestartTimeOutMs);
110             final TetheringInterface newIface;
111             if (restartedIface != null) {
112                 newIface = restartedIface;
113             } else {
114                 // Because of race inside tethering module, there is no guarantee wifi tethering
115                 // would restart successfully. If tethering don't auto restarted, restarting it
116                 // manually. TODO(b/242649651): remove this when tethering auto restart is reliable.
117                 newIface = mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
118             }
119 
120             nif = NetworkInterface.getByName(newIface.getInterface());
121             final LinkAddress newHotspotAddr = getFirstIpv4Address(nif);
122             assertNotNull(newHotspotAddr);
123 
124             assertFalse(testPrefix.containsPrefix(
125                     new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength())));
126 
127             mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
128         } finally {
129             teardown(tnt);
130             mCtsTetheringUtils.stopAllTethering();
131             mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
132         }
133     }
134 
getFirstIpv4Address(final NetworkInterface nif)135     private LinkAddress getFirstIpv4Address(final NetworkInterface nif) {
136         for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
137             final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
138             if (addr.isIpv4()) return addr;
139         }
140         return null;
141     }
142 
143     @NonNull
getConflictingPrefix(final LinkAddress address, final boolean wholeRangeConflict)144     private IpPrefix getConflictingPrefix(final LinkAddress address,
145             final boolean wholeRangeConflict) {
146         if (!wholeRangeConflict) {
147             return new IpPrefix(address.getAddress(), address.getPrefixLength());
148         }
149 
150         final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
151                 new IpPrefix("192.168.0.0/16"),
152                 new IpPrefix("172.16.0.0/12"),
153                 new IpPrefix("10.0.0.0/8")));
154 
155         for (IpPrefix prefix : prefixPool) {
156             if (prefix.contains(address.getAddress())) return prefix;
157         }
158 
159         fail("Could not find sutiable conflict prefix");
160 
161         // Never go here.
162         return null;
163     }
164 
setUpTestNetwork(final LinkAddress address)165     private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception {
166         return runAsShell(MANAGE_TEST_NETWORKS, WRITE_SETTINGS,
167                 () -> initTestNetwork(mContext, address, 10_000L /* test timeout ms*/));
168 
169     }
170 
teardown(TestNetworkTracker tracker)171     private void teardown(TestNetworkTracker tracker) throws Exception {
172         if (tracker == null) return;
173 
174         runAsShell(MANAGE_TEST_NETWORKS, () -> tracker.teardown());
175     }
176 
isFeatureEnabled(final String name, final boolean defaultValue)177     public static boolean isFeatureEnabled(final String name, final boolean defaultValue) {
178         return runAsShell(READ_DEVICE_CONFIG,
179                 () -> DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue));
180     }
181 }
182