Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3192dec3 authored by Robert Greenwalt's avatar Robert Greenwalt
Browse files

Refactor NetworkFactory.

Make NetworkFactory a concrete class and divide responsibilites between it and NetworkAgent.
Factory will track requests and by default give a single connect/disconnect api for ease
of use.  Then NetworkAgent is created and destroyed as needed with very simple logic.

Change-Id: I401c14a6e5466f2fc63b04219b97ff85bb9af291
parent 8f379986
Loading
Loading
Loading
Loading
+0 −70
Original line number Diff line number Diff line
/*
 * 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 static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE;

/**
 * Describes the Internal protocols used to communicate with ConnectivityService.
 * @hide
 */
public class 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 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;

        /**
         * Cancel a network request
         * msg.obj = NetworkRequest
         */
        public static final int CMD_CANCEL_REQUEST = BASE + 1;
    }
}
+63 −274
Original line number Diff line number Diff line
@@ -24,85 +24,39 @@ 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.ArrayList;
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 Utility class for handling for communicating between bearer-specific
 * code and ConnectivityService.
 *
 * 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.
 * perhaps connections with different SSID or P2P for Wi-Fi).
 *
 * 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 volatile AsyncChannel mAsyncChannel;
    private final String LOG_TAG;
    private static final boolean DBG = true;
    private static final boolean VDBG = 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 final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();

    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;
    public static final int CMD_SUSPECT_BAD = BASE;

    /**
     * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to
@@ -110,84 +64,63 @@ public abstract class NetworkAgent extends Handler {
     * Sent when the NetworkInfo changes, mainly due to change of state.
     * obj = NetworkInfo
     */
    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4;
    public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1;

    /**
     * Sent by the NetworkAgent to ConnectivityService to pass the current
     * NetworkCapabilties.
     * obj = NetworkCapabilities
     */
    public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5;
    public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2;

    /**
     * Sent by the NetworkAgent to ConnectivityService to pass the current
     * NetworkProperties.
     * obj = NetworkProperties
     */
    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6;
    public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;

    /**
     * Sent by the NetworkAgent to ConnectivityService to pass the current
     * network score.
     * arg1 = network score int
     * obj = network score Integer
     */
    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7;
    public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;

    public NetworkAgent(Looper looper, Context context, String logTag) {
    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
            NetworkCapabilities nc, LinkProperties lp, int score) {
        super(looper);
        LOG_TAG = logTag;
        mContext = context;
        if (ni == null || nc == null || lp == null) {
            throw new IllegalArgumentException();
        }

    /**
     * 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);
            }
        }
        cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
                new LinkProperties(lp), new NetworkCapabilities(nc), score);
    }

    @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 ac = new AsyncChannel();
                    ac.connected(null, this, msg.replyTo);
                    ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                            AsyncChannel.STATUS_SUCCESSFUL);
                    synchronized (mPreConnectedQueue) {
                        mAsyncChannel = ac;
                        for (Message m : mPreConnectedQueue) {
                            ac.sendMessage(m);
                        }
                        mPreConnectedQueue.clear();
                    }
                }
                break;
@@ -199,213 +132,69 @@ public abstract class NetworkAgent extends Handler {
            }
            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
                if (DBG) log("NetworkAgent channel lost");
                disconnect();
                clear();
                // let the client know CS is done with us.
                unwanted();
                synchronized (mPreConnectedQueue) {
                    mAsyncChannel = null;
                }
                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, or if we have gotten worse and
     * need to disconnect.
     *
     * Once we are registered, does nothing: 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() {
        synchronized(mLockObj) {
            if (mRegistered) {
                if (VDBG) log("evalScores - already connected - size=" + mNetworkRequests.size());
                // already trying
                return;
            }
            if (VDBG) log("evalScores!");
            for (int i=0; i < mNetworkRequests.size(); i++) {
                int score = mNetworkRequests.valueAt(i).score;
                if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore);
                if (score < mNetworkScore) {
                    // have a request that has a lower scored network servicing it
                    // (or no network) than we could provide, so let's connect!
                    mConnectionRequested = true;
                    connect();
                    return;
                }
            }
            // Our score is not high enough to satisfy any current request.
            // This can happen if our score goes down after a connection is
            // requested but before we actually connect. In this case, disconnect
            // rather than continue trying - there's no point connecting if we know
            // we'll just be torn down as soon as we do.
            if (mConnectionRequested) {
                mConnectionRequested = false;
                disconnect();
    private void queueOrSendMessage(int what, Object obj) {
        synchronized (mPreConnectedQueue) {
            if (mAsyncChannel != null) {
                mAsyncChannel.sendMessage(what, obj);
            } else {
                Message msg = Message.obtain();
                msg.what = what;
                msg.obj = obj;
                mPreConnectedQueue.add(msg);
            }
        }
    }

    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();
            }
        }
        queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
    }

    /**
     * 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();
            }
        }
        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
    }

    /**
     * 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);
        }
        queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
                new NetworkCapabilities(networkCapabilities));
    }

    /**
     * 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 void sendNetworkScore(int score) {
        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
    }

    public boolean isConnectionRequested() {
        synchronized(mLockObj) {
            return mConnectionRequested;
        }
    }


    abstract protected void connect();
    abstract protected void disconnect();
    /**
     * Called when ConnectivityService has indicated they no longer want this network.
     * The parent factory should (previously) have received indication of the change
     * as well, either canceling NetworkRequests or altering their score such that this
     * network won't be immediately requested again.
     */
    abstract protected void unwanted();

    protected void log(String s) {
        Log.d(LOG_TAG, "NetworkAgent: " + s);
+275 −0

File added.

Preview size limit exceeded, changes collapsed.

+4 −1
Original line number Diff line number Diff line
@@ -198,7 +198,10 @@ public class NetworkInfo implements Parcelable {
        }
    }

    void setSubtype(int subtype, String subtypeName) {
    /**
     * @hide
     */
    public void setSubtype(int subtype, String subtypeName) {
        synchronized (this) {
            mSubtype = subtype;
            mSubtypeName = subtypeName;
+2 −2
Original line number Diff line number Diff line
@@ -57,9 +57,9 @@ public class Protocol {
    public static final int BASE_DNS_PINGER                                         = 0x00050000;
    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_CONNECTIVITY_MANAGER                               = 0x00080000;
    public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
    public static final int BASE_NETWORK_MONITOR                                    = 0x00082000;
    public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00083000;
    public static final int BASE_NETWORK_FACTORY                                    = 0x00083000;
    //TODO: define all used protocols
}
Loading