1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.Message;
23import android.os.Messenger;
24import android.util.Log;
25import android.util.SparseArray;
26
27import com.android.internal.annotations.VisibleForTesting;
28import com.android.internal.util.IndentingPrintWriter;
29import com.android.internal.util.Protocol;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33
34/**
35 * A NetworkFactory is an entity that creates NetworkAgent objects.
36 * The bearers register with ConnectivityService using {@link #register} and
37 * their factory will start receiving scored NetworkRequests.  NetworkRequests
38 * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
39 * overridden function.  All of these can be dynamic - changing NetworkCapabilities
40 * or score forces re-evaluation of all current requests.
41 *
42 * If any requests pass the filter some overrideable functions will be called.
43 * If the bearer only cares about very simple start/stopNetwork callbacks, those
44 * functions can be overridden.  If the bearer needs more interaction, it can
45 * override addNetworkRequest and removeNetworkRequest which will give it each
46 * request that passes their current filters.
47 * @hide
48 **/
49public class NetworkFactory extends Handler {
50    private static final boolean DBG = true;
51    private static final boolean VDBG = false;
52
53    private static final int BASE = Protocol.BASE_NETWORK_FACTORY;
54    /**
55     * Pass a network request to the bearer.  If the bearer believes it can
56     * satisfy the request it should connect to the network and create a
57     * NetworkAgent.  Once the NetworkAgent is fully functional it will
58     * register itself with ConnectivityService using registerNetworkAgent.
59     * If the bearer cannot immediately satisfy the request (no network,
60     * user disabled the radio, lower-scored network) it should remember
61     * any NetworkRequests it may be able to satisfy in the future.  It may
62     * disregard any that it will never be able to service, for example
63     * those requiring a different bearer.
64     * msg.obj = NetworkRequest
65     * msg.arg1 = score - the score of the any network currently satisfying this
66     *            request.  If this bearer knows in advance it cannot
67     *            exceed this score it should not try to connect, holding the request
68     *            for the future.
69     *            Note that subsequent events may give a different (lower
70     *            or higher) score for this request, transmitted to each
71     *            NetworkFactory through additional CMD_REQUEST_NETWORK msgs
72     *            with the same NetworkRequest but an updated score.
73     *            Also, network conditions may change for this bearer
74     *            allowing for a better score in the future.
75     */
76    public static final int CMD_REQUEST_NETWORK = BASE;
77
78    /**
79     * Cancel a network request
80     * msg.obj = NetworkRequest
81     */
82    public static final int CMD_CANCEL_REQUEST = BASE + 1;
83
84    /**
85     * Internally used to set our best-guess score.
86     * msg.arg1 = new score
87     */
88    private static final int CMD_SET_SCORE = BASE + 2;
89
90    /**
91     * Internally used to set our current filter for coarse bandwidth changes with
92     * technology changes.
93     * msg.obj = new filter
94     */
95    private static final int CMD_SET_FILTER = BASE + 3;
96
97    private final Context mContext;
98    private final String LOG_TAG;
99
100    private final SparseArray<NetworkRequestInfo> mNetworkRequests =
101            new SparseArray<NetworkRequestInfo>();
102
103    private int mScore;
104    private NetworkCapabilities mCapabilityFilter;
105
106    private int mRefCount = 0;
107    private Messenger mMessenger = null;
108
109    public NetworkFactory(Looper looper, Context context, String logTag,
110            NetworkCapabilities filter) {
111        super(looper);
112        LOG_TAG = logTag;
113        mContext = context;
114        mCapabilityFilter = filter;
115    }
116
117    public void register() {
118        if (DBG) log("Registering NetworkFactory");
119        if (mMessenger == null) {
120            mMessenger = new Messenger(this);
121            ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
122        }
123    }
124
125    public void unregister() {
126        if (DBG) log("Unregistering NetworkFactory");
127        if (mMessenger != null) {
128            ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger);
129            mMessenger = null;
130        }
131    }
132
133    @Override
134    public void handleMessage(Message msg) {
135        switch (msg.what) {
136            case CMD_REQUEST_NETWORK: {
137                handleAddRequest((NetworkRequest)msg.obj, msg.arg1);
138                break;
139            }
140            case CMD_CANCEL_REQUEST: {
141                handleRemoveRequest((NetworkRequest) msg.obj);
142                break;
143            }
144            case CMD_SET_SCORE: {
145                handleSetScore(msg.arg1);
146                break;
147            }
148            case CMD_SET_FILTER: {
149                handleSetFilter((NetworkCapabilities) msg.obj);
150                break;
151            }
152        }
153    }
154
155    private class NetworkRequestInfo {
156        public final NetworkRequest request;
157        public int score;
158        public boolean requested; // do we have a request outstanding, limited by score
159
160        public NetworkRequestInfo(NetworkRequest request, int score) {
161            this.request = request;
162            this.score = score;
163            this.requested = false;
164        }
165
166        @Override
167        public String toString() {
168            return "{" + request + ", score=" + score + ", requested=" + requested + "}";
169        }
170    }
171
172    @VisibleForTesting
173    protected void handleAddRequest(NetworkRequest request, int score) {
174        NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
175        if (n == null) {
176            if (DBG) log("got request " + request + " with score " + score);
177            n = new NetworkRequestInfo(request, score);
178            mNetworkRequests.put(n.request.requestId, n);
179        } else {
180            if (VDBG) log("new score " + score + " for exisiting request " + request);
181            n.score = score;
182        }
183        if (VDBG) log("  my score=" + mScore + ", my filter=" + mCapabilityFilter);
184
185        evalRequest(n);
186    }
187
188    @VisibleForTesting
189    protected void handleRemoveRequest(NetworkRequest request) {
190        NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
191        if (n != null) {
192            mNetworkRequests.remove(request.requestId);
193            if (n.requested) releaseNetworkFor(n.request);
194        }
195    }
196
197    private void handleSetScore(int score) {
198        mScore = score;
199        evalRequests();
200    }
201
202    private void handleSetFilter(NetworkCapabilities netCap) {
203        mCapabilityFilter = netCap;
204        evalRequests();
205    }
206
207    /**
208     * Overridable function to provide complex filtering.
209     * Called for every request every time a new NetworkRequest is seen
210     * and whenever the filterScore or filterNetworkCapabilities change.
211     *
212     * acceptRequest can be overriden to provide complex filter behavior
213     * for the incoming requests
214     *
215     * For output, this class will call {@link #needNetworkFor} and
216     * {@link #releaseNetworkFor} for every request that passes the filters.
217     * If you don't need to see every request, you can leave the base
218     * implementations of those two functions and instead override
219     * {@link #startNetwork} and {@link #stopNetwork}.
220     *
221     * If you want to see every score fluctuation on every request, set
222     * your score filter to a very high number and watch {@link #needNetworkFor}.
223     *
224     * @return {@code true} to accept the request.
225     */
226    public boolean acceptRequest(NetworkRequest request, int score) {
227        return true;
228    }
229
230    private void evalRequest(NetworkRequestInfo n) {
231        if (VDBG) log("evalRequest");
232        if (n.requested == false && n.score < mScore &&
233                n.request.networkCapabilities.satisfiedByNetworkCapabilities(
234                mCapabilityFilter) && acceptRequest(n.request, n.score)) {
235            if (VDBG) log("  needNetworkFor");
236            needNetworkFor(n.request, n.score);
237            n.requested = true;
238        } else if (n.requested == true &&
239                (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
240                mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
241            if (VDBG) log("  releaseNetworkFor");
242            releaseNetworkFor(n.request);
243            n.requested = false;
244        } else {
245            if (VDBG) log("  done");
246        }
247    }
248
249    private void evalRequests() {
250        for (int i = 0; i < mNetworkRequests.size(); i++) {
251            NetworkRequestInfo n = mNetworkRequests.valueAt(i);
252
253            evalRequest(n);
254        }
255    }
256
257    // override to do simple mode (request independent)
258    protected void startNetwork() { }
259    protected void stopNetwork() { }
260
261    // override to do fancier stuff
262    protected void needNetworkFor(NetworkRequest networkRequest, int score) {
263        if (++mRefCount == 1) startNetwork();
264    }
265
266    protected void releaseNetworkFor(NetworkRequest networkRequest) {
267        if (--mRefCount == 0) stopNetwork();
268    }
269
270
271    public void addNetworkRequest(NetworkRequest networkRequest, int score) {
272        sendMessage(obtainMessage(CMD_REQUEST_NETWORK,
273                new NetworkRequestInfo(networkRequest, score)));
274    }
275
276    public void removeNetworkRequest(NetworkRequest networkRequest) {
277        sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest));
278    }
279
280    public void setScoreFilter(int score) {
281        sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
282    }
283
284    public void setCapabilityFilter(NetworkCapabilities netCap) {
285        sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap)));
286    }
287
288    @VisibleForTesting
289    protected int getRequestCount() {
290        return mNetworkRequests.size();
291    }
292
293    protected void log(String s) {
294        Log.d(LOG_TAG, s);
295    }
296
297    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
298        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
299        pw.println(toString());
300        pw.increaseIndent();
301        for (int i = 0; i < mNetworkRequests.size(); i++) {
302            pw.println(mNetworkRequests.valueAt(i));
303        }
304        pw.decreaseIndent();
305    }
306
307    @Override
308    public String toString() {
309        StringBuilder sb = new StringBuilder("{").append(LOG_TAG).append(" - ScoreFilter=").
310                append(mScore).append(", Filter=").append(mCapabilityFilter).append(", requests=").
311                append(mNetworkRequests.size()).append(", refCount=").append(mRefCount).
312                append("}");
313        return sb.toString();
314    }
315}
316