blob: b2d50515c39d843c0da359248b7dd4241438fb6b [file] [log] [blame]
Erik Kline9bba3402017-01-13 16:46:52 +09001/*
2 * Copyright (C) 2017 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 com.android.server.connectivity.tethering;
18
Erik Klinea1d368a2017-06-05 16:02:02 +090019import static android.net.ConnectivityManager.getNetworkTypeName;
20import static android.net.ConnectivityManager.TYPE_NONE;
Erik Klinedd8e8912017-01-18 16:08:06 +090021import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
22import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
23
Erik Kline9bba3402017-01-13 16:46:52 +090024import android.content.Context;
Erik Klineb583b032017-02-22 12:58:24 +090025import android.os.Handler;
26import android.os.Looper;
Erik Kline9bba3402017-01-13 16:46:52 +090027import android.net.ConnectivityManager;
28import android.net.ConnectivityManager.NetworkCallback;
29import android.net.LinkProperties;
30import android.net.Network;
31import android.net.NetworkCapabilities;
32import android.net.NetworkRequest;
33import android.net.NetworkState;
Erik Kline7747fd42017-05-12 16:52:48 +090034import android.net.util.SharedLog;
Erik Kline9bba3402017-01-13 16:46:52 +090035import android.util.Log;
36
Erik Kline885a9092017-01-16 16:27:22 +090037import com.android.internal.annotations.VisibleForTesting;
Erik Kline9bba3402017-01-13 16:46:52 +090038import com.android.internal.util.StateMachine;
39
40import java.util.HashMap;
41
42
43/**
44 * A class to centralize all the network and link properties information
45 * pertaining to the current and any potential upstream network.
46 *
47 * Calling #start() registers two callbacks: one to track the system default
Erik Klined2ec3912017-01-25 00:53:04 +090048 * network and a second to observe all networks. The latter is necessary
49 * while the expression of preferred upstreams remains a list of legacy
50 * connectivity types. In future, this can be revisited.
Erik Kline9bba3402017-01-13 16:46:52 +090051 *
52 * The methods and data members of this class are only to be accessed and
53 * modified from the tethering master state machine thread. Any other
54 * access semantics would necessitate the addition of locking.
55 *
56 * TODO: Move upstream selection logic here.
57 *
Erik Klined2ec3912017-01-25 00:53:04 +090058 * All callback methods are run on the same thread as the specified target
59 * state machine. This class does not require locking when accessed from this
60 * thread. Access from other threads is not advised.
61 *
Erik Kline9bba3402017-01-13 16:46:52 +090062 * @hide
63 */
64public class UpstreamNetworkMonitor {
65 private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
66 private static final boolean DBG = false;
67 private static final boolean VDBG = false;
68
69 public static final int EVENT_ON_AVAILABLE = 1;
70 public static final int EVENT_ON_CAPABILITIES = 2;
71 public static final int EVENT_ON_LINKPROPERTIES = 3;
72 public static final int EVENT_ON_LOST = 4;
73
Erik Kline35bf06c2017-01-26 18:08:28 +090074 private static final int CALLBACK_LISTEN_ALL = 1;
75 private static final int CALLBACK_TRACK_DEFAULT = 2;
76 private static final int CALLBACK_MOBILE_REQUEST = 3;
Erik Klined2ec3912017-01-25 00:53:04 +090077
Erik Kline9bba3402017-01-13 16:46:52 +090078 private final Context mContext;
Erik Kline7747fd42017-05-12 16:52:48 +090079 private final SharedLog mLog;
Erik Kline9bba3402017-01-13 16:46:52 +090080 private final StateMachine mTarget;
Erik Klineb583b032017-02-22 12:58:24 +090081 private final Handler mHandler;
Erik Kline9bba3402017-01-13 16:46:52 +090082 private final int mWhat;
83 private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
84 private ConnectivityManager mCM;
Erik Klined2ec3912017-01-25 00:53:04 +090085 private NetworkCallback mListenAllCallback;
Erik Kline9bba3402017-01-13 16:46:52 +090086 private NetworkCallback mDefaultNetworkCallback;
Erik Kline9bba3402017-01-13 16:46:52 +090087 private NetworkCallback mMobileNetworkCallback;
88 private boolean mDunRequired;
Erik Klined2ec3912017-01-25 00:53:04 +090089 private Network mCurrentDefault;
Erik Kline9bba3402017-01-13 16:46:52 +090090
Erik Kline7747fd42017-05-12 16:52:48 +090091 public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) {
Erik Kline9bba3402017-01-13 16:46:52 +090092 mContext = ctx;
93 mTarget = tgt;
Erik Klineb583b032017-02-22 12:58:24 +090094 mHandler = mTarget.getHandler();
Erik Kline9bba3402017-01-13 16:46:52 +090095 mWhat = what;
Erik Kline7747fd42017-05-12 16:52:48 +090096 mLog = log.forSubComponent(TAG);
Erik Kline9bba3402017-01-13 16:46:52 +090097 }
98
Erik Kline885a9092017-01-16 16:27:22 +090099 @VisibleForTesting
Erik Kline7747fd42017-05-12 16:52:48 +0900100 public UpstreamNetworkMonitor(
101 StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) {
102 this(null, tgt, what, log);
Erik Kline885a9092017-01-16 16:27:22 +0900103 mCM = cm;
104 }
105
Erik Kline9bba3402017-01-13 16:46:52 +0900106 public void start() {
107 stop();
108
Erik Klined2ec3912017-01-25 00:53:04 +0900109 final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
110 .clearCapabilities().build();
Erik Kline35bf06c2017-01-26 18:08:28 +0900111 mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
Erik Klineb583b032017-02-22 12:58:24 +0900112 cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900113
Erik Kline35bf06c2017-01-26 18:08:28 +0900114 mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
Erik Klineb583b032017-02-22 12:58:24 +0900115 cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900116 }
117
118 public void stop() {
119 releaseMobileNetworkRequest();
120
121 releaseCallback(mDefaultNetworkCallback);
122 mDefaultNetworkCallback = null;
123
Erik Klined2ec3912017-01-25 00:53:04 +0900124 releaseCallback(mListenAllCallback);
125 mListenAllCallback = null;
Erik Kline9bba3402017-01-13 16:46:52 +0900126
127 mNetworkMap.clear();
128 }
129
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900130 public void updateMobileRequiresDun(boolean dunRequired) {
Erik Kline9bba3402017-01-13 16:46:52 +0900131 final boolean valueChanged = (mDunRequired != dunRequired);
132 mDunRequired = dunRequired;
133 if (valueChanged && mobileNetworkRequested()) {
134 releaseMobileNetworkRequest();
135 registerMobileNetworkRequest();
136 }
137 }
138
139 public boolean mobileNetworkRequested() {
140 return (mMobileNetworkCallback != null);
141 }
142
143 public void registerMobileNetworkRequest() {
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900144 if (mMobileNetworkCallback != null) {
Erik Kline7747fd42017-05-12 16:52:48 +0900145 mLog.e("registerMobileNetworkRequest() already registered");
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900146 return;
147 }
Erik Kline9bba3402017-01-13 16:46:52 +0900148
Erik Kline35bf06c2017-01-26 18:08:28 +0900149 // The following use of the legacy type system cannot be removed until
150 // after upstream selection no longer finds networks by legacy type.
151 // See also http://b/34364553 .
152 final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
153
154 final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
155 .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType))
156 .build();
Erik Kline9bba3402017-01-13 16:46:52 +0900157
158 // The existing default network and DUN callbacks will be notified.
159 // Therefore, to avoid duplicate notifications, we only register a no-op.
Erik Kline35bf06c2017-01-26 18:08:28 +0900160 mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
Erik Kline9bba3402017-01-13 16:46:52 +0900161
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900162 // TODO: Change the timeout from 0 (no onUnavailable callback) to some
163 // moderate callback timeout. This might be useful for updating some UI.
164 // Additionally, we log a message to aid in any subsequent debugging.
Erik Kline7747fd42017-05-12 16:52:48 +0900165 mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
Erik Kline9bba3402017-01-13 16:46:52 +0900166
Erik Klineb583b032017-02-22 12:58:24 +0900167 cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900168 }
169
170 public void releaseMobileNetworkRequest() {
171 if (mMobileNetworkCallback == null) return;
172
173 cm().unregisterNetworkCallback(mMobileNetworkCallback);
174 mMobileNetworkCallback = null;
175 }
176
177 public NetworkState lookup(Network network) {
178 return (network != null) ? mNetworkMap.get(network) : null;
179 }
180
Erik Klinea1d368a2017-06-05 16:02:02 +0900181 // So many TODOs here, but chief among them is: make this functionality an
182 // integral part of this class such that whenever a higher priority network
183 // becomes available and useful we (a) file a request to keep it up as
184 // necessary and (b) change all upstream tracking state accordingly (by
185 // passing LinkProperties up to Tethering).
186 //
187 // Next TODO: return NetworkState instead of just the type.
188 public int selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
189 final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
190 mNetworkMap.values(), preferredTypes);
191
192 mLog.log("preferred upstream type: " + getNetworkTypeName(typeStatePair.type));
193
194 switch (typeStatePair.type) {
195 case TYPE_MOBILE_DUN:
196 case TYPE_MOBILE_HIPRI:
197 // If we're on DUN, put our own grab on it.
198 registerMobileNetworkRequest();
199 break;
200 case TYPE_NONE:
201 break;
202 default:
203 /* If we've found an active upstream connection that's not DUN/HIPRI
204 * we should stop any outstanding DUN/HIPRI start requests.
205 *
206 * If we found NONE we don't want to do this as we want any previous
207 * requests to keep trying to bring up something we can use.
208 */
209 releaseMobileNetworkRequest();
210 break;
211 }
212
213 return typeStatePair.type;
214 }
215
Erik Klined2ec3912017-01-25 00:53:04 +0900216 private void handleAvailable(int callbackType, Network network) {
217 if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
218
Erik Kline9bba3402017-01-13 16:46:52 +0900219 if (!mNetworkMap.containsKey(network)) {
220 mNetworkMap.put(network,
221 new NetworkState(null, null, null, network, null, null));
222 }
223
Erik Klined2ec3912017-01-25 00:53:04 +0900224 // Always request whatever extra information we can, in case this
225 // was already up when start() was called, in which case we would
226 // not have been notified of any information that had not changed.
Erik Kline35bf06c2017-01-26 18:08:28 +0900227 switch (callbackType) {
228 case CALLBACK_LISTEN_ALL:
229 break;
Erik Kline465c46d2017-03-07 15:25:32 +0900230
Erik Kline35bf06c2017-01-26 18:08:28 +0900231 case CALLBACK_TRACK_DEFAULT:
Erik Kline32858e12017-02-21 16:21:46 +0900232 if (mDefaultNetworkCallback == null) {
233 // The callback was unregistered in the interval between
Erik Klineb583b032017-02-22 12:58:24 +0900234 // ConnectivityService enqueueing onAvailable() and our
235 // handling of it here on the mHandler thread.
236 //
Erik Kline32858e12017-02-21 16:21:46 +0900237 // Clean-up of this network entry is deferred to the
238 // handling of onLost() by other callbacks.
Erik Klineb583b032017-02-22 12:58:24 +0900239 //
240 // These request*() calls can be deleted post oag/339444.
Erik Kline32858e12017-02-21 16:21:46 +0900241 return;
242 }
Erik Kline35bf06c2017-01-26 18:08:28 +0900243 mCurrentDefault = network;
244 break;
Erik Kline465c46d2017-03-07 15:25:32 +0900245
Erik Kline35bf06c2017-01-26 18:08:28 +0900246 case CALLBACK_MOBILE_REQUEST:
Erik Kline32858e12017-02-21 16:21:46 +0900247 if (mMobileNetworkCallback == null) {
248 // The callback was unregistered in the interval between
Erik Klineb583b032017-02-22 12:58:24 +0900249 // ConnectivityService enqueueing onAvailable() and our
250 // handling of it here on the mHandler thread.
251 //
Erik Kline32858e12017-02-21 16:21:46 +0900252 // Clean-up of this network entry is deferred to the
253 // handling of onLost() by other callbacks.
Erik Kline32858e12017-02-21 16:21:46 +0900254 return;
255 }
Erik Kline35bf06c2017-01-26 18:08:28 +0900256 break;
Erik Klined2ec3912017-01-25 00:53:04 +0900257 }
Erik Kline9bba3402017-01-13 16:46:52 +0900258
Erik Klined2ec3912017-01-25 00:53:04 +0900259 // Requesting updates for mListenAllCallback is not currently possible
260 // because it's a "listen". Two possible solutions to getting updates
261 // about networks without waiting for a change (which might never come)
262 // are:
263 //
264 // [1] extend request{NetworkCapabilities,LinkProperties}() to
265 // take a Network argument and have ConnectivityService do
266 // what's required (if the network satisfies the request)
267 //
268 // [2] explicitly file a NetworkRequest for each connectivity type
269 // listed as a preferred upstream and wait for these callbacks
270 // to be notified (requires tracking many more callbacks).
271 //
272 // Until this is addressed, networks that exist prior to the "listen"
273 // registration and which do not subsequently change will not cause
274 // us to learn their NetworkCapabilities nor their LinkProperties.
275
276 // TODO: If sufficient information is available to select a more
277 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900278 notifyTarget(EVENT_ON_AVAILABLE, network);
279 }
280
281 private void handleNetCap(Network network, NetworkCapabilities newNc) {
Erik Klined2ec3912017-01-25 00:53:04 +0900282 final NetworkState prev = mNetworkMap.get(network);
283 if (prev == null || newNc.equals(prev.networkCapabilities)) {
284 // Ignore notifications about networks for which we have not yet
285 // received onAvailable() (should never happen) and any duplicate
286 // notifications (e.g. matching more than one of our callbacks).
Erik Kline9bba3402017-01-13 16:46:52 +0900287 return;
288 }
Erik Klined2ec3912017-01-25 00:53:04 +0900289
Erik Kline9bba3402017-01-13 16:46:52 +0900290 if (VDBG) {
291 Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
292 network, newNc));
293 }
294
Erik Klined2ec3912017-01-25 00:53:04 +0900295 mNetworkMap.put(network, new NetworkState(
296 null, prev.linkProperties, newNc, network, null, null));
297 // TODO: If sufficient information is available to select a more
298 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900299 notifyTarget(EVENT_ON_CAPABILITIES, network);
300 }
301
302 private void handleLinkProp(Network network, LinkProperties newLp) {
Erik Klined2ec3912017-01-25 00:53:04 +0900303 final NetworkState prev = mNetworkMap.get(network);
304 if (prev == null || newLp.equals(prev.linkProperties)) {
305 // Ignore notifications about networks for which we have not yet
306 // received onAvailable() (should never happen) and any duplicate
307 // notifications (e.g. matching more than one of our callbacks).
Erik Kline9bba3402017-01-13 16:46:52 +0900308 return;
309 }
Erik Klined2ec3912017-01-25 00:53:04 +0900310
Erik Kline9bba3402017-01-13 16:46:52 +0900311 if (VDBG) {
312 Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
313 network, newLp));
314 }
315
Erik Klined2ec3912017-01-25 00:53:04 +0900316 mNetworkMap.put(network, new NetworkState(
317 null, newLp, prev.networkCapabilities, network, null, null));
318 // TODO: If sufficient information is available to select a more
319 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900320 notifyTarget(EVENT_ON_LINKPROPERTIES, network);
321 }
322
Erik Klined2ec3912017-01-25 00:53:04 +0900323 private void handleLost(int callbackType, Network network) {
Erik Kline35bf06c2017-01-26 18:08:28 +0900324 if (callbackType == CALLBACK_TRACK_DEFAULT) {
Erik Klined2ec3912017-01-25 00:53:04 +0900325 mCurrentDefault = null;
326 // Receiving onLost() for a default network does not necessarily
327 // mean the network is gone. We wait for a separate notification
328 // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
329 // clearing all state.
Erik Kline9bba3402017-01-13 16:46:52 +0900330 return;
331 }
Erik Klined2ec3912017-01-25 00:53:04 +0900332
333 if (!mNetworkMap.containsKey(network)) {
334 // Ignore loss of networks about which we had not previously
335 // learned any information or for which we have already processed
336 // an onLost() notification.
337 return;
Erik Kline9bba3402017-01-13 16:46:52 +0900338 }
Erik Klined2ec3912017-01-25 00:53:04 +0900339
340 if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network);
341
342 // TODO: If sufficient information is available to select a more
343 // preferable upstream, do so now and notify the target. Likewise,
344 // if the current upstream network is gone, notify the target of the
345 // fact that we now have no upstream at all.
Erik Kline9bba3402017-01-13 16:46:52 +0900346 notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
347 }
348
349 // Fetch (and cache) a ConnectivityManager only if and when we need one.
350 private ConnectivityManager cm() {
351 if (mCM == null) {
Erik Klineea9cc482017-03-10 19:35:34 +0900352 // MUST call the String variant to be able to write unittests.
353 mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
Erik Kline9bba3402017-01-13 16:46:52 +0900354 }
355 return mCM;
356 }
357
358 /**
Erik Klineb583b032017-02-22 12:58:24 +0900359 * A NetworkCallback class that handles information of interest directly
360 * in the thread on which it is invoked. To avoid locking, this MUST be
361 * run on the same thread as the target state machine's handler.
Erik Kline9bba3402017-01-13 16:46:52 +0900362 */
363 private class UpstreamNetworkCallback extends NetworkCallback {
Erik Klined2ec3912017-01-25 00:53:04 +0900364 private final int mCallbackType;
365
366 UpstreamNetworkCallback(int callbackType) {
367 mCallbackType = callbackType;
368 }
369
Erik Kline9bba3402017-01-13 16:46:52 +0900370 @Override
371 public void onAvailable(Network network) {
Erik Klineb583b032017-02-22 12:58:24 +0900372 handleAvailable(mCallbackType, network);
Erik Kline9bba3402017-01-13 16:46:52 +0900373 }
374
375 @Override
376 public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
Erik Klineb583b032017-02-22 12:58:24 +0900377 handleNetCap(network, newNc);
Erik Kline9bba3402017-01-13 16:46:52 +0900378 }
379
380 @Override
381 public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
Erik Klineb583b032017-02-22 12:58:24 +0900382 handleLinkProp(network, newLp);
Erik Kline9bba3402017-01-13 16:46:52 +0900383 }
384
Erik Klineb583b032017-02-22 12:58:24 +0900385 // TODO: Handle onNetworkSuspended();
386 // TODO: Handle onNetworkResumed();
387
Erik Kline9bba3402017-01-13 16:46:52 +0900388 @Override
389 public void onLost(Network network) {
Erik Klineb583b032017-02-22 12:58:24 +0900390 handleLost(mCallbackType, network);
391 }
Erik Kline9bba3402017-01-13 16:46:52 +0900392 }
393
394 private void releaseCallback(NetworkCallback cb) {
395 if (cb != null) cm().unregisterNetworkCallback(cb);
396 }
397
398 private void notifyTarget(int which, Network network) {
399 notifyTarget(which, mNetworkMap.get(network));
400 }
401
402 private void notifyTarget(int which, NetworkState netstate) {
403 mTarget.sendMessage(mWhat, which, 0, netstate);
404 }
Erik Klinea1d368a2017-06-05 16:02:02 +0900405
406 static private class TypeStatePair {
407 public int type = TYPE_NONE;
408 public NetworkState ns = null;
409 }
410
411 static private TypeStatePair findFirstAvailableUpstreamByType(
412 Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
413 final TypeStatePair result = new TypeStatePair();
414
415 for (int type : preferredTypes) {
416 NetworkCapabilities nc;
417 try {
418 nc = ConnectivityManager.networkCapabilitiesForType(type);
419 } catch (IllegalArgumentException iae) {
420 Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " +
421 ConnectivityManager.getNetworkTypeName(type));
422 continue;
423 }
424
425 for (NetworkState value : netStates) {
426 if (!nc.satisfiedByNetworkCapabilities(value.networkCapabilities)) {
427 continue;
428 }
429
430 result.type = type;
431 result.ns = value;
432 return result;
433 }
434 }
435
436 return result;
437 }
Erik Kline9bba3402017-01-13 16:46:52 +0900438}