blob: 4c950de2585eeea709caa7d7b8f70fac5e4d0ae6 [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;
23import android.net.ConnectivityManager;
24import android.net.ConnectivityManager.NetworkCallback;
25import android.net.LinkProperties;
26import android.net.Network;
27import android.net.NetworkCapabilities;
28import android.net.NetworkRequest;
29import android.net.NetworkState;
30import android.util.Log;
31
Erik Kline885a9092017-01-16 16:27:22 +090032import com.android.internal.annotations.VisibleForTesting;
Erik Kline9bba3402017-01-13 16:46:52 +090033import com.android.internal.util.StateMachine;
34
35import java.util.HashMap;
36
37
38/**
39 * A class to centralize all the network and link properties information
40 * pertaining to the current and any potential upstream network.
41 *
42 * Calling #start() registers two callbacks: one to track the system default
43 * network and a second to specifically observe TYPE_MOBILE_DUN networks.
44 *
45 * The methods and data members of this class are only to be accessed and
46 * modified from the tethering master state machine thread. Any other
47 * access semantics would necessitate the addition of locking.
48 *
49 * TODO: Move upstream selection logic here.
50 *
51 * @hide
52 */
53public class UpstreamNetworkMonitor {
54 private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
55 private static final boolean DBG = false;
56 private static final boolean VDBG = false;
57
58 public static final int EVENT_ON_AVAILABLE = 1;
59 public static final int EVENT_ON_CAPABILITIES = 2;
60 public static final int EVENT_ON_LINKPROPERTIES = 3;
61 public static final int EVENT_ON_LOST = 4;
62
63 private final Context mContext;
64 private final StateMachine mTarget;
65 private final int mWhat;
66 private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
67 private ConnectivityManager mCM;
68 private NetworkCallback mDefaultNetworkCallback;
69 private NetworkCallback mDunTetheringCallback;
70 private NetworkCallback mMobileNetworkCallback;
71 private boolean mDunRequired;
72
73 public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
74 mContext = ctx;
75 mTarget = tgt;
76 mWhat = what;
77 }
78
Erik Kline885a9092017-01-16 16:27:22 +090079 @VisibleForTesting
80 public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
81 this(null, tgt, what);
82 mCM = cm;
83 }
84
Erik Kline9bba3402017-01-13 16:46:52 +090085 public void start() {
86 stop();
87
88 mDefaultNetworkCallback = new UpstreamNetworkCallback();
89 cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
90
91 final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
92 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
93 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
94 .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
95 .build();
96 mDunTetheringCallback = new UpstreamNetworkCallback();
97 cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback);
98 }
99
100 public void stop() {
101 releaseMobileNetworkRequest();
102
103 releaseCallback(mDefaultNetworkCallback);
104 mDefaultNetworkCallback = null;
105
106 releaseCallback(mDunTetheringCallback);
107 mDunTetheringCallback = null;
108
109 mNetworkMap.clear();
110 }
111
112 public void mobileUpstreamRequiresDun(boolean dunRequired) {
113 final boolean valueChanged = (mDunRequired != dunRequired);
114 mDunRequired = dunRequired;
115 if (valueChanged && mobileNetworkRequested()) {
116 releaseMobileNetworkRequest();
117 registerMobileNetworkRequest();
118 }
119 }
120
121 public boolean mobileNetworkRequested() {
122 return (mMobileNetworkCallback != null);
123 }
124
125 public void registerMobileNetworkRequest() {
126 if (mMobileNetworkCallback != null) return;
127
128 final NetworkRequest.Builder builder = new NetworkRequest.Builder()
129 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
130 if (mDunRequired) {
131 builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
132 .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
133 } else {
134 builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
135 }
136 final NetworkRequest mobileUpstreamRequest = builder.build();
137
138 // The existing default network and DUN callbacks will be notified.
139 // Therefore, to avoid duplicate notifications, we only register a no-op.
140 mMobileNetworkCallback = new NetworkCallback();
141
142 // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
143 // moderate callback time (once timeout callbacks are implemented). This might
144 // be useful for updating some UI. Additionally, we should definitely log a
145 // message to aid in any subsequent debugging
146 if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
147
Erik Klinedd8e8912017-01-18 16:08:06 +0900148 // The following use of the legacy type system cannot be removed until
149 // after upstream selection no longer finds networks by legacy type.
150 // See also b/34364553.
151 final int apnType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
152 cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, apnType);
Erik Kline9bba3402017-01-13 16:46:52 +0900153 }
154
155 public void releaseMobileNetworkRequest() {
156 if (mMobileNetworkCallback == null) return;
157
158 cm().unregisterNetworkCallback(mMobileNetworkCallback);
159 mMobileNetworkCallback = null;
160 }
161
162 public NetworkState lookup(Network network) {
163 return (network != null) ? mNetworkMap.get(network) : null;
164 }
165
166 private void handleAvailable(Network network) {
167 if (VDBG) {
168 Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
169 }
170 if (!mNetworkMap.containsKey(network)) {
171 mNetworkMap.put(network,
172 new NetworkState(null, null, null, network, null, null));
173 }
174
175 final ConnectivityManager cm = cm();
176
177 if (mDefaultNetworkCallback != null) {
178 cm.requestNetworkCapabilities(mDefaultNetworkCallback);
179 cm.requestLinkProperties(mDefaultNetworkCallback);
180 }
181
182 // Requesting updates for mDunTetheringCallback is not
183 // necessary. Because it's a listen, it will already have
184 // heard all NetworkCapabilities and LinkProperties updates
185 // since UpstreamNetworkMonitor was started. Because we
186 // start UpstreamNetworkMonitor before chooseUpstreamType()
187 // is ever invoked (it can register a DUN request) this is
188 // mostly safe. However, if a DUN network is already up for
189 // some reason (unlikely, because DUN is restricted and,
190 // unless the DUN network is shared with another APN, only
191 // the system can request it and this is the only part of
192 // the system that requests it) we won't know its
193 // LinkProperties or NetworkCapabilities.
194
195 notifyTarget(EVENT_ON_AVAILABLE, network);
196 }
197
198 private void handleNetCap(Network network, NetworkCapabilities newNc) {
199 if (!mNetworkMap.containsKey(network)) {
200 // Ignore updates for networks for which we have not yet
201 // received onAvailable() - which should never happen -
202 // or for which we have already received onLost().
203 return;
204 }
205 if (VDBG) {
206 Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
207 network, newNc));
208 }
209
210 final NetworkState prev = mNetworkMap.get(network);
211 mNetworkMap.put(network,
212 new NetworkState(null, prev.linkProperties, newNc,
213 network, null, null));
214 notifyTarget(EVENT_ON_CAPABILITIES, network);
215 }
216
217 private void handleLinkProp(Network network, LinkProperties newLp) {
218 if (!mNetworkMap.containsKey(network)) {
219 // Ignore updates for networks for which we have not yet
220 // received onAvailable() - which should never happen -
221 // or for which we have already received onLost().
222 return;
223 }
224 if (VDBG) {
225 Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
226 network, newLp));
227 }
228
229 final NetworkState prev = mNetworkMap.get(network);
230 mNetworkMap.put(network,
231 new NetworkState(null, newLp, prev.networkCapabilities,
232 network, null, null));
233 notifyTarget(EVENT_ON_LINKPROPERTIES, network);
234 }
235
236 private void handleLost(Network network) {
237 if (!mNetworkMap.containsKey(network)) {
238 // Ignore updates for networks for which we have not yet
239 // received onAvailable() - which should never happen -
240 // or for which we have already received onLost().
241 return;
242 }
243 if (VDBG) {
244 Log.d(TAG, "EVENT_ON_LOST for " + network);
245 }
246 notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
247 }
248
249 // Fetch (and cache) a ConnectivityManager only if and when we need one.
250 private ConnectivityManager cm() {
251 if (mCM == null) {
252 mCM = mContext.getSystemService(ConnectivityManager.class);
253 }
254 return mCM;
255 }
256
257 /**
258 * A NetworkCallback class that relays information of interest to the
259 * tethering master state machine thread for subsequent processing.
260 */
261 private class UpstreamNetworkCallback extends NetworkCallback {
262 @Override
263 public void onAvailable(Network network) {
264 mTarget.getHandler().post(() -> handleAvailable(network));
265 }
266
267 @Override
268 public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
269 mTarget.getHandler().post(() -> handleNetCap(network, newNc));
270 }
271
272 @Override
273 public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
274 mTarget.getHandler().post(() -> handleLinkProp(network, newLp));
275 }
276
277 @Override
278 public void onLost(Network network) {
279 mTarget.getHandler().post(() -> handleLost(network));
280 }
281 }
282
283 private void releaseCallback(NetworkCallback cb) {
284 if (cb != null) cm().unregisterNetworkCallback(cb);
285 }
286
287 private void notifyTarget(int which, Network network) {
288 notifyTarget(which, mNetworkMap.get(network));
289 }
290
291 private void notifyTarget(int which, NetworkState netstate) {
292 mTarget.sendMessage(mWhat, which, 0, netstate);
293 }
294}