1 /* 2 * Copyright (C) 2017 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 17 package com.android.networkstack.tethering; 18 19 import static android.net.ConnectivityManager.TYPE_ETHERNET; 20 import static android.net.ConnectivityManager.TYPE_MOBILE; 21 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; 22 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; 23 import static android.net.ConnectivityManager.TYPE_WIFI; 24 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; 25 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 26 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 29 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertFalse; 32 import static org.junit.Assert.assertTrue; 33 import static org.mockito.Matchers.eq; 34 import static org.mockito.Mockito.when; 35 36 import android.content.Context; 37 import android.content.res.Resources; 38 import android.net.util.SharedLog; 39 import android.provider.DeviceConfig; 40 import android.telephony.TelephonyManager; 41 42 import androidx.test.filters.SmallTest; 43 import androidx.test.runner.AndroidJUnit4; 44 45 import com.android.internal.util.test.BroadcastInterceptingContext; 46 47 import org.junit.After; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Mock; 52 import org.mockito.MockitoSession; 53 import org.mockito.quality.Strictness; 54 55 import java.util.Arrays; 56 import java.util.Iterator; 57 58 @RunWith(AndroidJUnit4.class) 59 @SmallTest 60 public class TetheringConfigurationTest { 61 private final SharedLog mLog = new SharedLog("TetheringConfigurationTest"); 62 63 private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; 64 private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app"; 65 private static final String PROVISIONING_APP_RESPONSE = "app_response"; 66 @Mock private Context mContext; 67 @Mock private TelephonyManager mTelephonyManager; 68 @Mock private Resources mResources; 69 @Mock private Resources mResourcesForSubId; 70 private Context mMockContext; 71 private boolean mHasTelephonyManager; 72 private boolean mEnableLegacyDhcpServer; 73 private MockitoSession mMockingSession; 74 75 private class MockTetheringConfiguration extends TetheringConfiguration { MockTetheringConfiguration(Context ctx, SharedLog log, int id)76 MockTetheringConfiguration(Context ctx, SharedLog log, int id) { 77 super(ctx, log, id); 78 } 79 80 @Override getResourcesForSubIdWrapper(Context ctx, int subId)81 protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { 82 return mResourcesForSubId; 83 } 84 } 85 86 private class MockContext extends BroadcastInterceptingContext { MockContext(Context base)87 MockContext(Context base) { 88 super(base); 89 } 90 91 @Override getResources()92 public Resources getResources() { 93 return mResources; 94 } 95 96 @Override getSystemService(String name)97 public Object getSystemService(String name) { 98 if (Context.TELEPHONY_SERVICE.equals(name)) { 99 return mHasTelephonyManager ? mTelephonyManager : null; 100 } 101 return super.getSystemService(name); 102 } 103 } 104 105 @Before setUp()106 public void setUp() throws Exception { 107 // TODO: use a dependencies class instead of mock statics. 108 mMockingSession = mockitoSession() 109 .initMocks(this) 110 .mockStatic(DeviceConfig.class) 111 .strictness(Strictness.WARN) 112 .startMocking(); 113 doReturn(null).when( 114 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), 115 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); 116 117 when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( 118 new String[0]); 119 when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( 120 TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); 121 when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); 122 when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) 123 .thenReturn(new String[]{ "test_wlan\\d" }); 124 when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn( 125 new String[0]); 126 when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); 127 when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) 128 .thenReturn(new String[0]); 129 when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( 130 false); 131 initializeBpfOffloadConfiguration(true, null /* unset */); 132 133 mHasTelephonyManager = true; 134 mMockContext = new MockContext(mContext); 135 mEnableLegacyDhcpServer = false; 136 } 137 138 @After tearDown()139 public void tearDown() throws Exception { 140 mMockingSession.finishMocking(); 141 } 142 getTetheringConfiguration(int... legacyTetherUpstreamTypes)143 private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { 144 when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( 145 legacyTetherUpstreamTypes); 146 return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 147 } 148 149 @Test testNoTelephonyManagerMeansNoDun()150 public void testNoTelephonyManagerMeansNoDun() { 151 mHasTelephonyManager = false; 152 final TetheringConfiguration cfg = getTetheringConfiguration( 153 new int[]{TYPE_MOBILE_DUN, TYPE_WIFI}); 154 assertFalse(cfg.isDunRequired); 155 assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); 156 // Just to prove we haven't clobbered Wi-Fi: 157 assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); 158 } 159 160 @Test testDunFromTelephonyManagerMeansDun()161 public void testDunFromTelephonyManagerMeansDun() { 162 when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true); 163 164 final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); 165 final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( 166 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); 167 final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( 168 TYPE_WIFI, TYPE_MOBILE_DUN); 169 final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( 170 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); 171 172 for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, 173 cfgWifiDun, cfgMobileWifiHipriDun)) { 174 String msg = "config=" + cfg.toString(); 175 assertTrue(msg, cfg.isDunRequired); 176 assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); 177 assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); 178 assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); 179 // Just to prove we haven't clobbered Wi-Fi: 180 assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); 181 } 182 } 183 184 @Test testDunNotRequiredFromTelephonyManagerMeansNoDun()185 public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { 186 when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); 187 188 final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); 189 final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( 190 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI); 191 final TetheringConfiguration cfgWifiDun = getTetheringConfiguration( 192 TYPE_WIFI, TYPE_MOBILE_DUN); 193 final TetheringConfiguration cfgWifiMobile = getTetheringConfiguration( 194 TYPE_WIFI, TYPE_MOBILE); 195 final TetheringConfiguration cfgWifiHipri = getTetheringConfiguration( 196 TYPE_WIFI, TYPE_MOBILE_HIPRI); 197 final TetheringConfiguration cfgMobileWifiHipriDun = getTetheringConfiguration( 198 TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI, TYPE_MOBILE_DUN); 199 200 String msg; 201 // TYPE_MOBILE_DUN should be present in none of the combinations. 202 // TYPE_WIFI should not be affected. 203 for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, 204 cfgWifiMobile, cfgWifiHipri, cfgMobileWifiHipriDun)) { 205 msg = "config=" + cfg.toString(); 206 assertFalse(msg, cfg.isDunRequired); 207 assertFalse(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN)); 208 assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI)); 209 } 210 211 for (TetheringConfiguration cfg : Arrays.asList(cfgWifi, cfgMobileWifiHipri, cfgWifiDun, 212 cfgMobileWifiHipriDun)) { 213 msg = "config=" + cfg.toString(); 214 assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); 215 assertTrue(msg, cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); 216 } 217 msg = "config=" + cfgWifiMobile.toString(); 218 assertTrue(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); 219 assertFalse(msg, cfgWifiMobile.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); 220 msg = "config=" + cfgWifiHipri.toString(); 221 assertFalse(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE)); 222 assertTrue(msg, cfgWifiHipri.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)); 223 224 } 225 226 @Test testNoDefinedUpstreamTypesAddsEthernet()227 public void testNoDefinedUpstreamTypesAddsEthernet() { 228 when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{}); 229 when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); 230 231 final TetheringConfiguration cfg = new TetheringConfiguration( 232 mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 233 final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); 234 assertTrue(upstreamIterator.hasNext()); 235 assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); 236 // The following is because the code always adds some kind of mobile 237 // upstream, be it DUN or, in this case where DUN is NOT required, 238 // make sure there is at least one of MOBILE or HIPRI. With the empty 239 // list of the configuration in this test, it will always add both 240 // MOBILE and HIPRI, in that order. 241 assertTrue(upstreamIterator.hasNext()); 242 assertEquals(TYPE_MOBILE, upstreamIterator.next().intValue()); 243 assertTrue(upstreamIterator.hasNext()); 244 assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); 245 assertFalse(upstreamIterator.hasNext()); 246 } 247 248 @Test testDefinedUpstreamTypesSansEthernetAddsEthernet()249 public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { 250 when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( 251 new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); 252 when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); 253 254 final TetheringConfiguration cfg = new TetheringConfiguration( 255 mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 256 final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); 257 assertTrue(upstreamIterator.hasNext()); 258 assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); 259 assertTrue(upstreamIterator.hasNext()); 260 assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); 261 assertTrue(upstreamIterator.hasNext()); 262 assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); 263 assertFalse(upstreamIterator.hasNext()); 264 } 265 266 @Test testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet()267 public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { 268 when(mResources.getIntArray(R.array.config_tether_upstream_types)) 269 .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); 270 when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); 271 272 final TetheringConfiguration cfg = new TetheringConfiguration( 273 mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 274 final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator(); 275 assertTrue(upstreamIterator.hasNext()); 276 assertEquals(TYPE_WIFI, upstreamIterator.next().intValue()); 277 assertTrue(upstreamIterator.hasNext()); 278 assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue()); 279 assertTrue(upstreamIterator.hasNext()); 280 assertEquals(TYPE_MOBILE_HIPRI, upstreamIterator.next().intValue()); 281 assertFalse(upstreamIterator.hasNext()); 282 } 283 initializeBpfOffloadConfiguration( final boolean fromRes, final String fromDevConfig)284 private void initializeBpfOffloadConfiguration( 285 final boolean fromRes, final String fromDevConfig) { 286 when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes); 287 doReturn(fromDevConfig).when( 288 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), 289 eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD))); 290 } 291 292 @Test testBpfOffloadEnabledByResource()293 public void testBpfOffloadEnabledByResource() { 294 initializeBpfOffloadConfiguration(true, null /* unset */); 295 final TetheringConfiguration enableByRes = 296 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 297 assertTrue(enableByRes.isBpfOffloadEnabled()); 298 } 299 300 @Test testBpfOffloadEnabledByDeviceConfigOverride()301 public void testBpfOffloadEnabledByDeviceConfigOverride() { 302 for (boolean res : new boolean[]{true, false}) { 303 initializeBpfOffloadConfiguration(res, "true"); 304 final TetheringConfiguration enableByDevConOverride = 305 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 306 assertTrue(enableByDevConOverride.isBpfOffloadEnabled()); 307 } 308 } 309 310 @Test testBpfOffloadDisabledByResource()311 public void testBpfOffloadDisabledByResource() { 312 initializeBpfOffloadConfiguration(false, null /* unset */); 313 final TetheringConfiguration disableByRes = 314 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 315 assertFalse(disableByRes.isBpfOffloadEnabled()); 316 } 317 318 @Test testBpfOffloadDisabledByDeviceConfigOverride()319 public void testBpfOffloadDisabledByDeviceConfigOverride() { 320 for (boolean res : new boolean[]{true, false}) { 321 initializeBpfOffloadConfiguration(res, "false"); 322 final TetheringConfiguration disableByDevConOverride = 323 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 324 assertFalse(disableByDevConOverride.isBpfOffloadEnabled()); 325 } 326 } 327 328 @Test testNewDhcpServerDisabled()329 public void testNewDhcpServerDisabled() { 330 when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( 331 true); 332 doReturn("false").when( 333 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), 334 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); 335 336 final TetheringConfiguration enableByRes = 337 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 338 assertTrue(enableByRes.enableLegacyDhcpServer); 339 340 when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( 341 false); 342 doReturn("true").when( 343 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), 344 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); 345 346 final TetheringConfiguration enableByDevConfig = 347 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 348 assertTrue(enableByDevConfig.enableLegacyDhcpServer); 349 } 350 351 @Test testNewDhcpServerEnabled()352 public void testNewDhcpServerEnabled() { 353 when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( 354 false); 355 doReturn("false").when( 356 () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), 357 eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER))); 358 359 final TetheringConfiguration cfg = 360 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 361 362 assertFalse(cfg.enableLegacyDhcpServer); 363 } 364 365 @Test testOffloadIntervalByResource()366 public void testOffloadIntervalByResource() { 367 final TetheringConfiguration intervalByDefault = 368 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 369 assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, 370 intervalByDefault.getOffloadPollInterval()); 371 372 final int[] testOverrides = {0, 3000, -1}; 373 for (final int override : testOverrides) { 374 when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn( 375 override); 376 final TetheringConfiguration overrideByRes = 377 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 378 assertEquals(override, overrideByRes.getOffloadPollInterval()); 379 } 380 } 381 382 @Test testGetResourcesBySubId()383 public void testGetResourcesBySubId() { 384 setUpResourceForSubId(); 385 final TetheringConfiguration cfg = new TetheringConfiguration( 386 mMockContext, mLog, INVALID_SUBSCRIPTION_ID); 387 assertTrue(cfg.provisioningApp.length == 0); 388 final int anyValidSubId = 1; 389 final MockTetheringConfiguration mockCfg = 390 new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId); 391 assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]); 392 assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]); 393 assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME); 394 assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE); 395 } 396 setUpResourceForSubId()397 private void setUpResourceForSubId() { 398 when(mResourcesForSubId.getStringArray( 399 R.array.config_tether_dhcp_range)).thenReturn(new String[0]); 400 when(mResourcesForSubId.getStringArray( 401 R.array.config_tether_usb_regexs)).thenReturn(new String[0]); 402 when(mResourcesForSubId.getStringArray( 403 R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); 404 when(mResourcesForSubId.getStringArray( 405 R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]); 406 when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn( 407 new int[0]); 408 when(mResourcesForSubId.getStringArray( 409 R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); 410 when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui)) 411 .thenReturn(PROVISIONING_NO_UI_APP_NAME); 412 when(mResourcesForSubId.getString( 413 R.string.config_mobile_hotspot_provision_response)).thenReturn( 414 PROVISIONING_APP_RESPONSE); 415 } 416 } 417