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