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.DhcpInfoInternal; 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.Message; 33 import android.util.Log; 34 import java.net.InterfaceAddress; 35 import android.net.LinkAddress; 36 import android.net.RouteInfo; 37 import java.net.Inet4Address; 38 import android.os.SystemProperties; 39 40 import java.util.concurrent.atomic.AtomicBoolean; 41 import java.util.concurrent.atomic.AtomicInteger; 42 43 /** 44 * This class tracks the data connection associated with Bluetooth 45 * reverse tethering. This is a singleton class and an instance will be 46 * created by ConnectivityService. BluetoothService will call into this 47 * when a reverse tethered connection needs to be activated. 48 * 49 * @hide 50 */ 51 public class BluetoothTetheringDataTracker implements NetworkStateTracker { 52 private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; 53 private static final String TAG = "BluetoothTethering"; 54 private static final boolean DBG = true; 55 private static final boolean VDBG = false; 56 57 private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); 58 private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); 59 private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); 60 private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); 61 62 private LinkProperties mLinkProperties; 63 private LinkCapabilities mLinkCapabilities; 64 private NetworkInfo mNetworkInfo; 65 66 private BluetoothPan mBluetoothPan; 67 private static String mIface; 68 private Thread mDhcpThread; 69 /* For sending events to connectivity service handler */ 70 private Handler mCsHandler; 71 private Context mContext; 72 public static BluetoothTetheringDataTracker sInstance; 73 BluetoothTetheringDataTracker()74 private BluetoothTetheringDataTracker() { 75 mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); 76 mLinkProperties = new LinkProperties(); 77 mLinkCapabilities = new LinkCapabilities(); 78 79 mNetworkInfo.setIsAvailable(false); 80 setTeardownRequested(false); 81 } 82 getInstance()83 public static synchronized BluetoothTetheringDataTracker getInstance() { 84 if (sInstance == null) sInstance = new BluetoothTetheringDataTracker(); 85 return sInstance; 86 } 87 Clone()88 public Object Clone() throws CloneNotSupportedException { 89 throw new CloneNotSupportedException(); 90 } 91 setTeardownRequested(boolean isRequested)92 public void setTeardownRequested(boolean isRequested) { 93 mTeardownRequested.set(isRequested); 94 } 95 isTeardownRequested()96 public boolean isTeardownRequested() { 97 return mTeardownRequested.get(); 98 } 99 100 /** 101 * Begin monitoring connectivity 102 */ startMonitoring(Context context, Handler target)103 public void startMonitoring(Context context, Handler target) { 104 if (DBG) Log.d(TAG, "startMonitoring: target: " + target); 105 mContext = context; 106 mCsHandler = target; 107 if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler); 108 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 109 if (adapter != null) { 110 adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); 111 } 112 } 113 114 private BluetoothProfile.ServiceListener mProfileServiceListener = 115 new BluetoothProfile.ServiceListener() { 116 public void onServiceConnected(int profile, BluetoothProfile proxy) { 117 mBluetoothPan = (BluetoothPan) proxy; 118 } 119 public void onServiceDisconnected(int profile) { 120 mBluetoothPan = null; 121 } 122 }; 123 124 /** 125 * Disable connectivity to a network 126 * TODO: do away with return value after making MobileDataStateTracker async 127 */ teardown()128 public boolean teardown() { 129 mTeardownRequested.set(true); 130 if (mBluetoothPan != null) { 131 for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { 132 mBluetoothPan.disconnect(device); 133 } 134 } 135 return true; 136 } 137 138 @Override captivePortalCheckComplete()139 public void captivePortalCheckComplete() { 140 // not implemented 141 } 142 143 /** 144 * Re-enable connectivity to a network after a {@link #teardown()}. 145 */ reconnect()146 public boolean reconnect() { 147 mTeardownRequested.set(false); 148 //Ignore 149 return true; 150 } 151 152 /** 153 * Turn the wireless radio off for a network. 154 * @param turnOn {@code true} to turn the radio on, {@code false} 155 */ setRadio(boolean turnOn)156 public boolean setRadio(boolean turnOn) { 157 return true; 158 } 159 160 /** 161 * @return true - If are we currently tethered with another device. 162 */ isAvailable()163 public synchronized boolean isAvailable() { 164 return mNetworkInfo.isAvailable(); 165 } 166 167 /** 168 * Tells the underlying networking system that the caller wants to 169 * begin using the named feature. The interpretation of {@code feature} 170 * is completely up to each networking implementation. 171 * @param feature the name of the feature to be used 172 * @param callingPid the process ID of the process that is issuing this request 173 * @param callingUid the user ID of the process that is issuing this request 174 * @return an integer value representing the outcome of the request. 175 * The interpretation of this value is specific to each networking 176 * implementation+feature combination, except that the value {@code -1} 177 * always indicates failure. 178 * TODO: needs to go away 179 */ startUsingNetworkFeature(String feature, int callingPid, int callingUid)180 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { 181 return -1; 182 } 183 184 /** 185 * Tells the underlying networking system that the caller is finished 186 * 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 that is no longer needed. 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 */ stopUsingNetworkFeature(String feature, int callingPid, int callingUid)197 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { 198 return -1; 199 } 200 201 @Override setUserDataEnable(boolean enabled)202 public void setUserDataEnable(boolean enabled) { 203 Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); 204 } 205 206 @Override setPolicyDataEnable(boolean enabled)207 public void setPolicyDataEnable(boolean enabled) { 208 Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); 209 } 210 211 /** 212 * Check if private DNS route is set for the network 213 */ isPrivateDnsRouteSet()214 public boolean isPrivateDnsRouteSet() { 215 return mPrivateDnsRouteSet.get(); 216 } 217 218 /** 219 * Set a flag indicating private DNS route is set 220 */ privateDnsRouteSet(boolean enabled)221 public void privateDnsRouteSet(boolean enabled) { 222 mPrivateDnsRouteSet.set(enabled); 223 } 224 225 /** 226 * Fetch NetworkInfo for the network 227 */ getNetworkInfo()228 public synchronized NetworkInfo getNetworkInfo() { 229 return mNetworkInfo; 230 } 231 232 /** 233 * Fetch LinkProperties for the network 234 */ getLinkProperties()235 public synchronized LinkProperties getLinkProperties() { 236 return new LinkProperties(mLinkProperties); 237 } 238 239 /** 240 * A capability is an Integer/String pair, the capabilities 241 * are defined in the class LinkSocket#Key. 242 * 243 * @return a copy of this connections capabilities, may be empty but never null. 244 */ getLinkCapabilities()245 public LinkCapabilities getLinkCapabilities() { 246 return new LinkCapabilities(mLinkCapabilities); 247 } 248 249 /** 250 * Fetch default gateway address for the network 251 */ getDefaultGatewayAddr()252 public int getDefaultGatewayAddr() { 253 return mDefaultGatewayAddr.get(); 254 } 255 256 /** 257 * Check if default route is set 258 */ isDefaultRouteSet()259 public boolean isDefaultRouteSet() { 260 return mDefaultRouteSet.get(); 261 } 262 263 /** 264 * Set a flag indicating default route is set for the network 265 */ defaultRouteSet(boolean enabled)266 public void defaultRouteSet(boolean enabled) { 267 mDefaultRouteSet.set(enabled); 268 } 269 270 /** 271 * Return the system properties name associated with the tcp buffer sizes 272 * for this network. 273 */ getTcpBufferSizesPropName()274 public String getTcpBufferSizesPropName() { 275 return "net.tcp.buffersize.wifi"; 276 } 277 countPrefixLength(byte [] mask)278 private static short countPrefixLength(byte [] mask) { 279 short count = 0; 280 for (byte b : mask) { 281 for (int i = 0; i < 8; ++i) { 282 if ((b & (1 << i)) != 0) { 283 ++count; 284 } 285 } 286 } 287 return count; 288 } 289 290 readLinkProperty(String iface)291 private boolean readLinkProperty(String iface) { 292 String DhcpPrefix = "dhcp." + iface + "."; 293 String ip = SystemProperties.get(DhcpPrefix + "ipaddress"); 294 String dns1 = SystemProperties.get(DhcpPrefix + "dns1"); 295 String dns2 = SystemProperties.get(DhcpPrefix + "dns2"); 296 String gateway = SystemProperties.get(DhcpPrefix + "gateway"); 297 String mask = SystemProperties.get(DhcpPrefix + "mask"); 298 if(ip.isEmpty() || gateway.isEmpty()) { 299 Log.e(TAG, "readLinkProperty, ip: " + ip + ", gateway: " + gateway + ", can not be empty"); 300 return false; 301 } 302 int PrefixLen = countPrefixLength(NetworkUtils.numericToInetAddress(mask).getAddress()); 303 mLinkProperties.addLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(ip), PrefixLen)); 304 RouteInfo ri = new RouteInfo(NetworkUtils.numericToInetAddress(gateway)); 305 mLinkProperties.addRoute(ri); 306 if(!dns1.isEmpty()) 307 mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns1)); 308 if(!dns2.isEmpty()) 309 mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns2)); 310 mLinkProperties.setInterfaceName(iface); 311 return true; 312 } startReverseTether(String iface)313 public synchronized void startReverseTether(String iface) { 314 mIface = iface; 315 if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); 316 mDhcpThread = new Thread(new Runnable() { 317 public void run() { 318 //TODO(): Add callbacks for failure and success case. 319 //Currently this thread runs independently. 320 if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); 321 String DhcpResultName = "dhcp." + mIface + ".result";; 322 String result = ""; 323 if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); 324 for(int i = 0; i < 30*5; i++) { 325 try { Thread.sleep(200); } catch (InterruptedException ie) { return;} 326 result = SystemProperties.get(DhcpResultName); 327 if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result); 328 if(result.equals("failed")) { 329 Log.e(TAG, "startReverseTether, failed to start dhcp service"); 330 return; 331 } 332 if(result.equals("ok")) { 333 if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result); 334 if(readLinkProperty(mIface)) { 335 336 mNetworkInfo.setIsAvailable(true); 337 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); 338 339 if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); 340 if(mCsHandler != null) { 341 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); 342 msg.sendToTarget(); 343 344 msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); 345 msg.sendToTarget(); 346 } 347 } 348 return; 349 } 350 } 351 Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result); 352 } 353 }); 354 mDhcpThread.start(); 355 } 356 stopReverseTether()357 public synchronized void stopReverseTether() { 358 //NetworkUtils.stopDhcp(iface); 359 if(mDhcpThread != null && mDhcpThread.isAlive()) { 360 mDhcpThread.interrupt(); 361 try { mDhcpThread.join(); } catch (InterruptedException ie) { return; } 362 } 363 mLinkProperties.clear(); 364 mNetworkInfo.setIsAvailable(false); 365 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); 366 367 Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); 368 msg.sendToTarget(); 369 370 msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); 371 msg.sendToTarget(); 372 } 373 setDependencyMet(boolean met)374 public void setDependencyMet(boolean met) { 375 // not supported on this network 376 } 377 } 378