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