1 /* 2 * Copyright (C) 2011 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.server; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothPan; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.BluetoothTetheringDataTracker; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.res.Resources.NotFoundException; 29 import android.net.ConnectivityManager; 30 import android.net.InterfaceConfiguration; 31 import android.net.LinkAddress; 32 import android.net.NetworkUtils; 33 import android.os.IBinder; 34 import android.os.INetworkManagementService; 35 import android.os.ServiceManager; 36 import android.util.Log; 37 38 import java.net.InetAddress; 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.List; 42 43 /** 44 * This handles the PAN profile. All calls into this are made 45 * from Bluetooth Service. 46 */ 47 final class BluetoothPanProfileHandler { 48 private static final String TAG = "BluetoothPanProfileHandler"; 49 private static final boolean DBG = true; 50 51 private ArrayList<String> mBluetoothIfaceAddresses; 52 private int mMaxPanDevices; 53 54 private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; 55 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; 56 private static final int BLUETOOTH_PREFIX_LENGTH = 24; 57 public static BluetoothPanProfileHandler sInstance; 58 private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; 59 private boolean mTetheringOn; 60 private Context mContext; 61 private BluetoothService mBluetoothService; 62 63 static final String NAP_ROLE = "nap"; 64 static final String NAP_BRIDGE = "pan1"; 65 BluetoothPanProfileHandler(Context context, BluetoothService service)66 private BluetoothPanProfileHandler(Context context, BluetoothService service) { 67 mContext = context; 68 mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); 69 mBluetoothService = service; 70 mTetheringOn = false; 71 mBluetoothIfaceAddresses = new ArrayList<String>(); 72 try { 73 mMaxPanDevices = context.getResources().getInteger( 74 com.android.internal.R.integer.config_max_pan_devices); 75 } catch (NotFoundException e) { 76 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; 77 } 78 } 79 getInstance(Context context, BluetoothService service)80 static BluetoothPanProfileHandler getInstance(Context context, 81 BluetoothService service) { 82 if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service); 83 return sInstance; 84 } 85 isTetheringOn()86 boolean isTetheringOn() { 87 return mTetheringOn; 88 } 89 allowIncomingTethering()90 boolean allowIncomingTethering() { 91 if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) 92 return true; 93 return false; 94 } 95 96 private BroadcastReceiver mTetheringReceiver = null; 97 setBluetoothTethering(boolean value)98 void setBluetoothTethering(boolean value) { 99 if (!value) { 100 disconnectPanServerDevices(); 101 } 102 103 if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) { 104 IntentFilter filter = new IntentFilter(); 105 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 106 mTetheringReceiver = new BroadcastReceiver() { 107 @Override 108 public void onReceive(Context context, Intent intent) { 109 if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) 110 == BluetoothAdapter.STATE_ON) { 111 mTetheringOn = true; 112 mContext.unregisterReceiver(mTetheringReceiver); 113 } 114 } 115 }; 116 mContext.registerReceiver(mTetheringReceiver, filter); 117 } else { 118 mTetheringOn = value; 119 } 120 } 121 getPanDeviceConnectionState(BluetoothDevice device)122 int getPanDeviceConnectionState(BluetoothDevice device) { 123 BluetoothPanDevice panDevice = mPanDevices.get(device); 124 if (panDevice == null) { 125 return BluetoothPan.STATE_DISCONNECTED; 126 } 127 return panDevice.mState; 128 } 129 connectPanDevice(BluetoothDevice device)130 boolean connectPanDevice(BluetoothDevice device) { 131 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 132 if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); 133 if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) { 134 errorLog(device + " already connected to PAN"); 135 } 136 137 int connectedCount = 0; 138 for (BluetoothDevice panDevice: mPanDevices.keySet()) { 139 if (getPanDeviceConnectionState(panDevice) == BluetoothPan.STATE_CONNECTED) { 140 connectedCount ++; 141 } 142 } 143 if (connectedCount > 8) { 144 debugLog(device + " could not connect to PAN because 8 other devices are" 145 + "already connected"); 146 return false; 147 } 148 149 // Send interface as null as it is not known 150 handlePanDeviceStateChange(device, null, BluetoothPan.STATE_CONNECTING, 151 BluetoothPan.LOCAL_PANU_ROLE); 152 if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) { 153 debugLog("connecting to PAN"); 154 return true; 155 } else { 156 handlePanDeviceStateChange(device, null, BluetoothPan.STATE_DISCONNECTED, 157 BluetoothPan.LOCAL_PANU_ROLE); 158 errorLog("could not connect to PAN"); 159 return false; 160 } 161 } 162 disconnectPanServerDevices()163 private boolean disconnectPanServerDevices() { 164 debugLog("disconnect all PAN devices"); 165 166 for (BluetoothDevice device: mPanDevices.keySet()) { 167 BluetoothPanDevice panDevice = mPanDevices.get(device); 168 int state = panDevice.mState; 169 if (state == BluetoothPan.STATE_CONNECTED && 170 panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { 171 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 172 173 handlePanDeviceStateChange(device, panDevice.mIface, 174 BluetoothPan.STATE_DISCONNECTING, panDevice.mLocalRole); 175 176 if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, 177 device.getAddress(), 178 panDevice.mIface)) { 179 errorLog("could not disconnect Pan Server Device "+device.getAddress()); 180 181 // Restore prev state 182 handlePanDeviceStateChange(device, panDevice.mIface, state, 183 panDevice.mLocalRole); 184 185 return false; 186 } 187 } 188 } 189 return true; 190 } 191 getConnectedPanDevices()192 List<BluetoothDevice> getConnectedPanDevices() { 193 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 194 195 for (BluetoothDevice device: mPanDevices.keySet()) { 196 if (getPanDeviceConnectionState(device) == BluetoothPan.STATE_CONNECTED) { 197 devices.add(device); 198 } 199 } 200 return devices; 201 } 202 getPanDevicesMatchingConnectionStates(int[] states)203 List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) { 204 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 205 206 for (BluetoothDevice device: mPanDevices.keySet()) { 207 int panDeviceState = getPanDeviceConnectionState(device); 208 for (int state : states) { 209 if (state == panDeviceState) { 210 devices.add(device); 211 break; 212 } 213 } 214 } 215 return devices; 216 } 217 disconnectPanDevice(BluetoothDevice device)218 boolean disconnectPanDevice(BluetoothDevice device) { 219 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 220 debugLog("disconnect PAN(" + objectPath + ")"); 221 222 int state = getPanDeviceConnectionState(device); 223 if (state != BluetoothPan.STATE_CONNECTED) { 224 debugLog(device + " already disconnected from PAN"); 225 return false; 226 } 227 228 BluetoothPanDevice panDevice = mPanDevices.get(device); 229 230 if (panDevice == null) { 231 errorLog("No record for this Pan device:" + device); 232 return false; 233 } 234 235 handlePanDeviceStateChange(device, panDevice.mIface, BluetoothPan.STATE_DISCONNECTING, 236 panDevice.mLocalRole); 237 if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { 238 if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(), 239 panDevice.mIface)) { 240 // Restore prev state, this shouldn't happen 241 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole); 242 return false; 243 } 244 } else { 245 if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) { 246 // Restore prev state, this shouldn't happen 247 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole); 248 return false; 249 } 250 } 251 return true; 252 } 253 handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int role)254 void handlePanDeviceStateChange(BluetoothDevice device, 255 String iface, int state, int role) { 256 int prevState; 257 String ifaceAddr = null; 258 BluetoothPanDevice panDevice = mPanDevices.get(device); 259 260 if (panDevice == null) { 261 prevState = BluetoothPan.STATE_DISCONNECTED; 262 } else { 263 prevState = panDevice.mState; 264 ifaceAddr = panDevice.mIfaceAddr; 265 } 266 if (prevState == state) return; 267 268 if (role == BluetoothPan.LOCAL_NAP_ROLE) { 269 if (state == BluetoothPan.STATE_CONNECTED) { 270 ifaceAddr = enableTethering(iface); 271 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); 272 } else if (state == BluetoothPan.STATE_DISCONNECTED) { 273 if (ifaceAddr != null) { 274 mBluetoothIfaceAddresses.remove(ifaceAddr); 275 ifaceAddr = null; 276 } 277 } 278 } else { 279 // PANU Role = reverse Tether 280 if (state == BluetoothPan.STATE_CONNECTED) { 281 BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); 282 } else if (state == BluetoothPan.STATE_DISCONNECTED && 283 (prevState == BluetoothPan.STATE_CONNECTED || 284 prevState == BluetoothPan.STATE_DISCONNECTING)) { 285 BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface); 286 } 287 } 288 289 if (panDevice == null) { 290 panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role); 291 mPanDevices.put(device, panDevice); 292 } else { 293 panDevice.mState = state; 294 panDevice.mIfaceAddr = ifaceAddr; 295 panDevice.mLocalRole = role; 296 panDevice.mIface = iface; 297 } 298 299 Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 300 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 301 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState); 302 intent.putExtra(BluetoothPan.EXTRA_STATE, state); 303 intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); 304 mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); 305 306 debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state); 307 mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.PAN, state, 308 prevState); 309 } 310 311 private class BluetoothPanDevice { 312 private int mState; 313 private String mIfaceAddr; 314 private String mIface; 315 private int mLocalRole; // Which local role is this PAN device bound to 316 BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole)317 BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { 318 mState = state; 319 mIfaceAddr = ifaceAddr; 320 mIface = iface; 321 mLocalRole = localRole; 322 } 323 } 324 createNewTetheringAddressLocked()325 private String createNewTetheringAddressLocked() { 326 if (getConnectedPanDevices().size() == mMaxPanDevices) { 327 debugLog ("Max PAN device connections reached"); 328 return null; 329 } 330 String address = BLUETOOTH_IFACE_ADDR_START; 331 while (true) { 332 if (mBluetoothIfaceAddresses.contains(address)) { 333 String[] addr = address.split("\\."); 334 Integer newIp = Integer.parseInt(addr[2]) + 1; 335 address = address.replace(addr[2], newIp.toString()); 336 } else { 337 break; 338 } 339 } 340 mBluetoothIfaceAddresses.add(address); 341 return address; 342 } 343 344 // configured when we start tethering enableTethering(String iface)345 private String enableTethering(String iface) { 346 debugLog("updateTetherState:" + iface); 347 348 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 349 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 350 ConnectivityManager cm = 351 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 352 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); 353 354 // bring toggle the interfaces 355 String[] currentIfaces = new String[0]; 356 try { 357 currentIfaces = service.listInterfaces(); 358 } catch (Exception e) { 359 Log.e(TAG, "Error listing Interfaces :" + e); 360 return null; 361 } 362 363 boolean found = false; 364 for (String currIface: currentIfaces) { 365 if (currIface.equals(iface)) { 366 found = true; 367 break; 368 } 369 } 370 371 if (!found) return null; 372 373 String address = createNewTetheringAddressLocked(); 374 if (address == null) return null; 375 376 InterfaceConfiguration ifcg = null; 377 try { 378 ifcg = service.getInterfaceConfig(iface); 379 if (ifcg != null) { 380 final LinkAddress linkAddr = ifcg.getLinkAddress(); 381 InetAddress addr = null; 382 if (linkAddr == null || (addr = linkAddr.getAddress()) == null || 383 addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) || 384 addr.equals(NetworkUtils.numericToInetAddress("::0"))) { 385 addr = NetworkUtils.numericToInetAddress(address); 386 } 387 ifcg.setInterfaceUp(); 388 ifcg.clearFlag("running"); 389 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH)); 390 service.setInterfaceConfig(iface, ifcg); 391 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 392 Log.e(TAG, "Error tethering "+iface); 393 } 394 } 395 } catch (Exception e) { 396 Log.e(TAG, "Error configuring interface " + iface + ", :" + e); 397 return null; 398 } 399 return address; 400 } 401 debugLog(String msg)402 private static void debugLog(String msg) { 403 if (DBG) Log.d(TAG, msg); 404 } 405 errorLog(String msg)406 private static void errorLog(String msg) { 407 Log.e(TAG, msg); 408 } 409 } 410