summaryrefslogtreecommitdiffstats
path: root/core/java/android/net/NetworkAgent.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/net/NetworkAgent.java')
-rw-r--r--core/java/android/net/NetworkAgent.java397
1 files changed, 397 insertions, 0 deletions
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);
+ }
+}