Add global change ethernet state API

Provide a new API to enable or disable ethernet. Also have a listener
for call to check whether enable state is sucessful.

Bug: 171872016
Test: atest EthernetServiceTests

Change-Id: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940
Merged-In: Iee4b48511ff668a2a7df90fd9bfe563d7ff23940
This commit is contained in:
markchien
2022-03-16 15:59:33 +08:00
committed by Mark Chien
parent 641bc0c4dc
commit c0939b76f0
5 changed files with 157 additions and 3 deletions

View File

@@ -183,7 +183,8 @@ public class EthernetNetworkFactory extends NetworkFactory {
* Returns an array of available interface names. The array is sorted: unrestricted interfaces * Returns an array of available interface names. The array is sorted: unrestricted interfaces
* goes first, then sorted by name. * goes first, then sorted by name.
*/ */
String[] getAvailableInterfaces(boolean includeRestricted) { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected String[] getAvailableInterfaces(boolean includeRestricted) {
return mTrackingInterfaces.values() return mTrackingInterfaces.values()
.stream() .stream()
.filter(iface -> !iface.isRestricted() || includeRestricted) .filter(iface -> !iface.isRestricted() || includeRestricted)
@@ -195,7 +196,8 @@ public class EthernetNetworkFactory extends NetworkFactory {
.toArray(String[]::new); .toArray(String[]::new);
} }
void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress, @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected void addInterface(@NonNull final String ifaceName, @NonNull final String hwAddress,
@NonNull final IpConfiguration ipConfig, @NonNull final IpConfiguration ipConfig,
@NonNull final NetworkCapabilities capabilities) { @NonNull final NetworkCapabilities capabilities) {
if (mTrackingInterfaces.containsKey(ifaceName)) { if (mTrackingInterfaces.containsKey(ifaceName)) {
@@ -282,7 +284,8 @@ public class EthernetNetworkFactory extends NetworkFactory {
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build();
} }
void removeInterface(String interfaceName) { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected void removeInterface(String interfaceName) {
NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName); NetworkInterfaceState iface = mTrackingInterfaces.remove(interfaceName);
if (iface != null) { if (iface != null) {
iface.maybeSendNetworkManagementCallbackForAbort(); iface.maybeSendNetworkManagementCallbackForAbort();

View File

@@ -281,4 +281,12 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
mTracker.disconnectNetwork(iface, listener); mTracker.disconnectNetwork(iface, listener);
} }
@Override
public void setEthernetEnabled(boolean enabled) {
PermissionUtils.enforceNetworkStackPermissionOr(mContext,
android.Manifest.permission.NETWORK_SETTINGS);
mTracker.setEthernetEnabled(enabled);
}
} }

View File

@@ -16,6 +16,8 @@
package com.android.server.ethernet; package com.android.server.ethernet;
import static android.net.EthernetManager.ETHERNET_STATE_DISABLED;
import static android.net.EthernetManager.ETHERNET_STATE_ENABLED;
import static android.net.TestNetworkManager.TEST_TAP_PREFIX; import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -119,6 +121,8 @@ public class EthernetTracker {
private boolean mTetheredInterfaceWasAvailable = false; private boolean mTetheredInterfaceWasAvailable = false;
private volatile IpConfiguration mIpConfigForDefaultInterface; private volatile IpConfiguration mIpConfigForDefaultInterface;
private int mEthernetState = ETHERNET_STATE_ENABLED;
private class TetheredInterfaceRequestList extends private class TetheredInterfaceRequestList extends
RemoteCallbackList<ITetheredInterfaceCallback> { RemoteCallbackList<ITetheredInterfaceCallback> {
@Override @Override
@@ -355,6 +359,8 @@ public class EthernetTracker {
for (String iface : getInterfaces(canUseRestrictedNetworks)) { for (String iface : getInterfaces(canUseRestrictedNetworks)) {
unicastInterfaceStateChange(listener, iface); unicastInterfaceStateChange(listener, iface);
} }
unicastEthernetStateChange(listener, mEthernetState);
}); });
} }
@@ -825,6 +831,53 @@ public class EthernetTracker {
} }
} }
@VisibleForTesting(visibility = PACKAGE)
protected void setEthernetEnabled(boolean enabled) {
mHandler.post(() -> {
int newState = enabled ? ETHERNET_STATE_ENABLED : ETHERNET_STATE_DISABLED;
if (mEthernetState == newState) return;
mEthernetState = newState;
if (enabled) {
trackAvailableInterfaces();
} else {
// TODO: maybe also disable server mode interface as well.
untrackFactoryInterfaces();
}
broadcastEthernetStateChange(mEthernetState);
});
}
private void untrackFactoryInterfaces() {
for (String iface : mFactory.getAvailableInterfaces(true /* includeRestricted */)) {
stopTrackingInterface(iface);
}
}
private void unicastEthernetStateChange(@NonNull IEthernetServiceListener listener,
int state) {
ensureRunningOnEthernetServiceThread();
try {
listener.onEthernetStateChanged(state);
} catch (RemoteException e) {
// Do nothing here.
}
}
private void broadcastEthernetStateChange(int state) {
ensureRunningOnEthernetServiceThread();
final int n = mListeners.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
mListeners.getBroadcastItem(i).onEthernetStateChanged(state);
} catch (RemoteException e) {
// Do nothing here.
}
}
mListeners.finishBroadcast();
}
void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
postAndWaitForRunnable(() -> { postAndWaitForRunnable(() -> {
pw.println(getClass().getSimpleName()); pw.println(getClass().getSimpleName());

View File

@@ -19,12 +19,15 @@ package com.android.server.ethernet;
import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -339,4 +342,31 @@ public class EthernetServiceImplTest {
mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER); mEthernetServiceImpl.disconnectNetwork(TEST_IFACE, NULL_LISTENER);
verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER)); verify(mEthernetTracker).disconnectNetwork(eq(TEST_IFACE), eq(NULL_LISTENER));
} }
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);
}
} }

View File

@@ -25,19 +25,26 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.EthernetManager;
import android.net.InetAddresses; import android.net.InetAddresses;
import android.net.INetworkInterfaceOutcomeReceiver; import android.net.INetworkInterfaceOutcomeReceiver;
import android.net.IEthernetServiceListener;
import android.net.INetd; import android.net.INetd;
import android.net.IpConfiguration; import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment; import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings; import android.net.IpConfiguration.ProxySettings;
import android.net.InterfaceConfigurationParcel;
import android.net.LinkAddress; import android.net.LinkAddress;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.StaticIpConfiguration; import android.net.StaticIpConfiguration;
@@ -393,4 +400,57 @@ public class EthernetTrackerTest {
assertTrue(isValidTestInterface); assertTrue(isValidTestInterface);
} }
public static class EthernetStateListener extends IEthernetServiceListener.Stub {
@Override
public void onEthernetStateChanged(int state) { }
@Override
public void onInterfaceStateChanged(String iface, int state, int role,
IpConfiguration configuration) { }
}
@Test
public void testListenEthernetStateChange() throws Exception {
final String testIface = "testtap123";
final String testHwAddr = "11:22:33:44:55:66";
final InterfaceConfigurationParcel ifaceParcel = new InterfaceConfigurationParcel();
ifaceParcel.ifName = testIface;
ifaceParcel.hwAddr = testHwAddr;
ifaceParcel.flags = new String[] {INetd.IF_STATE_UP};
tracker.setIncludeTestInterfaces(true);
waitForIdle();
when(mNetd.interfaceGetList()).thenReturn(new String[] {testIface});
when(mNetd.interfaceGetCfg(eq(testIface))).thenReturn(ifaceParcel);
doReturn(new String[] {testIface}).when(mFactory).getAvailableInterfaces(anyBoolean());
doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface));
final EthernetStateListener listener = spy(new EthernetStateListener());
tracker.addListener(listener, true /* canUseRestrictedNetworks */);
// Check default state.
waitForIdle();
verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP),
anyInt(), any());
verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED));
reset(listener);
doReturn(EthernetManager.STATE_ABSENT).when(mFactory).getInterfaceState(eq(testIface));
tracker.setEthernetEnabled(false);
waitForIdle();
verify(mFactory).removeInterface(eq(testIface));
verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_DISABLED));
verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_ABSENT),
anyInt(), any());
reset(listener);
doReturn(EthernetManager.STATE_LINK_UP).when(mFactory).getInterfaceState(eq(testIface));
tracker.setEthernetEnabled(true);
waitForIdle();
verify(mFactory).addInterface(eq(testIface), eq(testHwAddr), any(), any());
verify(listener).onEthernetStateChanged(eq(EthernetManager.ETHERNET_STATE_ENABLED));
verify(listener).onInterfaceStateChanged(eq(testIface), eq(EthernetManager.STATE_LINK_UP),
anyInt(), any());
}
} }