1 /* 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 17 package android.bluetooth; 18 19 import android.os.IBinder; 20 import android.os.ServiceManager; 21 import android.os.INetworkManagementService; 22 import android.content.Context; 23 import android.net.ConnectivityManager; 24 import android.net.DhcpResults; 25 import android.net.LinkCapabilities; 26 import android.net.LinkProperties; 27 import android.net.NetworkInfo; 28 import android.net.NetworkInfo.DetailedState; 29 import android.net.NetworkStateTracker; 30 import android.net.NetworkUtils; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.Messenger; 35 import android.text.TextUtils; 36 import android.util.Log; 37 import java.net.InterfaceAddress; 38 import android.net.LinkAddress; 39 import android.net.RouteInfo; 40 import java.net.Inet4Address; 41 import android.os.SystemProperties; 42 43 import com.android.internal.util.AsyncChannel; 44 45 import java.util.concurrent.atomic.AtomicBoolean; 46 import java.util.concurrent.atomic.AtomicInteger; 47 import java.util.concurrent.atomic.AtomicReference; 48 49 /** 50 * This class tracks the data connection associated with Bluetooth 51 * reverse tethering. This is a singleton class and an instance will be 52 * created by ConnectivityService. BluetoothService will call into this 53 * when a reverse tethered connection needs to be activated. 54 * 55 * @hide 56 */ 57 public class BluetoothTetheringDataTracker implements NetworkStateTracker { 58 private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; 59 private static final String TAG = "BluetoothTethering"; 60 private static final boolean DBG = true; 61 private static final boolean VDBG = true; 62 63 private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); 64 private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); 65 private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); 66 private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); 67 68 private final Object mLinkPropertiesLock = new Object(); 69 private LinkProperties mLinkProperties; 70 71 private LinkCapabilities mLinkCapabilities; 72 73 private final Object mNetworkInfoLock = new Object(); 74 private NetworkInfo mNetworkInfo; 75 76 private BluetoothPan mBluetoothPan; 77 private static String mRevTetheredIface; 78 /* For sending events to connectivity service handler */ 79 private Handler mCsHandler; 80 protected Context mContext; 81 private static BluetoothTetheringDataTracker sInstance; 82 private BtdtHandler mBtdtHandler; 83 private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null); 84 85 private BluetoothTetheringDataTracker() { 86 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); 87 mLinkProperties = new LinkProperties(); 88 mLinkCapabilities = new LinkCapabilities(); 89 90 mNetworkInfo.setIsAvailable(false); 91 setTeardownRequested(false); 92 } 93 94 public static synchronized BluetoothTetheringDataTracker getInstance() { 95 if (sInstance == null) sInstance = new BluetoothTetheringDataTracker(); 96 return sInstance; 97 } 98 99 public Object Clone() throws CloneNotSupportedException { 100 throw new CloneNotSupportedException(); 101 } 102 103 public void setTeardownRequested(boolean isRequested) { 104 mTeardownRequested.set(isRequested); 105 } 106 107 public boolean isTeardownRequested() { 108 return mTeardownRequested.get(); 109 } 110 111 /** 112 * Begin monitoring connectivity 113 */ 114 public void startMonitoring(Context context, Handler target) { 115 if (DBG) Log.d(TAG, "startMonitoring: target: " + target); 116 mContext = context; 117 mCsHandler = target; 118 if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); 119 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 120 if (adapter != null) { 121 adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); 122 } 123 mBtdtHandler = new BtdtHandler(target.getLooper(), this); 124 } 125 126 private BluetoothProfile.ServiceListener mProfileServiceListener = 127 new BluetoothProfile.ServiceListener() { 128 public void onServiceConnected(int profile, BluetoothProfile proxy) { 129 mBluetoothPan = (BluetoothPan) proxy; 130 } 131 public void onServiceDisconnected(int profile) { 132 mBluetoothPan = null; 133 } 134 }; 135 136 /** 137 * Disable connectivity to a network 138 * TODO: do away with return value after making MobileDataStateTracker async 139 */ 140 public boolean teardown() { 141 mTeardownRequested.set(true); 142 if (mBluetoothPan != null) { 143 for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { 144 mBluetoothPan.disconnect(device); 145 } 146 } 147 return true; 148 } 149 150 @Override 151 public void captivePortalCheckComplete() { 152 // not implemented 153 } 154 155 @Override 156 public void captivePortalCheckCompleted(boolean isCaptivePortal) { 157 // not implemented 158 } 159 160 /** 161 * Re-enable connectivity to a network after a {@link #teardown()}. 162 */ 163 public boolean reconnect() { 164 mTeardownRequested.set(false); 165 //Ignore 166 return true; 167 } 168 169 /** 170 * Turn the wireless radio off for a network. 171 * @param turnOn {@code true} to turn the radio on, {@code false} 172 */ 173 public boolean setRadio(boolean turnOn) { 174 return true; 175 } 176 177 /** 178 * @return true - If are we currently tethered with another device. 179 */ 180 public synchronized boolean isAvailable() { 181 return mNetworkInfo.isAvailable(); 182 } 183 184 /** 185 * Tells the underlying networking system that the caller wants to 186 * begin using the named feature. The interpretation of {@code feature} 187 * is completely up to each networking implementation. 188 * @param feature the name of the feature to be used 189 * @param callingPid the process ID of the process that is issuing this request 190 * @param callingUid the user ID of the process that is issuing this request 191 * @return an integer value representing the outcome of the request. 192 * The interpretation of this value is specific to each networking 193 * implementation+feature combination, except that the value {@code -1} 194 * always indicates failure. 195 * TODO: needs to go away 196 */ 197 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { 198 return -1; 199 } 200 201 /** 202 * Tells the underlying networking system that the caller is finished 203 * using the named feature. The interpretation of {@code feature} 204 * is completely up to each networking implementation. 205 * @param feature the name of the feature that is no longer needed. 206 * @param callingPid the process ID of the process that is issuing this request 207 * @param callingUid the user ID of the process that is issuing this request 208 * @return an integer value representing the outcome of the request. 209 * The interpretation of this value is specific to each networking 210 * implementation+feature combination, except that the value {@code -1} 211 * always indicates failure. 212 * TODO: needs to go away 213 */ 214 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { 215 return -1; 216 } 217 218 @Override 219 public void setUserDataEnable(boolean enabled) { 220 Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); 221 } 222 223 @Override 224 public void setPolicyDataEnable(boolean enabled) { 225 Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); 226 } 227 228 /** 229 * Check if private DNS route is set for the network 230 */ 231 public boolean isPrivateDnsRouteSet() { 232 return mPrivateDnsRouteSet.get(); 233 } 234 235 /** 236 * Set a flag indicating private DNS route is set 237 */ 238 public void privateDnsRouteSet(boolean enabled) { 239 mPrivateDnsRouteSet.set(enabled); 240 } 241 242 /** 243 * Fetch NetworkInfo for the network 244 */ 245 public NetworkInfo getNetworkInfo() { 246 synchronized (mNetworkInfoLock) { 247 return new NetworkInfo(mNetworkInfo); 248 } 249 } 250 251 /** 252 * Fetch LinkProperties for the network 253 */ 254 public LinkProperties getLinkProperties() { 255 synchronized (mLinkPropertiesLock) { 256 return new LinkProperties(mLinkProperties); 257 } 258 } 259 260 /** 261 * A capability is an Integer/String pair, the capabilities 262 * are defined in the class LinkSocket#Key. 263 * 264 * @return a copy of this connections capabilities, may be empty but never null. 265 */ 266 public LinkCapabilities getLinkCapabilities() { 267 return new LinkCapabilities(mLinkCapabilities); 268 } 269 270 /** 271 * Fetch default gateway address for the network 272 */ 273 public int getDefaultGatewayAddr() { 274 return mDefaultGatewayAddr.get(); 275 } 276 277 /** 278 * Check if default route is set 279 */ 280 public boolean isDefaultRouteSet() { 281 return mDefaultRouteSet.get(); 282 } 283 284 /** 285 * Set a flag indicating default route is set for the network 286 */ 287 public void defaultRouteSet(boolean enabled) { 288 mDefaultRouteSet.set(enabled); 289 } 290 291 /** 292 * Return the system properties name associated with the tcp buffer sizes 293 * for this network. 294 */ 295 public String getTcpBufferSizesPropName() { 296 return "net.tcp.buffersize.wifi"; 297 } 298 299 private static short countPrefixLength(byte [] mask) { 300 short count = 0; 301 for (byte b : mask) { 302 for (int i = 0; i < 8; ++i) { 303 if ((b & (1 << i)) != 0) { 304 ++count; 305 } 306 } 307 } 308 return count; 309 } 310 311 void startReverseTether(final LinkProperties linkProperties) { 312 if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) { 313 Log.e(TAG, "attempted to reverse tether with empty interface"); 314 return; 315 } 316 synchronized (mLinkPropertiesLock) { 317 if (mLinkProperties.getInterfaceName() != null) { 318 Log.e(TAG, "attempted to reverse tether while already in process"); 319 return; 320 } 321 mLinkProperties = linkProperties; 322 } 323 Thread dhcpThread = new Thread(new Runnable() { 324 public void run() { 325 //Currently this thread runs independently. 326 DhcpResults dhcpResults = new DhcpResults(); 327 boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(), 328 dhcpResults); 329 synchronized (mLinkPropertiesLock) { 330 if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) { 331 Log.e(TAG, "obsolete DHCP run aborted"); 332 return; 333 } 334 if (!success) { 335 Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); 336 return; 337 } 338 mLinkProperties = dhcpResults.linkProperties; 339 synchronized (mNetworkInfoLock) { 340 mNetworkInfo.setIsAvailable(true); 341 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); 342 if (mCsHandler != null) { 343 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, 344 new NetworkInfo(mNetworkInfo)); 345 msg.sendToTarget(); 346 } 347 } 348 return; 349 } 350 } 351 }); 352 dhcpThread.start(); 353 } 354 355 void stopReverseTether() { 356 synchronized (mLinkPropertiesLock) { 357 if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) { 358 Log.e(TAG, "attempted to stop reverse tether with nothing tethered"); 359 return; 360 } 361 NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName()); 362 mLinkProperties.clear(); 363 synchronized (mNetworkInfoLock) { 364 mNetworkInfo.setIsAvailable(false); 365 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); 366 367 if (mCsHandler != null) { 368 mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)). 369 sendToTarget(); 370 } 371 } 372 } 373 } 374 375 public void setDependencyMet(boolean met) { 376 // not supported on this network 377 } 378 379 @Override 380 public void addStackedLink(LinkProperties link) { 381 mLinkProperties.addStackedLink(link); 382 } 383 384 @Override 385 public void removeStackedLink(LinkProperties link) { 386 mLinkProperties.removeStackedLink(link); 387 } 388 389 static class BtdtHandler extends Handler { 390 private AsyncChannel mStackChannel; 391 private final BluetoothTetheringDataTracker mBtdt; 392 393 BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) { 394 super(looper); 395 mBtdt = parent; 396 } 397 398 @Override 399 public void handleMessage(Message msg) { 400 switch (msg.what) { 401 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 402 if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED"); 403 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 404 AsyncChannel ac = (AsyncChannel)msg.obj; 405 if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) { 406 Log.e(TAG, "Trying to set mAsyncChannel twice!"); 407 } else { 408 ac.sendMessage( 409 AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 410 } 411 } 412 break; 413 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 414 if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED"); 415 mBtdt.stopReverseTether(); 416 mBtdt.mAsyncChannel.set(null); 417 break; 418 case NetworkStateTracker.EVENT_NETWORK_CONNECTED: 419 LinkProperties linkProperties = (LinkProperties)(msg.obj); 420 if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties); 421 mBtdt.startReverseTether(linkProperties); 422 break; 423 case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED: 424 linkProperties = (LinkProperties)(msg.obj); 425 if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); 426 mBtdt.stopReverseTether(); 427 break; 428 } 429 } 430 } 431 432 @Override 433 public void supplyMessenger(Messenger messenger) { 434 if (messenger != null) { 435 new AsyncChannel().connect(mContext, mBtdtHandler, messenger); 436 } 437 } 438 } 439