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 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 private boolean mIsRegistered = false; 96 97 // package and class name to which we send intent to check message access access permission 98 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 99 private static final String ACCESS_AUTHORITY_CLASS = 100 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 101 102 private static final ParcelUuid[] SAP_UUIDS = { 103 BluetoothUuid.SAP, 104 }; 105 106 107 public SapService() { 108 mState = BluetoothSap.STATE_DISCONNECTED; 109 } 110 111 /*** 112 * Call this when ever an activity is detected to renew the wakelock 113 * 114 * @param messageHandler reference to the handler to notify 115 * - typically mSessionStatusHandler, but it cannot be accessed in a static manner. 116 */ 117 public static void notifyUpdateWakeLock(Handler messageHandler) { 118 if (messageHandler != null) { 119 Message msg = Message.obtain(messageHandler); 120 msg.what = MSG_ACQUIRE_WAKE_LOCK; 121 msg.sendToTarget(); 122 } 123 } 124 125 private void removeSdpRecord() { 126 if (mAdapter != null && mSdpHandle >= 0 && 127 SdpManager.getDefaultManager() != null) { 128 if(VERBOSE) Log.d(TAG, "Removing SDP record handle: " + mSdpHandle); 129 boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle); 130 mSdpHandle = -1; 131 } 132 } 133 134 private void startRfcommSocketListener() { 135 if (VERBOSE) Log.v(TAG, "Sap Service startRfcommSocketListener"); 136 137 if (mAcceptThread == null) { 138 mAcceptThread = new SocketAcceptThread(); 139 mAcceptThread.setName("SapAcceptThread"); 140 mAcceptThread.start(); 141 } 142 } 143 144 private final boolean initSocket() { 145 if (VERBOSE) Log.v(TAG, "Sap Service initSocket"); 146 147 boolean initSocketOK = false; 148 final int CREATE_RETRY_TIME = 10; 149 150 // It's possible that create will fail in some cases. retry for 10 times 151 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 152 initSocketOK = true; 153 try { 154 // It is mandatory for MSE to support initiation of bonding and encryption. 155 // TODO: Consider reusing the mServerSocket - it is indented to be reused 156 // for multiple connections. 157 mServerSocket = mAdapter.listenUsingRfcommOn( 158 BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true); 159 removeSdpRecord(); 160 mSdpHandle = SdpManager.getDefaultManager().createSapsRecord(SDP_SAP_SERVICE_NAME, 161 mServerSocket.getChannel(), SDP_SAP_VERSION); 162 } catch (IOException e) { 163 Log.e(TAG, "Error create RfcommServerSocket ", e); 164 initSocketOK = false; 165 } 166 167 if (!initSocketOK) { 168 // Need to break out of this loop if BT is being turned off. 169 if (mAdapter == null) break; 170 int state = mAdapter.getState(); 171 if ((state != BluetoothAdapter.STATE_TURNING_ON) && 172 (state != BluetoothAdapter.STATE_ON)) { 173 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 174 break; 175 } 176 try { 177 if (VERBOSE) Log.v(TAG, "wait 300 ms"); 178 Thread.sleep(300); 179 } catch (InterruptedException e) { 180 Log.e(TAG, "socketAcceptThread thread was interrupted (3)", e); 181 } 182 } else { 183 break; 184 } 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 mIsRegistered = true; 584 } catch (Exception e) { 585 Log.w(TAG,"Unable to register sap receiver",e); 586 } 587 mInterrupted = false; 588 mAdapter = BluetoothAdapter.getDefaultAdapter(); 589 // start RFCOMM listener 590 mSessionStatusHandler.sendMessage(mSessionStatusHandler 591 .obtainMessage(START_LISTENER)); 592 return true; 593 } 594 595 @Override 596 protected boolean stop() { 597 Log.v(TAG, "stop()"); 598 if (!mIsRegistered){ 599 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 600 return true; 601 } 602 try { 603 mIsRegistered = false; 604 unregisterReceiver(mSapReceiver); 605 } catch (Exception e) { 606 Log.w(TAG,"Unable to unregister sap receiver",e); 607 } 608 setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED); 609 sendShutdownMessage(); 610 return true; 611 } 612 613 public boolean cleanup() { 614 setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED); 615 closeService(); 616 if (mSessionStatusHandler != null) { 617 mSessionStatusHandler.removeCallbacksAndMessages(null); 618 } 619 return true; 620 } 621 622 private void setUserTimeoutAlarm(){ 623 if (DEBUG)Log.d(TAG,"SetUserTimeOutAlarm()"); 624 if (mAlarmManager == null){ 625 mAlarmManager =(AlarmManager) this.getSystemService (Context.ALARM_SERVICE); 626 } 627 if(mRemoveTimeoutMsg) { 628 cancelUserTimeoutAlarm(); 629 } 630 mRemoveTimeoutMsg = true; 631 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 632 PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 633 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() 634 + USER_CONFIRM_TIMEOUT_VALUE,pIntent); 635 } 636 637 private void cancelUserTimeoutAlarm(){ 638 if (DEBUG)Log.d(TAG,"cancelUserTimeOutAlarm()"); 639 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 640 PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 641 AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 642 alarmManager.cancel(sender); 643 mRemoveTimeoutMsg = false; 644 } 645 646 private void sendCancelUserConfirmationIntent(BluetoothDevice device) { 647 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 648 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 649 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 650 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 651 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS); 652 sendBroadcast(intent, BLUETOOTH_PERM); 653 } 654 655 private void sendShutdownMessage() { 656 /* Any pending messages are no longer valid. 657 To speed up things, simply delete them. */ 658 if (mRemoveTimeoutMsg) { 659 Intent timeoutIntent = 660 new Intent(USER_CONFIRM_TIMEOUT_ACTION); 661 sendBroadcast(timeoutIntent, BLUETOOTH_PERM); 662 mIsWaitingAuthorization = false; 663 cancelUserTimeoutAlarm(); 664 } 665 removeSdpRecord(); 666 mSessionStatusHandler.removeCallbacksAndMessages(null); 667 // Request release of all resources 668 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 669 } 670 671 private void sendConnectTimeoutMessage() { 672 if (DEBUG) Log.d(TAG, "sendConnectTimeoutMessage()"); 673 if (mSessionStatusHandler != null) { 674 Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT); 675 msg.sendToTarget(); 676 } // Can only be null during shutdown 677 } 678 679 private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver(); 680 681 private class SapBroadcastReceiver extends BroadcastReceiver { 682 @Override 683 public void onReceive(Context context, Intent intent) { 684 685 if (VERBOSE) Log.v(TAG, "onReceive"); 686 String action = intent.getAction(); 687 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 688 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 689 BluetoothAdapter.ERROR); 690 if (state == BluetoothAdapter.STATE_TURNING_OFF) { 691 if (DEBUG) Log.d(TAG, "STATE_TURNING_OFF"); 692 sendShutdownMessage(); 693 } else if (state == BluetoothAdapter.STATE_ON) { 694 if (DEBUG) Log.d(TAG, "STATE_ON"); 695 // start RFCOMM listener 696 mSessionStatusHandler.sendMessage(mSessionStatusHandler 697 .obtainMessage(START_LISTENER)); 698 } 699 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 700 Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY"); 701 if (!mIsWaitingAuthorization) { 702 // this reply is not for us 703 return; 704 } 705 706 mIsWaitingAuthorization = false; 707 708 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 709 BluetoothDevice.CONNECTION_ACCESS_NO) == 710 BluetoothDevice.CONNECTION_ACCESS_YES) { 711 //bluetooth connection accepted by user 712 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 713 boolean result = mRemoteDevice.setSimAccessPermission( 714 BluetoothDevice.ACCESS_ALLOWED); 715 if (VERBOSE) { 716 Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result); 717 } } 718 try { 719 if (mConnSocket != null) { 720 // start obex server and rfcomm connection 721 startSapServerSession(); 722 } else { 723 stopSapServerSession(); 724 } 725 } catch (IOException ex) { 726 Log.e(TAG, "Caught the error: ", ex); 727 } 728 } else { 729 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 730 boolean result = mRemoteDevice.setSimAccessPermission( 731 BluetoothDevice.ACCESS_REJECTED); 732 if (VERBOSE) { 733 Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result=" 734 + result); 735 } 736 } 737 // Ensure proper cleanup, and prepare for new connect. 738 mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE); 739 } 740 } else 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 } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && 745 mIsWaitingAuthorization) { 746 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 747 748 if (mRemoteDevice == null || device == null) { 749 Log.i(TAG, "Unexpected error!"); 750 return; 751 } 752 753 if (DEBUG) Log.d(TAG,"ACL disconnected for " + device); 754 755 if (mRemoteDevice.equals(device)) { 756 if (mRemoveTimeoutMsg) { 757 // Send any pending timeout now, as ACL got disconnected. 758 cancelUserTimeoutAlarm(); 759 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 760 sendCancelUserConfirmationIntent(mRemoteDevice); 761 } 762 mIsWaitingAuthorization = false; 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