BluetoothMapService.java revision 70be005a18a35ec5fcb46152f0dfbe82156efa3a
1/* 2* Copyright (C) 2013 Samsung System LSI 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15 16package com.android.bluetooth.map; 17 18import java.io.IOException; 19import java.util.ArrayList; 20import java.util.List; 21import java.util.Set; 22 23import javax.obex.ServerSession; 24import android.app.Notification; 25import android.app.NotificationManager; 26import android.app.PendingIntent; 27import android.app.Service; 28import android.bluetooth.BluetoothAdapter; 29import android.bluetooth.BluetoothDevice; 30import android.bluetooth.BluetoothProfile; 31import android.bluetooth.BluetoothServerSocket; 32import android.bluetooth.IBluetooth; 33import android.bluetooth.IBluetoothMap; 34import android.bluetooth.BluetoothUuid; 35import android.bluetooth.BluetoothMap; 36import android.bluetooth.BluetoothSocket; 37import android.content.Context; 38import android.content.Intent; 39import android.os.Handler; 40import android.os.IBinder; 41import android.os.Message; 42import android.os.PowerManager; 43import android.os.ParcelUuid; 44import android.text.TextUtils; 45import android.util.Log; 46import android.provider.Settings; 47import android.content.IntentFilter; 48import android.content.BroadcastReceiver; 49 50import com.android.bluetooth.R; 51import com.android.bluetooth.Utils; 52import com.android.bluetooth.btservice.AdapterService; 53import com.android.bluetooth.btservice.ProfileService; 54import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; 55 56 57public class BluetoothMapService extends ProfileService { 58 private static final String TAG = "BluetoothMapService"; 59 60 /** 61 * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 62 * restart com.android.bluetooth process. only enable DEBUG log: 63 * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and 64 * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE" 65 */ 66 67 public static final boolean DEBUG = true; 68 69 public static final boolean VERBOSE = false; 70 71 /** 72 * Intent indicating incoming obex authentication request which is from 73 * PCE(Carkit) 74 */ 75 public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.map.authchall"; 76 77 /** 78 * Intent indicating timeout for user confirmation, which is sent to 79 * BluetoothMapActivity 80 */ 81 public static final String USER_CONFIRM_TIMEOUT_ACTION = 82 "com.android.bluetooth.map.userconfirmtimeout"; 83 84 /** 85 * Intent Extra name indicating session key which is sent from 86 * BluetoothMapActivity 87 */ 88 public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.map.sessionkey"; 89 90 public static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; 91 92 public static final int MSG_SERVERSESSION_CLOSE = 5000; 93 94 public static final int MSG_SESSION_ESTABLISHED = 5001; 95 96 public static final int MSG_SESSION_DISCONNECTED = 5002; 97 98 public static final int MSG_OBEX_AUTH_CHALL = 5003; 99 100 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 101 102 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 103 104 private static final int START_LISTENER = 1; 105 106 private static final int USER_TIMEOUT = 2; 107 108 private static final int DISCONNECT_MAP = 3; 109 110 private PowerManager.WakeLock mWakeLock = null; 111 112 private BluetoothAdapter mAdapter; 113 114 private SocketAcceptThread mAcceptThread = null; 115 116 private BluetoothMapAuthenticator mAuth = null; 117 118 private BluetoothMapObexServer mMapServer; 119 120 private ServerSession mServerSession = null; 121 122 private BluetoothMnsObexClient mBluetoothMnsObexClient = null; 123 124 private BluetoothServerSocket mServerSocket = null; 125 126 private BluetoothSocket mConnSocket = null; 127 128 private BluetoothDevice mRemoteDevice = null; 129 130 private static String sRemoteDeviceName = null; 131 132 private volatile boolean mInterrupted; 133 134 private int mState; 135 136 private boolean isWaitingAuthorization = false; 137 138 // package and class name to which we send intent to check message access access permission 139 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 140 private static final String ACCESS_AUTHORITY_CLASS = 141 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 142 143 private static final ParcelUuid[] MAP_UUIDS = { 144 BluetoothUuid.MAP, 145 BluetoothUuid.MNS, 146 }; 147 148 public BluetoothMapService() { 149 mState = BluetoothMap.STATE_DISCONNECTED; 150 } 151 152 private void startRfcommSocketListener() { 153 if (DEBUG) Log.d(TAG, "Map Service startRfcommSocketListener"); 154 155 if (mAcceptThread == null) { 156 mAcceptThread = new SocketAcceptThread(); 157 mAcceptThread.setName("BluetoothMapAcceptThread"); 158 mAcceptThread.start(); 159 } 160 } 161 162 private final boolean initSocket() { 163 if (DEBUG) Log.d(TAG, "Map Service initSocket"); 164 165 boolean initSocketOK = false; 166 final int CREATE_RETRY_TIME = 10; 167 168 // It's possible that create will fail in some cases. retry for 10 times 169 for (int i = 0; (i < CREATE_RETRY_TIME) && !mInterrupted; i++) { 170 initSocketOK = true; 171 try { 172 // It is mandatory for MSE to support initiation of bonding and 173 // encryption. 174 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord 175 ("MAP SMS/MMS", BluetoothUuid.MAS.getUuid()); 176 177 } catch (IOException e) { 178 Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); 179 initSocketOK = false; 180 } 181 if (!initSocketOK) { 182 // Need to break out of this loop if BT is being turned off. 183 if (mAdapter == null) break; 184 int state = mAdapter.getState(); 185 if ((state != BluetoothAdapter.STATE_TURNING_ON) && 186 (state != BluetoothAdapter.STATE_ON)) { 187 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 188 break; 189 } 190 try { 191 if (VERBOSE) Log.v(TAG, "wait 300 ms"); 192 Thread.sleep(300); 193 } catch (InterruptedException e) { 194 Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); 195 } 196 } else { 197 break; 198 } 199 } 200 if (mInterrupted) { 201 initSocketOK = false; 202 // close server socket to avoid resource leakage 203 closeServerSocket(); 204 } 205 206 if (initSocketOK) { 207 if (VERBOSE) Log.v(TAG, "Succeed to create listening socket "); 208 209 } else { 210 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 211 } 212 return initSocketOK; 213 } 214 215 private final synchronized void closeServerSocket() { 216 // exit SocketAcceptThread early 217 if (mServerSocket != null) { 218 try { 219 // this will cause mServerSocket.accept() return early with IOException 220 mServerSocket.close(); 221 mServerSocket = null; 222 } catch (IOException ex) { 223 Log.e(TAG, "Close Server Socket error: " + ex); 224 } 225 } 226 } 227 private final synchronized void closeConnectionSocket() { 228 if (mConnSocket != null) { 229 try { 230 mConnSocket.close(); 231 mConnSocket = null; 232 } catch (IOException e) { 233 Log.e(TAG, "Close Connection Socket error: " + e.toString()); 234 } 235 } 236 } 237 238 private final void closeService() { 239 if (DEBUG) Log.d(TAG, "MAP Service closeService in"); 240 241 // exit initSocket early 242 mInterrupted = true; 243 closeServerSocket(); 244 245 if (mAcceptThread != null) { 246 try { 247 mAcceptThread.shutdown(); 248 mAcceptThread.join(); 249 mAcceptThread = null; 250 } catch (InterruptedException ex) { 251 Log.w(TAG, "mAcceptThread close error" + ex); 252 } 253 } 254 255 if (mWakeLock != null) { 256 mWakeLock.release(); 257 mWakeLock = null; 258 } 259 260 if (mServerSession != null) { 261 mServerSession.close(); 262 mServerSession = null; 263 } 264 265 if (mBluetoothMnsObexClient != null) { 266 mBluetoothMnsObexClient.disconnect(); 267 mBluetoothMnsObexClient = null; 268 } 269 270 closeConnectionSocket(); 271 if (VERBOSE) Log.v(TAG, "MAP Service closeService out"); 272 } 273 274 private final void startObexServerSession() throws IOException { 275 if (DEBUG) Log.d(TAG, "Map Service startObexServerSession"); 276 277 // acquire the wakeLock before start Obex transaction thread 278 if (mWakeLock == null) { 279 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 280 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 281 "StartingObexMapTransaction"); 282 mWakeLock.setReferenceCounted(false); 283 mWakeLock.acquire(); 284 } 285 286 287 mMapServer = new BluetoothMapObexServer(mSessionStatusHandler, this); 288 synchronized (this) { 289 // We need to get authentication now that obex server is up 290 mAuth = new BluetoothMapAuthenticator(mSessionStatusHandler); 291 mAuth.setChallenged(false); 292 mAuth.setCancelled(false); 293 } 294 // setup RFCOMM transport 295 BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(mConnSocket); 296 mServerSession = new ServerSession(transport, mMapServer, mAuth); 297 mBluetoothMnsObexClient = new BluetoothMnsObexClient(this, mRemoteDevice); 298 mBluetoothMnsObexClient.start(); // Initiate the MNS message loop. 299 setState(BluetoothMap.STATE_CONNECTED); 300 if (VERBOSE) { 301 Log.v(TAG, "startObexServerSession() success!"); 302 } 303 } 304 305 private void stopObexServerSession() { 306 if (DEBUG) Log.d(TAG, "MAP Service stopObexServerSession"); 307 308 // Release the wake lock if obex transaction is over 309 if (mWakeLock != null) { 310 mWakeLock.release(); 311 mWakeLock = null; 312 } 313 314 if (mServerSession != null) { 315 mServerSession.close(); 316 mServerSession = null; 317 } 318 319 mAcceptThread = null; 320 321 if(mBluetoothMnsObexClient != null) { 322 mBluetoothMnsObexClient.disconnect(); 323 mBluetoothMnsObexClient = null; 324 } 325 closeConnectionSocket(); 326 327 // Last obex transaction is finished, we start to listen for incoming 328 // connection again 329 if (mAdapter.isEnabled()) { 330 startRfcommSocketListener(); 331 } 332 setState(BluetoothMap.STATE_DISCONNECTED); 333 } 334 335 336 337 /** 338 * A thread that runs in the background waiting for remote rfcomm 339 * connect.Once a remote socket connected, this thread shall be 340 * shutdown.When the remote disconnect,this thread shall run again waiting 341 * for next request. 342 */ 343 private class SocketAcceptThread extends Thread { 344 345 private boolean stopped = false; 346 347 @Override 348 public void run() { 349 BluetoothServerSocket serverSocket; 350 if (mServerSocket == null) { 351 if (!initSocket()) { 352 return; 353 } 354 } 355 356 while (!stopped) { 357 try { 358 if (DEBUG) Log.d(TAG, "Accepting socket connection..."); 359 serverSocket = mServerSocket; 360 if(serverSocket == null) { 361 Log.w(TAG, "mServerSocket is null"); 362 break; 363 } 364 mConnSocket = serverSocket.accept(); 365 if (DEBUG) Log.d(TAG, "Accepted socket connection..."); 366 synchronized (BluetoothMapService.this) { 367 if (mConnSocket == null) { 368 Log.w(TAG, "mConnSocket is null"); 369 break; 370 } 371 mRemoteDevice = mConnSocket.getRemoteDevice(); 372 } 373 if (mRemoteDevice == null) { 374 Log.i(TAG, "getRemoteDevice() = null"); 375 break; 376 } 377 378 sRemoteDeviceName = mRemoteDevice.getName(); 379 // In case getRemoteName failed and return null 380 if (TextUtils.isEmpty(sRemoteDeviceName)) { 381 sRemoteDeviceName = getString(R.string.defaultname); 382 } 383 boolean trust = mRemoteDevice.getTrustState(); 384 if (DEBUG) Log.d(TAG, "GetTrustState() = " + trust); 385 386 387 if (trust) { 388 try { 389 if (DEBUG) Log.d(TAG, "incoming connection accepted from: " 390 + sRemoteDeviceName + " automatically as trusted device"); 391 startObexServerSession(); 392 } catch (IOException ex) { 393 Log.e(TAG, "catch exception starting obex server session" 394 + ex.toString()); 395 } 396 } else { 397 Intent intent = new 398 Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 399 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 400 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 401 BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS); 402 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 403 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 404 isWaitingAuthorization = true; 405 406 if (DEBUG) Log.d(TAG, "waiting for authorization for connection from: " 407 + sRemoteDeviceName); 408 409 } 410 stopped = true; // job done ,close this thread; 411 } catch (IOException ex) { 412 stopped=true; 413 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 414 } 415 } 416 } 417 418 void shutdown() { 419 stopped = true; 420 interrupt(); 421 } 422 } 423 424 private final Handler mSessionStatusHandler = new Handler() { 425 @Override 426 public void handleMessage(Message msg) { 427 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 428 429 switch (msg.what) { 430 case START_LISTENER: 431 if (mAdapter.isEnabled()) { 432 startRfcommSocketListener(); 433 } 434 break; 435 case USER_TIMEOUT: 436 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 437 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 438 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 439 BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS); 440 sendBroadcast(intent); 441 isWaitingAuthorization = false; 442 stopObexServerSession(); 443 break; 444 case MSG_SERVERSESSION_CLOSE: 445 stopObexServerSession(); 446 break; 447 case MSG_SESSION_ESTABLISHED: 448 break; 449 case MSG_SESSION_DISCONNECTED: 450 // handled elsewhere 451 break; 452 case DISCONNECT_MAP: 453 disconnectMap((BluetoothDevice)msg.obj); 454 break; 455 default: 456 break; 457 } 458 } 459 }; 460 461 462 public int getState() { 463 return mState; 464 } 465 466 public BluetoothDevice getRemoteDevice() { 467 return mRemoteDevice; 468 } 469 private void setState(int state) { 470 setState(state, BluetoothMap.RESULT_SUCCESS); 471 } 472 473 private synchronized void setState(int state, int result) { 474 if (state != mState) { 475 if (DEBUG) Log.d(TAG, "Map state " + mState + " -> " + state + ", result = " 476 + result); 477 int prevState = mState; 478 mState = state; 479 Intent intent = new Intent(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED); 480 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 481 intent.putExtra(BluetoothProfile.EXTRA_STATE, mState); 482 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 483 sendBroadcast(intent, BLUETOOTH_PERM); 484 AdapterService s = AdapterService.getAdapterService(); 485 if (s != null) { 486 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.MAP, 487 mState, prevState); 488 } 489 } 490 } 491 492 public static String getRemoteDeviceName() { 493 return sRemoteDeviceName; 494 } 495 496 public boolean disconnect(BluetoothDevice device) { 497 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(DISCONNECT_MAP, 0, 0, device)); 498 return true; 499 } 500 501 public boolean disconnectMap(BluetoothDevice device) { 502 boolean result = false; 503 if (DEBUG) Log.d(TAG, "disconnectMap"); 504 if (getRemoteDevice().equals(device)) { 505 switch (mState) { 506 case BluetoothMap.STATE_CONNECTED: 507 if (mServerSession != null) { 508 mServerSession.close(); 509 mServerSession = null; 510 } 511 if(mBluetoothMnsObexClient != null) { 512 mBluetoothMnsObexClient.disconnect(); //FIXME should use shutdown when implemented 513 mBluetoothMnsObexClient = null; 514 } 515 closeConnectionSocket(); 516 517 setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); 518 result = true; 519 break; 520 default: 521 break; 522 } 523 } 524 return result; 525 } 526 527 public List<BluetoothDevice> getConnectedDevices() { 528 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 529 synchronized(this) { 530 if (mState == BluetoothMap.STATE_CONNECTED && mRemoteDevice != null) { 531 devices.add(mRemoteDevice); 532 } 533 } 534 return devices; 535 } 536 537 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 538 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 539 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 540 int connectionState; 541 synchronized (this) { 542 for (BluetoothDevice device : bondedDevices) { 543 ParcelUuid[] featureUuids = device.getUuids(); 544 if (!BluetoothUuid.containsAnyUuid(featureUuids, MAP_UUIDS)) { 545 continue; 546 } 547 connectionState = getConnectionState(device); 548 for(int i = 0; i < states.length; i++) { 549 if (connectionState == states[i]) { 550 deviceList.add(device); 551 } 552 } 553 } 554 } 555 return deviceList; 556 } 557 558 public int getConnectionState(BluetoothDevice device) { 559 synchronized(this) { 560 if (getState() == BluetoothMap.STATE_CONNECTED && getRemoteDevice().equals(device)) { 561 return BluetoothProfile.STATE_CONNECTED; 562 } else { 563 return BluetoothProfile.STATE_DISCONNECTED; 564 } 565 } 566 } 567 568 public boolean setPriority(BluetoothDevice device, int priority) { 569 Settings.Global.putInt(getContentResolver(), 570 Settings.Global.getBluetoothMapPriorityKey(device.getAddress()), 571 priority); 572 if (DEBUG) Log.d(TAG, "Saved priority " + device + " = " + priority); 573 return true; 574 } 575 576 public int getPriority(BluetoothDevice device) { 577 int priority = Settings.Global.getInt(getContentResolver(), 578 Settings.Global.getBluetoothMapPriorityKey(device.getAddress()), 579 BluetoothProfile.PRIORITY_UNDEFINED); 580 return priority; 581 } 582 583 @Override 584 protected IProfileServiceBinder initBinder() { 585 return new BluetoothMapBinder(this); 586 } 587 588 @Override 589 protected boolean start() { 590 if (DEBUG) Log.d(TAG, "start()"); 591 IntentFilter filter = new IntentFilter(); 592 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 593 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 594 try { 595 registerReceiver(mMapReceiver, filter); 596 } catch (Exception e) { 597 Log.w(TAG,"Unable to register map receiver",e); 598 } 599 mInterrupted = false; 600 mAdapter = BluetoothAdapter.getDefaultAdapter(); 601 // start RFCOMM listener 602 mSessionStatusHandler.sendMessage(mSessionStatusHandler 603 .obtainMessage(START_LISTENER)); 604 return true; 605 } 606 607 @Override 608 protected boolean stop() { 609 if (DEBUG) Log.d(TAG, "stop()"); 610 try { 611 unregisterReceiver(mMapReceiver); 612 } catch (Exception e) { 613 Log.w(TAG,"Unable to unregister map receiver",e); 614 } 615 616 setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); 617 closeService(); 618 if(mSessionStatusHandler != null) { 619 mSessionStatusHandler.removeCallbacksAndMessages(null); 620 } 621 isWaitingAuthorization = false; 622 return true; 623 } 624 625 public boolean cleanup() { 626 if (DEBUG) Log.d(TAG, "cleanup()"); 627 setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED); 628 closeService(); 629 if(mSessionStatusHandler != null) { 630 mSessionStatusHandler.removeCallbacksAndMessages(null); 631 } 632 isWaitingAuthorization = false; 633 return true; 634 } 635 636 private MapBroadcastReceiver mMapReceiver = new MapBroadcastReceiver(); 637 638 private class MapBroadcastReceiver extends BroadcastReceiver { 639 @Override 640 public void onReceive(Context context, Intent intent) { 641 if (DEBUG) Log.d(TAG, "onReceive"); 642 String action = intent.getAction(); 643 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 644 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 645 BluetoothAdapter.ERROR); 646 if (state == BluetoothAdapter.STATE_TURNING_OFF) { 647 if (DEBUG) Log.d(TAG, "STATE_TURNING_OFF"); 648 // Release all resources 649 closeService(); 650 isWaitingAuthorization = false; 651 } else if (state == BluetoothAdapter.STATE_ON) { 652 if (DEBUG) Log.d(TAG, "STATE_ON"); 653 mInterrupted = false; 654 // start RFCOMM listener 655 mSessionStatusHandler.sendMessage(mSessionStatusHandler 656 .obtainMessage(START_LISTENER)); 657 } 658 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 659 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 660 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 661 if (DEBUG) Log.d(TAG, "Received ACTION_CONNECTION_ACCESS_REPLY:" + 662 requestType + ":" + isWaitingAuthorization); 663 if ((!isWaitingAuthorization) || 664 (requestType != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS)) { 665 // this reply is not for us 666 return; 667 } 668 669 isWaitingAuthorization = false; 670 671 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 672 BluetoothDevice.CONNECTION_ACCESS_NO) == 673 BluetoothDevice.CONNECTION_ACCESS_YES) { 674 //bluetooth connection accepted by user 675 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 676 boolean result = mRemoteDevice.setTrust(true); 677 if (DEBUG) Log.d(TAG, "setTrust() result=" + result); 678 } 679 try { 680 if (mConnSocket != null) { 681 // start obex server and rfcomm connection 682 startObexServerSession(); 683 } else { 684 stopObexServerSession(); 685 } 686 } catch (IOException ex) { 687 Log.e(TAG, "Caught the error: " + ex.toString()); 688 } 689 } else { 690 stopObexServerSession(); 691 } 692 } 693 } 694 }; 695 696 //Binder object: Must be static class or memory leak may occur 697 /** 698 * This class implements the IBluetoothMap interface - or actually it validates the 699 * preconditions for calling the actual functionality in the MapService, and calls it. 700 */ 701 private static class BluetoothMapBinder extends IBluetoothMap.Stub 702 implements IProfileServiceBinder { 703 private BluetoothMapService mService; 704 705 private BluetoothMapService getService() { 706 if (!Utils.checkCaller()) { 707 Log.w(TAG,"MAP call not allowed for non-active user"); 708 return null; 709 } 710 711 if (mService != null && mService.isAvailable()) { 712 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 713 return mService; 714 } 715 return null; 716 } 717 718 BluetoothMapBinder(BluetoothMapService service) { 719 if (VERBOSE) Log.v(TAG, "BluetoothMapBinder()"); 720 mService = service; 721 } 722 723 public boolean cleanup() { 724 mService = null; 725 return true; 726 } 727 728 public int getState() { 729 if (VERBOSE) Log.v(TAG, "getState()"); 730 BluetoothMapService service = getService(); 731 if (service == null) return BluetoothMap.STATE_DISCONNECTED; 732 return getService().getState(); 733 } 734 735 public BluetoothDevice getClient() { 736 if (VERBOSE) Log.v(TAG, "getClient()"); 737 BluetoothMapService service = getService(); 738 if (service == null) return null; 739 Log.v(TAG, "getClient() - returning " + service.getRemoteDevice()); 740 return service.getRemoteDevice(); 741 } 742 743 public boolean isConnected(BluetoothDevice device) { 744 if (VERBOSE) Log.v(TAG, "isConnected()"); 745 BluetoothMapService service = getService(); 746 if (service == null) return false; 747 return service.getState() == BluetoothMap.STATE_CONNECTED && service.getRemoteDevice().equals(device); 748 } 749 750 public boolean connect(BluetoothDevice device) { 751 if (VERBOSE) Log.v(TAG, "connect()"); 752 BluetoothMapService service = getService(); 753 if (service == null) return false; 754 return false; 755 } 756 757 public boolean disconnect(BluetoothDevice device) { 758 if (VERBOSE) Log.v(TAG, "disconnect()"); 759 BluetoothMapService service = getService(); 760 if (service == null) return false; 761 return service.disconnect(device); 762 } 763 764 public List<BluetoothDevice> getConnectedDevices() { 765 if (VERBOSE) Log.v(TAG, "getConnectedDevices()"); 766 BluetoothMapService service = getService(); 767 if (service == null) return new ArrayList<BluetoothDevice>(0); 768 return service.getConnectedDevices(); 769 } 770 771 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 772 if (VERBOSE) Log.v(TAG, "getDevicesMatchingConnectionStates()"); 773 BluetoothMapService service = getService(); 774 if (service == null) return new ArrayList<BluetoothDevice>(0); 775 return service.getDevicesMatchingConnectionStates(states); 776 } 777 778 public int getConnectionState(BluetoothDevice device) { 779 if (VERBOSE) Log.v(TAG, "getConnectionState()"); 780 BluetoothMapService service = getService(); 781 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 782 return service.getConnectionState(device); 783 } 784 785 public boolean setPriority(BluetoothDevice device, int priority) { 786 BluetoothMapService service = getService(); 787 if (service == null) return false; 788 return service.setPriority(device, priority); 789 } 790 791 public int getPriority(BluetoothDevice device) { 792 BluetoothMapService service = getService(); 793 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 794 return service.getPriority(device); 795 } 796 }; 797} 798