1 package com.android.bluetooth.sap; 2 3 import android.annotation.TargetApi; 4 import android.app.AlarmManager; 5 import android.app.PendingIntent; 6 import android.bluetooth.BluetoothAdapter; 7 import android.bluetooth.BluetoothDevice; 8 import android.bluetooth.BluetoothProfile; 9 import android.bluetooth.BluetoothSap; 10 import android.bluetooth.BluetoothServerSocket; 11 import android.bluetooth.BluetoothSocket; 12 import android.bluetooth.BluetoothUuid; 13 import android.bluetooth.IBluetoothSap; 14 import android.content.BroadcastReceiver; 15 import android.content.Context; 16 import android.content.Intent; 17 import android.content.IntentFilter; 18 import android.os.Build; 19 import android.os.Handler; 20 import android.os.Message; 21 import android.os.ParcelUuid; 22 import android.os.PowerManager; 23 import android.provider.Settings; 24 import android.support.annotation.VisibleForTesting; 25 import android.text.TextUtils; 26 import android.util.Log; 27 28 import com.android.bluetooth.BluetoothMetricsProto; 29 import com.android.bluetooth.R; 30 import com.android.bluetooth.Utils; 31 import com.android.bluetooth.btservice.MetricsLogger; 32 import com.android.bluetooth.btservice.ProfileService; 33 import com.android.bluetooth.sdp.SdpManager; 34 35 import java.io.IOException; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Set; 39 40 @TargetApi(Build.VERSION_CODES.ECLAIR) 41 public class SapService extends ProfileService { 42 43 private static final String SDP_SAP_SERVICE_NAME = "SIM Access"; 44 private static final int SDP_SAP_VERSION = 0x0102; 45 private static final String TAG = "SapService"; 46 public static final boolean DEBUG = false; 47 public static final boolean VERBOSE = false; 48 49 /* Message ID's */ 50 private static final int START_LISTENER = 1; 51 private static final int USER_TIMEOUT = 2; 52 private static final int SHUTDOWN = 3; 53 54 public static final int MSG_SERVERSESSION_CLOSE = 5000; 55 public static final int MSG_SESSION_ESTABLISHED = 5001; 56 public static final int MSG_SESSION_DISCONNECTED = 5002; 57 58 public static final int MSG_ACQUIRE_WAKE_LOCK = 5005; 59 public static final int MSG_RELEASE_WAKE_LOCK = 5006; 60 61 public static final int MSG_CHANGE_STATE = 5007; 62 63 /* Each time a transaction between the SIM and the BT Client is detected a wakelock is taken. 64 * After an idle period of RELEASE_WAKE_LOCK_DELAY ms the wakelock is released. 65 * 66 * NOTE: While connected the the Nokia 616 car-kit it was noticed that the carkit do 67 * TRANSFER_APDU_REQ with 20-30 seconds interval, and it sends no requests less than 1 sec 68 * apart. Additionally the responses from the RIL seems to come within 100 ms, hence a 69 * one second timeout should be enough. 70 */ 71 private static final int RELEASE_WAKE_LOCK_DELAY = 1000; 72 73 /* Intent indicating timeout for user confirmation. */ 74 public static final String USER_CONFIRM_TIMEOUT_ACTION = 75 "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT"; 76 private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000; 77 78 private PowerManager.WakeLock mWakeLock = null; 79 private BluetoothAdapter mAdapter; 80 private SocketAcceptThread mAcceptThread = null; 81 private BluetoothServerSocket mServerSocket = null; 82 private int mSdpHandle = -1; 83 private BluetoothSocket mConnSocket = null; 84 private BluetoothDevice mRemoteDevice = null; 85 private static String sRemoteDeviceName = null; 86 private volatile boolean mInterrupted; 87 private int mState; 88 private SapServer mSapServer = null; 89 private AlarmManager mAlarmManager = null; 90 private boolean mRemoveTimeoutMsg = false; 91 92 private boolean mIsWaitingAuthorization = false; 93 private boolean mIsRegistered = false; 94 95 private static SapService sSapService; 96 97 private static final ParcelUuid[] SAP_UUIDS = { 98 BluetoothUuid.SAP, 99 }; 100 101 102 public SapService() { 103 mState = BluetoothSap.STATE_DISCONNECTED; 104 } 105 106 /*** 107 * Call this when ever an activity is detected to renew the wakelock 108 * 109 * @param messageHandler reference to the handler to notify 110 * - typically mSessionStatusHandler, but it cannot be accessed in a static manner. 111 */ 112 public static void notifyUpdateWakeLock(Handler messageHandler) { 113 if (messageHandler != null) { 114 Message msg = Message.obtain(messageHandler); 115 msg.what = MSG_ACQUIRE_WAKE_LOCK; 116 msg.sendToTarget(); 117 } 118 } 119 120 private void removeSdpRecord() { 121 if (mAdapter != null && mSdpHandle >= 0 && SdpManager.getDefaultManager() != null) { 122 if (VERBOSE) { 123 Log.d(TAG, "Removing SDP record handle: " + mSdpHandle); 124 } 125 boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle); 126 mSdpHandle = -1; 127 } 128 } 129 130 private void startRfcommSocketListener() { 131 if (VERBOSE) { 132 Log.v(TAG, "Sap Service startRfcommSocketListener"); 133 } 134 135 if (mAcceptThread == null) { 136 mAcceptThread = new SocketAcceptThread(); 137 mAcceptThread.setName("SapAcceptThread"); 138 mAcceptThread.start(); 139 } 140 } 141 142 private static final int CREATE_RETRY_TIME = 10; 143 144 private boolean initSocket() { 145 if (VERBOSE) { 146 Log.v(TAG, "Sap Service initSocket"); 147 } 148 149 boolean initSocketOK = false; 150 151 // It's possible that create will fail in some cases. retry for 10 times 152 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 153 initSocketOK = true; 154 try { 155 // It is mandatory for MSE to support initiation of bonding and encryption. 156 // TODO: Consider reusing the mServerSocket - it is indented to be reused 157 // for multiple connections. 158 mServerSocket = mAdapter.listenUsingRfcommOn( 159 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true); 160 removeSdpRecord(); 161 mSdpHandle = SdpManager.getDefaultManager() 162 .createSapsRecord(SDP_SAP_SERVICE_NAME, mServerSocket.getChannel(), 163 SDP_SAP_VERSION); 164 } catch (IOException e) { 165 Log.e(TAG, "Error create RfcommServerSocket ", e); 166 initSocketOK = false; 167 } 168 169 if (!initSocketOK) { 170 // Need to break out of this loop if BT is being turned off. 171 if (mAdapter == null) { 172 break; 173 } 174 int state = mAdapter.getState(); 175 if ((state != BluetoothAdapter.STATE_TURNING_ON) && (state 176 != BluetoothAdapter.STATE_ON)) { 177 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 178 break; 179 } 180 try { 181 if (VERBOSE) { 182 Log.v(TAG, "wait 300 ms"); 183 } 184 Thread.sleep(300); 185 } catch (InterruptedException e) { 186 Log.e(TAG, "socketAcceptThread thread was interrupted (3)", e); 187 } 188 } else { 189 break; 190 } 191 } 192 193 if (initSocketOK) { 194 if (VERBOSE) { 195 Log.v(TAG, "Succeed to create listening socket "); 196 } 197 198 } else { 199 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 200 } 201 return initSocketOK; 202 } 203 204 private synchronized void closeServerSocket() { 205 // exit SocketAcceptThread early 206 if (mServerSocket != null) { 207 try { 208 // this will cause mServerSocket.accept() return early with IOException 209 mServerSocket.close(); 210 mServerSocket = null; 211 } catch (IOException ex) { 212 Log.e(TAG, "Close Server Socket error: ", ex); 213 } 214 } 215 } 216 217 private synchronized void closeConnectionSocket() { 218 if (mConnSocket != null) { 219 try { 220 mConnSocket.close(); 221 mConnSocket = null; 222 } catch (IOException e) { 223 Log.e(TAG, "Close Connection Socket error: ", e); 224 } 225 } 226 } 227 228 private void closeService() { 229 if (VERBOSE) { 230 Log.v(TAG, "SAP Service closeService in"); 231 } 232 233 // exit initSocket early 234 mInterrupted = true; 235 closeServerSocket(); 236 237 if (mAcceptThread != null) { 238 try { 239 mAcceptThread.shutdown(); 240 mAcceptThread.join(); 241 mAcceptThread = null; 242 } catch (InterruptedException ex) { 243 Log.w(TAG, "mAcceptThread close error", ex); 244 } 245 } 246 247 if (mWakeLock != null) { 248 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 249 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 250 mWakeLock.release(); 251 mWakeLock = null; 252 } 253 254 closeConnectionSocket(); 255 256 if (VERBOSE) { 257 Log.v(TAG, "SAP Service closeService out"); 258 } 259 } 260 261 private void startSapServerSession() throws IOException { 262 if (VERBOSE) { 263 Log.v(TAG, "Sap Service startSapServerSession"); 264 } 265 266 // acquire the wakeLock before start SAP transaction thread 267 if (mWakeLock == null) { 268 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 269 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingSapTransaction"); 270 mWakeLock.setReferenceCounted(false); 271 mWakeLock.acquire(); 272 } 273 274 /* Start the SAP I/O thread and associate with message handler */ 275 mSapServer = new SapServer(mSessionStatusHandler, this, mConnSocket.getInputStream(), 276 mConnSocket.getOutputStream()); 277 mSapServer.start(); 278 /* Warning: at this point we most likely have already handled the initial connect 279 * request from the SAP client, hence we need to be prepared to handle the 280 * response. (the SapHandler should have been started before this point)*/ 281 282 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 283 mSessionStatusHandler.sendMessageDelayed( 284 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 285 RELEASE_WAKE_LOCK_DELAY); 286 287 if (VERBOSE) { 288 Log.v(TAG, "startSapServerSession() success!"); 289 } 290 } 291 292 private void stopSapServerSession() { 293 294 /* When we reach this point, the SapServer is closed down, and the client is 295 * supposed to close the RFCOMM connection. */ 296 if (VERBOSE) { 297 Log.v(TAG, "SAP Service stopSapServerSession"); 298 } 299 300 mAcceptThread = null; 301 closeConnectionSocket(); 302 closeServerSocket(); 303 304 setState(BluetoothSap.STATE_DISCONNECTED); 305 306 if (mWakeLock != null) { 307 mWakeLock.release(); 308 mWakeLock = null; 309 } 310 311 // Last SAP transaction is finished, we start to listen for incoming 312 // rfcomm connection again 313 if (mAdapter.isEnabled()) { 314 startRfcommSocketListener(); 315 } 316 } 317 318 /** 319 * A thread that runs in the background waiting for remote rfcomm 320 * connect.Once a remote socket connected, this thread shall be 321 * shutdown.When the remote disconnect,this thread shall run again waiting 322 * for next request. 323 */ 324 private class SocketAcceptThread extends Thread { 325 326 private boolean mStopped = false; 327 328 @Override 329 public void run() { 330 BluetoothServerSocket serverSocket; 331 if (mServerSocket == null) { 332 if (!initSocket()) { 333 return; 334 } 335 } 336 337 while (!mStopped) { 338 try { 339 if (VERBOSE) { 340 Log.v(TAG, "Accepting socket connection..."); 341 } 342 serverSocket = mServerSocket; 343 if (serverSocket == null) { 344 Log.w(TAG, "mServerSocket is null"); 345 break; 346 } 347 mConnSocket = mServerSocket.accept(); 348 if (VERBOSE) { 349 Log.v(TAG, "Accepted socket connection..."); 350 } 351 synchronized (SapService.this) { 352 if (mConnSocket == null) { 353 Log.w(TAG, "mConnSocket is null"); 354 break; 355 } 356 mRemoteDevice = mConnSocket.getRemoteDevice(); 357 } 358 if (mRemoteDevice == null) { 359 Log.i(TAG, "getRemoteDevice() = null"); 360 break; 361 } 362 363 sRemoteDeviceName = mRemoteDevice.getName(); 364 // In case getRemoteName failed and return null 365 if (TextUtils.isEmpty(sRemoteDeviceName)) { 366 sRemoteDeviceName = getString(R.string.defaultname); 367 } 368 int permission = mRemoteDevice.getSimAccessPermission(); 369 370 if (VERBOSE) { 371 Log.v(TAG, "getSimAccessPermission() = " + permission); 372 } 373 374 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 375 try { 376 if (VERBOSE) { 377 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName 378 + " automatically as trusted device"); 379 } 380 startSapServerSession(); 381 } catch (IOException ex) { 382 Log.e(TAG, "catch exception starting obex server session", ex); 383 } 384 } else if (permission != BluetoothDevice.ACCESS_REJECTED) { 385 Intent intent = 386 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 387 intent.setPackage(getString(R.string.pairing_ui_package)); 388 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 389 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS); 390 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 391 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 392 393 mIsWaitingAuthorization = true; 394 setUserTimeoutAlarm(); 395 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 396 397 if (VERBOSE) { 398 Log.v(TAG, "waiting for authorization for connection from: " 399 + sRemoteDeviceName); 400 } 401 402 } else { 403 // Close RFCOMM socket for current connection and start listening 404 // again for new connections. 405 Log.w(TAG, "Can't connect with " + sRemoteDeviceName 406 + " as access is rejected"); 407 if (mSessionStatusHandler != null) { 408 mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE); 409 } 410 } 411 mStopped = true; // job done ,close this thread; 412 } catch (IOException ex) { 413 mStopped = true; 414 if (VERBOSE) { 415 Log.v(TAG, "Accept exception: ", ex); 416 } 417 } 418 } 419 } 420 421 void shutdown() { 422 mStopped = true; 423 interrupt(); 424 } 425 } 426 427 private final Handler mSessionStatusHandler = new Handler() { 428 @Override 429 public void handleMessage(Message msg) { 430 if (VERBOSE) { 431 Log.v(TAG, "Handler(): got msg=" + msg.what); 432 } 433 434 switch (msg.what) { 435 case START_LISTENER: 436 if (mAdapter.isEnabled()) { 437 startRfcommSocketListener(); 438 } 439 break; 440 case USER_TIMEOUT: 441 if (mIsWaitingAuthorization) { 442 sendCancelUserConfirmationIntent(mRemoteDevice); 443 cancelUserTimeoutAlarm(); 444 mIsWaitingAuthorization = false; 445 stopSapServerSession(); // And restart RfcommListener if needed 446 } 447 break; 448 case MSG_SERVERSESSION_CLOSE: 449 stopSapServerSession(); 450 break; 451 case MSG_SESSION_ESTABLISHED: 452 break; 453 case MSG_SESSION_DISCONNECTED: 454 // handled elsewhere 455 break; 456 case MSG_ACQUIRE_WAKE_LOCK: 457 if (VERBOSE) { 458 Log.i(TAG, "Acquire Wake Lock request message"); 459 } 460 if (mWakeLock == null) { 461 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 462 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 463 "StartingObexMapTransaction"); 464 mWakeLock.setReferenceCounted(false); 465 } 466 if (!mWakeLock.isHeld()) { 467 mWakeLock.acquire(); 468 if (DEBUG) { 469 Log.i(TAG, " Acquired Wake Lock by message"); 470 } 471 } 472 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 473 mSessionStatusHandler.sendMessageDelayed( 474 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 475 RELEASE_WAKE_LOCK_DELAY); 476 break; 477 case MSG_RELEASE_WAKE_LOCK: 478 if (VERBOSE) { 479 Log.i(TAG, "Release Wake Lock request message"); 480 } 481 if (mWakeLock != null) { 482 mWakeLock.release(); 483 if (DEBUG) { 484 Log.i(TAG, " Released Wake Lock by message"); 485 } 486 } 487 break; 488 case MSG_CHANGE_STATE: 489 if (DEBUG) { 490 Log.d(TAG, "change state message: newState = " + msg.arg1); 491 } 492 setState(msg.arg1); 493 break; 494 case SHUTDOWN: 495 /* Ensure to call close from this handler to avoid starting new stuff 496 because of pending messages */ 497 closeService(); 498 break; 499 default: 500 break; 501 } 502 } 503 }; 504 505 private void setState(int state) { 506 setState(state, BluetoothSap.RESULT_SUCCESS); 507 } 508 509 private synchronized void setState(int state, int result) { 510 if (state != mState) { 511 if (DEBUG) { 512 Log.d(TAG, "Sap state " + mState + " -> " + state + ", result = " + result); 513 } 514 if (state == BluetoothProfile.STATE_CONNECTED) { 515 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.SAP); 516 } 517 int prevState = mState; 518 mState = state; 519 Intent intent = new Intent(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); 520 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 521 intent.putExtra(BluetoothProfile.EXTRA_STATE, mState); 522 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 523 sendBroadcast(intent, BLUETOOTH_PERM); 524 } 525 } 526 527 public int getState() { 528 return mState; 529 } 530 531 public BluetoothDevice getRemoteDevice() { 532 return mRemoteDevice; 533 } 534 535 public static String getRemoteDeviceName() { 536 return sRemoteDeviceName; 537 } 538 539 public boolean disconnect(BluetoothDevice device) { 540 boolean result = false; 541 synchronized (SapService.this) { 542 if (getRemoteDevice().equals(device)) { 543 switch (mState) { 544 case BluetoothSap.STATE_CONNECTED: 545 closeConnectionSocket(); 546 setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED); 547 result = true; 548 break; 549 default: 550 break; 551 } 552 } 553 } 554 return result; 555 } 556 557 public List<BluetoothDevice> getConnectedDevices() { 558 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 559 synchronized (this) { 560 if (mState == BluetoothSap.STATE_CONNECTED && mRemoteDevice != null) { 561 devices.add(mRemoteDevice); 562 } 563 } 564 return devices; 565 } 566 567 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 568 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 569 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 570 int connectionState; 571 synchronized (this) { 572 for (BluetoothDevice device : bondedDevices) { 573 ParcelUuid[] featureUuids = device.getUuids(); 574 if (!BluetoothUuid.containsAnyUuid(featureUuids, SAP_UUIDS)) { 575 continue; 576 } 577 connectionState = getConnectionState(device); 578 for (int i = 0; i < states.length; i++) { 579 if (connectionState == states[i]) { 580 deviceList.add(device); 581 } 582 } 583 } 584 } 585 return deviceList; 586 } 587 588 public int getConnectionState(BluetoothDevice device) { 589 synchronized (this) { 590 if (getState() == BluetoothSap.STATE_CONNECTED && getRemoteDevice().equals(device)) { 591 return BluetoothProfile.STATE_CONNECTED; 592 } else { 593 return BluetoothProfile.STATE_DISCONNECTED; 594 } 595 } 596 } 597 598 public boolean setPriority(BluetoothDevice device, int priority) { 599 Settings.Global.putInt(getContentResolver(), 600 Settings.Global.getBluetoothSapPriorityKey(device.getAddress()), priority); 601 if (DEBUG) { 602 Log.d(TAG, "Saved priority " + device + " = " + priority); 603 } 604 return true; 605 } 606 607 public int getPriority(BluetoothDevice device) { 608 int priority = Settings.Global.getInt(getContentResolver(), 609 Settings.Global.getBluetoothSapPriorityKey(device.getAddress()), 610 BluetoothProfile.PRIORITY_UNDEFINED); 611 return priority; 612 } 613 614 @Override 615 protected IProfileServiceBinder initBinder() { 616 return new SapBinder(this); 617 } 618 619 @Override 620 protected boolean start() { 621 Log.v(TAG, "start()"); 622 IntentFilter filter = new IntentFilter(); 623 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 624 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 625 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 626 filter.addAction(USER_CONFIRM_TIMEOUT_ACTION); 627 628 try { 629 registerReceiver(mSapReceiver, filter); 630 mIsRegistered = true; 631 } catch (Exception e) { 632 Log.w(TAG, "Unable to register sap receiver", e); 633 } 634 mInterrupted = false; 635 mAdapter = BluetoothAdapter.getDefaultAdapter(); 636 // start RFCOMM listener 637 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 638 setSapService(this); 639 return true; 640 } 641 642 @Override 643 protected boolean stop() { 644 Log.v(TAG, "stop()"); 645 if (!mIsRegistered) { 646 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 647 return true; 648 } 649 setSapService(null); 650 try { 651 mIsRegistered = false; 652 unregisterReceiver(mSapReceiver); 653 } catch (Exception e) { 654 Log.w(TAG, "Unable to unregister sap receiver", e); 655 } 656 setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED); 657 sendShutdownMessage(); 658 return true; 659 } 660 661 @Override 662 public void cleanup() { 663 setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED); 664 closeService(); 665 if (mSessionStatusHandler != null) { 666 mSessionStatusHandler.removeCallbacksAndMessages(null); 667 } 668 } 669 670 /** 671 * Get the current instance of {@link SapService} 672 * 673 * @return current instance of {@link SapService} 674 */ 675 @VisibleForTesting 676 public static synchronized SapService getSapService() { 677 if (sSapService == null) { 678 Log.w(TAG, "getSapService(): service is null"); 679 return null; 680 } 681 if (!sSapService.isAvailable()) { 682 Log.w(TAG, "getSapService(): service is not available"); 683 return null; 684 } 685 return sSapService; 686 } 687 688 private static synchronized void setSapService(SapService instance) { 689 if (DEBUG) { 690 Log.d(TAG, "setSapService(): set to: " + instance); 691 } 692 sSapService = instance; 693 } 694 695 private void setUserTimeoutAlarm() { 696 if (DEBUG) { 697 Log.d(TAG, "setUserTimeOutAlarm()"); 698 } 699 cancelUserTimeoutAlarm(); 700 mRemoveTimeoutMsg = true; 701 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 702 PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 703 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 704 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent); 705 } 706 707 private void cancelUserTimeoutAlarm() { 708 if (DEBUG) { 709 Log.d(TAG, "cancelUserTimeOutAlarm()"); 710 } 711 if (mAlarmManager == null) { 712 mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 713 } 714 if (mRemoveTimeoutMsg) { 715 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 716 PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 717 mAlarmManager.cancel(sender); 718 mRemoveTimeoutMsg = false; 719 } 720 } 721 722 private void sendCancelUserConfirmationIntent(BluetoothDevice device) { 723 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 724 intent.setPackage(getString(R.string.pairing_ui_package)); 725 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 726 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 727 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS); 728 sendBroadcast(intent, BLUETOOTH_PERM); 729 } 730 731 private void sendShutdownMessage() { 732 /* Any pending messages are no longer valid. 733 To speed up things, simply delete them. */ 734 if (mRemoveTimeoutMsg) { 735 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 736 sendBroadcast(timeoutIntent, BLUETOOTH_PERM); 737 mIsWaitingAuthorization = false; 738 cancelUserTimeoutAlarm(); 739 } 740 removeSdpRecord(); 741 mSessionStatusHandler.removeCallbacksAndMessages(null); 742 // Request release of all resources 743 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 744 } 745 746 private void sendConnectTimeoutMessage() { 747 if (DEBUG) { 748 Log.d(TAG, "sendConnectTimeoutMessage()"); 749 } 750 if (mSessionStatusHandler != null) { 751 Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT); 752 msg.sendToTarget(); 753 } // Can only be null during shutdown 754 } 755 756 private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver(); 757 758 private class SapBroadcastReceiver extends BroadcastReceiver { 759 @Override 760 public void onReceive(Context context, Intent intent) { 761 762 if (VERBOSE) { 763 Log.v(TAG, "onReceive"); 764 } 765 String action = intent.getAction(); 766 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 767 int state = 768 intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 769 if (state == BluetoothAdapter.STATE_TURNING_OFF) { 770 if (DEBUG) { 771 Log.d(TAG, "STATE_TURNING_OFF"); 772 } 773 sendShutdownMessage(); 774 } else if (state == BluetoothAdapter.STATE_ON) { 775 if (DEBUG) { 776 Log.d(TAG, "STATE_ON"); 777 } 778 // start RFCOMM listener 779 mSessionStatusHandler.sendMessage( 780 mSessionStatusHandler.obtainMessage(START_LISTENER)); 781 } 782 return; 783 } 784 785 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 786 Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY"); 787 if (!mIsWaitingAuthorization) { 788 // this reply is not for us 789 return; 790 } 791 792 mIsWaitingAuthorization = false; 793 794 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 795 BluetoothDevice.CONNECTION_ACCESS_NO) 796 == BluetoothDevice.CONNECTION_ACCESS_YES) { 797 //bluetooth connection accepted by user 798 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 799 boolean result = mRemoteDevice.setSimAccessPermission( 800 BluetoothDevice.ACCESS_ALLOWED); 801 if (VERBOSE) { 802 Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result); 803 } 804 } 805 try { 806 if (mConnSocket != null) { 807 // start obex server and rfcomm connection 808 startSapServerSession(); 809 } else { 810 stopSapServerSession(); 811 } 812 } catch (IOException ex) { 813 Log.e(TAG, "Caught the error: ", ex); 814 } 815 } else { 816 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 817 boolean result = mRemoteDevice.setSimAccessPermission( 818 BluetoothDevice.ACCESS_REJECTED); 819 if (VERBOSE) { 820 Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result=" + result); 821 } 822 } 823 // Ensure proper cleanup, and prepare for new connect. 824 mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE); 825 } 826 return; 827 } 828 829 if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) { 830 if (DEBUG) { 831 Log.d(TAG, "USER_CONFIRM_TIMEOUT_ACTION Received."); 832 } 833 // send us self a message about the timeout. 834 sendConnectTimeoutMessage(); 835 return; 836 } 837 838 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) { 839 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 840 841 if (mRemoteDevice == null || device == null) { 842 Log.i(TAG, "Unexpected error!"); 843 return; 844 } 845 846 if (DEBUG) { 847 Log.d(TAG, "ACL disconnected for " + device); 848 } 849 850 if (mRemoteDevice.equals(device)) { 851 if (mRemoveTimeoutMsg) { 852 // Send any pending timeout now, as ACL got disconnected. 853 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 854 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 855 } 856 setState(BluetoothSap.STATE_DISCONNECTED); 857 // Ensure proper cleanup, and prepare for new connect. 858 mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE); 859 } 860 } 861 } 862 } 863 864 ; 865 866 //Binder object: Must be static class or memory leak may occur 867 868 /** 869 * This class implements the IBluetoothSap interface - or actually it validates the 870 * preconditions for calling the actual functionality in the SapService, and calls it. 871 */ 872 private static class SapBinder extends IBluetoothSap.Stub implements IProfileServiceBinder { 873 private SapService mService; 874 875 private SapService getService() { 876 if (!Utils.checkCaller()) { 877 Log.w(TAG, "call not allowed for non-active user"); 878 return null; 879 } 880 881 if (mService != null && mService.isAvailable()) { 882 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 883 "Need BLUETOOTH permission"); 884 return mService; 885 } 886 return null; 887 } 888 889 SapBinder(SapService service) { 890 Log.v(TAG, "SapBinder()"); 891 mService = service; 892 } 893 894 @Override 895 public void cleanup() { 896 mService = null; 897 } 898 899 @Override 900 public int getState() { 901 Log.v(TAG, "getState()"); 902 SapService service = getService(); 903 if (service == null) { 904 return BluetoothSap.STATE_DISCONNECTED; 905 } 906 return getService().getState(); 907 } 908 909 @Override 910 public BluetoothDevice getClient() { 911 Log.v(TAG, "getClient()"); 912 SapService service = getService(); 913 if (service == null) { 914 return null; 915 } 916 Log.v(TAG, "getClient() - returning " + service.getRemoteDevice()); 917 return service.getRemoteDevice(); 918 } 919 920 @Override 921 public boolean isConnected(BluetoothDevice device) { 922 Log.v(TAG, "isConnected()"); 923 SapService service = getService(); 924 if (service == null) { 925 return false; 926 } 927 return (service.getState() == BluetoothSap.STATE_CONNECTED && service.getRemoteDevice() 928 .equals(device)); 929 } 930 931 @Override 932 public boolean connect(BluetoothDevice device) { 933 Log.v(TAG, "connect()"); 934 SapService service = getService(); 935 if (service == null) { 936 return false; 937 } 938 return false; 939 } 940 941 @Override 942 public boolean disconnect(BluetoothDevice device) { 943 Log.v(TAG, "disconnect()"); 944 SapService service = getService(); 945 if (service == null) { 946 return false; 947 } 948 return service.disconnect(device); 949 } 950 951 @Override 952 public List<BluetoothDevice> getConnectedDevices() { 953 Log.v(TAG, "getConnectedDevices()"); 954 SapService service = getService(); 955 if (service == null) { 956 return new ArrayList<BluetoothDevice>(0); 957 } 958 return service.getConnectedDevices(); 959 } 960 961 @Override 962 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 963 Log.v(TAG, "getDevicesMatchingConnectionStates()"); 964 SapService service = getService(); 965 if (service == null) { 966 return new ArrayList<BluetoothDevice>(0); 967 } 968 return service.getDevicesMatchingConnectionStates(states); 969 } 970 971 @Override 972 public int getConnectionState(BluetoothDevice device) { 973 Log.v(TAG, "getConnectionState()"); 974 SapService service = getService(); 975 if (service == null) { 976 return BluetoothProfile.STATE_DISCONNECTED; 977 } 978 return service.getConnectionState(device); 979 } 980 981 @Override 982 public boolean setPriority(BluetoothDevice device, int priority) { 983 SapService service = getService(); 984 if (service == null) { 985 return false; 986 } 987 return service.setPriority(device, priority); 988 } 989 990 @Override 991 public int getPriority(BluetoothDevice device) { 992 SapService service = getService(); 993 if (service == null) { 994 return BluetoothProfile.PRIORITY_UNDEFINED; 995 } 996 return service.getPriority(device); 997 } 998 } 999 } 1000