1/* 2 * Copyright (C) 2012 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 com.android.bluetooth.pan; 18 19import android.app.Service; 20import android.bluetooth.BluetoothDevice; 21import android.bluetooth.BluetoothPan; 22import android.bluetooth.BluetoothProfile; 23import android.bluetooth.IBluetooth; 24import android.bluetooth.IBluetoothPan; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.PackageManager; 28import android.content.res.Resources.NotFoundException; 29import android.net.ConnectivityManager; 30import android.net.InterfaceConfiguration; 31import android.net.LinkAddress; 32import android.net.NetworkUtils; 33import android.os.Handler; 34import android.os.IBinder; 35import android.os.INetworkManagementService; 36import android.os.Message; 37import android.os.RemoteException; 38import android.os.ServiceManager; 39import android.os.UserManager; 40import android.provider.Settings; 41import android.util.Log; 42 43import com.android.bluetooth.btservice.ProfileService; 44import com.android.bluetooth.Utils; 45 46import java.net.InetAddress; 47import java.util.ArrayList; 48import java.util.Collections; 49import java.util.HashMap; 50import java.util.List; 51import java.util.Map; 52 53import android.content.SharedPreferences; 54import android.content.SharedPreferences.Editor; 55 56/** 57 * Provides Bluetooth Pan Device profile, as a service in 58 * the Bluetooth application. 59 * @hide 60 */ 61public class PanService extends ProfileService { 62 private static final String TAG = "PanService"; 63 private static final boolean DBG = false; 64 65 private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; 66 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; 67 private static final int BLUETOOTH_PREFIX_LENGTH = 24; 68 69 private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; 70 private ArrayList<String> mBluetoothIfaceAddresses; 71 private int mMaxPanDevices; 72 private String mPanIfName; 73 private boolean mNativeAvailable; 74 75 private static final int MESSAGE_CONNECT = 1; 76 private static final int MESSAGE_DISCONNECT = 2; 77 private static final int MESSAGE_CONNECT_STATE_CHANGED = 11; 78 private boolean mTetherOn = false; 79 private static final String PAN_PREFERENCE_FILE = "PANMGR"; 80 private static final String PAN_TETHER_SETTING = "TETHERSTATE"; 81 82 private BluetoothTetheringNetworkFactory mNetworkFactory; 83 84 85 static { 86 classInitNative(); 87 } 88 89 protected String getName() { 90 return TAG; 91 } 92 93 public IProfileServiceBinder initBinder() { 94 return new BluetoothPanBinder(this); 95 } 96 97 protected boolean start() { 98 mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); 99 mBluetoothIfaceAddresses = new ArrayList<String>(); 100 try { 101 mMaxPanDevices = getResources().getInteger( 102 com.android.internal.R.integer.config_max_pan_devices); 103 } catch (NotFoundException e) { 104 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; 105 } 106 initializeNative(); 107 mNativeAvailable=true; 108 109 mNetworkFactory = new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(), 110 this); 111 112 // Set mTetherOn based on the last saved tethering preference while starting the Pan service 113 SharedPreferences tetherSetting = getSharedPreferences(PAN_PREFERENCE_FILE, 0); 114 mTetherOn = tetherSetting.getBoolean(PAN_TETHER_SETTING, false); 115 116 return true; 117 } 118 119 protected boolean stop() { 120 mHandler.removeCallbacksAndMessages(null); 121 return true; 122 } 123 124 protected boolean cleanup() { 125 if (mNativeAvailable) { 126 cleanupNative(); 127 mNativeAvailable=false; 128 } 129 if(mPanDevices != null) { 130 List<BluetoothDevice> DevList = getConnectedDevices(); 131 for(BluetoothDevice dev : DevList) { 132 handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED, 133 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 134 } 135 mPanDevices.clear(); 136 } 137 if(mBluetoothIfaceAddresses != null) { 138 mBluetoothIfaceAddresses.clear(); 139 } 140 return true; 141 } 142 143 private final Handler mHandler = new Handler() { 144 @Override 145 public void handleMessage(Message msg) { 146 switch (msg.what) { 147 case MESSAGE_CONNECT: 148 { 149 BluetoothDevice device = (BluetoothDevice) msg.obj; 150 if (!connectPanNative(Utils.getByteAddress(device), 151 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) { 152 handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING, 153 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE); 154 handlePanDeviceStateChange(device, null, 155 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE, 156 BluetoothPan.REMOTE_NAP_ROLE); 157 break; 158 } 159 } 160 break; 161 case MESSAGE_DISCONNECT: 162 { 163 BluetoothDevice device = (BluetoothDevice) msg.obj; 164 if (!disconnectPanNative(Utils.getByteAddress(device)) ) { 165 handlePanDeviceStateChange(device, mPanIfName, 166 BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE, 167 BluetoothPan.REMOTE_NAP_ROLE); 168 handlePanDeviceStateChange(device, mPanIfName, 169 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE, 170 BluetoothPan.REMOTE_NAP_ROLE); 171 break; 172 } 173 } 174 break; 175 case MESSAGE_CONNECT_STATE_CHANGED: 176 { 177 ConnectState cs = (ConnectState)msg.obj; 178 BluetoothDevice device = getDevice(cs.addr); 179 // TBD get iface from the msg 180 if (DBG) { 181 log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state); 182 } 183 handlePanDeviceStateChange(device, mPanIfName /* iface */, 184 convertHalState(cs.state), cs.local_role, cs.remote_role); 185 } 186 break; 187 } 188 } 189 }; 190 191 /** 192 * Handlers for incoming service calls 193 */ 194 private static class BluetoothPanBinder extends IBluetoothPan.Stub 195 implements IProfileServiceBinder { 196 private PanService mService; 197 public BluetoothPanBinder(PanService svc) { 198 mService = svc; 199 } 200 public boolean cleanup() { 201 mService = null; 202 return true; 203 } 204 private PanService getService() { 205 if (!Utils.checkCaller()) { 206 Log.w(TAG,"Pan call not allowed for non-active user"); 207 return null; 208 } 209 210 if (mService != null && mService.isAvailable()) { 211 return mService; 212 } 213 return null; 214 } 215 public boolean connect(BluetoothDevice device) { 216 PanService service = getService(); 217 if (service == null) return false; 218 return service.connect(device); 219 } 220 public boolean disconnect(BluetoothDevice device) { 221 PanService service = getService(); 222 if (service == null) return false; 223 return service.disconnect(device); 224 } 225 public int getConnectionState(BluetoothDevice device) { 226 PanService service = getService(); 227 if (service == null) return BluetoothPan.STATE_DISCONNECTED; 228 return service.getConnectionState(device); 229 } 230 private boolean isPanNapOn() { 231 PanService service = getService(); 232 if (service == null) return false; 233 return service.isPanNapOn(); 234 } 235 private boolean isPanUOn() { 236 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 237 PanService service = getService(); 238 return service.isPanUOn(); 239 } 240 public boolean isTetheringOn() { 241 PanService service = getService(); 242 if (service == null) return false; 243 return service.isTetheringOn(); 244 } 245 public void setBluetoothTethering(boolean value) { 246 PanService service = getService(); 247 if (service == null) return; 248 Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn); 249 service.setBluetoothTethering(value); 250 } 251 252 public List<BluetoothDevice> getConnectedDevices() { 253 PanService service = getService(); 254 if (service == null) return new ArrayList<BluetoothDevice>(0); 255 return service.getConnectedDevices(); 256 } 257 258 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 259 PanService service = getService(); 260 if (service == null) return new ArrayList<BluetoothDevice>(0); 261 return service.getDevicesMatchingConnectionStates(states); 262 } 263 }; 264 265 boolean connect(BluetoothDevice device) { 266 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 267 if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) { 268 Log.e(TAG, "Pan Device not disconnected: " + device); 269 return false; 270 } 271 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device); 272 mHandler.sendMessage(msg); 273 return true; 274 } 275 276 boolean disconnect(BluetoothDevice device) { 277 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 278 Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device); 279 mHandler.sendMessage(msg); 280 return true; 281 } 282 283 int getConnectionState(BluetoothDevice device) { 284 BluetoothPanDevice panDevice = mPanDevices.get(device); 285 if (panDevice == null) { 286 return BluetoothPan.STATE_DISCONNECTED; 287 } 288 return panDevice.mState; 289 } 290 291 boolean isPanNapOn() { 292 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 293 return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0; 294 } 295 boolean isPanUOn() { 296 if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative"); 297 return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0; 298 } 299 boolean isTetheringOn() { 300 return mTetherOn; 301 } 302 303 void setBluetoothTethering(boolean value) { 304 if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn); 305 ConnectivityManager.enforceTetherChangePermission(getBaseContext()); 306 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 307 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 308 if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) { 309 throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user."); 310 } 311 if(mTetherOn != value) { 312 313 SharedPreferences tetherSetting = getSharedPreferences(PAN_PREFERENCE_FILE, 0); 314 SharedPreferences.Editor editor = tetherSetting.edit(); 315 316 editor.putBoolean(PAN_TETHER_SETTING, value); 317 318 // Commit the edit! 319 editor.commit(); 320 //drop any existing panu or pan-nap connection when changing the tethering state 321 mTetherOn = value; 322 List<BluetoothDevice> DevList = getConnectedDevices(); 323 for(BluetoothDevice dev : DevList) 324 disconnect(dev); 325 } 326 } 327 328 List<BluetoothDevice> getConnectedDevices() { 329 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 330 List<BluetoothDevice> devices = getDevicesMatchingConnectionStates( 331 new int[] {BluetoothProfile.STATE_CONNECTED}); 332 return devices; 333 } 334 335 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 336 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 337 List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>(); 338 339 for (BluetoothDevice device: mPanDevices.keySet()) { 340 int panDeviceState = getConnectionState(device); 341 for (int state : states) { 342 if (state == panDeviceState) { 343 panDevices.add(device); 344 break; 345 } 346 } 347 } 348 return panDevices; 349 } 350 351 static protected class ConnectState { 352 public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) { 353 this.addr = address; 354 this.state = state; 355 this.error = error; 356 this.local_role = local_role; 357 this.remote_role = remote_role; 358 } 359 byte[] addr; 360 int state; 361 int error; 362 int local_role; 363 int remote_role; 364 }; 365 private void onConnectStateChanged(byte[] address, int state, int error, int local_role, 366 int remote_role) { 367 if (DBG) { 368 log("onConnectStateChanged: " + state + ", local role:" + local_role + 369 ", remote_role: " + remote_role); 370 } 371 Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED); 372 msg.obj = new ConnectState(address, state, error, local_role, remote_role); 373 mHandler.sendMessage(msg); 374 } 375 private void onControlStateChanged(int local_role, int state, int error, String ifname) { 376 if (DBG) 377 log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname); 378 if(error == 0) 379 mPanIfName = ifname; 380 } 381 382 private static int convertHalState(int halState) { 383 switch (halState) { 384 case CONN_STATE_CONNECTED: 385 return BluetoothProfile.STATE_CONNECTED; 386 case CONN_STATE_CONNECTING: 387 return BluetoothProfile.STATE_CONNECTING; 388 case CONN_STATE_DISCONNECTED: 389 return BluetoothProfile.STATE_DISCONNECTED; 390 case CONN_STATE_DISCONNECTING: 391 return BluetoothProfile.STATE_DISCONNECTING; 392 default: 393 Log.e(TAG, "bad pan connection state: " + halState); 394 return BluetoothProfile.STATE_DISCONNECTED; 395 } 396 } 397 398 void handlePanDeviceStateChange(BluetoothDevice device, 399 String iface, int state, int local_role, int remote_role) { 400 if(DBG) { 401 Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface + 402 ", state: " + state + ", local_role:" + local_role + ", remote_role:" + 403 remote_role); 404 } 405 int prevState; 406 String ifaceAddr = null; 407 BluetoothPanDevice panDevice = mPanDevices.get(device); 408 if (panDevice == null) { 409 prevState = BluetoothProfile.STATE_DISCONNECTED; 410 } else { 411 prevState = panDevice.mState; 412 ifaceAddr = panDevice.mIfaceAddr; 413 } 414 415 // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we 416 // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original 417 // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and 418 // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect 419 // will fail until the caller explicitly calls BluetoothPan#disconnect. 420 if (prevState == BluetoothProfile.STATE_DISCONNECTED && state == BluetoothProfile.STATE_DISCONNECTING) { 421 Log.d(TAG, "Ignoring state change from " + prevState + " to " + state); 422 return; 423 } 424 425 Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state); 426 if (prevState == state) return; 427 if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) { 428 if (state == BluetoothProfile.STATE_CONNECTED) { 429 if((!mTetherOn)||(local_role == BluetoothPan.LOCAL_PANU_ROLE)){ 430 Log.d(TAG,"handlePanDeviceStateChange BT tethering is off/Local role is PANU "+ 431 "drop the connection"); 432 disconnectPanNative(Utils.getByteAddress(device)); 433 return; 434 } 435 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE"); 436 ifaceAddr = enableTethering(iface); 437 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); 438 439 } else if (state == BluetoothProfile.STATE_DISCONNECTED) { 440 if (ifaceAddr != null) { 441 mBluetoothIfaceAddresses.remove(ifaceAddr); 442 ifaceAddr = null; 443 } 444 } 445 } else if (mNetworkFactory != null) { 446 // PANU Role = reverse Tether 447 Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " + 448 state + ", prevState = " + prevState); 449 if (state == BluetoothProfile.STATE_CONNECTED) { 450 mNetworkFactory.startReverseTether(iface); 451 } else if (state == BluetoothProfile.STATE_DISCONNECTED && 452 (prevState == BluetoothProfile.STATE_CONNECTED || 453 prevState == BluetoothProfile.STATE_DISCONNECTING)) { 454 mNetworkFactory.stopReverseTether(); 455 } 456 } 457 458 if (panDevice == null) { 459 panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role); 460 mPanDevices.put(device, panDevice); 461 } else { 462 panDevice.mState = state; 463 panDevice.mIfaceAddr = ifaceAddr; 464 panDevice.mLocalRole = local_role; 465 panDevice.mIface = iface; 466 } 467 468 /* Notifying the connection state change of the profile before sending the intent for 469 connection state change, as it was causing a race condition, with the UI not being 470 updated with the correct connection state. */ 471 Log.d(TAG, "Pan Device state : device: " + device + " State:" + 472 prevState + "->" + state); 473 notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState); 474 Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 475 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 476 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState); 477 intent.putExtra(BluetoothPan.EXTRA_STATE, state); 478 intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role); 479 sendBroadcast(intent, BLUETOOTH_PERM); 480 } 481 482 // configured when we start tethering 483 private String enableTethering(String iface) { 484 if (DBG) Log.d(TAG, "updateTetherState:" + iface); 485 486 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 487 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); 488 ConnectivityManager cm = 489 (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 490 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); 491 492 // bring toggle the interfaces 493 String[] currentIfaces = new String[0]; 494 try { 495 currentIfaces = service.listInterfaces(); 496 } catch (Exception e) { 497 Log.e(TAG, "Error listing Interfaces :" + e); 498 return null; 499 } 500 501 boolean found = false; 502 for (String currIface: currentIfaces) { 503 if (currIface.equals(iface)) { 504 found = true; 505 break; 506 } 507 } 508 509 if (!found) return null; 510 511 String address = createNewTetheringAddressLocked(); 512 if (address == null) return null; 513 514 InterfaceConfiguration ifcg = null; 515 try { 516 ifcg = service.getInterfaceConfig(iface); 517 if (ifcg != null) { 518 InetAddress addr = null; 519 LinkAddress linkAddr = ifcg.getLinkAddress(); 520 if (linkAddr == null || (addr = linkAddr.getAddress()) == null || 521 addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) || 522 addr.equals(NetworkUtils.numericToInetAddress("::0"))) { 523 addr = NetworkUtils.numericToInetAddress(address); 524 } 525 ifcg.setInterfaceUp(); 526 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH)); 527 ifcg.clearFlag("running"); 528 // TODO(BT) ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); 529 service.setInterfaceConfig(iface, ifcg); 530 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { 531 Log.e(TAG, "Error tethering "+iface); 532 } 533 } 534 } catch (Exception e) { 535 Log.e(TAG, "Error configuring interface " + iface + ", :" + e); 536 return null; 537 } 538 return address; 539 } 540 541 private String createNewTetheringAddressLocked() { 542 if (getConnectedPanDevices().size() == mMaxPanDevices) { 543 if (DBG) Log.d(TAG, "Max PAN device connections reached"); 544 return null; 545 } 546 String address = BLUETOOTH_IFACE_ADDR_START; 547 while (true) { 548 if (mBluetoothIfaceAddresses.contains(address)) { 549 String[] addr = address.split("\\."); 550 Integer newIp = Integer.parseInt(addr[2]) + 1; 551 address = address.replace(addr[2], newIp.toString()); 552 } else { 553 break; 554 } 555 } 556 mBluetoothIfaceAddresses.add(address); 557 return address; 558 } 559 560 private List<BluetoothDevice> getConnectedPanDevices() { 561 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 562 563 for (BluetoothDevice device: mPanDevices.keySet()) { 564 if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) { 565 devices.add(device); 566 } 567 } 568 return devices; 569 } 570 571 private int getPanDeviceConnectionState(BluetoothDevice device) { 572 BluetoothPanDevice panDevice = mPanDevices.get(device); 573 if (panDevice == null) { 574 return BluetoothProfile.STATE_DISCONNECTED; 575 } 576 return panDevice.mState; 577 } 578 579 private class BluetoothPanDevice { 580 private int mState; 581 private String mIfaceAddr; 582 private String mIface; 583 private int mLocalRole; // Which local role is this PAN device bound to 584 585 BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { 586 mState = state; 587 mIfaceAddr = ifaceAddr; 588 mIface = iface; 589 mLocalRole = localRole; 590 } 591 } 592 593 // Constants matching Hal header file bt_hh.h 594 // bthh_connection_state_t 595 private final static int CONN_STATE_CONNECTED = 0; 596 private final static int CONN_STATE_CONNECTING = 1; 597 private final static int CONN_STATE_DISCONNECTED = 2; 598 private final static int CONN_STATE_DISCONNECTING = 3; 599 600 private native static void classInitNative(); 601 private native void initializeNative(); 602 private native void cleanupNative(); 603 private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role); 604 private native boolean disconnectPanNative(byte[] btAddress); 605 private native boolean enablePanNative(int local_role); 606 private native int getPanLocalRoleNative(); 607 608} 609