Merge "Add networks and requests to CS.dump"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1ee4ec0..e14f555 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -13,26 +13,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.net;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkActivityListener;
 import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import java.net.InetAddress;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+
+import com.android.internal.util.Protocol;
 
 /**
  * Class that answers queries about the state of network connectivity. It also
@@ -699,7 +708,25 @@
      */
     public LinkProperties getLinkProperties(int networkType) {
         try {
-            return mService.getLinkProperties(networkType);
+            return mService.getLinkPropertiesForType(networkType);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public LinkProperties getLinkProperties(Network network) {
+        try {
+            return mService.getLinkProperties(network);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** {@hide} */
+    public NetworkCapabilities getNetworkCapabilities(Network network) {
+        try {
+            return mService.getNetworkCapabilities(network);
         } catch (RemoteException e) {
             return null;
         }
@@ -1303,6 +1330,22 @@
     }
 
     /**
+     * Report a problem network to the framework.  This will cause the framework
+     * to evaluate the situation and try to fix any problems.  Note that false
+     * may be subsequently ignored.
+     *
+     * @param network The Network the application was attempting to use or null
+     *                to indicate the current default network.
+     * {@hide}
+     */
+    public void reportBadNetwork(Network network) {
+        try {
+            mService.reportBadNetwork(network);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Set a network-independent global http proxy.  This is not normally what you want
      * for typical HTTP proxies - they are general network dependent.  However if you're
      * doing something unusual like general internal filtering this may be useful.  On
@@ -1599,15 +1642,27 @@
         } catch (RemoteException e) { }
     }
 
-    /** Interface for NetworkRequest callbacks {@hide} */
+    /**
+     * Interface for NetworkRequest callbacks.  Used for notifications about network
+     * changes.
+     * @hide
+     */
     public static class NetworkCallbacks {
+        /** @hide */
         public static final int PRECHECK     = 1;
+        /** @hide */
         public static final int AVAILABLE    = 2;
+        /** @hide */
         public static final int LOSING       = 3;
+        /** @hide */
         public static final int LOST         = 4;
+        /** @hide */
         public static final int UNAVAIL      = 5;
+        /** @hide */
         public static final int CAP_CHANGED  = 6;
+        /** @hide */
         public static final int PROP_CHANGED = 7;
+        /** @hide */
         public static final int CANCELED     = 8;
 
         /**
@@ -1650,21 +1705,361 @@
          * Called when the network the framework connected to for this request
          * changes capabilities but still satisfies the stated need.
          */
-        public void onCapabilitiesChanged(NetworkRequest networkRequest, Network network,
+        public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network,
                 NetworkCapabilities networkCapabilities) {}
 
         /**
          * Called when the network the framework connected to for this request
-         * changes properties.
+         * changes LinkProperties.
          */
-        public void onPropertiesChanged(NetworkRequest networkRequest, Network network,
+        public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network,
                 LinkProperties linkProperties) {}
 
         /**
-         * Called when a CancelRequest call concludes and the registered callbacks will
+         * Called when a releaseNetworkRequest call concludes and the registered callbacks will
          * no longer be used.
          */
-        public void onCanceled(NetworkRequest networkRequest) {}
+        public void onReleased(NetworkRequest networkRequest) {}
     }
 
+    private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
+    /** @hide obj = pair(NetworkRequest, Network) */
+    public static final int CALLBACK_PRECHECK           = BASE + 1;
+    /** @hide obj = pair(NetworkRequest, Network) */
+    public static final int CALLBACK_AVAILABLE          = BASE + 2;
+    /** @hide obj = pair(NetworkRequest, Network), arg1 = ttl */
+    public static final int CALLBACK_LOSING             = BASE + 3;
+    /** @hide obj = pair(NetworkRequest, Network) */
+    public static final int CALLBACK_LOST               = BASE + 4;
+    /** @hide obj = NetworkRequest */
+    public static final int CALLBACK_UNAVAIL            = BASE + 5;
+    /** @hide obj = pair(NetworkRequest, Network) */
+    public static final int CALLBACK_CAP_CHANGED        = BASE + 6;
+    /** @hide obj = pair(NetworkRequest, Network) */
+    public static final int CALLBACK_IP_CHANGED         = BASE + 7;
+    /** @hide obj = NetworkRequest */
+    public static final int CALLBACK_RELEASED           = BASE + 8;
+    /** @hide */
+    public static final int CALLBACK_EXIT               = BASE + 9;
+
+    private static class CallbackHandler extends Handler {
+        private final HashMap<NetworkRequest, NetworkCallbacks>mCallbackMap;
+        private final AtomicInteger mRefCount;
+        private static final String TAG = "ConnectivityManager.CallbackHandler";
+        private final ConnectivityManager mCm;
+
+        CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbacks>callbackMap,
+                AtomicInteger refCount, ConnectivityManager cm) {
+            super(looper);
+            mCallbackMap = callbackMap;
+            mRefCount = refCount;
+            mCm = cm;
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            Log.d(TAG, "CM callback handler got msg " + message.what);
+            switch (message.what) {
+                case CALLBACK_PRECHECK: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        callbacks.onPreCheck(request, getNetwork(message));
+                    } else {
+                        Log.e(TAG, "callback not found for PRECHECK message");
+                    }
+                    break;
+                }
+                case CALLBACK_AVAILABLE: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        callbacks.onAvailable(request, getNetwork(message));
+                    } else {
+                        Log.e(TAG, "callback not found for AVAILABLE message");
+                    }
+                    break;
+                }
+                case CALLBACK_LOSING: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        callbacks.onLosing(request, getNetwork(message), message.arg1);
+                    } else {
+                        Log.e(TAG, "callback not found for LOSING message");
+                    }
+                    break;
+                }
+                case CALLBACK_LOST: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        callbacks.onLost(request, getNetwork(message));
+                    } else {
+                        Log.e(TAG, "callback not found for LOST message");
+                    }
+                    break;
+                }
+                case CALLBACK_UNAVAIL: {
+                    NetworkRequest req = (NetworkRequest)message.obj;
+                    NetworkCallbacks callbacks = null;
+                    synchronized(mCallbackMap) {
+                        callbacks = mCallbackMap.get(req);
+                    }
+                    if (callbacks != null) {
+                        callbacks.onUnavailable(req);
+                    } else {
+                        Log.e(TAG, "callback not found for UNAVAIL message");
+                    }
+                    break;
+                }
+                case CALLBACK_CAP_CHANGED: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        Network network = getNetwork(message);
+                        NetworkCapabilities cap = mCm.getNetworkCapabilities(network);
+
+                        callbacks.onNetworkCapabilitiesChanged(request, network, cap);
+                    } else {
+                        Log.e(TAG, "callback not found for CHANGED message");
+                    }
+                    break;
+                }
+                case CALLBACK_IP_CHANGED: {
+                    NetworkRequest request = getNetworkRequest(message);
+                    NetworkCallbacks callbacks = getCallbacks(request);
+                    if (callbacks != null) {
+                        Network network = getNetwork(message);
+                        LinkProperties lp = mCm.getLinkProperties(network);
+
+                        callbacks.onLinkPropertiesChanged(request, network, lp);
+                    } else {
+                        Log.e(TAG, "callback not found for CHANGED message");
+                    }
+                    break;
+                }
+                case CALLBACK_RELEASED: {
+                    NetworkRequest req = (NetworkRequest)message.obj;
+                    NetworkCallbacks callbacks = null;
+                    synchronized(mCallbackMap) {
+                        callbacks = mCallbackMap.remove(req);
+                    }
+                    if (callbacks != null) {
+                        callbacks.onReleased(req);
+                    } else {
+                        Log.e(TAG, "callback not found for CANCELED message");
+                    }
+                    synchronized(mRefCount) {
+                        if (mRefCount.decrementAndGet() == 0) {
+                            getLooper().quit();
+                        }
+                    }
+                    break;
+                }
+                case CALLBACK_EXIT: {
+                    Log.d(TAG, "Listener quiting");
+                    getLooper().quit();
+                    break;
+                }
+            }
+        }
+
+        private NetworkRequest getNetworkRequest(Message msg) {
+            return (NetworkRequest)(msg.obj);
+        }
+        private NetworkCallbacks getCallbacks(NetworkRequest req) {
+            synchronized(mCallbackMap) {
+                return mCallbackMap.get(req);
+            }
+        }
+        private Network getNetwork(Message msg) {
+            return new Network(msg.arg2);
+        }
+        private NetworkCallbacks removeCallbacks(Message msg) {
+            NetworkRequest req = (NetworkRequest)msg.obj;
+            synchronized(mCallbackMap) {
+                return mCallbackMap.remove(req);
+            }
+        }
+    }
+
+    private void addCallbackListener() {
+        synchronized(sCallbackRefCount) {
+            if (sCallbackRefCount.incrementAndGet() == 1) {
+                // TODO - switch this over to a ManagerThread or expire it when done
+                HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
+                callbackThread.start();
+                sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
+                        sNetworkCallbacks, sCallbackRefCount, this);
+            }
+        }
+    }
+
+    private void removeCallbackListener() {
+        synchronized(sCallbackRefCount) {
+            if (sCallbackRefCount.decrementAndGet() == 0) {
+                sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
+                sCallbackHandler = null;
+            }
+        }
+    }
+
+    static final HashMap<NetworkRequest, NetworkCallbacks> sNetworkCallbacks =
+            new HashMap<NetworkRequest, NetworkCallbacks>();
+    static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
+    static CallbackHandler sCallbackHandler = null;
+
+    private final static int LISTEN  = 1;
+    private final static int REQUEST = 2;
+
+    private NetworkRequest somethingForNetwork(NetworkCapabilities need,
+            NetworkCallbacks networkCallbacks, int timeoutSec, int action) {
+        NetworkRequest networkRequest = null;
+        if (networkCallbacks == null) throw new IllegalArgumentException("null NetworkCallbacks");
+        if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
+        try {
+            addCallbackListener();
+            if (action == LISTEN) {
+                networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler),
+                        new Binder());
+            } else {
+                networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler),
+                        timeoutSec, new Binder());
+            }
+            if (networkRequest != null) {
+                synchronized(sNetworkCallbacks) {
+                    sNetworkCallbacks.put(networkRequest, networkCallbacks);
+                }
+            }
+        } catch (RemoteException e) {}
+        if (networkRequest == null) removeCallbackListener();
+        return networkRequest;
+    }
+
+    /**
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
+     *
+     * This {@link NetworkRequest} will live until released via
+     * {@link releaseNetworkRequest} or the calling application exits.
+     * Status of the request can be follwed by listening to the various
+     * callbacks described in {@link NetworkCallbacks}.  The {@link Network}
+     * can be used by using the {@link bindSocketToNetwork},
+     * {@link bindApplicationToNetwork} and {@link getAddrInfoOnNetwork} functions.
+     *
+     * @param need {@link NetworkCapabilities} required by this request.
+     * @param networkCallbacks The callbacks to be utilized for this request.  Note
+     *                         the callbacks can be shared by multiple requests and
+     *                         the NetworkRequest token utilized to determine to which
+     *                         request the callback relates.
+     * @return A {@link NetworkRequest} object identifying the request.
+     * @hide
+     */
+    public NetworkRequest requestNetwork(NetworkCapabilities need,
+            NetworkCallbacks networkCallbacks) {
+        return somethingForNetwork(need, networkCallbacks, 0, REQUEST);
+    }
+
+    /**
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
+     * by a timeout.
+     *
+     * This function behaves identically, but if a suitable network is not found
+     * within the given time (in Seconds) the {@link NetworkCallbacks#unavailable}
+     * callback is called.  The request must still be released normally by
+     * calling {@link releaseNetworkRequest}.
+     * @param need {@link NetworkCapabilities} required by this request.
+     * @param networkCallbacks The callbacks to be utilized for this request.  Note
+     *                         the callbacks can be shared by multiple requests and
+     *                         the NetworkRequest token utilized to determine to which
+     *                         request the callback relates.
+     * @param timeoutSec The time in seconds to attempt looking for a suitable network
+     *                   before {@link NetworkCallbacks#unavailable} is called.
+     * @return A {@link NetworkRequest} object identifying the request.
+     * @hide
+     */
+    public NetworkRequest requestNetwork(NetworkCapabilities need,
+            NetworkCallbacks networkCallbacks, int timeoutSec) {
+        return somethingForNetwork(need, networkCallbacks, timeoutSec, REQUEST);
+    }
+
+    /**
+     * The maximum number of seconds the framework will look for a suitable network
+     * during a timeout-equiped call to {@link requestNetwork}.
+     * {@hide}
+     */
+    public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60;
+
+    /**
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
+     *
+     * This function behavies identically, but instead of {@link NetworkCallbacks}
+     * a {@link PendingIntent} is used.  This means the request may outlive the
+     * calling application and get called back when a suitable network is found.
+     * <p>
+     * The operation is an Intent broadcast that goes to a broadcast receiver that
+     * you registered with {@link Context#registerReceiver} or through the
+     * &lt;receiver&gt; tag in an AndroidManifest.xml file
+     * <p>
+     * The operation Intent is delivered with two extras, a {@link Network} typed
+     * extra called {@link EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities}
+     * typed extra called {@link EXTRA_NETWORK_REQUEST_NETWORK_CAPABILTIES} containing
+     * the original requests parameters.  It is important to create a new,
+     * {@link NetworkCallbacks} based request before completing the processing of the
+     * Intent to reserve the network or it will be released shortly after the Intent
+     * is processed.
+     * <p>
+     * If there is already an request for this Intent registered (with the equality of
+     * two Intents defined by {@link Intent#filterEquals}), then it will be removed and
+     * replace by this one, effectively releasing the previous {@link NetworkRequest}.
+     * <p>
+     * The request may be released normally by calling {@link releaseNetworkRequest}.
+     *
+     * @param need {@link NetworkCapabilties} required by this request.
+     * @param operation Action to perform when the network is available (corresponds
+     *                  to the {@link NetworkCallbacks#onAvailable} call.  Typically
+     *                  comes from {@link PendingIntent#getBroadcast}.
+     * @return A {@link NetworkRequest} object identifying the request.
+     * @hide
+     */
+    public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) {
+        try {
+            return mService.pendingRequestForNetwork(need, operation);
+        } catch (RemoteException e) {}
+        return null;
+    }
+
+    /**
+     * Registers to receive notifications about all networks which satisfy the given
+     * {@link NetworkCapabilities}.  The callbacks will continue to be called until
+     * either the application exits or the request is released using
+     * {@link releaseNetworkRequest}.
+     *
+     * @param need {@link NetworkCapabilities} required by this request.
+     * @param networkCallbacks The {@link NetworkCallbacks} to be called as suitable
+     *                         networks change state.
+     * @return A {@link NetworkRequest} object identifying the request.
+     * @hide
+     */
+    public NetworkRequest listenForNetwork(NetworkCapabilities need,
+            NetworkCallbacks networkCallbacks) {
+        return somethingForNetwork(need, networkCallbacks, 0, LISTEN);
+    }
+
+    /**
+     * Releases a {NetworkRequest} generated either through a {@link requestNetwork}
+     * or a {@link listenForNetwork} call.  The {@link NetworkCallbacks} given in the
+     * earlier call may continue receiving calls until the {@link NetworkCallbacks#onReleased}
+     * function is called, signifiying the end of the request.
+     *
+     * @param networkRequest The {@link NetworkRequest} generated by an earlier call to
+     *                       {@link requestNetwork} or {@link listenForNetwork}.
+     * @hide
+     */
+    public void releaseNetworkRequest(NetworkRequest networkRequest) {
+        if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest");
+        try {
+            mService.releaseNetworkRequest(networkRequest);
+        } catch (RemoteException e) {}
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0d2e14d..885b8b6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -16,11 +16,14 @@
 
 package android.net;
 
+import android.app.PendingIntent;
 import android.net.LinkQualityInfo;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.ProxyInfo;
 import android.os.IBinder;
@@ -52,7 +55,10 @@
     boolean isNetworkSupported(int networkType);
 
     LinkProperties getActiveLinkProperties();
-    LinkProperties getLinkProperties(int networkType);
+    LinkProperties getLinkPropertiesForType(int networkType);
+    LinkProperties getLinkProperties(in Network network);
+
+    NetworkCapabilities getNetworkCapabilities(in Network network);
 
     NetworkState[] getAllNetworkState();
 
@@ -100,6 +106,8 @@
 
     void reportInetCondition(int networkType, int percentage);
 
+    void reportBadNetwork(in Network network);
+
     ProxyInfo getGlobalProxy();
 
     void setGlobalProxy(in ProxyInfo p);
@@ -147,5 +155,20 @@
 
     void registerNetworkFactory(in Messenger messenger);
 
-    void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, in NetworkCapabilities nc, int score);
+    void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+            in NetworkCapabilities nc, int score);
+
+    NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
+            in Messenger messenger, int timeoutSec, in IBinder binder);
+
+    NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
+            in PendingIntent operation);
+
+    NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
+            in Messenger messenger, in IBinder binder);
+
+    void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
+            in PendingIntent operation);
+
+    void releaseNetworkRequest(in NetworkRequest networkRequest);
 }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index c7591f4..4b84941 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -58,5 +58,6 @@
     public static final int BASE_CONNECTIVITY_SERVICE                               = 0x00080000;
     public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
     public static final int BASE_NETWORK_MONITOR                                    = 0x00082000;
+    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00083000;
     //TODO: define all used protocols
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1857aa9..0708e55 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -384,7 +384,7 @@
     private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
 
     /**
-     * user internally to indicate that data sampling interval is up
+     * used internally to indicate that data sampling interval is up
      */
     private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
 
@@ -405,6 +405,32 @@
      */
     private static final int EVENT_REGISTER_NETWORK_AGENT = 18;
 
+    /**
+     * used to add a network request
+     * includes a NetworkRequestInfo
+     */
+    private static final int EVENT_REGISTER_NETWORK_REQUEST = 19;
+
+    /**
+     * indicates a timeout period is over - check if we had a network yet or not
+     * and if not, call the timeout calback (but leave the request live until they
+     * cancel it.
+     * includes a NetworkRequestInfo
+     */
+    private static final int EVENT_TIMEOUT_NETWORK_REQUEST = 20;
+
+    /**
+     * used to add a network listener - no request
+     * includes a NetworkRequestInfo
+     */
+    private static final int EVENT_REGISTER_NETWORK_LISTENER = 21;
+
+    /**
+     * used to remove a network request, either a listener or a real request
+     * includes a NetworkRequest
+     */
+    private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
+
     /** Handler used for internal events. */
     final private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -498,7 +524,9 @@
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         mDefaultRequest = new NetworkRequest(netCap, true);
-        mNetworkRequests.append(mDefaultRequest.requestId, mDefaultRequest);
+        NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
+                NetworkRequestInfo.REQUEST);
+        mNetworkRequests.put(mDefaultRequest, nri);
 
         HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
         handlerThread.start();
@@ -1058,20 +1086,36 @@
      */
     @Override
     public LinkProperties getActiveLinkProperties() {
-        return getLinkProperties(mActiveDefaultNetwork);
+        return getLinkPropertiesForType(mActiveDefaultNetwork);
     }
 
     @Override
-    public LinkProperties getLinkProperties(int networkType) {
+    public LinkProperties getLinkPropertiesForType(int networkType) {
         enforceAccessPermission();
         if (isNetworkTypeValid(networkType)) {
-            return getLinkPropertiesForType(networkType);
+            return getLinkPropertiesForTypeInternal(networkType);
         }
         return null;
     }
 
     // TODO - this should be ALL networks
     @Override
+    public LinkProperties getLinkProperties(Network network) {
+        enforceAccessPermission();
+        NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
+        if (nai != null) return new LinkProperties(nai.linkProperties);
+        return null;
+    }
+
+    @Override
+    public NetworkCapabilities getNetworkCapabilities(Network network) {
+        enforceAccessPermission();
+        NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
+        if (nai != null) return new NetworkCapabilities(nai.networkCapabilities);
+        return null;
+    }
+
+    @Override
     public NetworkState[] getAllNetworkState() {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
@@ -1081,7 +1125,7 @@
                     networkType++) {
                 if (getNetworkInfoForType(networkType) != null) {
                     final NetworkInfo info = getFilteredNetworkInfo(networkType, uid);
-                    final LinkProperties lp = getLinkPropertiesForType(networkType);
+                    final LinkProperties lp = getLinkPropertiesForTypeInternal(networkType);
                     final NetworkCapabilities netcap = getNetworkCapabilitiesForType(networkType);
                     result.add(new NetworkState(info, lp, netcap));
                 }
@@ -1095,7 +1139,7 @@
             NetworkInfo info = getNetworkInfoForType(networkType);
             if (info != null) {
                 return new NetworkState(info,
-                        getLinkPropertiesForType(networkType),
+                        getLinkPropertiesForTypeInternal(networkType),
                         getNetworkCapabilitiesForType(networkType));
             }
         }
@@ -2997,7 +3041,7 @@
                          * to the link that may have incorrectly setup by the lower
                          * levels.
                          */
-                        LinkProperties lp = getLinkProperties(info.getType());
+                        LinkProperties lp = getLinkPropertiesForTypeInternal(info.getType());
                         if (DBG) {
                             log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
                         }
@@ -3045,11 +3089,10 @@
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                 if (VDBG) log("NetworkFactory connected");
                 // A network factory has connected.  Send it all current NetworkRequests.
-                for (int i = 0; i < mNetworkRequests.size(); i++) {
-                    NetworkRequest request = mNetworkRequests.valueAt(i);
-                    NetworkAgentInfo nai = mNetworkForRequestId.get(request.requestId);
+                for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+                    NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
                     ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
-                            (nai != null ? nai.currentScore : 0), 0, request);
+                            (nai != null ? nai.currentScore : 0), 0, nri.request);
                 }
             } else {
                 loge("Error connecting NetworkFactory");
@@ -3067,13 +3110,18 @@
                 try {
                     mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
                 } catch (NullPointerException e) {}
+                if (nai != null) {
+                    mNetworkForNetId.remove(nai.network.netId);
+                }
             }
         }
     }
     private void handleAsyncChannelDisconnected(Message msg) {
         NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
         if (nai != null) {
-            if (DBG) log(nai.name() + " got DISCONNECTED");
+            if (DBG) {
+                log(nai.name() + " got DISCONNECTED, was satisfying " + nai.networkRequests.size());
+            }
             // A network agent has disconnected.
             // Tell netd to clean up the configuration for this network
             // (routing rules, DNS, etc).
@@ -3082,7 +3130,7 @@
             } catch (Exception e) {
                 loge("Exception removing network: " + e);
             }
-            notifyNetworkCallbacks(nai, NetworkCallbacks.LOST);
+            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
             updateClat(null, nai.linkProperties, nai);
@@ -3090,12 +3138,17 @@
                 mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
             } catch (NullPointerException e) {}
 
+            mNetworkForNetId.remove(nai.network.netId);
             // Since we've lost the network, go through all the requests that
             // it was satisfying and see if any other factory can satisfy them.
             final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>();
             for (int i = 0; i < nai.networkRequests.size(); i++) {
                 NetworkRequest request = nai.networkRequests.valueAt(i);
                 NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+                if (VDBG) {
+                    log(" checking request " + request + ", currentNetwork = " +
+                            currentNetwork != null ? currentNetwork.name() : "null");
+                }
                 if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
                     mNetworkForRequestId.remove(request.requestId);
                     sendUpdatedScoreToFactories(request, 0);
@@ -3125,6 +3178,77 @@
         }
     }
 
+    private void handleRegisterNetworkRequest(Message msg) {
+        final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
+        final NetworkCapabilities newCap = nri.request.networkCapabilities;
+        int score = 0;
+
+        // Check for the best currently alive network that satisfies this request
+        NetworkAgentInfo bestNetwork = null;
+        for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
+            if (VDBG) log("handleRegisterNetworkRequest checking " + network.name());
+            if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
+                if (VDBG) log("apparently satisfied.  currentScore=" + network.currentScore);
+                if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
+                    bestNetwork = network;
+                }
+            }
+        }
+        if (bestNetwork != null) {
+            if (VDBG) log("using " + bestNetwork.name());
+            bestNetwork.networkRequests.put(nri.request.requestId, nri.request);
+            notifyNetworkCallback(bestNetwork, nri);
+            score = bestNetwork.currentScore;
+        }
+        mNetworkRequests.put(nri.request, nri);
+        if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
+            if (DBG) log("sending new NetworkRequest to factories");
+            for (AsyncChannel ac : mNetworkFactories) {
+                ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
+            }
+        }
+    }
+
+    private void handleReleaseNetworkRequest(NetworkRequest request) {
+        if (DBG) log("releasing NetworkRequest " + request);
+        NetworkRequestInfo nri = mNetworkRequests.remove(request);
+        if (nri != null) {
+            // tell the network currently servicing this that it's no longer interested
+            NetworkAgentInfo affectedNetwork = mNetworkForRequestId.get(nri.request.requestId);
+            if (affectedNetwork != null) {
+                affectedNetwork.networkRequests.remove(nri.request.requestId);
+                if (VDBG) {
+                    log(" Removing from current network " + affectedNetwork.name() + ", leaving " +
+                            affectedNetwork.networkRequests.size() + " requests.");
+                }
+            }
+
+            if (nri.isRequest) {
+                for (AsyncChannel factory : mNetworkFactories) {
+                    factory.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
+                }
+
+                if (affectedNetwork != null) {
+                    // check if this network still has live requests - otherwise, tear down
+                    // TODO - probably push this to the NF/NA
+                    boolean keep = false;
+                    for (int i = 0; i < affectedNetwork.networkRequests.size(); i++) {
+                        NetworkRequest r = affectedNetwork.networkRequests.valueAt(i);
+                        if (mNetworkRequests.get(r).isRequest) {
+                            keep = true;
+                            break;
+                        }
+                    }
+                    if (keep == false) {
+                        if (DBG) log("no live requests for " + affectedNetwork.name() +
+                                "; disconnecting");
+                        affectedNetwork.asyncChannel.disconnect();
+                    }
+                }
+            }
+            callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
+        }
+    }
 
     private class InternalHandler extends Handler {
         public InternalHandler(Looper looper) {
@@ -3227,6 +3351,15 @@
                     handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
                     break;
                 }
+                case EVENT_REGISTER_NETWORK_REQUEST:
+                case EVENT_REGISTER_NETWORK_LISTENER: {
+                    handleRegisterNetworkRequest(msg);
+                    break;
+                }
+                case EVENT_RELEASE_NETWORK_REQUEST: {
+                    handleReleaseNetworkRequest((NetworkRequest) msg.obj);
+                    break;
+                }
             }
         }
     }
@@ -3373,6 +3506,10 @@
             EVENT_INET_CONDITION_CHANGE, networkType, percentage));
     }
 
+    public void reportBadNetwork(Network network) {
+        //TODO
+    }
+
     private void handleInetConditionChange(int netType, int condition) {
         if (mActiveDefaultNetwork == -1) {
             if (DBG) log("handleInetConditionChange: no active default network - ignore");
@@ -4443,7 +4580,7 @@
                         log("isMobileOk: addresses=" + inetAddressesToString(addresses));
 
                         // Get the type of addresses supported by this link
-                        LinkProperties lp = mCs.getLinkProperties(
+                        LinkProperties lp = mCs.getLinkPropertiesForTypeInternal(
                                 ConnectivityManager.TYPE_MOBILE_HIPRI);
                         boolean linkHasIpv4 = lp.hasIPv4Address();
                         boolean linkHasIpv6 = lp.hasIPv6Address();
@@ -5074,7 +5211,109 @@
     }
 
     private final ArrayList<AsyncChannel> mNetworkFactories = new ArrayList<AsyncChannel>();
+    private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
+            new HashMap<NetworkRequest, NetworkRequestInfo>();
 
+
+    private class NetworkRequestInfo implements IBinder.DeathRecipient {
+        static final boolean REQUEST = true;
+        static final boolean LISTEN = false;
+
+        final NetworkRequest request;
+        IBinder mBinder;
+        final int mPid;
+        final int mUid;
+        final Messenger messenger;
+        final boolean isRequest;
+
+        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
+            super();
+            messenger = m;
+            request = r;
+            mBinder = binder;
+            mPid = getCallingPid();
+            mUid = getCallingUid();
+            this.isRequest = isRequest;
+
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        void unlinkDeathRecipient() {
+            mBinder.unlinkToDeath(this, 0);
+        }
+
+        public void binderDied() {
+            log("ConnectivityService NetworkRequestInfo binderDied(" +
+                    request + ", " + mBinder + ")");
+            releaseNetworkRequest(request);
+        }
+    }
+
+    @Override
+    public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
+            Messenger messenger, int timeoutSec, IBinder binder) {
+        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                == false) {
+            enforceConnectivityInternalPermission();
+        } else {
+            enforceChangePermission();
+        }
+
+        if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) {
+            throw new IllegalArgumentException("Bad timeout specified");
+        }
+        NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
+                networkCapabilities));
+        if (DBG) log("requestNetwork for " + networkRequest);
+        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
+                NetworkRequestInfo.REQUEST);
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
+        if (timeoutSec > 0) {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
+                    nri), timeoutSec * 1000);
+        }
+        return networkRequest;
+    }
+
+    @Override
+    public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
+            PendingIntent operation) {
+        // TODO
+        return null;
+    }
+
+    @Override
+    public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
+            Messenger messenger, IBinder binder) {
+        enforceAccessPermission();
+
+        NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
+                networkCapabilities));
+        if (DBG) log("listenForNetwork for " + networkRequest);
+        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
+                NetworkRequestInfo.LISTEN);
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
+        return networkRequest;
+    }
+
+    @Override
+    public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
+            PendingIntent operation) {
+    }
+
+    @Override
+    public void releaseNetworkRequest(NetworkRequest networkRequest) {
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST,
+                networkRequest));
+    }
+
+    @Override
     public void registerNetworkFactory(Messenger messenger) {
         enforceConnectivityInternalPermission();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger));
@@ -5085,11 +5324,16 @@
         AsyncChannel ac = new AsyncChannel();
         mNetworkFactories.add(ac);
         ac.connect(mContext, mTrackerHandler, messenger);
+        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (nri.isRequest) {
+                int score = 0;
+                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
+                if (currentNetwork != null) score = currentNetwork.currentScore;
+                ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
+            }
+        }
     }
 
-    // NetworkRequest by requestId
-    private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<NetworkRequest>();
-
     /**
      * NetworkAgentInfo supporting a request by requestId.
      * These have already been vetted (their Capabilities satisfy the request)
@@ -5099,6 +5343,9 @@
     private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
             new SparseArray<NetworkAgentInfo>();
 
+    private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
+            new SparseArray<NetworkAgentInfo>();
+
     // NetworkAgentInfo keyed off its connecting messenger
     // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
     private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
@@ -5126,6 +5373,7 @@
         } catch (NullPointerException e) {
             loge("registered NetworkAgent for unsupported type: " + na);
         }
+        mNetworkForNetId.put(na.network.netId, na);
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
@@ -5261,9 +5509,47 @@
         }
     }
 
-    private void callCallbackForRequest(NetworkRequest networkRequest,
+    private void callCallbackForRequest(NetworkRequestInfo nri,
             NetworkAgentInfo networkAgent, int notificationType) {
-        // TODO
+        if (nri.messenger == null) return;  // Default request has no msgr
+        Object o;
+        int a1 = 0;
+        int a2 = 0;
+        switch (notificationType) {
+            case ConnectivityManager.CALLBACK_LOSING:
+                a1 = 30; // TODO - read this from NetworkMonitor
+                // fall through
+            case ConnectivityManager.CALLBACK_PRECHECK:
+            case ConnectivityManager.CALLBACK_AVAILABLE:
+            case ConnectivityManager.CALLBACK_LOST:
+            case ConnectivityManager.CALLBACK_CAP_CHANGED:
+            case ConnectivityManager.CALLBACK_IP_CHANGED: {
+                o = new NetworkRequest(nri.request);
+                a2 = networkAgent.network.netId;
+                break;
+            }
+            case ConnectivityManager.CALLBACK_UNAVAIL:
+            case ConnectivityManager.CALLBACK_RELEASED: {
+                o = new NetworkRequest(nri.request);
+                break;
+            }
+            default: {
+                loge("Unknown notificationType " + notificationType);
+                return;
+            }
+        }
+        Message msg = Message.obtain();
+        msg.arg1 = a1;
+        msg.arg2 = a2;
+        msg.obj = o;
+        msg.what = notificationType;
+        try {
+            if (VDBG) log("sending notification " + notificationType + " for " + nri.request);
+            nri.messenger.send(msg);
+        } catch (RemoteException e) {
+            // may occur naturally in the race of binder death.
+            loge("RemoteException caught trying to send a callback msg for " + nri.request);
+        }
     }
 
     private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
@@ -5290,13 +5576,15 @@
         if (DBG) log("handleConnectionValidated for "+newNetwork.name());
         // check if any NetworkRequest wants this NetworkAgent
         // first check if it satisfies the NetworkCapabilities
-        for (int i = 0; i < mNetworkRequests.size(); i++) {
-            NetworkRequest nr = mNetworkRequests.valueAt(i);
-            if (nr.networkCapabilities.satisfiedByNetworkCapabilities(
+        ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
+        if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
+        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (VDBG) log("  checking if request is satisfied: " + nri.request);
+            if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
                     newNetwork.networkCapabilities)) {
                 // next check if it's better than any current network we're using for
                 // this request
-                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nr.requestId);
+                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
                 if (VDBG) {
                     log("currentScore = " +
                             (currentNetwork != null ? currentNetwork.currentScore : 0) +
@@ -5305,28 +5593,52 @@
                 if (currentNetwork == null ||
                         currentNetwork.currentScore < newNetwork.currentScore) {
                     if (currentNetwork != null) {
-                        currentNetwork.networkRequests.remove(nr.requestId);
-                        currentNetwork.networkListens.add(nr);
-                        if (currentNetwork.networkRequests.size() == 0) {
-                            currentNetwork.networkMonitor.sendMessage(
-                                    NetworkMonitor.CMD_NETWORK_LINGER);
-                            notifyNetworkCallbacks(currentNetwork, NetworkCallbacks.LOSING);
-                        }
+                        if (VDBG) log("   accepting network in place of " + currentNetwork.name());
+                        currentNetwork.networkRequests.remove(nri.request.requestId);
+                        currentNetwork.networkLingered.add(nri.request);
+                        affectedNetworks.add(currentNetwork);
+                    } else {
+                        if (VDBG) log("   accepting network in place of null");
                     }
-                    mNetworkForRequestId.put(nr.requestId, newNetwork);
-                    newNetwork.networkRequests.put(nr.requestId, nr);
+                    mNetworkForRequestId.put(nri.request.requestId, newNetwork);
+                    newNetwork.networkRequests.put(nri.request.requestId, nri.request);
                     keep = true;
                     // TODO - this could get expensive if we have alot of requests for this
                     // network.  Think about if there is a way to reduce this.  Push
                     // netid->request mapping to each factory?
-                    sendUpdatedScoreToFactories(nr, newNetwork.currentScore);
-                    if (mDefaultRequest.requestId == nr.requestId) {
+                    sendUpdatedScoreToFactories(nri.request, newNetwork.currentScore);
+                    if (mDefaultRequest.requestId == nri.request.requestId) {
                         isNewDefault = true;
                         updateActiveDefaultNetwork(newNetwork);
                     }
                 }
             }
         }
+        for (NetworkAgentInfo nai : affectedNetworks) {
+            boolean teardown = true;
+            for (int i = 0; i < nai.networkRequests.size(); i++) {
+                NetworkRequest nr = nai.networkRequests.valueAt(i);
+                try {
+                if (mNetworkRequests.get(nr).isRequest) {
+                    teardown = false;
+                }
+                } catch (Exception e) {
+                    loge("Request " + nr + " not found in mNetworkRequests.");
+                    loge("  it came from request list  of " + nai.name());
+                }
+            }
+            if (teardown) {
+                nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
+                notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
+            } else {
+                // not going to linger, so kill the list of linger networks..  only
+                // notify them of linger if it happens as the result of gaining another,
+                // but if they transition and old network stays up, don't tell them of linger
+                // or very delayed loss
+                nai.networkLingered.clear();
+                if (VDBG) log("Lingered for " + nai.name() + " cleared");
+            }
+        }
         if (keep) {
             if (isNewDefault) {
                 if (VDBG) log("Switching to new default network: " + newNetwork);
@@ -5365,8 +5677,14 @@
                 // TODO
                 //BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType);
 //            } catch (RemoteException e) { }
-            notifyNetworkCallbacks(newNetwork, NetworkCallbacks.AVAILABLE);
-        } else if (newNetwork.networkRequests.size() == 0) {
+            notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
+        } else {
+            if (DBG && newNetwork.networkRequests.size() != 0) {
+                loge("tearing down network with live requests:");
+                for (int i=0; i < newNetwork.networkRequests.size(); i++) {
+                    loge("  " + newNetwork.networkRequests.valueAt(i));
+                }
+            }
             if (VDBG) log("Validated network turns out to be unwanted.  Tear it down.");
             newNetwork.asyncChannel.disconnect();
         }
@@ -5396,7 +5714,7 @@
                 loge("Error creating Network " + networkAgent.network.netId);
             }
             updateLinkProperties(networkAgent, null);
-            notifyNetworkCallbacks(networkAgent, NetworkCallbacks.PRECHECK);
+            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
         } else if (state == NetworkInfo.State.DISCONNECTED ||
                 state == NetworkInfo.State.SUSPENDED) {
@@ -5404,24 +5722,37 @@
         }
     }
 
+    // notify only this one new request of the current state
+    protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
+        int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
+        // TODO - read state from monitor to decide what to send.
+//        if (nai.networkMonitor.isLingering()) {
+//            notifyType = NetworkCallbacks.LOSING;
+//        } else if (nai.networkMonitor.isEvaluating()) {
+//            notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
+//        }
+        if (nri.request.needsBroadcasts) {
+        // TODO
+//            sendNetworkBroadcast(nai, notifyType);
+        }
+        callCallbackForRequest(nri, nai, notifyType);
+    }
+
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
         if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
         boolean needsBroadcasts = false;
         for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
-            NetworkRequest request = networkAgent.networkRequests.valueAt(i);
-            if (request == null) continue;
-            if (request.needsBroadcasts) needsBroadcasts = true;
-            callCallbackForRequest(request, networkAgent, notifyType);
-        }
-        for (NetworkRequest request : networkAgent.networkListens) {
-            if (request.needsBroadcasts) needsBroadcasts = true;
-            callCallbackForRequest(request, networkAgent, notifyType);
+            NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
+            NetworkRequestInfo nri = mNetworkRequests.get(nr);
+            if (VDBG) log(" sending notification for " + nr);
+            if (nr.needsBroadcasts) needsBroadcasts = true;
+            callCallbackForRequest(nri, networkAgent, notifyType);
         }
         if (needsBroadcasts) {
-            if (notifyType == NetworkCallbacks.AVAILABLE) {
+            if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) {
                 sendConnectedBroadcastDelayed(networkAgent.networkInfo,
                         getConnectivityChangeDelay());
-            } else if (notifyType == NetworkCallbacks.LOST) {
+            } else if (notifyType == ConnectivityManager.CALLBACK_LOST) {
                 NetworkInfo info = new NetworkInfo(networkAgent.networkInfo);
                 Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
                 intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
@@ -5460,7 +5791,7 @@
         }
     }
 
-    private LinkProperties getLinkPropertiesForType(int networkType) {
+    private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
         ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
         if (list == null) return null;
         try {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 0c568b7..8102591 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -48,9 +48,8 @@
 
     // The list of NetworkRequests being satisfied by this Network.
     public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
+    public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
 
-    // The list of NetworkListens listening for changes on this Network.
-    public final ArrayList<NetworkRequest> networkListens = new ArrayList<NetworkRequest>();
     public final Messenger messenger;
     public final AsyncChannel asyncChannel;
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index abe362a..92b5f52 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1325,7 +1325,7 @@
                 } else {
                     LinkProperties linkProperties = null;
                     try {
-                        linkProperties = mConnService.getLinkProperties(upType);
+                        linkProperties = mConnService.getLinkPropertiesForType(upType);
                     } catch (RemoteException e) { }
                     if (linkProperties != null) {
                         // Find the interface with the default IPv4 route. It may be the