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