/* * 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.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Protocol; import java.io.FileDescriptor; import java.io.PrintWriter; /** * A NetworkFactory is an entity that creates NetworkAgent objects. * The bearers register with ConnectivityService using {@link #register} and * their factory will start receiving scored NetworkRequests. NetworkRequests * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by * overridden function. All of these can be dynamic - changing NetworkCapabilities * or score forces re-evaluation of all current requests. * * If any requests pass the filter some overrideable functions will be called. * If the bearer only cares about very simple start/stopNetwork callbacks, those * functions can be overridden. If the bearer needs more interaction, it can * override addNetworkRequest and removeNetworkRequest which will give it each * request that passes their current filters. * @hide **/ public class NetworkFactory extends Handler { private static final boolean DBG = true; private static final boolean VDBG = false; private static final int BASE = Protocol.BASE_NETWORK_FACTORY; /** * 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; /** * Internally used to set our best-guess score. * msg.arg1 = new score */ private static final int CMD_SET_SCORE = BASE + 2; /** * Internally used to set our current filter for coarse bandwidth changes with * technology changes. * msg.obj = new filter */ private static final int CMD_SET_FILTER = BASE + 3; private final Context mContext; private final String LOG_TAG; private final SparseArray mNetworkRequests = new SparseArray(); private int mScore; private NetworkCapabilities mCapabilityFilter; private int mRefCount = 0; private Messenger mMessenger = null; public NetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { super(looper); LOG_TAG = logTag; mContext = context; mCapabilityFilter = filter; } public void register() { if (DBG) log("Registering NetworkFactory"); if (mMessenger == null) { mMessenger = new Messenger(this); ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG); } } public void unregister() { if (DBG) log("Unregistering NetworkFactory"); if (mMessenger != null) { ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger); mMessenger = null; } } @Override public void handleMessage(Message msg) { switch (msg.what) { case CMD_REQUEST_NETWORK: { handleAddRequest((NetworkRequest)msg.obj, msg.arg1); break; } case CMD_CANCEL_REQUEST: { handleRemoveRequest((NetworkRequest) msg.obj); break; } case CMD_SET_SCORE: { handleSetScore(msg.arg1); break; } case CMD_SET_FILTER: { handleSetFilter((NetworkCapabilities) msg.obj); break; } } } private class NetworkRequestInfo { public final NetworkRequest request; public int score; public boolean requested; // do we have a request outstanding, limited by score public NetworkRequestInfo(NetworkRequest request, int score) { this.request = request; this.score = score; this.requested = false; } @Override public String toString() { return "{" + request + ", score=" + score + ", requested=" + requested + "}"; } } @VisibleForTesting protected void handleAddRequest(NetworkRequest request, int score) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n == null) { if (DBG) log("got request " + request + " with score " + score); n = new NetworkRequestInfo(request, score); mNetworkRequests.put(n.request.requestId, n); } else { if (VDBG) log("new score " + score + " for exisiting request " + request); n.score = score; } if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); evalRequest(n); } @VisibleForTesting protected void handleRemoveRequest(NetworkRequest request) { NetworkRequestInfo n = mNetworkRequests.get(request.requestId); if (n != null) { mNetworkRequests.remove(request.requestId); if (n.requested) releaseNetworkFor(n.request); } } private void handleSetScore(int score) { mScore = score; evalRequests(); } private void handleSetFilter(NetworkCapabilities netCap) { mCapabilityFilter = netCap; evalRequests(); } /** * Overridable function to provide complex filtering. * Called for every request every time a new NetworkRequest is seen * and whenever the filterScore or filterNetworkCapabilities change. * * acceptRequest can be overriden to provide complex filter behavior * for the incoming requests * * For output, this class will call {@link #needNetworkFor} and * {@link #releaseNetworkFor} for every request that passes the filters. * If you don't need to see every request, you can leave the base * implementations of those two functions and instead override * {@link #startNetwork} and {@link #stopNetwork}. * * If you want to see every score fluctuation on every request, set * your score filter to a very high number and watch {@link #needNetworkFor}. * * @return {@code true} to accept the request. */ public boolean acceptRequest(NetworkRequest request, int score) { return true; } private void evalRequest(NetworkRequestInfo n) { if (VDBG) log("evalRequest"); if (n.requested == false && n.score < mScore && n.request.networkCapabilities.satisfiedByNetworkCapabilities( mCapabilityFilter) && acceptRequest(n.request, n.score)) { if (VDBG) log(" needNetworkFor"); needNetworkFor(n.request, n.score); n.requested = true; } else if (n.requested == true && (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities( mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) { if (VDBG) log(" releaseNetworkFor"); releaseNetworkFor(n.request); n.requested = false; } else { if (VDBG) log(" done"); } } private void evalRequests() { for (int i = 0; i < mNetworkRequests.size(); i++) { NetworkRequestInfo n = mNetworkRequests.valueAt(i); evalRequest(n); } } /** * Post a command, on this NetworkFactory Handler, to re-evaluate all * oustanding requests. Can be called from a factory implementation. */ protected void reevaluateAllRequests() { post(() -> { evalRequests(); }); } // override to do simple mode (request independent) protected void startNetwork() { } protected void stopNetwork() { } // override to do fancier stuff protected void needNetworkFor(NetworkRequest networkRequest, int score) { if (++mRefCount == 1) startNetwork(); } protected void releaseNetworkFor(NetworkRequest networkRequest) { if (--mRefCount == 0) stopNetwork(); } public void addNetworkRequest(NetworkRequest networkRequest, int score) { sendMessage(obtainMessage(CMD_REQUEST_NETWORK, new NetworkRequestInfo(networkRequest, score))); } public void removeNetworkRequest(NetworkRequest networkRequest) { sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest)); } public void setScoreFilter(int score) { sendMessage(obtainMessage(CMD_SET_SCORE, score, 0)); } public void setCapabilityFilter(NetworkCapabilities netCap) { sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); } @VisibleForTesting protected int getRequestCount() { return mNetworkRequests.size(); } protected void log(String s) { Log.d(LOG_TAG, s); } public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println(toString()); pw.increaseIndent(); for (int i = 0; i < mNetworkRequests.size(); i++) { pw.println(mNetworkRequests.valueAt(i)); } pw.decreaseIndent(); } @Override public String toString() { StringBuilder sb = new StringBuilder("{").append(LOG_TAG).append(" - ScoreFilter="). append(mScore).append(", Filter=").append(mCapabilityFilter).append(", requests="). append(mNetworkRequests.size()).append(", refCount=").append(mRefCount). append("}"); return sb.toString(); } }