Network data activity change intent for network interfaces.

The activity notification is received from netd, an intent
DATA_ACTIVITY_CHANGE is then raised for other part of the system to
consume.

Change-Id: Idfcc4763c51c5b314c57f546c12557082f06bebf
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
index 9a25d2e..c316733 100644
--- a/services/java/com/android/server/CommonTimeManagementService.java
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -120,6 +120,8 @@
             reevaluateServiceState();
         }
         public void limitReached(String limitName, String iface) { }
+
+        public void interfaceClassDataActivityChanged(String label, boolean active) {}
     };
 
     private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 230f07b..9f93901 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
@@ -35,6 +36,7 @@
 import android.net.DummyDataStateTracker;
 import android.net.EthernetDataTracker;
 import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
@@ -546,6 +548,13 @@
         mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
         mSettingsObserver.observe(mContext);
 
+        INetworkManagementEventObserver netdObserver = new NetdObserver();
+        try {
+            mNetd.registerObserver(netdObserver);
+        } catch (RemoteException e) {
+            loge("Error registering observer :" + e);
+        }
+
         loadGlobalProxy();
     }
 private NetworkStateTracker makeWimaxStateTracker() {
@@ -923,6 +932,19 @@
         return tracker != null && tracker.setRadio(turnOn);
     }
 
+    private class NetdObserver extends INetworkManagementEventObserver.Stub {
+        public void interfaceClassDataActivityChanged(String label, boolean active) {
+            int deviceType = Integer.parseInt(label);
+            sendDataActivityBroadcast(deviceType, active);
+        }
+
+        public void interfaceStatusChanged(String iface, boolean up) {}
+        public void interfaceLinkStateChanged(String iface, boolean up) {}
+        public void interfaceAdded(String iface) {}
+        public void interfaceRemoved(String iface) {}
+        public void limitReached(String limitName, String iface) {}
+    }
+
     /**
      * Used to notice when the calling process dies so we can self-expire
      *
@@ -1759,6 +1781,13 @@
         sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
     }
 
+    private void sendDataActivityBroadcast(int deviceType, boolean active) {
+        Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
+        intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
+        intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+        mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE);
+    }
+
     /**
      * Called when an attempt to fail over to another network has failed.
      * @param info the {@link NetworkInfo} for the failed network
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 67bb680..c3f3a5d 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -280,6 +280,20 @@
     }
 
     /**
+     * Notify our observers of a change in the data activity state of the interface
+     */
+    private void notifyInterfaceClassActivity(String label, boolean active) {
+        final int length = mObservers.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            try {
+                mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
+            } catch (RemoteException e) {
+            }
+        }
+        mObservers.finishBroadcast();
+    }
+
+    /**
      * Prepare native daemon once connected, enabling modules and pushing any
      * existing in-memory rules.
      */
@@ -405,6 +419,19 @@
                     throw new IllegalStateException(
                             String.format("Invalid event from daemon (%s)", raw));
                     // break;
+            case NetdResponseCode.InterfaceClassActivity:
+                    /*
+                     * An network interface class state changed (active/idle)
+                     * Format: "NNN IfaceClass <active/idle> <label>"
+                     */
+                    if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
+                        throw new IllegalStateException(
+                                String.format("Invalid event from daemon (%s)", raw));
+                    }
+                    boolean isActive = cooked[2].equals("active");
+                    notifyInterfaceClassActivity(cooked[3], isActive);
+                    return true;
+                    // break;
             default: break;
             }
             return false;
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index f35a5af..98e6dc0 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -195,6 +195,7 @@
 
         public void interfaceRemoved(String iface) {}
         public void limitReached(String limitName, String iface) {}
+        public void interfaceClassDataActivityChanged(String label, boolean active) {}
     }
 
 
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 5aa3a29..e4f1f7a 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -319,6 +319,8 @@
 
     public void limitReached(String limitName, String iface) {}
 
+    public void interfaceClassDataActivityChanged(String label, boolean active) {}
+
     public int tether(String iface) {
         if (DBG) Log.d(TAG, "Tethering " + iface);
         TetherInterfaceSM sm = null;
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 4b82037..b12d597 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -291,6 +291,9 @@
     public void limitReached(String limit, String interfaze) {
     }
 
+    public void interfaceClassDataActivityChanged(String label, boolean active) {
+    }
+
     private void enforceControlPermission() {
         // System user is allowed to control VPN.
         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
diff --git a/services/java/com/android/server/net/NetworkAlertObserver.java b/services/java/com/android/server/net/NetworkAlertObserver.java
index 0d1c3b2..9e181c5 100644
--- a/services/java/com/android/server/net/NetworkAlertObserver.java
+++ b/services/java/com/android/server/net/NetworkAlertObserver.java
@@ -41,4 +41,7 @@
     public void interfaceAdded(String iface) {
         // ignored; interface changes come through ConnectivityService
     }
+    public void interfaceClassDataActivityChanged(String label, boolean active) {
+        // ignored; interface changes come through ConnectivityService
+    }
 }