Notify IP address changes to interface observers.

1. Add addressUpdated and addressRemoved methods to
   INetworkManagementEventObserver. (The -Updated method is not
   called -Added because it gets called for both adds and
   changes.) Update all its callers in the tree.
2. Make NetworkManagementService parse IP address notifications
   from NetlinkHandler and call the address{Removed,Updated} on
   its observers.

Bug: 10232006
Change-Id: Ieb185dbba052bdbff03caafc0cf5397a7f04dc6d
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 6f4dd5f..b76e4c2 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -53,6 +53,27 @@
      */
     void interfaceRemoved(String iface);
 
+
+    /**
+     * An interface address has been added or updated
+     *
+     * @param address The address.
+     * @param iface The interface.
+     * @param flags The address flags.
+     * @param scope The address scope.
+     */
+    void addressUpdated(String address, String iface, int flags, int scope);
+
+    /**
+     * An interface address has been removed
+     *
+     * @param address The address.
+     * @param iface The interface.
+     * @param flags The address flags.
+     * @param scope The address scope.
+     */
+    void addressRemoved(String address, String iface, int flags, int scope);
+
     /**
      * A networking quota limit has been reached. The quota might not
      * be specific to an interface.
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 8b2aa5d..fa54c5f 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -36,6 +36,16 @@
     }
 
     @Override
+    public void addressUpdated(String address, String iface, int flags, int scope) {
+        // default no-op
+    }
+
+    @Override
+    public void addressRemoved(String address, String iface, int flags, int scope) {
+        // default no-op
+    }
+
+    @Override
     public void interfaceLinkStateChanged(String iface, boolean up) {
         // default no-op
     }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 988b1f2..16cfa666 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -133,6 +133,7 @@
         public static final int InterfaceChange           = 600;
         public static final int BandwidthControl          = 601;
         public static final int InterfaceClassActivity    = 613;
+        public static final int InterfaceAddressChange    = 614;
     }
 
     /**
@@ -393,6 +394,36 @@
         setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
     }
 
+    /**
+     * Notify our observers of a new or updated interface address.
+     */
+    private void notifyAddressUpdated(String address, String iface, int flags, int scope) {
+        final int length = mObservers.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            try {
+                mObservers.getBroadcastItem(i).addressUpdated(address, iface, flags, scope);
+            } catch (RemoteException e) {
+            } catch (RuntimeException e) {
+            }
+        }
+        mObservers.finishBroadcast();
+    }
+
+    /**
+     * Notify our observers of a deleted interface address.
+     */
+    private void notifyAddressRemoved(String address, String iface, int flags, int scope) {
+        final int length = mObservers.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            try {
+                mObservers.getBroadcastItem(i).addressRemoved(address, iface, flags, scope);
+            } catch (RemoteException e) {
+            } catch (RuntimeException e) {
+            }
+        }
+        mObservers.finishBroadcast();
+    }
+
     //
     // Netd Callback handling
     //
@@ -475,6 +506,32 @@
                     notifyInterfaceClassActivity(cooked[3], isActive);
                     return true;
                     // break;
+            case NetdResponseCode.InterfaceAddressChange:
+                    /*
+                     * A network address change occurred
+                     * Format: "NNN Address updated <addr> <iface> <flags> <scope>"
+                     *         "NNN Address removed <addr> <iface> <flags> <scope>"
+                     */
+                    String msg = String.format("Invalid event from daemon (%s)", raw);
+                    if (cooked.length < 6 || !cooked[1].equals("Address")) {
+                        throw new IllegalStateException(msg);
+                    }
+
+                    int flags, scope;
+                    try {
+                        flags = Integer.parseInt(cooked[5]);
+                        scope = Integer.parseInt(cooked[6]);
+                    } catch(NumberFormatException e) {
+                        throw new IllegalStateException(msg);
+                    }
+
+                    if (cooked[2].equals("updated")) {
+                        notifyAddressUpdated(cooked[3], cooked[4], flags, scope);
+                    } else {
+                        notifyAddressRemoved(cooked[3], cooked[4], flags, scope);
+                    }
+                    return true;
+                    // break;
             default: break;
             }
             return false;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 22a7841..fb4c1cf 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -315,6 +315,10 @@
         }
     }
 
+    public void addressUpdated(String address, String iface, int flags, int scope) {}
+
+    public void addressRemoved(String address, String iface, int flags, int scope) {}
+
     public void limitReached(String limitName, String iface) {}
 
     public void interfaceClassDataActivityChanged(String label, boolean active) {}