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