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