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