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