blob: 6106093e4008d03ae29648c2282106f3ddf693c5 [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 Klinedd8e8912017-01-18 16:08:06 +090019import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
20import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
21
Erik Kline9bba3402017-01-13 16:46:52 +090022import android.content.Context;
Erik Klineb583b032017-02-22 12:58:24 +090023import android.os.Handler;
24import android.os.Looper;
Erik Kline9bba3402017-01-13 16:46:52 +090025import android.net.ConnectivityManager;
26import android.net.ConnectivityManager.NetworkCallback;
27import android.net.LinkProperties;
28import android.net.Network;
29import android.net.NetworkCapabilities;
30import android.net.NetworkRequest;
31import android.net.NetworkState;
32import android.util.Log;
33
Erik Kline885a9092017-01-16 16:27:22 +090034import com.android.internal.annotations.VisibleForTesting;
Erik Kline9bba3402017-01-13 16:46:52 +090035import com.android.internal.util.StateMachine;
36
37import java.util.HashMap;
38
39
40/**
41 * A class to centralize all the network and link properties information
42 * pertaining to the current and any potential upstream network.
43 *
44 * Calling #start() registers two callbacks: one to track the system default
Erik Klined2ec3912017-01-25 00:53:04 +090045 * network and a second to observe all networks. The latter is necessary
46 * while the expression of preferred upstreams remains a list of legacy
47 * connectivity types. In future, this can be revisited.
Erik Kline9bba3402017-01-13 16:46:52 +090048 *
49 * The methods and data members of this class are only to be accessed and
50 * modified from the tethering master state machine thread. Any other
51 * access semantics would necessitate the addition of locking.
52 *
53 * TODO: Move upstream selection logic here.
54 *
Erik Klined2ec3912017-01-25 00:53:04 +090055 * All callback methods are run on the same thread as the specified target
56 * state machine. This class does not require locking when accessed from this
57 * thread. Access from other threads is not advised.
58 *
Erik Kline9bba3402017-01-13 16:46:52 +090059 * @hide
60 */
61public class UpstreamNetworkMonitor {
62 private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
63 private static final boolean DBG = false;
64 private static final boolean VDBG = false;
65
66 public static final int EVENT_ON_AVAILABLE = 1;
67 public static final int EVENT_ON_CAPABILITIES = 2;
68 public static final int EVENT_ON_LINKPROPERTIES = 3;
69 public static final int EVENT_ON_LOST = 4;
70
Erik Kline35bf06c2017-01-26 18:08:28 +090071 private static final int CALLBACK_LISTEN_ALL = 1;
72 private static final int CALLBACK_TRACK_DEFAULT = 2;
73 private static final int CALLBACK_MOBILE_REQUEST = 3;
Erik Klined2ec3912017-01-25 00:53:04 +090074
Erik Kline9bba3402017-01-13 16:46:52 +090075 private final Context mContext;
76 private final StateMachine mTarget;
Erik Klineb583b032017-02-22 12:58:24 +090077 private final Handler mHandler;
Erik Kline9bba3402017-01-13 16:46:52 +090078 private final int mWhat;
79 private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
80 private ConnectivityManager mCM;
Erik Klined2ec3912017-01-25 00:53:04 +090081 private NetworkCallback mListenAllCallback;
Erik Kline9bba3402017-01-13 16:46:52 +090082 private NetworkCallback mDefaultNetworkCallback;
Erik Kline9bba3402017-01-13 16:46:52 +090083 private NetworkCallback mMobileNetworkCallback;
84 private boolean mDunRequired;
Erik Klined2ec3912017-01-25 00:53:04 +090085 private Network mCurrentDefault;
Erik Kline9bba3402017-01-13 16:46:52 +090086
87 public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
88 mContext = ctx;
89 mTarget = tgt;
Erik Klineb583b032017-02-22 12:58:24 +090090 mHandler = mTarget.getHandler();
Erik Kline9bba3402017-01-13 16:46:52 +090091 mWhat = what;
92 }
93
Erik Kline885a9092017-01-16 16:27:22 +090094 @VisibleForTesting
95 public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
96 this(null, tgt, what);
97 mCM = cm;
98 }
99
Erik Kline9bba3402017-01-13 16:46:52 +0900100 public void start() {
101 stop();
102
Erik Klined2ec3912017-01-25 00:53:04 +0900103 final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
104 .clearCapabilities().build();
Erik Kline35bf06c2017-01-26 18:08:28 +0900105 mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
Erik Klineb583b032017-02-22 12:58:24 +0900106 cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900107
Erik Kline35bf06c2017-01-26 18:08:28 +0900108 mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
Erik Klineb583b032017-02-22 12:58:24 +0900109 cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900110 }
111
112 public void stop() {
113 releaseMobileNetworkRequest();
114
115 releaseCallback(mDefaultNetworkCallback);
116 mDefaultNetworkCallback = null;
117
Erik Klined2ec3912017-01-25 00:53:04 +0900118 releaseCallback(mListenAllCallback);
119 mListenAllCallback = null;
Erik Kline9bba3402017-01-13 16:46:52 +0900120
121 mNetworkMap.clear();
122 }
123
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900124 public void updateMobileRequiresDun(boolean dunRequired) {
Erik Kline9bba3402017-01-13 16:46:52 +0900125 final boolean valueChanged = (mDunRequired != dunRequired);
126 mDunRequired = dunRequired;
127 if (valueChanged && mobileNetworkRequested()) {
128 releaseMobileNetworkRequest();
129 registerMobileNetworkRequest();
130 }
131 }
132
133 public boolean mobileNetworkRequested() {
134 return (mMobileNetworkCallback != null);
135 }
136
137 public void registerMobileNetworkRequest() {
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900138 if (mMobileNetworkCallback != null) {
139 Log.e(TAG, "registerMobileNetworkRequest() already registered");
140 return;
141 }
Erik Kline9bba3402017-01-13 16:46:52 +0900142
Erik Kline35bf06c2017-01-26 18:08:28 +0900143 // The following use of the legacy type system cannot be removed until
144 // after upstream selection no longer finds networks by legacy type.
145 // See also http://b/34364553 .
146 final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
147
148 final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
149 .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType))
150 .build();
Erik Kline9bba3402017-01-13 16:46:52 +0900151
152 // The existing default network and DUN callbacks will be notified.
153 // Therefore, to avoid duplicate notifications, we only register a no-op.
Erik Kline35bf06c2017-01-26 18:08:28 +0900154 mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST);
Erik Kline9bba3402017-01-13 16:46:52 +0900155
Erik Klineefdd3f4c2017-01-20 16:31:29 +0900156 // TODO: Change the timeout from 0 (no onUnavailable callback) to some
157 // moderate callback timeout. This might be useful for updating some UI.
158 // Additionally, we log a message to aid in any subsequent debugging.
159 Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
Erik Kline9bba3402017-01-13 16:46:52 +0900160
Erik Klineb583b032017-02-22 12:58:24 +0900161 cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
Erik Kline9bba3402017-01-13 16:46:52 +0900162 }
163
164 public void releaseMobileNetworkRequest() {
165 if (mMobileNetworkCallback == null) return;
166
167 cm().unregisterNetworkCallback(mMobileNetworkCallback);
168 mMobileNetworkCallback = null;
169 }
170
171 public NetworkState lookup(Network network) {
172 return (network != null) ? mNetworkMap.get(network) : null;
173 }
174
Erik Klined2ec3912017-01-25 00:53:04 +0900175 private void handleAvailable(int callbackType, Network network) {
176 if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
177
Erik Kline9bba3402017-01-13 16:46:52 +0900178 if (!mNetworkMap.containsKey(network)) {
179 mNetworkMap.put(network,
180 new NetworkState(null, null, null, network, null, null));
181 }
182
Erik Klined2ec3912017-01-25 00:53:04 +0900183 // Always request whatever extra information we can, in case this
184 // was already up when start() was called, in which case we would
185 // not have been notified of any information that had not changed.
Erik Kline35bf06c2017-01-26 18:08:28 +0900186 switch (callbackType) {
187 case CALLBACK_LISTEN_ALL:
188 break;
189 case CALLBACK_TRACK_DEFAULT:
Erik Kline32858e12017-02-21 16:21:46 +0900190 if (mDefaultNetworkCallback == null) {
191 // The callback was unregistered in the interval between
Erik Klineb583b032017-02-22 12:58:24 +0900192 // ConnectivityService enqueueing onAvailable() and our
193 // handling of it here on the mHandler thread.
194 //
Erik Kline32858e12017-02-21 16:21:46 +0900195 // Clean-up of this network entry is deferred to the
196 // handling of onLost() by other callbacks.
Erik Klineb583b032017-02-22 12:58:24 +0900197 //
198 // These request*() calls can be deleted post oag/339444.
Erik Kline32858e12017-02-21 16:21:46 +0900199 return;
200 }
201
Erik Kline35bf06c2017-01-26 18:08:28 +0900202 cm().requestNetworkCapabilities(mDefaultNetworkCallback);
203 cm().requestLinkProperties(mDefaultNetworkCallback);
204 mCurrentDefault = network;
205 break;
206 case CALLBACK_MOBILE_REQUEST:
Erik Kline32858e12017-02-21 16:21:46 +0900207 if (mMobileNetworkCallback == null) {
208 // The callback was unregistered in the interval between
Erik Klineb583b032017-02-22 12:58:24 +0900209 // ConnectivityService enqueueing onAvailable() and our
210 // handling of it here on the mHandler thread.
211 //
Erik Kline32858e12017-02-21 16:21:46 +0900212 // Clean-up of this network entry is deferred to the
213 // handling of onLost() by other callbacks.
Erik Klineb583b032017-02-22 12:58:24 +0900214 //
215 // These request*() calls can be deleted post oag/339444.
Erik Kline32858e12017-02-21 16:21:46 +0900216 return;
217 }
218
Erik Kline35bf06c2017-01-26 18:08:28 +0900219 cm().requestNetworkCapabilities(mMobileNetworkCallback);
220 cm().requestLinkProperties(mMobileNetworkCallback);
221 break;
Erik Klined2ec3912017-01-25 00:53:04 +0900222 }
Erik Kline9bba3402017-01-13 16:46:52 +0900223
Erik Klined2ec3912017-01-25 00:53:04 +0900224 // Requesting updates for mListenAllCallback is not currently possible
225 // because it's a "listen". Two possible solutions to getting updates
226 // about networks without waiting for a change (which might never come)
227 // are:
228 //
229 // [1] extend request{NetworkCapabilities,LinkProperties}() to
230 // take a Network argument and have ConnectivityService do
231 // what's required (if the network satisfies the request)
232 //
233 // [2] explicitly file a NetworkRequest for each connectivity type
234 // listed as a preferred upstream and wait for these callbacks
235 // to be notified (requires tracking many more callbacks).
236 //
237 // Until this is addressed, networks that exist prior to the "listen"
238 // registration and which do not subsequently change will not cause
239 // us to learn their NetworkCapabilities nor their LinkProperties.
240
241 // TODO: If sufficient information is available to select a more
242 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900243 notifyTarget(EVENT_ON_AVAILABLE, network);
244 }
245
246 private void handleNetCap(Network network, NetworkCapabilities newNc) {
Erik Klined2ec3912017-01-25 00:53:04 +0900247 final NetworkState prev = mNetworkMap.get(network);
248 if (prev == null || newNc.equals(prev.networkCapabilities)) {
249 // Ignore notifications about networks for which we have not yet
250 // received onAvailable() (should never happen) and any duplicate
251 // notifications (e.g. matching more than one of our callbacks).
Erik Kline9bba3402017-01-13 16:46:52 +0900252 return;
253 }
Erik Klined2ec3912017-01-25 00:53:04 +0900254
Erik Kline9bba3402017-01-13 16:46:52 +0900255 if (VDBG) {
256 Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
257 network, newNc));
258 }
259
Erik Klined2ec3912017-01-25 00:53:04 +0900260 mNetworkMap.put(network, new NetworkState(
261 null, prev.linkProperties, newNc, network, null, null));
262 // TODO: If sufficient information is available to select a more
263 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900264 notifyTarget(EVENT_ON_CAPABILITIES, network);
265 }
266
267 private void handleLinkProp(Network network, LinkProperties newLp) {
Erik Klined2ec3912017-01-25 00:53:04 +0900268 final NetworkState prev = mNetworkMap.get(network);
269 if (prev == null || newLp.equals(prev.linkProperties)) {
270 // Ignore notifications about networks for which we have not yet
271 // received onAvailable() (should never happen) and any duplicate
272 // notifications (e.g. matching more than one of our callbacks).
Erik Kline9bba3402017-01-13 16:46:52 +0900273 return;
274 }
Erik Klined2ec3912017-01-25 00:53:04 +0900275
Erik Kline9bba3402017-01-13 16:46:52 +0900276 if (VDBG) {
277 Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
278 network, newLp));
279 }
280
Erik Klined2ec3912017-01-25 00:53:04 +0900281 mNetworkMap.put(network, new NetworkState(
282 null, newLp, prev.networkCapabilities, network, null, null));
283 // TODO: If sufficient information is available to select a more
284 // preferable upstream, do so now and notify the target.
Erik Kline9bba3402017-01-13 16:46:52 +0900285 notifyTarget(EVENT_ON_LINKPROPERTIES, network);
286 }
287
Erik Klined2ec3912017-01-25 00:53:04 +0900288 private void handleLost(int callbackType, Network network) {
Erik Kline35bf06c2017-01-26 18:08:28 +0900289 if (callbackType == CALLBACK_TRACK_DEFAULT) {
Erik Klined2ec3912017-01-25 00:53:04 +0900290 mCurrentDefault = null;
291 // Receiving onLost() for a default network does not necessarily
292 // mean the network is gone. We wait for a separate notification
293 // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
294 // clearing all state.
Erik Kline9bba3402017-01-13 16:46:52 +0900295 return;
296 }
Erik Klined2ec3912017-01-25 00:53:04 +0900297
298 if (!mNetworkMap.containsKey(network)) {
299 // Ignore loss of networks about which we had not previously
300 // learned any information or for which we have already processed
301 // an onLost() notification.
302 return;
Erik Kline9bba3402017-01-13 16:46:52 +0900303 }
Erik Klined2ec3912017-01-25 00:53:04 +0900304
305 if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network);
306
307 // TODO: If sufficient information is available to select a more
308 // preferable upstream, do so now and notify the target. Likewise,
309 // if the current upstream network is gone, notify the target of the
310 // fact that we now have no upstream at all.
Erik Kline9bba3402017-01-13 16:46:52 +0900311 notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
312 }
313
314 // Fetch (and cache) a ConnectivityManager only if and when we need one.
315 private ConnectivityManager cm() {
316 if (mCM == null) {
317 mCM = mContext.getSystemService(ConnectivityManager.class);
318 }
319 return mCM;
320 }
321
322 /**
Erik Klineb583b032017-02-22 12:58:24 +0900323 * A NetworkCallback class that handles information of interest directly
324 * in the thread on which it is invoked. To avoid locking, this MUST be
325 * run on the same thread as the target state machine's handler.
Erik Kline9bba3402017-01-13 16:46:52 +0900326 */
327 private class UpstreamNetworkCallback extends NetworkCallback {
Erik Klined2ec3912017-01-25 00:53:04 +0900328 private final int mCallbackType;
329
330 UpstreamNetworkCallback(int callbackType) {
331 mCallbackType = callbackType;
332 }
333
Erik Kline9bba3402017-01-13 16:46:52 +0900334 @Override
335 public void onAvailable(Network network) {
Erik Klineb583b032017-02-22 12:58:24 +0900336 checkExpectedThread();
337 handleAvailable(mCallbackType, network);
Erik Kline9bba3402017-01-13 16:46:52 +0900338 }
339
340 @Override
341 public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
Erik Klineb583b032017-02-22 12:58:24 +0900342 checkExpectedThread();
343 handleNetCap(network, newNc);
Erik Kline9bba3402017-01-13 16:46:52 +0900344 }
345
346 @Override
347 public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
Erik Klineb583b032017-02-22 12:58:24 +0900348 checkExpectedThread();
349 handleLinkProp(network, newLp);
Erik Kline9bba3402017-01-13 16:46:52 +0900350 }
351
Erik Klineb583b032017-02-22 12:58:24 +0900352 // TODO: Handle onNetworkSuspended();
353 // TODO: Handle onNetworkResumed();
354
Erik Kline9bba3402017-01-13 16:46:52 +0900355 @Override
356 public void onLost(Network network) {
Erik Klineb583b032017-02-22 12:58:24 +0900357 checkExpectedThread();
358 handleLost(mCallbackType, network);
359 }
360
361 private void checkExpectedThread() {
362 if (Looper.myLooper() != mHandler.getLooper()) {
363 Log.wtf(TAG, "Handling callback in unexpected thread.");
364 }
Erik Kline9bba3402017-01-13 16:46:52 +0900365 }
366 }
367
368 private void releaseCallback(NetworkCallback cb) {
369 if (cb != null) cm().unregisterNetworkCallback(cb);
370 }
371
372 private void notifyTarget(int which, Network network) {
373 notifyTarget(which, mNetworkMap.get(network));
374 }
375
376 private void notifyTarget(int which, NetworkState netstate) {
377 mTarget.sendMessage(mWhat, which, 0, netstate);
378 }
379}