Implement the new added EthernetManager APIs.

EthernetManager is moving to the connectivity mainline module
and some of its hidden methods are being exposed as module-lib
APIs. This CL updates the implementation.

1. Rename onAvailabilityChanged to onInterfaceStateChanged in
   IEthernetServiceListener.aidl, to match the name of the public
   callback method.
2. Add the interface state, role and IpConfiguration to the
   callback, so that clients can use this information without
   having to call synchronous methods.
3. Call the new callback whenever any of the above parameters
   changes, or when a callback is registered.

Also make some package-private methods in EthernetNetworkFactory
protected @VisibleForTesting because otherwise mockito can't
mock them.

Bug: 210586283
Test: m
Test: atest EthernetServiceTests EthernetTetheringTest
Change-Id: Ib27bf119b0d61b78f19b6447a38b4468d8035c78
Merged-In: Ib27bf119b0d61b78f19b6447a38b4468d8035c78
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index a15fec4..2823391 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.EthernetManager;
 import android.net.EthernetNetworkSpecifier;
 import android.net.IEthernetNetworkManagementListener;
 import android.net.EthernetNetworkManagementException;
@@ -186,6 +187,18 @@
         updateCapabilityFilter();
     }
 
+    @VisibleForTesting
+    protected int getInterfaceState(@NonNull String iface) {
+        final NetworkInterfaceState interfaceState = mTrackingInterfaces.get(iface);
+        if (interfaceState == null) {
+            return EthernetManager.STATE_ABSENT;
+        } else if (!interfaceState.mLinkUp) {
+            return EthernetManager.STATE_LINK_DOWN;
+        } else {
+            return EthernetManager.STATE_LINK_UP;
+        }
+    }
+
     /**
      * Update a network's configuration and restart it if necessary.
      *
@@ -270,7 +283,8 @@
         return iface.updateLinkState(up, listener);
     }
 
-    boolean hasInterface(String ifaceName) {
+    @VisibleForTesting
+    protected boolean hasInterface(String ifaceName) {
         return mTrackingInterfaces.containsKey(ifaceName);
     }
 
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index ffd6d40..c1c6d3a 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -143,10 +143,8 @@
      * Adds a listener.
      * @param listener A {@link IEthernetServiceListener} to add.
      */
-    public void addListener(IEthernetServiceListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener must not be null");
-        }
+    public void addListener(IEthernetServiceListener listener) throws RemoteException {
+        Objects.requireNonNull(listener, "listener must not be null");
         PermissionUtils.enforceAccessNetworkStatePermission(mContext, TAG);
         mTracker.addListener(listener, checkUseRestrictedNetworksPermission());
     }
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 6c8e6d3..2571fe6 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.net.EthernetManager;
 import android.net.IEthernetServiceListener;
 import android.net.IEthernetNetworkManagementListener;
 import android.net.INetd;
@@ -165,7 +166,10 @@
             Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
         }
         writeIpConfiguration(iface, ipConfiguration);
-        mHandler.post(() -> mFactory.updateInterface(iface, ipConfiguration, null, null));
+        mHandler.post(() -> {
+            mFactory.updateInterface(iface, ipConfiguration, null, null);
+            broadcastInterfaceStateChange(iface);
+        });
     }
 
     private void writeIpConfiguration(@NonNull final String iface,
@@ -174,6 +178,55 @@
         mIpConfigurations.put(iface, ipConfig);
     }
 
+    private IpConfiguration getIpConfigurationForCallback(String iface, int state) {
+        return (state == EthernetManager.STATE_ABSENT) ? null : getOrCreateIpConfiguration(iface);
+    }
+
+    private void ensureRunningOnEthernetServiceThread() {
+        if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on EthernetService thread: "
+                            + Thread.currentThread().getName());
+        }
+    }
+
+    /**
+     * Broadcast the link state or IpConfiguration change of existing Ethernet interfaces to all
+     * listeners.
+     */
+    protected void broadcastInterfaceStateChange(@NonNull String iface) {
+        ensureRunningOnEthernetServiceThread();
+        final int state = mFactory.getInterfaceState(iface);
+        final int role = getInterfaceRole(iface);
+        final IpConfiguration config = getIpConfigurationForCallback(iface, state);
+        final int n = mListeners.beginBroadcast();
+        for (int i = 0; i < n; i++) {
+            try {
+                mListeners.getBroadcastItem(i).onInterfaceStateChanged(iface, state, role, config);
+            } catch (RemoteException e) {
+                // Do nothing here.
+            }
+        }
+        mListeners.finishBroadcast();
+    }
+
+    /**
+     * Unicast the interface state or IpConfiguration change of existing Ethernet interfaces to a
+     * specific listener.
+     */
+    protected void unicastInterfaceStateChange(@NonNull IEthernetServiceListener listener,
+            @NonNull String iface) {
+        ensureRunningOnEthernetServiceThread();
+        final int state = mFactory.getInterfaceState(iface);
+        final int role = getInterfaceRole(iface);
+        final IpConfiguration config = getIpConfigurationForCallback(iface, state);
+        try {
+            listener.onInterfaceStateChanged(iface, state, role, config);
+        } catch (RemoteException e) {
+            // Do nothing here.
+        }
+    }
+
     @VisibleForTesting(visibility = PACKAGE)
     protected void updateConfiguration(@NonNull final String iface,
             @NonNull final StaticIpConfiguration staticIpConfig,
@@ -186,8 +239,10 @@
         final IpConfiguration ipConfig = createIpConfiguration(staticIpConfig);
         writeIpConfiguration(iface, ipConfig);
         mNetworkCapabilities.put(iface, capabilities);
-        mHandler.post(() ->
-                mFactory.updateInterface(iface, ipConfig, capabilities, listener));
+        mHandler.post(() -> {
+            mFactory.updateInterface(iface, ipConfig, capabilities, listener);
+            broadcastInterfaceStateChange(iface);
+        });
     }
 
     @VisibleForTesting(visibility = PACKAGE)
@@ -225,11 +280,19 @@
     }
 
     void addListener(IEthernetServiceListener listener, boolean canUseRestrictedNetworks) {
-        mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks));
+        mHandler.post(() -> {
+            if (!mListeners.register(listener, new ListenerInfo(canUseRestrictedNetworks))) {
+                // Remote process has already died
+                return;
+            }
+            for (String iface : getInterfaces(canUseRestrictedNetworks)) {
+                unicastInterfaceStateChange(listener, iface);
+            }
+        });
     }
 
     void removeListener(IEthernetServiceListener listener) {
-        mListeners.unregister(listener);
+        mHandler.post(() -> mListeners.unregister(listener));
     }
 
     public void setIncludeTestInterfaces(boolean include) {
@@ -295,6 +358,14 @@
         }
     }
 
+    private int getInterfaceRole(final String iface) {
+        if (!mFactory.hasInterface(iface)) return EthernetManager.ROLE_NONE;
+        final int mode = getInterfaceMode(iface);
+        return (mode == INTERFACE_MODE_CLIENT)
+                ? EthernetManager.ROLE_CLIENT
+                : EthernetManager.ROLE_SERVER;
+    }
+
     private int getInterfaceMode(final String iface) {
         if (iface.equals(mDefaultInterface)) {
             return mDefaultInterfaceMode;
@@ -312,6 +383,7 @@
         if (iface.equals(mDefaultInterface)) {
             mDefaultInterface = null;
         }
+        broadcastInterfaceStateChange(iface);
     }
 
     private void addInterface(String iface) {
@@ -346,11 +418,7 @@
 
         final int mode = getInterfaceMode(iface);
         if (mode == INTERFACE_MODE_CLIENT) {
-            IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
-            if (ipConfiguration == null) {
-                ipConfiguration = createDefaultIpConfiguration();
-            }
-
+            IpConfiguration ipConfiguration = getOrCreateIpConfiguration(iface);
             Log.d(TAG, "Tracking interface in client mode: " + iface);
             mFactory.addInterface(iface, hwAddress, ipConfiguration, nc);
         } else {
@@ -376,22 +444,7 @@
                 && mFactory.updateInterfaceLinkState(iface, up, listener);
 
         if (factoryLinkStateUpdated) {
-            boolean restricted = isRestrictedInterface(iface);
-            int n = mListeners.beginBroadcast();
-            for (int i = 0; i < n; i++) {
-                try {
-                    if (restricted) {
-                        ListenerInfo listenerInfo = (ListenerInfo) mListeners.getBroadcastCookie(i);
-                        if (!listenerInfo.canUseRestrictedNetworks) {
-                            continue;
-                        }
-                    }
-                    mListeners.getBroadcastItem(i).onAvailabilityChanged(iface, up);
-                } catch (RemoteException e) {
-                    // Do nothing here.
-                }
-            }
-            mListeners.finishBroadcast();
+            broadcastInterfaceStateChange(iface);
         }
     }
 
@@ -438,6 +491,8 @@
         }
 
         addInterface(iface);
+
+        broadcastInterfaceStateChange(iface);
     }
 
     private void trackAvailableInterfaces() {
@@ -463,11 +518,17 @@
 
         @Override
         public void onInterfaceAdded(String iface) {
+            if (DBG) {
+                Log.i(TAG, "onInterfaceAdded, iface: " + iface);
+            }
             mHandler.post(() -> maybeTrackInterface(iface));
         }
 
         @Override
         public void onInterfaceRemoved(String iface) {
+            if (DBG) {
+                Log.i(TAG, "onInterfaceRemoved, iface: " + iface);
+            }
             mHandler.post(() -> stopTrackingInterface(iface));
         }
     }
@@ -663,8 +724,10 @@
         return ret;
     }
 
-    private static IpConfiguration createDefaultIpConfiguration() {
-        final IpConfiguration ret = new IpConfiguration();
+    private IpConfiguration getOrCreateIpConfiguration(String iface) {
+        IpConfiguration ret = mIpConfigurations.get(iface);
+        if (ret != null) return ret;
+        ret = new IpConfiguration();
         ret.setIpAssignment(IpAssignment.DHCP);
         ret.setProxySettings(ProxySettings.NONE);
         return ret;