Enabling internal msg apis

NetworkFactory and NetworkAgent.  First trying with wifi and
getting rid of WifiStateTracker.

Conflicts:
	api/current.txt
	services/core/java/com/android/server/ConnectivityService.java

Change-Id: I7f0ec13d7d8988b32f3c6dc71f72012f3349fe02
diff --git a/cmds/svc/src/com/android/commands/svc/DataCommand.java b/cmds/svc/src/com/android/commands/svc/DataCommand.java
index 72cb86d..406e33b 100644
--- a/cmds/svc/src/com/android/commands/svc/DataCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/DataCommand.java
@@ -36,9 +36,7 @@
         return shortHelp() + "\n"
                 + "\n"
                 + "usage: svc data [enable|disable]\n"
-                + "         Turn mobile data on or off.\n\n"
-                + "       svc data prefer\n"
-                + "          Set mobile as the preferred data network\n";
+                + "         Turn mobile data on or off.\n\n";
     }
 
     public void run(String[] args) {
@@ -51,15 +49,6 @@
             } else if ("disable".equals(args[1])) {
                 flag = false;
                 validCommand = true;
-            } else if ("prefer".equals(args[1])) {
-                IConnectivityManager connMgr =
-                        IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-                try {
-                    connMgr.setNetworkPreference(ConnectivityManager.TYPE_MOBILE);
-                } catch (RemoteException e) {
-                    System.err.println("Failed to set preferred network: " + e);
-                }
-                return;
             }
             if (validCommand) {
                 ITelephony phoneMgr
@@ -78,4 +67,4 @@
         }
         System.err.println(longHelp());
     }
-}
\ No newline at end of file
+}
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
index d29e8b2..39f0e35 100644
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
@@ -36,9 +36,7 @@
         return shortHelp() + "\n"
                 + "\n"
                 + "usage: svc wifi [enable|disable]\n"
-                + "         Turn Wi-Fi on or off.\n\n"
-                + "       svc wifi prefer\n"
-                + "          Set Wi-Fi as the preferred data network\n";
+                + "         Turn Wi-Fi on or off.\n\n";
     }
 
     public void run(String[] args) {
@@ -51,15 +49,6 @@
             } else if ("disable".equals(args[1])) {
                 flag = false;
                 validCommand = true;
-            } else if ("prefer".equals(args[1])) {
-                IConnectivityManager connMgr =
-                        IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-                try {
-                    connMgr.setNetworkPreference(ConnectivityManager.TYPE_WIFI);
-                } catch (RemoteException e) {
-                    System.err.println("Failed to set preferred network: " + e);
-                }
-                return;
             }
             if (validCommand) {
                 IWifiManager wifiMgr
@@ -75,4 +64,4 @@
         }
         System.err.println(longHelp());
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2406cba..1ee4ec0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -534,38 +534,32 @@
     /**
      * Specifies the preferred network type.  When the device has more
      * than one type available the preferred network type will be used.
-     * Note that this made sense when we only had 2 network types,
-     * but with more and more default networks we need an array to list
-     * their ordering.  This will be deprecated soon.
      *
      * @param preference the network type to prefer over all others.  It is
      *         unspecified what happens to the old preferred network in the
      *         overall ordering.
+     * @Deprecated Functionality has been removed as it no longer makes sense,
+     *         with many more than two networks - we'd need an array to express
+     *         preference.  Instead we use dynamic network properties of
+     *         the networks to describe their precedence.
      */
     public void setNetworkPreference(int preference) {
-        try {
-            mService.setNetworkPreference(preference);
-        } catch (RemoteException e) {
-        }
     }
 
     /**
      * Retrieves the current preferred network type.
-     * Note that this made sense when we only had 2 network types,
-     * but with more and more default networks we need an array to list
-     * their ordering.  This will be deprecated soon.
      *
      * @return an integer representing the preferred network type
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     * @Deprecated Functionality has been removed as it no longer makes sense,
+     *         with many more than two networks - we'd need an array to express
+     *         preference.  Instead we use dynamic network properties of
+     *         the networks to describe their precedence.
      */
     public int getNetworkPreference() {
-        try {
-            return mService.getNetworkPreference();
-        } catch (RemoteException e) {
-            return -1;
-        }
+        return -1;
     }
 
     /**
@@ -723,13 +717,14 @@
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
-    public boolean setRadios(boolean turnOn) {
-        try {
-            return mService.setRadios(turnOn);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
+// TODO - check for any callers and remove
+//    public boolean setRadios(boolean turnOn) {
+//        try {
+//            return mService.setRadios(turnOn);
+//        } catch (RemoteException e) {
+//            return false;
+//        }
+//    }
 
     /**
      * Tells a given networkType to set its radio power state as directed.
@@ -743,13 +738,14 @@
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
-    public boolean setRadio(int networkType, boolean turnOn) {
-        try {
-            return mService.setRadio(networkType, turnOn);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
+// TODO - check for any callers and remove
+//    public boolean setRadio(int networkType, boolean turnOn) {
+//        try {
+//            return mService.setRadio(networkType, turnOn);
+//        } catch (RemoteException e) {
+//            return false;
+//        }
+//    }
 
     /**
      * Tells the underlying networking system that the caller wants to
@@ -1594,4 +1590,81 @@
             mService.registerNetworkFactory(messenger);
         } catch (RemoteException e) { }
     }
+
+    /** {@hide} */
+    public void registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+            NetworkCapabilities nc, int score) {
+        try {
+            mService.registerNetworkAgent(messenger, ni, lp, nc, score);
+        } catch (RemoteException e) { }
+    }
+
+    /** Interface for NetworkRequest callbacks {@hide} */
+    public static class NetworkCallbacks {
+        public static final int PRECHECK     = 1;
+        public static final int AVAILABLE    = 2;
+        public static final int LOSING       = 3;
+        public static final int LOST         = 4;
+        public static final int UNAVAIL      = 5;
+        public static final int CAP_CHANGED  = 6;
+        public static final int PROP_CHANGED = 7;
+        public static final int CANCELED     = 8;
+
+        /**
+         * @hide
+         * Called whenever the framework connects to a network that it may use to
+         * satisfy this request
+         */
+        public void onPreCheck(NetworkRequest networkRequest, Network network) {}
+
+        /**
+         * Called when the framework connects and has validated the new network.
+         */
+        public void onAvailable(NetworkRequest networkRequest, Network network) {}
+
+        /**
+         * Called when the framework is losing the network.  Often paired with an
+         * onAvailable call with the new replacement network for graceful handover.
+         * This may not be called if we have a hard loss (loss without warning).
+         * This may be followed by either an onLost call or an onAvailable call for this
+         * network depending on if we lose or regain it.
+         */
+        public void onLosing(NetworkRequest networkRequest, Network network, int maxSecToLive) {}
+
+        /**
+         * Called when the framework has a hard loss of the network or when the
+         * graceful failure ends.  Note applications should only request this callback
+         * if the application is willing to track the Available and Lost callbacks
+         * together, else the application may think it has no network when it
+         * really does (A Avail, B Avail, A Lost..  still have B).
+         */
+        public void onLost(NetworkRequest networkRequest, Network network) {}
+
+        /**
+         * Called if no network is found in the given timeout time.  If no timeout is given,
+         * this will not be called.
+         */
+        public void onUnavailable(NetworkRequest networkRequest) {}
+
+        /**
+         * 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,
+                NetworkCapabilities networkCapabilities) {}
+
+        /**
+         * Called when the network the framework connected to for this request
+         * changes properties.
+         */
+        public void onPropertiesChanged(NetworkRequest networkRequest, Network network,
+                LinkProperties linkProperties) {}
+
+        /**
+         * Called when a CancelRequest call concludes and the registered callbacks will
+         * no longer be used.
+         */
+        public void onCanceled(NetworkRequest networkRequest) {}
+    }
+
 }
diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java
index 34ba645..c670166 100644
--- a/core/java/android/net/ConnectivityServiceProtocol.java
+++ b/core/java/android/net/ConnectivityServiceProtocol.java
@@ -23,15 +23,41 @@
  * @hide
  */
 public class ConnectivityServiceProtocol {
-    private ConnectivityServiceProtocol() {}
 
     private static final int BASE = BASE_CONNECTIVITY_SERVICE;
 
+    private ConnectivityServiceProtocol() {}
+
+    /**
+     * This is a contract between ConnectivityService and various bearers.
+     * A NetworkFactory is an abstract entity that creates NetworkAgent objects.
+     * The bearers register with ConnectivityService using
+     * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger
+     * to be used to deliver the following Messages.
+     */
     public static class NetworkFactoryProtocol {
         private NetworkFactoryProtocol() {}
         /**
-         * Pass a network request to the transport
+         * Pass a network request to the bearer.  If the bearer believes it can
+         * satisfy the request it should connect to the network and create a
+         * NetworkAgent.  Once the NetworkAgent is fully functional it will
+         * register itself with ConnectivityService using registerNetworkAgent.
+         * If the bearer cannot immediately satisfy the request (no network,
+         * user disabled the radio, lower-scored network) it should remember
+         * any NetworkRequests it may be able to satisfy in the future.  It may
+         * disregard any that it will never be able to service, for example
+         * those requiring a different bearer.
          * msg.obj = NetworkRequest
+         * msg.arg1 = score - the score of the any network currently satisfying this
+         *            request.  If this bearer knows in advance it cannot
+         *            exceed this score it should not try to connect, holding the request
+         *            for the future.
+         *            Note that subsequent events may give a different (lower
+         *            or higher) score for this request, transmitted to each
+         *            NetworkFactory through additional CMD_REQUEST_NETWORK msgs
+         *            with the same NetworkRequest but an updated score.
+         *            Also, network conditions may change for this bearer
+         *            allowing for a better score in the future.
          */
         public static final int CMD_REQUEST_NETWORK = BASE;
 
@@ -42,7 +68,42 @@
         public static final int CMD_CANCEL_REQUEST = BASE + 1;
     }
 
-    public static class NetworkAgentProtocol {
-        private NetworkAgentProtocol() {}
+    /**
+     * TODO - move to NetworkMonitor and document
+     */
+    public static class NetworkMonitorProtocol {
+        private NetworkMonitorProtocol() {}
+        /**
+         * Inform NetworkMonitor that their network is connected.
+         * Initiates Network Validation.
+         */
+        public static final int CMD_NETWORK_CONNECTED = BASE + 200;
+
+        /**
+         * Inform ConnectivityService that the network is validated.
+         * obj = NetworkAgent
+         */
+        public static final int EVENT_NETWORK_VALIDATED = BASE + 201;
+
+        /**
+         * Inform NetworkMonitor to linger a network.  The Monitor should
+         * start a timer and/or start watching for zero live connections while
+         * moving towards LINGER_COMPLETE.  After the Linger period expires
+         * (or other events mark the end of the linger state) the LINGER_COMPLETE
+         * event should be sent to ConnectivityService and ConnectivityService
+         * will shut down the network, telling the corresponding NetworkAgent
+         * to disconnect.  If a CMD_NETWORK_CONNECTED happens before the LINGER completes
+         * it indicates further desire to keep the network alive and so
+         * the LINGER is aborted.
+         * TODO - figure out who manages/does this simple state machine
+         */
+        public static final int CMD_NETWORK_LINGER = BASE + 202;
+
+        /**
+         * Inform ConnectivityService that the network LINGER period has
+         * expired.
+         * obj = NetworkAgent
+         */
+        public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 203;
     }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index b69797e..0d2e14d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
 
 import android.net.LinkQualityInfo;
 import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
@@ -41,10 +42,6 @@
     // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
     void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
 
-    void setNetworkPreference(int pref);
-
-    int getNetworkPreference();
-
     NetworkInfo getActiveNetworkInfo();
     NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
@@ -62,10 +59,6 @@
     NetworkQuotaInfo getActiveNetworkQuotaInfo();
     boolean isActiveNetworkMetered();
 
-    boolean setRadios(boolean onOff);
-
-    boolean setRadio(int networkType, boolean turnOn);
-
     int startUsingNetworkFeature(int networkType, in String feature,
             in IBinder binder);
 
@@ -147,9 +140,12 @@
 
     LinkQualityInfo[] getAllLinkQualityInfo();
 
-    void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
+    void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo,
+            in String url);
 
     void setAirplaneMode(boolean enable);
 
     void registerNetworkFactory(in Messenger messenger);
+
+    void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, in NetworkCapabilities nc, int score);
 }
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
new file mode 100644
index 0000000..4b85398
--- /dev/null
+++ b/core/java/android/net/NetworkAgent.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A Utility class for handling NetworkRequests.
+ *
+ * Created by bearer-specific code to handle tracking requests, scores,
+ * network data and handle communicating with ConnectivityService.  Two
+ * abstract methods: connect and disconnect are used to act on the
+ * underlying bearer code.  Connect is called when we have a NetworkRequest
+ * and our score is better than the current handling network's score, while
+ * disconnect is used when ConnectivityService requests a disconnect.
+ *
+ * A bearer may have more than one NetworkAgent if it can simultaneously
+ * support separate networks (IMS / Internet / MMS Apns on cellular, or
+ * perhaps connections with different SSID or P2P for Wi-Fi).  The bearer
+ * code should pass its NetworkAgents the NetworkRequests each NetworkAgent
+ * can handle, demultiplexing for different network types.  The bearer code
+ * can also filter out requests it can never handle.
+ *
+ * Each NetworkAgent needs to be given a score and NetworkCapabilities for
+ * their potential network.  While disconnected, the NetworkAgent will check
+ * each time its score changes or a NetworkRequest changes to see if
+ * the NetworkAgent can provide a higher scored network for a NetworkRequest
+ * that the NetworkAgent's NetworkCapabilties can satisfy.  This condition will
+ * trigger a connect request via connect().  After connection, connection data
+ * should be given to the NetworkAgent by the bearer, including LinkProperties
+ * NetworkCapabilties and NetworkInfo.  After that the NetworkAgent will register
+ * with ConnectivityService and forward the data on.
+ * @hide
+ */
+public abstract class NetworkAgent extends Handler {
+    private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>();
+    private boolean mConnectionRequested = false;
+
+    private AsyncChannel mAsyncChannel;
+    private final String LOG_TAG;
+    private static final boolean DBG = true;
+    // TODO - this class shouldn't cache data or it runs the risk of getting out of sync
+    // Make the API require each of these when any is updated so we have the data we need,
+    // without caching.
+    private LinkProperties mLinkProperties;
+    private NetworkInfo mNetworkInfo;
+    private NetworkCapabilities mNetworkCapabilities;
+    private int mNetworkScore;
+    private boolean mRegistered = false;
+    private final Context mContext;
+    private AtomicBoolean mHasRequests = new AtomicBoolean(false);
+
+    // TODO - add a name member for logging purposes.
+
+    protected final Object mLockObj = new Object();
+
+
+    private static final int BASE = Protocol.BASE_NETWORK_AGENT;
+
+    /**
+     * Sent by self to queue up a new/modified request.
+     * obj = NetworkRequestAndScore
+     */
+    private static final int CMD_ADD_REQUEST = BASE + 1;
+
+    /**
+     * Sent by self to queue up the removal of a request.
+     * obj = NetworkRequest
+     */
+    private static final int CMD_REMOVE_REQUEST = BASE + 2;
+
+    /**
+     * Sent by ConnectivityService to the NetworkAgent to inform it of
+     * suspected connectivity problems on its network.  The NetworkAgent
+     * should take steps to verify and correct connectivity.
+     */
+    public static final int CMD_SUSPECT_BAD = BASE + 3;
+
+    /**
+     * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
+     * ConnectivityService to pass the current NetworkInfo (connection state).
+     * Sent when the NetworkInfo changes, mainly due to change of state.
+     * obj = NetworkInfo
+     */
+    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4;
+
+    /**
+     * Sent by the NetworkAgent to ConnectivityService to pass the current
+     * NetworkCapabilties.
+     * obj = NetworkCapabilities
+     */
+    public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5;
+
+    /**
+     * Sent by the NetworkAgent to ConnectivityService to pass the current
+     * NetworkProperties.
+     * obj = NetworkProperties
+     */
+    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6;
+
+    /**
+     * Sent by the NetworkAgent to ConnectivityService to pass the current
+     * network score.
+     * arg1 = network score int
+     */
+    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7;
+
+    public NetworkAgent(Looper looper, Context context, String logTag) {
+        super(looper);
+        LOG_TAG = logTag;
+        mContext = context;
+    }
+
+    /**
+     * When conditions are right, register with ConnectivityService.
+     * Connditions include having a well defined network and a request
+     * that justifies it.  The NetworkAgent will remain registered until
+     * disconnected.
+     * TODO - this should have all data passed in rather than caching
+     */
+    private void registerSelf() {
+        synchronized(mLockObj) {
+            if (!mRegistered && mConnectionRequested &&
+                    mNetworkInfo != null && mNetworkInfo.isConnected() &&
+                    mNetworkCapabilities != null &&
+                    mLinkProperties != null &&
+                    mNetworkScore != 0) {
+                if (DBG) log("Registering NetworkAgent");
+                mRegistered = true;
+                ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+                        Context.CONNECTIVITY_SERVICE);
+                cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo),
+                        new LinkProperties(mLinkProperties),
+                        new NetworkCapabilities(mNetworkCapabilities), mNetworkScore);
+            } else if (DBG && !mRegistered) {
+                String err = "Not registering due to ";
+                if (mConnectionRequested == false) err += "no Connect requested ";
+                if (mNetworkInfo == null) err += "null NetworkInfo ";
+                if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) {
+                    err += "NetworkInfo disconnected ";
+                }
+                if (mLinkProperties == null) err += "null LinkProperties ";
+                if (mNetworkCapabilities == null) err += "null NetworkCapabilities ";
+                if (mNetworkScore == 0) err += "null NetworkScore";
+                log(err);
+            }
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                synchronized (mLockObj) {
+                    if (mAsyncChannel != null) {
+                        log("Received new connection while already connected!");
+                    } else {
+                        if (DBG) log("NetworkAgent fully connected");
+                        mAsyncChannel = new AsyncChannel();
+                        mAsyncChannel.connected(null, this, msg.replyTo);
+                        mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                                AsyncChannel.STATUS_SUCCESSFUL);
+                    }
+                }
+                break;
+            }
+            case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                if (DBG) log("CMD_CHANNEL_DISCONNECT");
+                if (mAsyncChannel != null) mAsyncChannel.disconnect();
+                break;
+            }
+            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                if (DBG) log("NetworkAgent channel lost");
+                disconnect();
+                clear();
+                break;
+            }
+            case CMD_SUSPECT_BAD: {
+                log("Unhandled Message " + msg);
+                break;
+            }
+            case CMD_ADD_REQUEST: {
+                handleAddRequest(msg);
+                break;
+            }
+            case CMD_REMOVE_REQUEST: {
+                handleRemoveRequest(msg);
+                break;
+            }
+        }
+    }
+
+    private void clear() {
+        synchronized(mLockObj) {
+            mNetworkRequests.clear();
+            mHasRequests.set(false);
+            mConnectionRequested = false;
+            mAsyncChannel = null;
+            mRegistered = false;
+        }
+    }
+
+    private static class NetworkRequestAndScore {
+        NetworkRequest req;
+        int score;
+
+        NetworkRequestAndScore(NetworkRequest networkRequest, int score) {
+            req = networkRequest;
+            this.score = score;
+        }
+    }
+
+    private void handleAddRequest(Message msg) {
+        NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj;
+        // replaces old request, updating score
+        mNetworkRequests.put(n.req.requestId, n);
+        mHasRequests.set(true);
+        evalScores();
+    }
+
+    private void handleRemoveRequest(Message msg) {
+        NetworkRequest networkRequest = (NetworkRequest)msg.obj;
+
+        if (mNetworkRequests.get(networkRequest.requestId) != null) {
+            mNetworkRequests.remove(networkRequest.requestId);
+            if (mNetworkRequests.size() == 0) mHasRequests.set(false);
+            evalScores();
+        }
+    }
+
+    /**
+     * called to go through our list of requests and see if we're
+     * good enough to try connecting.
+     *
+     * Only does connects - we disconnect when requested via
+     * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection
+     * between modules (bearer or ConnectivityService dies) or more commonly
+     * when the NetworkInfo reports to ConnectivityService it is disconnected.
+     */
+    private void evalScores() {
+        if (mConnectionRequested) {
+            // already trying
+            return;
+        }
+        for (int i=0; i < mNetworkRequests.size(); i++) {
+            int score = mNetworkRequests.valueAt(i).score;
+            if (score < mNetworkScore) {
+                // have a request that has a lower scored network servicing it
+                // (or no network) than we could provide, so lets connect!
+                mConnectionRequested = true;
+                connect();
+                return;
+            }
+        }
+    }
+
+    public void addNetworkRequest(NetworkRequest networkRequest, int score) {
+        if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score);
+        sendMessage(obtainMessage(CMD_ADD_REQUEST,
+                new NetworkRequestAndScore(networkRequest, score)));
+    }
+
+    public void removeNetworkRequest(NetworkRequest networkRequest) {
+        if (DBG) log("removing NetworkRequest " + networkRequest);
+        sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest));
+    }
+
+    /**
+     * Called by the bearer code when it has new LinkProperties data.
+     * If we're a registered NetworkAgent, this new data will get forwarded on,
+     * otherwise we store a copy in anticipation of registering.  This call
+     * may also prompt registration if it causes the NetworkAgent to meet
+     * the conditions (fully configured, connected, satisfys a request and
+     * has sufficient score).
+     */
+    public void sendLinkProperties(LinkProperties linkProperties) {
+        linkProperties = new LinkProperties(linkProperties);
+        synchronized(mLockObj) {
+            mLinkProperties = linkProperties;
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties);
+            } else {
+                registerSelf();
+            }
+        }
+    }
+
+    /**
+     * Called by the bearer code when it has new NetworkInfo data.
+     * If we're a registered NetworkAgent, this new data will get forwarded on,
+     * otherwise we store a copy in anticipation of registering.  This call
+     * may also prompt registration if it causes the NetworkAgent to meet
+     * the conditions (fully configured, connected, satisfys a request and
+     * has sufficient score).
+     */
+    public void sendNetworkInfo(NetworkInfo networkInfo) {
+        networkInfo = new NetworkInfo(networkInfo);
+        synchronized(mLockObj) {
+            mNetworkInfo = networkInfo;
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo);
+            } else {
+                registerSelf();
+            }
+        }
+    }
+
+    /**
+     * Called by the bearer code when it has new NetworkCapabilities data.
+     * If we're a registered NetworkAgent, this new data will get forwarded on,
+     * otherwise we store a copy in anticipation of registering.  This call
+     * may also prompt registration if it causes the NetworkAgent to meet
+     * the conditions (fully configured, connected, satisfys a request and
+     * has sufficient score).
+     * Note that if these capabilities make the network non-useful,
+     * ConnectivityServce will tear this network down.
+     */
+    public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+        networkCapabilities = new NetworkCapabilities(networkCapabilities);
+        synchronized(mLockObj) {
+            mNetworkCapabilities = networkCapabilities;
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities);
+            } else {
+                registerSelf();
+            }
+        }
+    }
+
+    public NetworkCapabilities getNetworkCapabilities() {
+        synchronized(mLockObj) {
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+    }
+
+    /**
+     * Called by the bearer code when it has a new score for this network.
+     * If we're a registered NetworkAgent, this new data will get forwarded on,
+     * otherwise we store a copy.
+     */
+    public synchronized void sendNetworkScore(int score) {
+        synchronized(mLockObj) {
+            mNetworkScore = score;
+            evalScores();
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore);
+            } else {
+                registerSelf();
+            }
+        }
+    }
+
+    public boolean hasRequests() {
+        return mHasRequests.get();
+    }
+
+    public boolean isConnectionRequested() {
+        synchronized(mLockObj) {
+            return mConnectionRequested;
+        }
+    }
+
+
+    abstract protected void connect();
+    abstract protected void disconnect();
+
+    protected void log(String s) {
+        Log.d(LOG_TAG, "NetworkAgent: " + s);
+    }
+}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 7e3a06d..b3ae3f5 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -25,23 +25,55 @@
  * @hide
  */
 public class NetworkRequest implements Parcelable {
+    /**
+     * The NetworkCapabilities that define this request
+     */
     public final NetworkCapabilities networkCapabilities;
+
+    /**
+     * Identifies the request.  NetworkRequests should only be constructed by
+     * the Framework and given out to applications as tokens to be used to identify
+     * the request.
+     * TODO - make sure this input is checked whenever a NR is passed in a public API
+     */
     public final int requestId;
-    public final boolean legacy;
-    private static final AtomicInteger sRequestId = new AtomicInteger();
 
+    /**
+     * Set for legacy requests and the default.
+     * Causes CONNECTIVITY_ACTION broadcasts to be sent.
+     * @hide
+     */
+    public final boolean needsBroadcasts;
+
+    private static final AtomicInteger sNextRequestId = new AtomicInteger(1);
+
+    /**
+     * @hide
+     */
     public NetworkRequest(NetworkCapabilities nc) {
-        this(nc, false, sRequestId.incrementAndGet());
+        this(nc, false, sNextRequestId.getAndIncrement());
     }
 
-    public NetworkRequest(NetworkCapabilities nc, boolean legacy) {
-        this(nc, legacy, sRequestId.incrementAndGet());
+    /**
+     * @hide
+     */
+    public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts) {
+        this(nc, needsBroadcasts, sNextRequestId.getAndIncrement());
     }
 
-    private NetworkRequest(NetworkCapabilities nc, boolean legacy, int rId) {
+    /**
+     * @hide
+     */
+    private NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) {
         requestId = rId;
         networkCapabilities = nc;
-        this.legacy = legacy;
+        this.needsBroadcasts = needsBroadcasts;
+    }
+
+    public NetworkRequest(NetworkRequest that) {
+        networkCapabilities = new NetworkCapabilities(that.networkCapabilities);
+        requestId = that.requestId;
+        needsBroadcasts = that.needsBroadcasts;
     }
 
     // implement the Parcelable interface
@@ -50,16 +82,17 @@
     }
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(networkCapabilities, flags);
-        dest.writeInt(legacy ? 1 : 0);
+        dest.writeInt(needsBroadcasts ? 1 : 0);
         dest.writeInt(requestId);
     }
     public static final Creator<NetworkRequest> CREATOR =
         new Creator<NetworkRequest>() {
             public NetworkRequest createFromParcel(Parcel in) {
                 NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
-                boolean legacy = (in.readInt() == 1);
+                boolean needsBroadcasts = (in.readInt() == 1);
                 int requestId = in.readInt();
-                return new NetworkRequest(nc, legacy, requestId);
+                NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId);
+                return result;
             }
             public NetworkRequest[] newArray(int size) {
                 return new NetworkRequest[size];
@@ -67,14 +100,14 @@
         };
 
     public String toString() {
-        return "NetworkRequest [ id=" + requestId + ", legacy=" + legacy + ", " +
-                networkCapabilities.toString() + " ]";
+        return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts +
+                ", " + networkCapabilities.toString() + " ]";
     }
 
     public boolean equals(Object obj) {
         if (obj instanceof NetworkRequest == false) return false;
         NetworkRequest that = (NetworkRequest)obj;
-        return (that.legacy == this.legacy &&
+        return (that.needsBroadcasts == this.needsBroadcasts &&
                 that.requestId == this.requestId &&
                 ((that.networkCapabilities == null && this.networkCapabilities == null) ||
                  (that.networkCapabilities != null &&
@@ -82,6 +115,7 @@
     }
 
     public int hashCode() {
-        return requestId + (legacy ? 1013 : 2026) + (networkCapabilities.hashCode() * 1051);
+        return requestId + (needsBroadcasts ? 1013 : 2026) +
+                (networkCapabilities.hashCode() * 1051);
     }
 }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 0937ec3..4c4c76f1 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -56,5 +56,6 @@
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
     public static final int BASE_CONNECTIVITY_SERVICE                               = 0x00080000;
+    public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
     //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 1cb873e..cdb82e7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,6 +20,7 @@
 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.NetworkCallbacks;
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_DUMMY;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -30,6 +31,7 @@
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol;
+import static android.net.ConnectivityServiceProtocol.NetworkMonitorProtocol;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
@@ -67,6 +69,7 @@
 import android.net.LinkQualityInfo;
 import android.net.MobileDataStateTracker;
 import android.net.Network;
+import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
@@ -82,7 +85,6 @@
 import android.net.RouteInfo;
 import android.net.SamplingDataTracker;
 import android.net.Uri;
-import android.net.wifi.WifiStateTracker;
 import android.net.wimax.WimaxManagerConstants;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -128,6 +130,7 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
@@ -179,7 +182,7 @@
     private static final String TAG = "ConnectivityService";
 
     private static final boolean DBG = true;
-    private static final boolean VDBG = false;
+    private static final boolean VDBG = true; // STOPSHIP
 
     private static final boolean LOGD_RULES = false;
 
@@ -306,12 +309,6 @@
     private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
 
     /**
-     * used internally to change our network preference setting
-     * arg1 = networkType to prefer
-     */
-    private static final int EVENT_SET_NETWORK_PREFERENCE = 3;
-
-    /**
      * used internally to synchronize inet condition reports
      * arg1 = networkType
      * arg2 = condition (0 bad, 100 good)
@@ -382,10 +379,16 @@
      */
     private static final int EVENT_REGISTER_NETWORK_FACTORY = 17;
 
+    /**
+     * used internally when registering NetworkAgents
+     * obj = Messenger
+     */
+    private static final int EVENT_REGISTER_NETWORK_AGENT = 18;
+
     /** Handler used for internal events. */
-    private InternalHandler mHandler;
+    final private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
-    private NetworkStateTrackerHandler mTrackerHandler;
+    final private NetworkStateTrackerHandler mTrackerHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
     // a process dies
@@ -474,8 +477,8 @@
         NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
         netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
-        NetworkRequest netRequest = new NetworkRequest(netCap);
-        mNetworkRequests.append(netRequest.requestId, netRequest);
+        mDefaultRequest = new NetworkRequest(netCap, true);
+        mNetworkRequests.append(mDefaultRequest.requestId, mDefaultRequest);
 
         HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
         handlerThread.start();
@@ -624,21 +627,6 @@
             }
         }
 
-        // Update mNetworkPreference according to user mannually first then overlay config.xml
-        mNetworkPreference = getPersistedNetworkPreference();
-        if (mNetworkPreference == -1) {
-            for (int n : mPriorityList) {
-                if (mNetConfigs[n].isDefault() && ConnectivityManager.isNetworkTypeValid(n)) {
-                    mNetworkPreference = n;
-                    break;
-                }
-            }
-            if (mNetworkPreference == -1) {
-                throw new IllegalStateException(
-                        "You should set at least one default Network in config.xml!");
-            }
-        }
-
         mNetRequestersPids =
                 (List<Integer> [])new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
         for (int i : mPriorityList) {
@@ -738,6 +726,10 @@
     /**
      * Factory that creates {@link NetworkStateTracker} instances using given
      * {@link NetworkConfig}.
+     *
+     * TODO - this is obsolete and will be deleted.  It's replaced by the
+     * registerNetworkFactory call and protocol.
+     * @Deprecated in favor of registerNetworkFactory dynamic bindings
      */
     public interface NetworkFactory {
         public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
@@ -755,10 +747,6 @@
         @Override
         public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
             switch (config.radio) {
-                case TYPE_WIFI:
-                    return new WifiStateTracker(targetNetworkType, config.name);
-                case TYPE_MOBILE:
-                    return new MobileDataStateTracker(targetNetworkType, config.name);
                 case TYPE_DUMMY:
                     return new DummyDataStateTracker(targetNetworkType, config.name);
                 case TYPE_BLUETOOTH:
@@ -859,41 +847,6 @@
         return wimaxStateTracker;
     }
 
-    /**
-     * Sets the preferred network.
-     * @param preference the new preference
-     */
-    public void setNetworkPreference(int preference) {
-        enforceChangePermission();
-
-        mHandler.sendMessage(
-                mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
-    }
-
-    public int getNetworkPreference() {
-        enforceAccessPermission();
-        int preference;
-        synchronized(this) {
-            preference = mNetworkPreference;
-        }
-        return preference;
-    }
-
-    private void handleSetNetworkPreference(int preference) {
-        if (ConnectivityManager.isNetworkTypeValid(preference) &&
-                mNetConfigs[preference] != null &&
-                mNetConfigs[preference].isDefault()) {
-            if (mNetworkPreference != preference) {
-                final ContentResolver cr = mContext.getContentResolver();
-                Settings.Global.putInt(cr, Settings.Global.NETWORK_PREFERENCE, preference);
-                synchronized(this) {
-                    mNetworkPreference = preference;
-                }
-                enforcePreference();
-            }
-        }
-    }
-
     private int getConnectivityChangeDelay() {
         final ContentResolver cr = mContext.getContentResolver();
 
@@ -905,41 +858,6 @@
                 defaultDelay);
     }
 
-    private int getPersistedNetworkPreference() {
-        final ContentResolver cr = mContext.getContentResolver();
-
-        final int networkPrefSetting = Settings.Global
-                .getInt(cr, Settings.Global.NETWORK_PREFERENCE, -1);
-
-        return networkPrefSetting;
-    }
-
-    /**
-     * Make the state of network connectivity conform to the preference settings
-     * In this method, we only tear down a non-preferred network. Establishing
-     * a connection to the preferred network is taken care of when we handle
-     * the disconnect event from the non-preferred network
-     * (see {@link #handleDisconnect(NetworkInfo)}).
-     */
-    private void enforcePreference() {
-        if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
-            return;
-
-        if (!mNetTrackers[mNetworkPreference].isAvailable())
-            return;
-
-        for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
-            if (t != mNetworkPreference && mNetTrackers[t] != null &&
-                    mNetTrackers[t].getNetworkInfo().isConnected()) {
-                if (DBG) {
-                    log("tearing down " + mNetTrackers[t].getNetworkInfo() +
-                            " in enforcePreference");
-                }
-                teardown(mNetTrackers[t]);
-            }
-        }
-    }
-
     private boolean teardown(NetworkStateTracker netTracker) {
         if (netTracker.teardown()) {
             netTracker.setTeardownRequested(true);
@@ -1192,24 +1110,6 @@
         return false;
     }
 
-    public boolean setRadios(boolean turnOn) {
-        boolean result = true;
-        enforceChangePermission();
-        for (NetworkStateTracker t : mNetTrackers) {
-            if (t != null) result = t.setRadio(turnOn) && result;
-        }
-        return result;
-    }
-
-    public boolean setRadio(int netType, boolean turnOn) {
-        enforceChangePermission();
-        if (!ConnectivityManager.isNetworkTypeValid(netType)) {
-            return false;
-        }
-        NetworkStateTracker tracker = mNetTrackers[netType];
-        return tracker != null && tracker.setRadio(turnOn);
-    }
-
     private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
         @Override
         public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
@@ -1929,18 +1829,8 @@
     }
 
     private void handleSetMobileData(boolean enabled) {
-        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-            if (VDBG) {
-                log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
-            }
-            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
-        }
-        if (mNetTrackers[ConnectivityManager.TYPE_WIMAX] != null) {
-            if (VDBG) {
-                log(mNetTrackers[ConnectivityManager.TYPE_WIMAX].toString() + enabled);
-            }
-            mNetTrackers[ConnectivityManager.TYPE_WIMAX].setUserDataEnable(enabled);
-        }
+    // TODO - handle this - probably generalize passing in a transport type and send to the
+    // factories?
     }
 
     @Override
@@ -1953,12 +1843,13 @@
     }
 
     private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
-        if (isNetworkTypeValid(networkType)) {
-            final NetworkStateTracker tracker = mNetTrackers[networkType];
-            if (tracker != null) {
-                tracker.setPolicyDataEnable(enabled);
-            }
-        }
+   // TODO - handle this passing to factories
+//        if (isNetworkTypeValid(networkType)) {
+//            final NetworkStateTracker tracker = mNetTrackers[networkType];
+//            if (tracker != null) {
+//                tracker.setPolicyDataEnable(enabled);
+//            }
+//        }
     }
 
     private void enforceAccessPermission() {
@@ -2226,67 +2117,6 @@
         }
     }
 
-    /**
-     * Called when an attempt to fail over to another network has failed.
-     * @param info the {@link NetworkInfo} for the failed network
-     */
-    private void handleConnectionFailure(NetworkInfo info) {
-        mNetTrackers[info.getType()].setTeardownRequested(false);
-
-        String reason = info.getReason();
-        String extraInfo = info.getExtraInfo();
-
-        String reasonText;
-        if (reason == null) {
-            reasonText = ".";
-        } else {
-            reasonText = " (" + reason + ").";
-        }
-        loge("Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
-
-        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
-        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, new NetworkInfo(info));
-        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
-        if (getActiveNetworkInfo() == null) {
-            intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
-        }
-        if (reason != null) {
-            intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
-        }
-        if (extraInfo != null) {
-            intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
-        }
-        if (info.isFailover()) {
-            intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
-            info.setFailover(false);
-        }
-
-        if (mNetConfigs[info.getType()].isDefault()) {
-            tryFailover(info.getType());
-            if (mActiveDefaultNetwork != -1) {
-                NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-                intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
-            } else {
-                mDefaultInetConditionPublished = 0;
-                intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
-            }
-        }
-
-        intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
-
-        final Intent immediateIntent = new Intent(intent);
-        immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
-        sendStickyBroadcast(immediateIntent);
-        sendStickyBroadcast(intent);
-        /*
-         * If the failover network is already connected, then immediately send
-         * out a followup broadcast indicating successful failover
-         */
-        if (mActiveDefaultNetwork != -1) {
-            sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo());
-        }
-    }
-
     private void sendStickyBroadcast(Intent intent) {
         synchronized(this) {
             if (!mSystemReady) {
@@ -2478,33 +2308,35 @@
     }
 
     /**
-     * Setup data activity tracking for the given network interface.
+     * Setup data activity tracking for the given network.
      *
      * Every {@code setupDataActivityTracking} should be paired with a
      * {@link #removeDataActivityTracking} for cleanup.
      */
-    private void setupDataActivityTracking(int type) {
-        final NetworkStateTracker thisNet = mNetTrackers[type];
-        final String iface = thisNet.getLinkProperties().getInterfaceName();
+    private void setupDataActivityTracking(NetworkAgentInfo networkAgent) {
+        final String iface = networkAgent.linkProperties.getInterfaceName();
 
         final int timeout;
+        int type = ConnectivityManager.TYPE_NONE;
 
-        if (ConnectivityManager.isNetworkTypeMobile(type)) {
+        if (networkAgent.networkCapabilities.hasTransport(
+                NetworkCapabilities.TRANSPORT_CELLULAR)) {
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
                                              5);
-            // Canonicalize mobile network type
             type = ConnectivityManager.TYPE_MOBILE;
-        } else if (ConnectivityManager.TYPE_WIFI == type) {
+        } else if (networkAgent.networkCapabilities.hasTransport(
+                NetworkCapabilities.TRANSPORT_WIFI)) {
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
                                              0);
+            type = ConnectivityManager.TYPE_WIFI;
         } else {
             // do not track any other networks
             timeout = 0;
         }
 
-        if (timeout > 0 && iface != null) {
+        if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
             try {
                 mNetd.addIdleTimer(iface, timeout, type);
             } catch (Exception e) {
@@ -2517,12 +2349,12 @@
     /**
      * Remove data activity tracking when network disconnects.
      */
-    private void removeDataActivityTracking(int type) {
-        final NetworkStateTracker net = mNetTrackers[type];
-        final String iface = net.getLinkProperties().getInterfaceName();
+    private void removeDataActivityTracking(NetworkAgentInfo networkAgent) {
+        final String iface = networkAgent.linkProperties.getInterfaceName();
+        final NetworkCapabilities caps = networkAgent.networkCapabilities;
 
-        if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
-                              ConnectivityManager.TYPE_WIFI == type)) {
+        if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
+                              caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
             try {
                 // the call fails silently if no idletimer setup for this interface
                 mNetd.removeIdleTimer(iface);
@@ -2537,8 +2369,10 @@
      * concerned with making sure that the list of DNS servers is set up
      * according to which networks are connected, and ensuring that the
      * right routing table entries exist.
+     *
+     * TODO - delete when we're sure all this functionallity is captured.
      */
-    private void handleConnectivityChange(int netType, boolean doReset) {
+    private void handleConnectivityChange(int netType, LinkProperties curLp, boolean doReset) {
         int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
         boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
         if (VDBG) {
@@ -2552,7 +2386,6 @@
          */
         handleDnsConfigurationChange(netType);
 
-        LinkProperties curLp = mCurrentLinkProperties[netType];
         LinkProperties newLp = null;
 
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
@@ -2742,26 +2575,30 @@
         return routesChanged;
     }
 
-   /**
+    /**
      * Reads the network specific MTU size from reources.
      * and set it on it's iface.
      */
-   private void updateMtuSizeSettings(NetworkStateTracker nt) {
-       final String iface = nt.getLinkProperties().getInterfaceName();
-       final int mtu = nt.getLinkProperties().getMtu();
+    private void updateMtu(LinkProperties newLp, LinkProperties oldLp) {
+        final String iface = newLp.getInterfaceName();
+        final int mtu = newLp.getMtu();
+        if (oldLp != null && newLp.isIdenticalMtu(oldLp)) {
+            if (VDBG) log("identical MTU - not setting");
+            return;
+        }
 
-       if (mtu < 68 || mtu > 10000) {
-           loge("Unexpected mtu value: " + mtu + ", " + nt);
-           return;
-       }
+        if (mtu < 68 || mtu > 10000) {
+            loge("Unexpected mtu value: " + mtu + ", " + iface);
+            return;
+        }
 
-       try {
-           if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
-           mNetd.setMtu(iface, mtu);
-       } catch (Exception e) {
-           Slog.e(TAG, "exception in setMtu()" + e);
-       }
-   }
+        try {
+            if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
+            mNetd.setMtu(iface, mtu);
+        } catch (Exception e) {
+            Slog.e(TAG, "exception in setMtu()" + e);
+        }
+    }
 
     /**
      * Reads the network specific TCP buffer sizes from SystemProperties
@@ -3053,21 +2890,58 @@
             NetworkInfo info;
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
-                    AsyncChannel ac = (AsyncChannel) msg.obj;
-                    if (mNetworkFactories.contains(ac)) {
-                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                            if (VDBG) log("NetworkFactory connected");
-                            for (int i = 0; i < mNetworkRequests.size(); i++) {
-                                ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
-                                    mNetworkRequests.valueAt(i));
-                            }
-                        } else {
-                            loge("Error connecting NetworkFactory");
-                            mNetworkFactories.remove((AsyncChannel) msg.obj);
-                        }
+                    handleAsyncChannelHalfConnect(msg);
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai != null) nai.asyncChannel.disconnect();
+                    break;
+                }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    handleAsyncChannelDisconnected(msg);
+                    break;
+                }
+                case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_NETWORK_CAPABILITIES_CHANGED from unknown NetworkAgent");
+                    } else {
+                        updateCapabilities(nai, (NetworkCapabilities)msg.obj);
                     }
                     break;
                 }
+                case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("NetworkAgent not found for EVENT_NETWORK_PROPERTIES_CHANGED");
+                    } else {
+                        LinkProperties oldLp = nai.linkProperties;
+                        nai.linkProperties = (LinkProperties)msg.obj;
+                        updateLinkProperties(nai, oldLp);
+                    }
+                    break;
+                }
+                case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_NETWORK_INFO_CHANGED from unknown NetworkAgent");
+                        break;
+                    }
+                    info = (NetworkInfo) msg.obj;
+                    updateNetworkInfo(nai, info);
+                    break;
+                }
+                case NetworkMonitorProtocol.EVENT_NETWORK_VALIDATED: {
+                    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
+                    handleConnectionValidated(nai);
+                    break;
+                }
+                case NetworkMonitorProtocol.EVENT_NETWORK_LINGER_COMPLETE: {
+                    NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
+                    handleLingerComplete(nai);
+                    break;
+                }
                 case NetworkStateTracker.EVENT_STATE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     NetworkInfo.State state = info.getState();
@@ -3100,10 +2974,7 @@
                     EventLogTags.writeConnectivityStateChanged(
                             info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
 
-                    if (info.getDetailedState() ==
-                            NetworkInfo.DetailedState.FAILED) {
-                        handleConnectionFailure(info);
-                    } else if (info.isConnectedToProvisioningNetwork()) {
+                    if (info.isConnectedToProvisioningNetwork()) {
                         /**
                          * TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
                          * for now its an in between network, its a network that
@@ -3128,18 +2999,9 @@
                                         mNetTrackers[info.getType()].getNetwork().netId);
                         }
                     } else if (state == NetworkInfo.State.DISCONNECTED) {
-                        handleDisconnect(info);
                     } else if (state == NetworkInfo.State.SUSPENDED) {
-                        // TODO: need to think this over.
-                        // the logic here is, handle SUSPENDED the same as
-                        // DISCONNECTED. The only difference being we are
-                        // broadcasting an intent with NetworkInfo that's
-                        // suspended. This allows the applications an
-                        // opportunity to handle DISCONNECTED and SUSPENDED
-                        // differently, or not.
-                        handleDisconnect(info);
                     } else if (state == NetworkInfo.State.CONNECTED) {
-                        handleConnect(info);
+                    //    handleConnect(info);
                     }
                     if (mLockdownTracker != null) {
                         mLockdownTracker.onNetworkInfoChanged(info);
@@ -3151,7 +3013,8 @@
                     // TODO: Temporary allowing network configuration
                     //       change not resetting sockets.
                     //       @see bug/4455071
-                    handleConnectivityChange(info.getType(), false);
+                    handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()],
+                            false);
                     break;
                 }
                 case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
@@ -3164,6 +3027,66 @@
         }
     }
 
+    private void handleAsyncChannelHalfConnect(Message msg) {
+        AsyncChannel ac = (AsyncChannel) msg.obj;
+        if (mNetworkFactories.contains(ac)) {
+            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);
+                    ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
+                            (nai != null ? nai.currentScore : 0), 0, request);
+                }
+            } else {
+                loge("Error connecting NetworkFactory");
+                mNetworkFactories.remove(ac);
+            }
+        } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
+            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                if (VDBG) log("NetworkAgent connected");
+                // A network agent has requested a connection.  Establish the connection.
+                mNetworkAgentInfos.get(msg.replyTo).asyncChannel.
+                        sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+            } else {
+                loge("Error connecting NetworkAgent");
+                mNetworkAgentInfos.remove(msg.replyTo);
+            }
+        }
+    }
+    private void handleAsyncChannelDisconnected(Message msg) {
+        NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+        if (nai != null) {
+            if (DBG) log(nai.name() + " got DISCONNECTED");
+            // A network agent has disconnected.
+            // Tell netd to clean up the configuration for this network
+            // (routing rules, DNS, etc).
+            try {
+                mNetd.removeNetwork(nai.network.netId);
+            } catch (Exception e) {
+                loge("Exception removing network: " + e);
+            }
+            notifyNetworkCallbacks(nai, NetworkCallbacks.LOST);
+            mNetworkAgentInfos.remove(nai);
+            // Since we've lost the network, go through all the requests that
+            // it was satisfying and see if any other factory can satisfy them.
+            for (int i = 0; i < nai.networkRequests.size(); i++) {
+                NetworkRequest request = nai.networkRequests.valueAt(i);
+                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
+                if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
+                    mNetworkForRequestId.remove(request.requestId);
+                    // TODO Check if any other live network will work
+                    sendUpdatedScoreToFactories(request, 0);
+                }
+            }
+            if (nai.networkRequests.get(mDefaultRequest.requestId) != null) {
+                removeDataActivityTracking(nai);
+            }
+        }
+    }
+
+
     private class InternalHandler extends Handler {
         public InternalHandler(Looper looper) {
             super(looper);
@@ -3204,11 +3127,6 @@
                     handleInetConditionHoldEnd(netType, sequence);
                     break;
                 }
-                case EVENT_SET_NETWORK_PREFERENCE: {
-                    int preference = msg.arg1;
-                    handleSetNetworkPreference(preference);
-                    break;
-                }
                 case EVENT_SET_MOBILE_DATA: {
                     boolean enabled = (msg.arg1 == ENABLED);
                     handleSetMobileData(enabled);
@@ -3266,6 +3184,10 @@
                     handleRegisterNetworkFactory((Messenger)msg.obj);
                     break;
                 }
+                case EVENT_REGISTER_NETWORK_AGENT: {
+                    handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
+                    break;
+                }
             }
         }
     }
@@ -5112,7 +5034,6 @@
 
     public void registerNetworkFactory(Messenger messenger) {
         enforceConnectivityInternalPermission();
-
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger));
     }
 
@@ -5123,5 +5044,335 @@
         ac.connect(mContext, mTrackerHandler, messenger);
     }
 
+    // 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)
+     * and the are the highest scored network available.
+     * the are keyed off the Requests requestId.
+     */
+    private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
+            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 =
+            new HashMap<Messenger, NetworkAgentInfo>();
+
+    private final NetworkRequest mDefaultRequest;
+
+    public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
+            int currentScore) {
+        enforceConnectivityInternalPermission();
+
+        NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
+            new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
+            new NetworkCapabilities(networkCapabilities), currentScore);
+
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+    }
+
+    private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
+        if (VDBG) log("Got NetworkAgent Messenger");
+        mNetworkAgentInfos.put(na.messenger, na);
+        na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
+        NetworkInfo networkInfo = na.networkInfo;
+        na.networkInfo = null;
+        updateNetworkInfo(na, networkInfo);
+    }
+
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
+        LinkProperties newLp = networkAgent.linkProperties;
+        int netId = networkAgent.network.netId;
+
+        updateMtu(newLp, oldLp);
+        // TODO - figure out what to do for clat
+//        for (LinkProperties lp : newLp.getStackedLinks()) {
+//            updateMtu(lp, null);
+//        }
+        updateRoutes(newLp, oldLp, netId);
+        updateDnses(newLp, oldLp, netId);
+    }
+    private void updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
+        CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
+        if (oldLp != null) {
+            routeDiff = oldLp.compareAllRoutes(newLp);
+        } else if (newLp != null) {
+            routeDiff.added = newLp.getAllRoutes();
+        }
+
+        // add routes before removing old in case it helps with continuous connectivity
+
+        // do this twice, adding non-nexthop routes first, then routes they are dependent on
+        for (RouteInfo route : routeDiff.added) {
+            if (route.hasGateway()) continue;
+            try {
+                mNetd.addRoute(netId, route);
+            } catch (Exception e) {
+                loge("Exception in addRoute for non-gateway: " + e);
+            }
+        }
+        for (RouteInfo route : routeDiff.added) {
+            if (route.hasGateway() == false) continue;
+            try {
+                mNetd.addRoute(netId, route);
+            } catch (Exception e) {
+                loge("Exception in addRoute for gateway: " + e);
+            }
+        }
+
+        for (RouteInfo route : routeDiff.removed) {
+            try {
+                mNetd.removeRoute(netId, route);
+            } catch (Exception e) {
+                loge("Exception in removeRoute: " + e);
+            }
+        }
+    }
+    private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
+        if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
+            Collection<InetAddress> dnses = newLp.getDnses();
+            if (dnses.size() == 0 && mDefaultDns != null) {
+                dnses = new ArrayList();
+                dnses.add(mDefaultDns);
+                if (DBG) {
+                    loge("no dns provided for netId " + netId + ", so using defaults");
+                }
+            }
+            try {
+                mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
+                    newLp.getDomains());
+            } catch (Exception e) {
+                loge("Exception in setDnsServersForNetwork: " + e);
+            }
+            // TODO - setprop "net.dnsX"
+        }
+    }
+
+    private void updateCapabilities(NetworkAgentInfo networkAgent,
+            NetworkCapabilities networkCapabilities) {
+        // TODO - what else here?  Verify still satisfies everybody?
+        // Check if satisfies somebody new?  call callbacks?
+        networkAgent.networkCapabilities = networkCapabilities;
+    }
+
+    private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
+        if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
+        for (AsyncChannel ac : mNetworkFactories) {
+            ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest);
+        }
+    }
+
+    private void callCallbackForRequest(NetworkRequest networkRequest,
+            NetworkAgentInfo networkAgent, int notificationType) {
+        // TODO
+    }
+
+    private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
+        if (oldNetwork == null) {
+            loge("Unknown NetworkAgentInfo in handleLingerComplete");
+            return;
+        }
+        if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+        if (DBG) {
+            if (oldNetwork.networkRequests.size() != 0) {
+                loge("Dead network still had " + oldNetwork.networkRequests.size() + " requests");
+            }
+        }
+        oldNetwork.asyncChannel.disconnect();
+    }
+
+    private void handleConnectionValidated(NetworkAgentInfo newNetwork) {
+        if (newNetwork == null) {
+            loge("Unknown NetworkAgentInfo in handleConnectionValidated");
+            return;
+        }
+        boolean keep = false;
+        boolean isNewDefault = false;
+        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(
+                    newNetwork.networkCapabilities)) {
+                // next check if it's better than any current network we're using for
+                // this request
+                NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nr.requestId);
+                if (VDBG) {
+                    log("currentScore = " +
+                            (currentNetwork != null ? currentNetwork.currentScore : 0) +
+                            ", newScore = " + newNetwork.currentScore);
+                }
+                if (currentNetwork == null ||
+                        currentNetwork.currentScore < newNetwork.currentScore) {
+                    if (currentNetwork != null) {
+                        currentNetwork.networkRequests.remove(nr.requestId);
+                        currentNetwork.networkListens.add(nr);
+                        if (currentNetwork.networkRequests.size() == 0) {
+                            // TODO tell current Network to go to linger state
+
+                            // fake the linger state:
+                            Message message = Message.obtain();
+                            message.obj = currentNetwork;
+                            message.what = NetworkMonitorProtocol.EVENT_NETWORK_LINGER_COMPLETE;
+                            mTrackerHandler.sendMessage(message);
+
+                            notifyNetworkCallbacks(currentNetwork, NetworkCallbacks.LOSING);
+                        }
+                    }
+                    mNetworkForRequestId.put(nr.requestId, newNetwork);
+                    newNetwork.networkRequests.put(nr.requestId, nr);
+                    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) {
+                        isNewDefault = true;
+                    }
+                }
+            }
+        }
+        if (keep) {
+            if (isNewDefault) {
+                if (VDBG) log("Switching to new default network: " + newNetwork);
+                setupDataActivityTracking(newNetwork);
+                try {
+                    mNetd.setDefaultNetId(newNetwork.network.netId);
+                } catch (Exception e) {
+                    loge("Exception setting default network :" + e);
+                }
+                if (newNetwork.equals(mNetworkForRequestId.get(mDefaultRequest.requestId))) {
+                    handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
+                }
+                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 new network
+                    if (mNetTransitionWakeLock.isHeld()) {
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                                mNetTransitionWakeLockSerialNumber, 0),
+                                1000);
+                    }
+                }
+
+                // this will cause us to come up initially as unconnected and switching
+                // to connected after our normal pause unless somebody reports us as
+                // really disconnected
+                mDefaultInetConditionPublished = 0;
+                mDefaultConnectionSequence++;
+                mInetConditionChangeInFlight = false;
+                // TODO - read the tcp buffer size config string from somewhere
+                // updateNetworkSettings();
+            }
+            // notify battery stats service about this network
+//            try {
+                // TODO
+                //BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType);
+//            } catch (RemoteException e) { }
+            notifyNetworkCallbacks(newNetwork, NetworkCallbacks.AVAILABLE);
+        } else {
+            if (VDBG) log("Validated network turns out to be unwanted.  Tear it down.");
+            newNetwork.asyncChannel.disconnect();
+        }
+    }
+
+
+    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+        NetworkInfo.State state = newInfo.getState();
+        NetworkInfo oldInfo = networkAgent.networkInfo;
+        networkAgent.networkInfo = newInfo;
+
+        if (oldInfo != null && oldInfo.getState() == state) {
+            if (VDBG) log("ignoring duplicate network state non-change");
+            return;
+        }
+        if (DBG) {
+            log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
+                    (oldInfo == null ? "null" : oldInfo.getState()) +
+                    " to " + state);
+        }
+        if (state == NetworkInfo.State.CONNECTED) {
+            // TODO - check if we want it (optimization)
+            try {
+                mNetd.createNetwork(networkAgent.network.netId,
+                        networkAgent.linkProperties.getInterfaceName());
+            } catch (Exception e) {
+                loge("Error creating Network " + networkAgent.network.netId);
+            }
+            updateLinkProperties(networkAgent, null);
+            notifyNetworkCallbacks(networkAgent, NetworkCallbacks.PRECHECK);
+            // TODO - kick the network monitor
+
+            // Fake things by sending self a NETWORK_VALIDATED msg
+            Message message = Message.obtain();
+            message.obj = networkAgent;
+            message.what = NetworkMonitorProtocol.EVENT_NETWORK_VALIDATED;
+            mTrackerHandler.sendMessage(message);
+        } else if (state == NetworkInfo.State.DISCONNECTED ||
+                state == NetworkInfo.State.SUSPENDED) {
+            networkAgent.asyncChannel.disconnect();
+        }
+    }
+
+    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);
+        }
+        if (needsBroadcasts) {
+            if (notifyType == NetworkCallbacks.AVAILABLE) {
+                sendConnectedBroadcastDelayed(networkAgent.networkInfo,
+                        getConnectivityChangeDelay());
+            } else if (notifyType == NetworkCallbacks.LOST) {
+                NetworkInfo info = new NetworkInfo(networkAgent.networkInfo);
+                Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+                intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+                intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
+                if (info.isFailover()) {
+                    intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+                    networkAgent.networkInfo.setFailover(false);
+                }
+                if (info.getReason() != null) {
+                    intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
+                }
+                if (info.getExtraInfo() != null) {
+                    intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+                }
+                NetworkAgentInfo newDefaultAgent = null;
+                if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) {
+                    newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId);
+                    if (newDefaultAgent != null) {
+                        intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
+                                newDefaultAgent.networkInfo);
+                    } else {
+                        intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+                    }
+                }
+                intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION,
+                        mDefaultInetConditionPublished);
+                final Intent immediateIntent = new Intent(intent);
+                immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
+                sendStickyBroadcast(immediateIntent);
+                sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
+                if (newDefaultAgent != null) {
+                    sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+                            getConnectivityChangeDelay());
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgent.java b/services/core/java/com/android/server/connectivity/NetworkAgent.java
deleted file mode 100644
index a0af06d..0000000
--- a/services/core/java/com/android/server/connectivity/NetworkAgent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import android.net.Network;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-
-import com.android.internal.util.AsyncChannel;
-
-import java.util.ArrayList;
-
-/**
- * {@hide}
- */
-public class NetworkAgent {
-    public NetworkInfo networkInfo;
-    public Network network;
-    public final ArrayList<NetworkRequest> networkRequests = new ArrayList<NetworkRequest>();
-    public final ArrayList<NetworkRequest> networkListens = new ArrayList<NetworkRequest>();
-    public final AsyncChannel asyncChannel;
-
-    public NetworkAgent(AsyncChannel ac) {
-        asyncChannel = ac;
-    }
-}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
new file mode 100644
index 0000000..4747487
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.os.Messenger;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+
+import java.util.ArrayList;
+
+/**
+ * A bag class used by ConnectivityService for holding a collection of most recent
+ * information published by a particular NetworkAgent as well as the
+ * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
+ * interested in using it.
+ */
+public class NetworkAgentInfo {
+    public NetworkInfo networkInfo;
+    public final Network network;
+    public LinkProperties linkProperties;
+    public NetworkCapabilities networkCapabilities;
+    public int currentScore;
+
+    // The list of NetworkRequests being satisfied by this Network.
+    public final SparseArray<NetworkRequest> networkRequests = new SparseArray<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;
+
+    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info,
+            LinkProperties lp, NetworkCapabilities nc, int score) {
+        this.messenger = messenger;
+        asyncChannel = ac;
+        network = new Network(netId);
+        networkInfo = info;
+        linkProperties = lp;
+        networkCapabilities = nc;
+        currentScore = score;
+
+    }
+
+    public String toString() {
+        return "NetworkAgentInfo{ ni{" + networkInfo + "}  network{" +
+                network + "}  lp{" +
+                linkProperties + "}  nc{" +
+                networkCapabilities + "}  Score{" + currentScore + "} }";
+    }
+
+    public String name() {
+        return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
+                networkInfo.getSubtypeName() + ")]";
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
deleted file mode 100644
index 40e6649..0000000
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.BaseNetworkStateTracker;
-import android.net.NetworkCapabilities;
-import android.net.LinkQualityInfo;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
-import android.net.SamplingDataTracker;
-import android.net.WifiLinkQualityInfo;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Slog;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Track the state of wifi for connectivity service.
- *
- * @hide
- */
-public class WifiStateTracker extends BaseNetworkStateTracker {
-
-    private static final String NETWORKTYPE = "WIFI";
-    private static final String TAG = "WifiStateTracker";
-
-    private static final boolean LOGV = true;
-
-    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
-    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
-    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
-
-    private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN;
-
-    private WifiInfo mWifiInfo;
-
-    /* For sending events to connectivity service handler */
-    private Handler mCsHandler;
-    private BroadcastReceiver mWifiStateReceiver;
-    private WifiManager mWifiManager;
-
-    private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
-
-    public WifiStateTracker(int netType, String networkName) {
-        mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
-        mLinkProperties = new LinkProperties();
-        mNetworkCapabilities = new NetworkCapabilities();
-
-        mNetworkInfo.setIsAvailable(false);
-        setTeardownRequested(false);
-    }
-
-
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested.set(isRequested);
-    }
-
-    public boolean isTeardownRequested() {
-        return mTeardownRequested.get();
-    }
-
-    /**
-     * Begin monitoring wifi connectivity
-     */
-    public void startMonitoring(Context context, Handler target) {
-        mCsHandler = target;
-        mContext = context;
-
-        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
-
-        mWifiStateReceiver = new WifiStateReceiver();
-        mContext.registerReceiver(mWifiStateReceiver, filter);
-    }
-
-    /**
-     * Disable connectivity to a network
-     * TODO: do away with return value after making MobileDataStateTracker async
-     */
-    public boolean teardown() {
-        mTeardownRequested.set(true);
-        mWifiManager.stopWifi();
-        return true;
-    }
-
-    /**
-     * Re-enable connectivity to a network after a {@link #teardown()}.
-     */
-    public boolean reconnect() {
-        mTeardownRequested.set(false);
-        mWifiManager.startWifi();
-        return true;
-    }
-
-    @Override
-    public void captivePortalCheckCompleted(boolean isCaptivePortal) {
-        // not implemented
-    }
-
-    /**
-     * Turn the wireless radio off for a network.
-     * @param turnOn {@code true} to turn the radio on, {@code false}
-     */
-    public boolean setRadio(boolean turnOn) {
-        mWifiManager.setWifiEnabled(turnOn);
-        return true;
-    }
-
-    /**
-     * Wi-Fi is considered available as long as we have a connection to the
-     * supplicant daemon and there is at least one enabled network. If a teardown
-     * was explicitly requested, then Wi-Fi can be restarted with a reconnect
-     * request, so it is considered available. If the driver has been stopped
-     * for any reason other than a teardown request, Wi-Fi is considered
-     * unavailable.
-     * @return {@code true} if Wi-Fi connections are possible
-     */
-    public boolean isAvailable() {
-        return mNetworkInfo.isAvailable();
-    }
-
-    @Override
-    public void setUserDataEnable(boolean enabled) {
-        Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
-    }
-
-    @Override
-    public void setPolicyDataEnable(boolean enabled) {
-        // ignored
-    }
-
-    /**
-     * Check if private DNS route is set for the network
-     */
-    public boolean isPrivateDnsRouteSet() {
-        return mPrivateDnsRouteSet.get();
-    }
-
-    /**
-     * Set a flag indicating private DNS route is set
-     */
-    public void privateDnsRouteSet(boolean enabled) {
-        mPrivateDnsRouteSet.set(enabled);
-    }
-
-    /**
-     * Fetch NetworkInfo for the network
-     */
-    @Override
-    public NetworkInfo getNetworkInfo() {
-        return new NetworkInfo(mNetworkInfo);
-    }
-
-    /**
-     * Fetch LinkProperties for the network
-     */
-    @Override
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    /**
-     * Return link info
-     * @return an object of type WifiLinkQualityInfo
-     */
-    @Override
-    public LinkQualityInfo getLinkQualityInfo() {
-        if (mNetworkInfo == null) {
-            // no data available yet; just return
-            return null;
-        }
-
-        WifiLinkQualityInfo li = new WifiLinkQualityInfo();
-        li.setNetworkType(mNetworkInfo.getType());
-
-        synchronized(mSamplingDataTracker.mSamplingDataLock) {
-            mSamplingDataTracker.setCommonLinkQualityInfoFields(li);
-            li.setTxGood(mSamplingDataTracker.getSampledTxPacketCount());
-            li.setTxBad(mSamplingDataTracker.getSampledTxPacketErrorCount());
-        }
-
-        // li.setTheoreticalRxBandwidth(??);
-        // li.setTheoreticalTxBandwidth(??);
-
-        if (mWifiInfo != null) {
-            li.setBssid(mWifiInfo.getBSSID());
-
-            int rssi = mWifiInfo.getRssi();
-            li.setRssi(rssi);
-
-            li.setNormalizedSignalStrength(mWifiManager.calculateSignalLevel(rssi,
-                    LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE));
-        }
-
-        return li;
-    }
-
-    /**
-     * Check if default route is set
-     */
-    public boolean isDefaultRouteSet() {
-        return mDefaultRouteSet.get();
-    }
-
-    /**
-     * Set a flag indicating default route is set for the network
-     */
-    public void defaultRouteSet(boolean enabled) {
-        mDefaultRouteSet.set(enabled);
-    }
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName() {
-        return "net.tcp.buffersize.wifi";
-    }
-
-    private class WifiStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-
-            if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
-                        WifiManager.EXTRA_NETWORK_INFO);
-
-                mLinkProperties = intent.getParcelableExtra(
-                        WifiManager.EXTRA_LINK_PROPERTIES);
-                if (mLinkProperties == null) {
-                    mLinkProperties = new LinkProperties();
-                }
-                mNetworkCapabilities = intent.getParcelableExtra(
-                        WifiManager.EXTRA_NETWORK_CAPABILITIES);
-                if (mNetworkCapabilities == null) {
-                    mNetworkCapabilities = new NetworkCapabilities();
-                }
-
-                mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
-                // don't want to send redundant state messages
-                // but send portal check detailed state notice
-                NetworkInfo.State state = mNetworkInfo.getState();
-                if (mLastState == state &&
-                        mNetworkInfo.getDetailedState() != DetailedState.CAPTIVE_PORTAL_CHECK) {
-                    return;
-                } else {
-                    mLastState = state;
-                    /* lets not sample traffic data across state changes */
-                    mSamplingDataTracker.resetSamplingData();
-                }
-
-                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
-                        new NetworkInfo(mNetworkInfo));
-                msg.sendToTarget();
-            } else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) {
-                mLinkProperties = intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES);
-                Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
-                msg.sendToTarget();
-            }
-        }
-    }
-
-    public void setDependencyMet(boolean met) {
-        // not supported on this network
-    }
-
-    @Override
-    public void addStackedLink(LinkProperties link) {
-        mLinkProperties.addStackedLink(link);
-    }
-
-    @Override
-    public void removeStackedLink(LinkProperties link) {
-        mLinkProperties.removeStackedLink(link);
-    }
-
-    @Override
-    public void supplyMessenger(Messenger messenger) {
-        // not supported on this network
-    }
-
-    @Override
-    public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
-        mSamplingDataTracker.startSampling(s);
-    }
-
-    @Override
-    public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
-        mSamplingDataTracker.stopSampling(s);
-    }
-}