Add an API to check availability of Ethernet interface.

Bug: 18045481
Change-Id: I3fb517c3e7fa807e2937fda92026301e312be986
diff --git a/java/com/android/server/ethernet/EthernetNetworkFactory.java b/java/com/android/server/ethernet/EthernetNetworkFactory.java
index 2d0a957..bbb99d8 100644
--- a/java/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/java/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -19,27 +19,25 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpResults;
+import android.net.EthernetManager;
+import android.net.IEthernetServiceListener;
 import android.net.InterfaceConfiguration;
-import android.net.NetworkUtils;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkRequest;
-import android.net.EthernetManager;
+import android.net.NetworkUtils;
 import android.net.StaticIpConfiguration;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
@@ -50,9 +48,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
@@ -96,6 +91,9 @@
     /** Product-dependent regular expression of interface names we track. */
     private static String mIfaceMatch = "";
 
+    /** To notify Ethernet status. */
+    private final RemoteCallbackList<IEthernetServiceListener> mListeners;
+
     /** Data members. All accesses to these must be synchronized(this). */
     private static String mIface = "";
     private String mHwAddr;
@@ -103,10 +101,11 @@
     private NetworkInfo mNetworkInfo;
     private LinkProperties mLinkProperties;
 
-    EthernetNetworkFactory() {
+    EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
         mLinkProperties = new LinkProperties();
         initNetworkCapabilities();
+        mListeners = listeners;
     }
 
     private class LocalNetworkFactory extends NetworkFactory {
@@ -177,9 +176,8 @@
             }
 
             synchronized (this) {
-                if (mIface.isEmpty()) {
-                    mIface = iface;
-                    mHwAddr = config.getHardwareAddress();
+                if (!isTrackingInterface()) {
+                    setInterfaceInfoLocked(iface, config.getHardwareAddress());
                     mNetworkInfo.setIsAvailable(true);
                     mNetworkInfo.setExtraInfo(mHwAddr);
                 } else {
@@ -195,7 +193,7 @@
     private boolean maybeTrackInterface(String iface) {
         // If we don't already have an interface, and if this interface matches
         // our regex, start tracking it.
-        if (!iface.matches(mIfaceMatch) || !mIface.isEmpty())
+        if (!iface.matches(mIfaceMatch) || isTrackingInterface())
             return false;
 
         Log.d(TAG, "Started tracking interface " + iface);
@@ -211,8 +209,7 @@
         // TODO: Unify this codepath with stop().
         synchronized (this) {
             NetworkUtils.stopDhcp(mIface);
-            mIface = "";
-            mHwAddr = null;
+            setInterfaceInfoLocked("", null);
             mNetworkInfo.setExtraInfo(null);
             mLinkUp = false;
             mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
@@ -413,8 +410,7 @@
         updateAgent();
         mLinkProperties = new LinkProperties();
         mNetworkAgent = null;
-        mIface = "";
-        mHwAddr = null;
+        setInterfaceInfoLocked("", null);
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");
         mFactory.unregister();
     }
@@ -429,8 +425,35 @@
         mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100 * 1000);
     }
 
+    public synchronized boolean isTrackingInterface() {
+        return !TextUtils.isEmpty(mIface);
+    }
+
+    /**
+     * Set interface information and notify listeners if availability is changed.
+     * This should be called with the lock held.
+     */
+    private void setInterfaceInfoLocked(String iface, String hwAddr) {
+        boolean oldAvailable = isTrackingInterface();
+        mIface = iface;
+        mHwAddr = hwAddr;
+        boolean available = isTrackingInterface();
+
+        if (oldAvailable != available) {
+            int n = mListeners.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mListeners.getBroadcastItem(i).onAvailabilityChanged(available);
+                } catch (RemoteException e) {
+                    // Do nothing here.
+                }
+            }
+            mListeners.finishBroadcast();
+        }
+    }
+
     synchronized void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
-        if (!TextUtils.isEmpty(mIface)) {
+        if (isTrackingInterface()) {
             pw.println("Tracking interface: " + mIface);
             pw.increaseIndent();
             pw.println("MAC address: " + mHwAddr);
diff --git a/java/com/android/server/ethernet/EthernetServiceImpl.java b/java/com/android/server/ethernet/EthernetServiceImpl.java
index 42d98f6..bb1cc51 100644
--- a/java/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/java/com/android/server/ethernet/EthernetServiceImpl.java
@@ -18,37 +18,25 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import com.android.internal.util.IndentingPrintWriter;
-import android.net.ConnectivityManager;
 import android.net.IEthernetManager;
+import android.net.IEthernetServiceListener;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
-import android.net.LinkAddress;
-import android.net.NetworkAgent;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-import android.net.RouteInfo;
-import android.net.StaticIpConfiguration;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.INetworkManagementService;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-
 /**
  * EthernetServiceImpl handles remote Ethernet operation requests by implementing
  * the IEthernetManager interface.
@@ -60,14 +48,13 @@
 
     private final Context mContext;
     private final EthernetConfigStore mEthernetConfigStore;
-    private final INetworkManagementService mNMService;
     private final AtomicBoolean mStarted = new AtomicBoolean(false);
     private IpConfiguration mIpConfiguration;
-    private ConnectivityManager mCM;
 
     private Handler mHandler;
-    private NetworkInfo mNetworkInfo;
     private final EthernetNetworkFactory mTracker;
+    private final RemoteCallbackList<IEthernetServiceListener> mListeners =
+            new RemoteCallbackList<IEthernetServiceListener>();
 
     public EthernetServiceImpl(Context context) {
         mContext = context;
@@ -77,10 +64,7 @@
 
         Log.i(TAG, "Read stored IP configuration: " + mIpConfiguration);
 
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        mNMService = INetworkManagementService.Stub.asInterface(b);
-
-        mTracker = new EthernetNetworkFactory();
+        mTracker = new EthernetNetworkFactory(mListeners);
     }
 
     private void enforceAccessPermission() {
@@ -103,7 +87,6 @@
 
     public void start() {
         Log.i(TAG, "Starting Ethernet service");
-        mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
 
         HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");
         handlerThread.start();
@@ -118,6 +101,7 @@
      * Get Ethernet configuration
      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
      */
+    @Override
     public IpConfiguration getConfiguration() {
         enforceAccessPermission();
 
@@ -129,6 +113,7 @@
     /**
      * Set Ethernet configuration
      */
+    @Override
     public void setConfiguration(IpConfiguration config) {
         if (!mStarted.get()) {
             Log.w(TAG, "System isn't ready enough to change ethernet configuration");
@@ -150,6 +135,40 @@
         }
     }
 
+    /**
+     * Indicates whether the system currently has one or more
+     * Ethernet interfaces.
+     */
+    @Override
+    public boolean isAvailable() {
+        enforceAccessPermission();
+        return mTracker.isTrackingInterface();
+    }
+
+    /**
+     * Addes 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");
+        }
+        enforceAccessPermission();
+        mListeners.register(listener);
+    }
+
+    /**
+     * Removes a listener.
+     * @param listener A {@link IEthernetServiceListener} to remove.
+     */
+    public void removeListener(IEthernetServiceListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        enforceAccessPermission();
+        mListeners.unregister(listener);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");