Hold a wake lock while dispatching network activity events.

Also add new API for determining whether the current data network
is active, and thus better scheduling network operations.  This
API is designed to not be tied to a mobile network -- regardless
of the network, apps can use it to determine whether they should
initiate activity or wait.  On non-mobile networks, it simply always
reports as the network being active.

This changed involved reworking how the idle timers are done so
that we only register an idle timer with the current default
network.  This way, we can know whether we currently expect to
get callbacks about the network being active, or should just always
report that it is active.  (Ultimately we need to be getting this
radio active data from the radio itself.)

Change-Id: Iaf6cc91a960d7542a70b72f87a7db26d12c4ea8e
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e6b9d4c..5b2a29e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -23,9 +23,14 @@
 import android.content.Context;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
+import android.os.IBinder;
+import android.os.INetworkActivityListener;
+import android.os.INetworkManagementService;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
+import android.util.ArrayMap;
 
 import java.net.InetAddress;
 
@@ -76,7 +81,7 @@
 
     /**
      * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
-     * applicable {@link Settings.Secure#CONNECTIVITY_CHANGE_DELAY}.
+     * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
      *
      * @hide
      */
@@ -402,6 +407,8 @@
 
     private final String mPackageName;
 
+    private INetworkManagementService mNMService;
+
     /**
      * Tests if a given integer represents a valid network type.
      * @param networkType the type to be tested
@@ -907,6 +914,92 @@
     }
 
     /**
+     * Callback for use with {@link ConnectivityManager#registerNetworkActiveListener} to
+     * find out when the current network has gone in to a high power state.
+     */
+    public interface OnNetworkActiveListener {
+        /**
+         * Called on the main thread of the process to report that the current data network
+         * has become active, and it is now a good time to perform any pending network
+         * operations.  Note that this listener only tells you when the network becomes
+         * active; if at any other time you want to know whether it is active (and thus okay
+         * to initiate network traffic), you can retrieve its instantaneous state with
+         * {@link ConnectivityManager#isNetworkActive}.
+         */
+        public void onNetworkActive();
+    }
+
+    private INetworkManagementService getNetworkManagementService() {
+        synchronized (this) {
+            if (mNMService != null) {
+                return mNMService;
+            }
+            IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+            mNMService = INetworkManagementService.Stub.asInterface(b);
+            return mNMService;
+        }
+    }
+
+    private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
+            mNetworkActivityListeners
+                    = new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>();
+
+    /**
+     * Start listening to reports when the data network is active, meaning it is
+     * a good time to perform network traffic.  Use {@link #isNetworkActive()}
+     * to determine the current state of the network after registering the listener.
+     *
+     * @param l The listener to be told when the network is active.
+     */
+    public void registerNetworkActiveListener(final OnNetworkActiveListener l) {
+        INetworkActivityListener rl = new INetworkActivityListener.Stub() {
+            @Override
+            public void onNetworkActive() throws RemoteException {
+                l.onNetworkActive();
+            }
+        };
+
+        try {
+            getNetworkManagementService().registerNetworkActivityListener(rl);
+            mNetworkActivityListeners.put(l, rl);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Remove network active listener previously registered with
+     * {@link #registerNetworkActiveListener}.
+     *
+     * @param l Previously registered listener.
+     */
+    public void unregisterNetworkActiveListener(OnNetworkActiveListener l) {
+        INetworkActivityListener rl = mNetworkActivityListeners.get(l);
+        if (rl == null) {
+            throw new IllegalArgumentException("Listener not registered: " + l);
+        }
+        try {
+            getNetworkManagementService().unregisterNetworkActivityListener(rl);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Return whether the data network is currently active.  An active network means that
+     * it is currently in a high power state for performing data transmission.  On some
+     * types of networks, it may be expensive to move and stay in such a state, so it is
+     * more power efficient to batch network traffic together when the radio is already in
+     * this state.  This method tells you whether right now is currently a good time to
+     * initiate network traffic, as the network is already active.
+     */
+    public boolean isNetworkActive() {
+        try {
+            return getNetworkManagementService().isNetworkActive();
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * {@hide}
      */
     public ConnectivityManager(IConnectivityManager service, String packageName) {
@@ -1021,7 +1114,7 @@
 
     /**
      * Check if the device allows for tethering.  It may be disabled via
-     * {@code ro.tether.denied} system property, {@link Settings#TETHER_SUPPORTED} or
+     * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
      * due to device configuration.
      *
      * @return a boolean - {@code true} indicating Tethering is supported.
@@ -1209,7 +1302,7 @@
      * doing something unusual like general internal filtering this may be useful.  On
      * a private network where the proxy is not accessible, you may break HTTP using this.
      *
-     * @param proxyProperties The a {@link ProxyProperites} object defining the new global
+     * @param p The a {@link ProxyProperties} object defining the new global
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      *
      * <p>This method requires the call to hold the permission
@@ -1362,7 +1455,7 @@
     /**
      * Supply the backend messenger for a network tracker
      *
-     * @param type NetworkType to set
+     * @param networkType NetworkType to set
      * @param messenger {@link Messenger}
      * {@hide}
      */
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fc38019..d5c2a0f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1986,7 +1986,9 @@
         mNetTrackers[prevNetType].setTeardownRequested(false);
 
         // Remove idletimer previously setup in {@code handleConnect}
-        removeDataActivityTracking(prevNetType);
+        if (mNetConfigs[prevNetType].isDefault()) {
+            removeDataActivityTracking(prevNetType);
+        }
 
         /*
          * If the disconnected network is not the active one, then don't report
@@ -2318,8 +2320,6 @@
     private void handleConnect(NetworkInfo info) {
         final int newNetType = info.getType();
 
-        setupDataActivityTracking(newNetType);
-
         // snapshot isFailover, because sendConnectedBroadcast() resets it
         boolean isFailover = info.isFailover();
         final NetworkStateTracker thisNet = mNetTrackers[newNetType];
@@ -2357,6 +2357,7 @@
                         return;
                 }
             }
+            setupDataActivityTracking(newNetType);
             synchronized (ConnectivityService.this) {
                 // have a new default network, release the transition wakelock in a second
                 // if it's held.  The second pause is to allow apps to reconnect over the
@@ -2406,7 +2407,7 @@
      * Setup data activity tracking for the given network interface.
      *
      * Every {@code setupDataActivityTracking} should be paired with a
-     * {@link removeDataActivityTracking} for cleanup.
+     * {@link #removeDataActivityTracking} for cleanup.
      */
     private void setupDataActivityTracking(int type) {
         final NetworkStateTracker thisNet = mNetTrackers[type];
@@ -2431,7 +2432,7 @@
 
         if (timeout > 0 && iface != null) {
             try {
-                mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+                mNetd.addIdleTimer(iface, timeout, type);
             } catch (RemoteException e) {
             }
         }
@@ -2944,6 +2945,9 @@
             }
         }
 
+        pw.print("Active default network: "); pw.println(getNetworkTypeName(mActiveDefaultNetwork));
+        pw.println();
+
         pw.println("Network Requester Pids:");
         pw.increaseIndent();
         for (int net : mPriorityList) {