blob: 6dd551e8525130e468496c6d8209757b2619298b [file] [log] [blame]
Jaikumar Ganesh15c74392010-12-21 22:31:44 -08001/*
2 * Copyright (C) 2010 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.bluetooth;
18
Vinit Deshapnde1f12cb52013-08-21 13:09:01 -070019import android.net.BaseNetworkStateTracker;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080020import android.content.Context;
21import android.net.ConnectivityManager;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070022import android.net.DhcpResults;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080023import android.net.LinkCapabilities;
24import android.net.LinkProperties;
25import android.net.NetworkInfo;
26import android.net.NetworkInfo.DetailedState;
27import android.net.NetworkStateTracker;
28import android.net.NetworkUtils;
29import android.os.Handler;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070030import android.os.Looper;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080031import android.os.Message;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070032import android.os.Messenger;
33import android.text.TextUtils;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080034import android.util.Log;
35
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070036import com.android.internal.util.AsyncChannel;
37
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080038import java.util.concurrent.atomic.AtomicBoolean;
39import java.util.concurrent.atomic.AtomicInteger;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070040import java.util.concurrent.atomic.AtomicReference;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080041
42/**
43 * This class tracks the data connection associated with Bluetooth
44 * reverse tethering. This is a singleton class and an instance will be
45 * created by ConnectivityService. BluetoothService will call into this
46 * when a reverse tethered connection needs to be activated.
47 *
48 * @hide
49 */
Vinit Deshapnde1f12cb52013-08-21 13:09:01 -070050public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080051 private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
52 private static final String TAG = "BluetoothTethering";
Matthew Xie563e4142012-10-09 22:10:37 -070053 private static final boolean DBG = true;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070054 private static final boolean VDBG = true;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080055
56 private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
57 private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
58 private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
59 private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
60
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070061 private final Object mLinkPropertiesLock = new Object();
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070062 private final Object mNetworkInfoLock = new Object();
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080063
64 private BluetoothPan mBluetoothPan;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070065 private static String mRevTetheredIface;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080066 /* For sending events to connectivity service handler */
67 private Handler mCsHandler;
Robert Greenwalt665e1ae2012-08-21 19:27:00 -070068 private static BluetoothTetheringDataTracker sInstance;
69 private BtdtHandler mBtdtHandler;
70 private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);
Jaikumar Ganesh15c74392010-12-21 22:31:44 -080071
72 private BluetoothTetheringDataTracker() {
73 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
74 mLinkProperties = new LinkProperties();
75 mLinkCapabilities = new LinkCapabilities();
76
77 mNetworkInfo.setIsAvailable(false);
78 setTeardownRequested(false);
79 }
80
81 public static synchronized BluetoothTetheringDataTracker getInstance() {
82 if (sInstance == null) sInstance = new BluetoothTetheringDataTracker();
83 return sInstance;
84 }
85
86 public Object Clone() throws CloneNotSupportedException {
87 throw new CloneNotSupportedException();
88 }
89
90 public void setTeardownRequested(boolean isRequested) {
91 mTeardownRequested.set(isRequested);
92 }
93
94 public boolean isTeardownRequested() {
95 return mTeardownRequested.get();
96 }
97
98 /**
99 * Begin monitoring connectivity
100 */
101 public void startMonitoring(Context context, Handler target) {
Matthew Xie563e4142012-10-09 22:10:37 -0700102 if (DBG) Log.d(TAG, "startMonitoring: target: " + target);
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800103 mContext = context;
104 mCsHandler = target;
Matthew Xie563e4142012-10-09 22:10:37 -0700105 if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800106 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
107 if (adapter != null) {
108 adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
109 }
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700110 mBtdtHandler = new BtdtHandler(target.getLooper(), this);
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800111 }
112
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800113 private BluetoothProfile.ServiceListener mProfileServiceListener =
114 new BluetoothProfile.ServiceListener() {
115 public void onServiceConnected(int profile, BluetoothProfile proxy) {
116 mBluetoothPan = (BluetoothPan) proxy;
117 }
118 public void onServiceDisconnected(int profile) {
119 mBluetoothPan = null;
120 }
121 };
122
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800123 /**
124 * Disable connectivity to a network
125 * TODO: do away with return value after making MobileDataStateTracker async
126 */
127 public boolean teardown() {
128 mTeardownRequested.set(true);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800129 if (mBluetoothPan != null) {
130 for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) {
131 mBluetoothPan.disconnect(device);
132 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800133 }
134 return true;
135 }
136
Irfan Sheriffda6da092012-08-16 12:49:23 -0700137 @Override
Wink Savilled747cbc2013-08-07 16:22:47 -0700138 public void captivePortalCheckCompleted(boolean isCaptivePortal) {
139 // not implemented
140 }
141
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800142 /**
143 * Re-enable connectivity to a network after a {@link #teardown()}.
144 */
145 public boolean reconnect() {
146 mTeardownRequested.set(false);
147 //Ignore
148 return true;
149 }
150
151 /**
152 * Turn the wireless radio off for a network.
153 * @param turnOn {@code true} to turn the radio on, {@code false}
154 */
155 public boolean setRadio(boolean turnOn) {
156 return true;
157 }
158
159 /**
160 * @return true - If are we currently tethered with another device.
161 */
162 public synchronized boolean isAvailable() {
163 return mNetworkInfo.isAvailable();
164 }
165
166 /**
167 * Tells the underlying networking system that the caller wants to
168 * begin using the named feature. The interpretation of {@code feature}
169 * is completely up to each networking implementation.
170 * @param feature the name of the feature to be used
171 * @param callingPid the process ID of the process that is issuing this request
172 * @param callingUid the user ID of the process that is issuing this request
173 * @return an integer value representing the outcome of the request.
174 * The interpretation of this value is specific to each networking
175 * implementation+feature combination, except that the value {@code -1}
176 * always indicates failure.
177 * TODO: needs to go away
178 */
179 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
180 return -1;
181 }
182
183 /**
184 * Tells the underlying networking system that the caller is finished
185 * using the named feature. The interpretation of {@code feature}
186 * is completely up to each networking implementation.
187 * @param feature the name of the feature that is no longer needed.
188 * @param callingPid the process ID of the process that is issuing this request
189 * @param callingUid the user ID of the process that is issuing this request
190 * @return an integer value representing the outcome of the request.
191 * The interpretation of this value is specific to each networking
192 * implementation+feature combination, except that the value {@code -1}
193 * always indicates failure.
194 * TODO: needs to go away
195 */
196 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
197 return -1;
198 }
199
Jeff Sharkey8e28b7d2011-08-19 02:24:24 -0700200 @Override
201 public void setUserDataEnable(boolean enabled) {
202 Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
203 }
204
205 @Override
206 public void setPolicyDataEnable(boolean enabled) {
207 Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800208 }
209
210 /**
211 * Check if private DNS route is set for the network
212 */
213 public boolean isPrivateDnsRouteSet() {
214 return mPrivateDnsRouteSet.get();
215 }
216
217 /**
218 * Set a flag indicating private DNS route is set
219 */
220 public void privateDnsRouteSet(boolean enabled) {
221 mPrivateDnsRouteSet.set(enabled);
222 }
223
224 /**
225 * Fetch NetworkInfo for the network
226 */
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700227 public NetworkInfo getNetworkInfo() {
228 synchronized (mNetworkInfoLock) {
229 return new NetworkInfo(mNetworkInfo);
230 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800231 }
232
233 /**
234 * Fetch LinkProperties for the network
235 */
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700236 public LinkProperties getLinkProperties() {
237 synchronized (mLinkPropertiesLock) {
238 return new LinkProperties(mLinkProperties);
239 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800240 }
241
242 /**
243 * A capability is an Integer/String pair, the capabilities
244 * are defined in the class LinkSocket#Key.
245 *
246 * @return a copy of this connections capabilities, may be empty but never null.
247 */
248 public LinkCapabilities getLinkCapabilities() {
249 return new LinkCapabilities(mLinkCapabilities);
250 }
251
252 /**
253 * Fetch default gateway address for the network
254 */
255 public int getDefaultGatewayAddr() {
256 return mDefaultGatewayAddr.get();
257 }
258
259 /**
260 * Check if default route is set
261 */
262 public boolean isDefaultRouteSet() {
263 return mDefaultRouteSet.get();
264 }
265
266 /**
267 * Set a flag indicating default route is set for the network
268 */
269 public void defaultRouteSet(boolean enabled) {
270 mDefaultRouteSet.set(enabled);
271 }
272
273 /**
274 * Return the system properties name associated with the tcp buffer sizes
275 * for this network.
276 */
277 public String getTcpBufferSizesPropName() {
278 return "net.tcp.buffersize.wifi";
279 }
280
fredc0f420372012-04-12 00:02:00 -0700281 private static short countPrefixLength(byte [] mask) {
282 short count = 0;
283 for (byte b : mask) {
284 for (int i = 0; i < 8; ++i) {
285 if ((b & (1 << i)) != 0) {
286 ++count;
287 }
288 }
289 }
290 return count;
291 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800292
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700293 void startReverseTether(final LinkProperties linkProperties) {
294 if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) {
295 Log.e(TAG, "attempted to reverse tether with empty interface");
296 return;
fredc0f420372012-04-12 00:02:00 -0700297 }
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700298 synchronized (mLinkPropertiesLock) {
299 if (mLinkProperties.getInterfaceName() != null) {
300 Log.e(TAG, "attempted to reverse tether while already in process");
301 return;
302 }
303 mLinkProperties = linkProperties;
304 }
305 Thread dhcpThread = new Thread(new Runnable() {
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800306 public void run() {
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800307 //Currently this thread runs independently.
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700308 DhcpResults dhcpResults = new DhcpResults();
309 boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(),
310 dhcpResults);
311 synchronized (mLinkPropertiesLock) {
312 if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) {
313 Log.e(TAG, "obsolete DHCP run aborted");
fredc0f420372012-04-12 00:02:00 -0700314 return;
315 }
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700316 if (!success) {
317 Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
fredc0f420372012-04-12 00:02:00 -0700318 return;
319 }
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700320 mLinkProperties = dhcpResults.linkProperties;
321 synchronized (mNetworkInfoLock) {
322 mNetworkInfo.setIsAvailable(true);
323 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
324 if (mCsHandler != null) {
325 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
326 new NetworkInfo(mNetworkInfo));
327 msg.sendToTarget();
328 }
329 }
330 return;
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800331 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800332 }
333 });
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700334 dhcpThread.start();
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800335 }
336
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700337 void stopReverseTether() {
338 synchronized (mLinkPropertiesLock) {
339 if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
340 Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
341 return;
342 }
343 NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
344 mLinkProperties.clear();
345 synchronized (mNetworkInfoLock) {
346 mNetworkInfo.setIsAvailable(false);
347 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
348
349 if (mCsHandler != null) {
350 mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)).
351 sendToTarget();
352 }
353 }
zzy7329e702012-04-27 12:15:57 -0700354 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800355 }
Robert Greenwaltd55a6b42011-03-25 13:09:25 -0700356
357 public void setDependencyMet(boolean met) {
358 // not supported on this network
359 }
Lorenzo Colitti69edd642013-03-07 11:01:12 -0800360
361 @Override
362 public void addStackedLink(LinkProperties link) {
363 mLinkProperties.addStackedLink(link);
364 }
365
366 @Override
367 public void removeStackedLink(LinkProperties link) {
368 mLinkProperties.removeStackedLink(link);
369 }
Robert Greenwalt665e1ae2012-08-21 19:27:00 -0700370
371 static class BtdtHandler extends Handler {
372 private AsyncChannel mStackChannel;
373 private final BluetoothTetheringDataTracker mBtdt;
374
375 BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) {
376 super(looper);
377 mBtdt = parent;
378 }
379
380 @Override
381 public void handleMessage(Message msg) {
382 switch (msg.what) {
383 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
384 if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED");
385 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
386 AsyncChannel ac = (AsyncChannel)msg.obj;
387 if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) {
388 Log.e(TAG, "Trying to set mAsyncChannel twice!");
389 } else {
390 ac.sendMessage(
391 AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
392 }
393 }
394 break;
395 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
396 if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED");
397 mBtdt.stopReverseTether();
398 mBtdt.mAsyncChannel.set(null);
399 break;
400 case NetworkStateTracker.EVENT_NETWORK_CONNECTED:
401 LinkProperties linkProperties = (LinkProperties)(msg.obj);
402 if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties);
403 mBtdt.startReverseTether(linkProperties);
404 break;
405 case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED:
406 linkProperties = (LinkProperties)(msg.obj);
407 if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
408 mBtdt.stopReverseTether();
409 break;
410 }
411 }
412 }
413
414 @Override
415 public void supplyMessenger(Messenger messenger) {
416 if (messenger != null) {
417 new AsyncChannel().connect(mContext, mBtdtHandler, messenger);
418 }
419 }
Jaikumar Ganesh15c74392010-12-21 22:31:44 -0800420}