/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.ethernet; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; import android.annotation.NonNull; import android.content.Context; import android.content.pm.PackageManager; import android.net.EthernetNetworkSpecifier; import android.net.EthernetNetworkUpdateRequest; import android.net.INetworkInterfaceOutcomeReceiver; import android.net.IpConfiguration; import android.net.NetworkCapabilities; import android.net.StringNetworkSpecifier; import android.os.Build; import android.os.Handler; import androidx.test.filters.SmallTest; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(DevSdkIgnoreRunner.class) @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) public class EthernetServiceImplTest { private static final String TEST_IFACE = "test123"; private static final NetworkCapabilities DEFAULT_CAPS = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_ETHERNET) .setNetworkSpecifier(new EthernetNetworkSpecifier(TEST_IFACE)) .build(); private static final EthernetNetworkUpdateRequest UPDATE_REQUEST = new EthernetNetworkUpdateRequest.Builder() .setIpConfiguration(new IpConfiguration()) .setNetworkCapabilities(DEFAULT_CAPS) .build(); private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_CAPABILITIES = new EthernetNetworkUpdateRequest.Builder() .setIpConfiguration(new IpConfiguration()) .build(); private static final EthernetNetworkUpdateRequest UPDATE_REQUEST_WITHOUT_IP_CONFIG = new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(DEFAULT_CAPS) .build(); private static final INetworkInterfaceOutcomeReceiver NULL_LISTENER = null; private EthernetServiceImpl mEthernetServiceImpl; private Context mContext; private Handler mHandler; private EthernetTracker mEthernetTracker; private PackageManager mPackageManager; @Before public void setup() { mContext = mock(Context.class); mHandler = mock(Handler.class); mEthernetTracker = mock(EthernetTracker.class); mPackageManager = mock(PackageManager.class); doReturn(mPackageManager).when(mContext).getPackageManager(); mEthernetServiceImpl = new EthernetServiceImpl(mContext, mHandler, mEthernetTracker); mEthernetServiceImpl.mStarted.set(true); toggleAutomotiveFeature(true); shouldTrackIface(TEST_IFACE, true); } private void toggleAutomotiveFeature(final boolean isEnabled) { doReturn(isEnabled) .when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); } private void shouldTrackIface(@NonNull final String iface, final boolean shouldTrack) { doReturn(shouldTrack).when(mEthernetTracker).isTrackingInterface(iface); } @Test public void testSetConfigurationRejectsWhenEthNotStarted() { mEthernetServiceImpl.mStarted.set(false); assertThrows(IllegalStateException.class, () -> { mEthernetServiceImpl.setConfiguration("" /* iface */, new IpConfiguration()); }); } @Test public void testUpdateConfigurationRejectsWhenEthNotStarted() { mEthernetServiceImpl.mStarted.set(false); assertThrows(IllegalStateException.class, () -> { mEthernetServiceImpl.updateConfiguration( "" /* iface */, UPDATE_REQUEST, null /* listener */); }); } @Test public void testEnableInterfaceRejectsWhenEthNotStarted() { mEthernetServiceImpl.mStarted.set(false); assertThrows(IllegalStateException.class, () -> { mEthernetServiceImpl.enableInterface("" /* iface */, null /* listener */); }); } @Test public void testDisableInterfaceRejectsWhenEthNotStarted() { mEthernetServiceImpl.mStarted.set(false); assertThrows(IllegalStateException.class, () -> { mEthernetServiceImpl.disableInterface("" /* iface */, null /* listener */); }); } @Test public void testUpdateConfigurationRejectsNullIface() { assertThrows(NullPointerException.class, () -> { mEthernetServiceImpl.updateConfiguration(null, UPDATE_REQUEST, NULL_LISTENER); }); } @Test public void testEnableInterfaceRejectsNullIface() { assertThrows(NullPointerException.class, () -> { mEthernetServiceImpl.enableInterface(null /* iface */, NULL_LISTENER); }); } @Test public void testDisableInterfaceRejectsNullIface() { assertThrows(NullPointerException.class, () -> { mEthernetServiceImpl.disableInterface(null /* iface */, NULL_LISTENER); }); } @Test public void testUpdateConfigurationWithCapabilitiesRejectsWithoutAutomotiveFeature() { toggleAutomotiveFeature(false); assertThrows(UnsupportedOperationException.class, () -> { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); }); } @Test public void testUpdateConfigurationRejectsWithInvalidSpecifierType() { final StringNetworkSpecifier invalidSpecifierType = new StringNetworkSpecifier("123"); final EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities( new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_ETHERNET) .setNetworkSpecifier(invalidSpecifierType) .build() ).build(); assertThrows(IllegalArgumentException.class, () -> { mEthernetServiceImpl.updateConfiguration( "" /* iface */, request, null /* listener */); }); } @Test public void testUpdateConfigurationRejectsWithInvalidSpecifierName() { final String ifaceToUpdate = "eth0"; final String ifaceOnSpecifier = "wlan0"; EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities( new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_ETHERNET) .setNetworkSpecifier( new EthernetNetworkSpecifier(ifaceOnSpecifier)) .build() ).build(); assertThrows(IllegalArgumentException.class, () -> { mEthernetServiceImpl.updateConfiguration(ifaceToUpdate, request, null /* listener */); }); } @Test public void testUpdateConfigurationWithCapabilitiesWithAutomotiveFeature() { toggleAutomotiveFeature(false); mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_CAPABILITIES, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getIpConfiguration()), eq(UPDATE_REQUEST_WITHOUT_CAPABILITIES.getNetworkCapabilities()), any(EthernetCallback.class)); } private void denyManageEthPermission() { doThrow(new SecurityException("")).when(mContext) .enforceCallingOrSelfPermission( eq(Manifest.permission.MANAGE_ETHERNET_NETWORKS), anyString()); } private void denyManageTestNetworksPermission() { doThrow(new SecurityException("")).when(mContext) .enforceCallingOrSelfPermission( eq(Manifest.permission.MANAGE_TEST_NETWORKS), anyString()); } @Test public void testUpdateConfigurationRejectsWithoutManageEthPermission() { denyManageEthPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); }); } @Test public void testEnableInterfaceRejectsWithoutManageEthPermission() { denyManageEthPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER); }); } @Test public void testDisableInterfaceRejectsWithoutManageEthPermission() { denyManageEthPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER); }); } private void enableTestInterface() { when(mEthernetTracker.isValidTestInterface(eq(TEST_IFACE))).thenReturn(true); } @Test public void testUpdateConfigurationRejectsTestRequestWithoutTestPermission() { enableTestInterface(); denyManageTestNetworksPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); }); } @Test public void testEnableInterfaceRejectsTestRequestWithoutTestPermission() { enableTestInterface(); denyManageTestNetworksPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER); }); } @Test public void testDisableInterfaceRejectsTestRequestWithoutTestPermission() { enableTestInterface(); denyManageTestNetworksPermission(); assertThrows(SecurityException.class, () -> { mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER); }); } @Test public void testUpdateConfiguration() { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration( eq(TEST_IFACE), eq(UPDATE_REQUEST.getIpConfiguration()), eq(UPDATE_REQUEST.getNetworkCapabilities()), any(EthernetCallback.class)); } @Test public void testUpdateConfigurationAddsSpecifierWhenNotSet() { final NetworkCapabilities nc = new NetworkCapabilities.Builder() .addTransportType(TRANSPORT_ETHERNET).build(); final EthernetNetworkUpdateRequest requestSansSpecifier = new EthernetNetworkUpdateRequest.Builder() .setNetworkCapabilities(nc) .build(); final NetworkCapabilities ncWithSpecifier = new NetworkCapabilities(nc) .setNetworkSpecifier(new EthernetNetworkSpecifier(TEST_IFACE)); mEthernetServiceImpl.updateConfiguration(TEST_IFACE, requestSansSpecifier, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration( eq(TEST_IFACE), isNull(), eq(ncWithSpecifier), any(EthernetCallback.class)); } @Test public void testEnableInterface() { mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(true), any(EthernetCallback.class)); } @Test public void testDisableInterface() { mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(false), any(EthernetCallback.class)); } @Test public void testUpdateConfigurationAcceptsTestRequestWithNullCapabilities() { enableTestInterface(); final EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest .Builder() .setIpConfiguration(new IpConfiguration()).build(); mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), eq(request.getIpConfiguration()), eq(request.getNetworkCapabilities()), any(EthernetCallback.class)); } @Test public void testUpdateConfigurationAcceptsRequestWithNullIpConfiguration() { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST_WITHOUT_IP_CONFIG, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration(eq(TEST_IFACE), eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getIpConfiguration()), eq(UPDATE_REQUEST_WITHOUT_IP_CONFIG.getNetworkCapabilities()), any(EthernetCallback.class)); } @Test public void testUpdateConfigurationRejectsInvalidTestRequest() { enableTestInterface(); assertThrows(IllegalArgumentException.class, () -> { mEthernetServiceImpl.updateConfiguration(TEST_IFACE, UPDATE_REQUEST, NULL_LISTENER); }); } private EthernetNetworkUpdateRequest createTestNetworkUpdateRequest() { final NetworkCapabilities nc = new NetworkCapabilities .Builder(UPDATE_REQUEST.getNetworkCapabilities()) .addTransportType(TRANSPORT_TEST).build(); return new EthernetNetworkUpdateRequest .Builder(UPDATE_REQUEST) .setNetworkCapabilities(nc).build(); } @Test public void testUpdateConfigurationForTestRequestDoesNotRequireAutoOrEthernetPermission() { enableTestInterface(); toggleAutomotiveFeature(false); denyManageEthPermission(); final EthernetNetworkUpdateRequest request = createTestNetworkUpdateRequest(); mEthernetServiceImpl.updateConfiguration(TEST_IFACE, request, NULL_LISTENER); verify(mEthernetTracker).updateConfiguration( eq(TEST_IFACE), eq(request.getIpConfiguration()), eq(request.getNetworkCapabilities()), any(EthernetCallback.class)); } @Test public void testEnableInterfaceForTestRequestDoesNotRequireNetPermission() { enableTestInterface(); toggleAutomotiveFeature(false); denyManageEthPermission(); mEthernetServiceImpl.enableInterface(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(true), any(EthernetCallback.class)); } @Test public void testDisableInterfaceForTestRequestDoesNotRequireAutoOrNetPermission() { enableTestInterface(); toggleAutomotiveFeature(false); denyManageEthPermission(); mEthernetServiceImpl.disableInterface(TEST_IFACE, NULL_LISTENER); verify(mEthernetTracker).setInterfaceEnabled(eq(TEST_IFACE), eq(false), any(EthernetCallback.class)); } private void denyPermissions(String... permissions) { for (String permission: permissions) { doReturn(PackageManager.PERMISSION_DENIED).when(mContext) .checkCallingOrSelfPermission(eq(permission)); } } @Test public void testSetEthernetEnabled() { denyPermissions(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); mEthernetServiceImpl.setEthernetEnabled(true); verify(mEthernetTracker).setEthernetEnabled(true); reset(mEthernetTracker); denyPermissions(Manifest.permission.NETWORK_STACK); mEthernetServiceImpl.setEthernetEnabled(false); verify(mEthernetTracker).setEthernetEnabled(false); reset(mEthernetTracker); denyPermissions(Manifest.permission.NETWORK_SETTINGS); try { mEthernetServiceImpl.setEthernetEnabled(true); fail("Should get SecurityException"); } catch (SecurityException e) { } verify(mEthernetTracker, never()).setEthernetEnabled(false); } }