BluetoothPbapService.java revision aed51b5e1151240567d990bacff01b66572fd45b
1/* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * - Neither the name of the Motorola, Inc. nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.android.bluetooth.pbap; 34 35import android.app.Notification; 36import android.app.NotificationManager; 37import android.app.PendingIntent; 38import android.app.Service; 39import android.bluetooth.BluetoothAdapter; 40import android.bluetooth.BluetoothDevice; 41import android.bluetooth.BluetoothPbap; 42import android.bluetooth.BluetoothProfile; 43import android.bluetooth.BluetoothServerSocket; 44import android.bluetooth.BluetoothSocket; 45import android.bluetooth.BluetoothUuid; 46import android.bluetooth.IBluetoothPbap; 47import android.content.Context; 48import android.content.Intent; 49import android.os.Handler; 50import android.os.IBinder; 51import android.os.Message; 52import android.os.PowerManager; 53import android.telephony.TelephonyManager; 54import android.text.TextUtils; 55import android.util.Log; 56 57import com.android.bluetooth.BluetoothObexTransport; 58import com.android.bluetooth.R; 59import com.android.bluetooth.Utils; 60import com.android.bluetooth.btservice.AdapterService; 61 62import java.io.IOException; 63 64import javax.obex.ServerSession; 65 66public class BluetoothPbapService extends Service { 67 private static final String TAG = "BluetoothPbapService"; 68 69 /** 70 * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 71 * restart com.android.bluetooth process. only enable DEBUG log: 72 * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and 73 * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" 74 */ 75 76 public static final boolean DEBUG = true; 77 78 public static final boolean VERBOSE = false; 79 80 /** 81 * Intent indicating incoming obex authentication request which is from 82 * PCE(Carkit) 83 */ 84 public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall"; 85 86 /** 87 * Intent indicating obex session key input complete by user which is sent 88 * from BluetoothPbapActivity 89 */ 90 public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse"; 91 92 /** 93 * Intent indicating user canceled obex authentication session key input 94 * which is sent from BluetoothPbapActivity 95 */ 96 public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled"; 97 98 /** 99 * Intent indicating timeout for user confirmation, which is sent to 100 * BluetoothPbapActivity 101 */ 102 public static final String USER_CONFIRM_TIMEOUT_ACTION = 103 "com.android.bluetooth.pbap.userconfirmtimeout"; 104 105 /** 106 * Intent Extra name indicating session key which is sent from 107 * BluetoothPbapActivity 108 */ 109 public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey"; 110 111 public static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; 112 113 public static final int MSG_SERVERSESSION_CLOSE = 5000; 114 115 public static final int MSG_SESSION_ESTABLISHED = 5001; 116 117 public static final int MSG_SESSION_DISCONNECTED = 5002; 118 119 public static final int MSG_OBEX_AUTH_CHALL = 5003; 120 121 public static final int MSG_ACQUIRE_WAKE_LOCK = 5004; 122 123 public static final int MSG_RELEASE_WAKE_LOCK = 5005; 124 125 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 126 127 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 128 129 private static final int START_LISTENER = 1; 130 131 private static final int USER_TIMEOUT = 2; 132 133 private static final int AUTH_TIMEOUT = 3; 134 135 136 private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; 137 138 private static final int RELEASE_WAKE_LOCK_DELAY = 10000; 139 140 // Ensure not conflict with Opp notification ID 141 private static final int NOTIFICATION_ID_ACCESS = -1000001; 142 143 private static final int NOTIFICATION_ID_AUTH = -1000002; 144 145 private PowerManager.WakeLock mWakeLock = null; 146 147 private BluetoothAdapter mAdapter; 148 149 private SocketAcceptThread mAcceptThread = null; 150 151 private BluetoothPbapAuthenticator mAuth = null; 152 153 private BluetoothPbapObexServer mPbapServer; 154 155 private ServerSession mServerSession = null; 156 157 private BluetoothServerSocket mServerSocket = null; 158 159 private BluetoothSocket mConnSocket = null; 160 161 private BluetoothDevice mRemoteDevice = null; 162 163 private static String sLocalPhoneNum = null; 164 165 private static String sLocalPhoneName = null; 166 167 private static String sRemoteDeviceName = null; 168 169 private boolean mHasStarted = false; 170 171 private volatile boolean mInterrupted; 172 173 private int mState; 174 175 private int mStartId = -1; 176 177 private boolean mIsWaitingAuthorization = false; 178 179 public BluetoothPbapService() { 180 mState = BluetoothPbap.STATE_DISCONNECTED; 181 } 182 183 @Override 184 public void onCreate() { 185 super.onCreate(); 186 if (VERBOSE) Log.v(TAG, "Pbap Service onCreate"); 187 188 mInterrupted = false; 189 mAdapter = BluetoothAdapter.getDefaultAdapter(); 190 191 if (!mHasStarted) { 192 mHasStarted = true; 193 if (VERBOSE) Log.v(TAG, "Starting PBAP service"); 194 BluetoothPbapConfig.init(this); 195 int state = mAdapter.getState(); 196 if (state == BluetoothAdapter.STATE_ON) { 197 mSessionStatusHandler.sendMessage(mSessionStatusHandler 198 .obtainMessage(START_LISTENER)); 199 } 200 } 201 } 202 203 @Override 204 public int onStartCommand(Intent intent, int flags, int startId) { 205 mStartId = startId; 206 if (mAdapter == null) { 207 Log.w(TAG, "Stopping BluetoothPbapService: " 208 + "device does not have BT or device is not ready"); 209 // Release all resources 210 closeService(); 211 } else { 212 // No need to handle the null intent case, because we have 213 // all restart work done in onCreate() 214 if (intent != null) { 215 parseIntent(intent); 216 } 217 } 218 return START_NOT_STICKY; 219 } 220 221 // process the intent from receiver 222 private void parseIntent(final Intent intent) { 223 String action = intent.getStringExtra("action"); 224 if (action == null) return; // Nothing to do 225 if (VERBOSE) Log.v(TAG, "action: " + action); 226 227 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 228 if (VERBOSE) Log.v(TAG, "state: " + state); 229 230 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) 231 && (state == BluetoothAdapter.STATE_TURNING_OFF)) { 232 // Send any pending timeout now, as this service will be destroyed. 233 if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) { 234 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 235 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 236 } 237 // Release all resources 238 closeService(); 239 return; 240 } 241 242 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) { 243 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 244 245 if (mRemoteDevice == null) return; 246 if (DEBUG) Log.d(TAG,"ACL disconnected for "+ device); 247 if (mRemoteDevice.equals(device)) { 248 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 249 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 250 } 251 return; 252 } 253 254 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 255 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 256 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 257 258 if ((!mIsWaitingAuthorization) 259 || (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) { 260 // this reply is not for us 261 return; 262 } 263 264 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 265 mIsWaitingAuthorization = false; 266 267 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 268 BluetoothDevice.CONNECTION_ACCESS_NO) 269 == BluetoothDevice.CONNECTION_ACCESS_YES) { 270 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 271 boolean result = mRemoteDevice.setPhonebookAccessPermission( 272 BluetoothDevice.ACCESS_ALLOWED); 273 if (VERBOSE) { 274 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)=" + result); 275 } 276 } 277 try { 278 if (mConnSocket != null) { 279 startObexServerSession(); 280 } else { 281 stopObexServerSession(); 282 } 283 } catch (IOException ex) { 284 Log.e(TAG, "Caught the error: " + ex.toString()); 285 } 286 } else { 287 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 288 boolean result = mRemoteDevice.setPhonebookAccessPermission( 289 BluetoothDevice.ACCESS_REJECTED); 290 if (VERBOSE) { 291 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)=" + result); 292 } 293 } 294 stopObexServerSession(); 295 } 296 return; 297 } 298 299 if (action.equals(AUTH_RESPONSE_ACTION)) { 300 String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); 301 notifyAuthKeyInput(sessionkey); 302 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 303 notifyAuthCancelled(); 304 } else { 305 Log.w(TAG, "Unrecognized intent!"); 306 return; 307 } 308 309 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 310 } 311 312 @Override 313 public void onDestroy() { 314 if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy"); 315 316 super.onDestroy(); 317 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 318 closeService(); 319 if(mSessionStatusHandler != null) { 320 mSessionStatusHandler.removeCallbacksAndMessages(null); 321 } 322 } 323 324 @Override 325 public IBinder onBind(Intent intent) { 326 if (VERBOSE) Log.v(TAG, "Pbap Service onBind"); 327 return mBinder; 328 } 329 330 private void startRfcommSocketListener() { 331 if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener"); 332 333 if (mAcceptThread == null) { 334 mAcceptThread = new SocketAcceptThread(); 335 mAcceptThread.setName("BluetoothPbapAcceptThread"); 336 mAcceptThread.start(); 337 } 338 } 339 340 private final boolean initSocket() { 341 if (VERBOSE) Log.v(TAG, "Pbap Service initSocket"); 342 343 boolean initSocketOK = false; 344 final int CREATE_RETRY_TIME = 10; 345 346 // It's possible that create will fail in some cases. retry for 10 times 347 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 348 initSocketOK = true; 349 try { 350 // It is mandatory for PSE to support initiation of bonding and 351 // encryption. 352 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord 353 ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid()); 354 355 } catch (IOException e) { 356 Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); 357 initSocketOK = false; 358 } 359 if (!initSocketOK) { 360 // Need to break out of this loop if BT is being turned off. 361 if (mAdapter == null) break; 362 int state = mAdapter.getState(); 363 if ((state != BluetoothAdapter.STATE_TURNING_ON) && 364 (state != BluetoothAdapter.STATE_ON)) { 365 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 366 break; 367 } 368 try { 369 if (VERBOSE) Log.v(TAG, "wait 300 ms"); 370 Thread.sleep(300); 371 } catch (InterruptedException e) { 372 Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); 373 break; 374 } 375 } else { 376 break; 377 } 378 } 379 380 if (mInterrupted) { 381 initSocketOK = false; 382 // close server socket to avoid resource leakage 383 closeServerSocket(); 384 } 385 386 if (initSocketOK) { 387 if (VERBOSE) Log.v(TAG, "Succeed to create listening socket "); 388 389 } else { 390 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 391 } 392 return initSocketOK; 393 } 394 395 private final synchronized void closeServerSocket() { 396 // exit SocketAcceptThread early 397 if (mServerSocket != null) { 398 try { 399 // this will cause mServerSocket.accept() return early with IOException 400 mServerSocket.close(); 401 mServerSocket = null; 402 } catch (IOException ex) { 403 Log.e(TAG, "Close Server Socket error: " + ex); 404 } 405 } 406 } 407 408 private final synchronized void closeConnectionSocket() { 409 if (mConnSocket != null) { 410 try { 411 mConnSocket.close(); 412 mConnSocket = null; 413 } catch (IOException e) { 414 Log.e(TAG, "Close Connection Socket error: " + e.toString()); 415 } 416 } 417 } 418 419 private final void closeService() { 420 if (VERBOSE) Log.v(TAG, "Pbap Service closeService in"); 421 422 // exit initSocket early 423 mInterrupted = true; 424 closeServerSocket(); 425 426 if (mAcceptThread != null) { 427 try { 428 mAcceptThread.shutdown(); 429 mAcceptThread.join(); 430 mAcceptThread = null; 431 } catch (InterruptedException ex) { 432 Log.w(TAG, "mAcceptThread close error" + ex); 433 } 434 } 435 436 if (mWakeLock != null) { 437 mWakeLock.release(); 438 mWakeLock = null; 439 } 440 441 if (mServerSession != null) { 442 mServerSession.close(); 443 mServerSession = null; 444 } 445 446 closeConnectionSocket(); 447 448 mHasStarted = false; 449 if (mStartId != -1 && stopSelfResult(mStartId)) { 450 if (VERBOSE) Log.v(TAG, "successfully stopped pbap service"); 451 mStartId = -1; 452 } 453 if (VERBOSE) Log.v(TAG, "Pbap Service closeService out"); 454 } 455 456 private final void startObexServerSession() throws IOException { 457 if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession"); 458 459 // acquire the wakeLock before start Obex transaction thread 460 if (mWakeLock == null) { 461 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 462 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 463 "StartingObexPbapTransaction"); 464 mWakeLock.setReferenceCounted(false); 465 mWakeLock.acquire(); 466 } 467 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 468 if (tm != null) { 469 sLocalPhoneNum = tm.getLine1Number(); 470 sLocalPhoneName = tm.getLine1AlphaTag(); 471 if (TextUtils.isEmpty(sLocalPhoneName)) { 472 sLocalPhoneName = this.getString(R.string.localPhoneName); 473 } 474 } 475 476 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this); 477 synchronized (this) { 478 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 479 mAuth.setChallenged(false); 480 mAuth.setCancelled(false); 481 } 482 BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket); 483 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 484 setState(BluetoothPbap.STATE_CONNECTED); 485 486 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 487 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 488 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 489 490 if (VERBOSE) { 491 Log.v(TAG, "startObexServerSession() success!"); 492 } 493 } 494 495 private void stopObexServerSession() { 496 if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession"); 497 498 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 499 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 500 // Release the wake lock if obex transaction is over 501 if (mWakeLock != null) { 502 mWakeLock.release(); 503 mWakeLock = null; 504 } 505 506 if (mServerSession != null) { 507 mServerSession.close(); 508 mServerSession = null; 509 } 510 511 mAcceptThread = null; 512 513 closeConnectionSocket(); 514 515 // Last obex transaction is finished, we start to listen for incoming 516 // connection again 517 if (mAdapter.isEnabled()) { 518 startRfcommSocketListener(); 519 } 520 setState(BluetoothPbap.STATE_DISCONNECTED); 521 } 522 523 private void notifyAuthKeyInput(final String key) { 524 synchronized (mAuth) { 525 if (key != null) { 526 mAuth.setSessionKey(key); 527 } 528 mAuth.setChallenged(true); 529 mAuth.notify(); 530 } 531 } 532 533 private void notifyAuthCancelled() { 534 synchronized (mAuth) { 535 mAuth.setCancelled(true); 536 mAuth.notify(); 537 } 538 } 539 540 /** 541 * A thread that runs in the background waiting for remote rfcomm 542 * connect.Once a remote socket connected, this thread shall be 543 * shutdown.When the remote disconnect,this thread shall run again waiting 544 * for next request. 545 */ 546 private class SocketAcceptThread extends Thread { 547 548 private boolean stopped = false; 549 550 @Override 551 public void run() { 552 BluetoothServerSocket serverSocket; 553 if (mServerSocket == null) { 554 if (!initSocket()) { 555 return; 556 } 557 } 558 559 while (!stopped) { 560 try { 561 if (VERBOSE) Log.v(TAG, "Accepting socket connection..."); 562 serverSocket = mServerSocket; 563 if (serverSocket == null) { 564 Log.w(TAG, "mServerSocket is null"); 565 break; 566 } 567 mConnSocket = serverSocket.accept(); 568 if (VERBOSE) Log.v(TAG, "Accepted socket connection..."); 569 570 synchronized (BluetoothPbapService.this) { 571 if (mConnSocket == null) { 572 Log.w(TAG, "mConnSocket is null"); 573 break; 574 } 575 mRemoteDevice = mConnSocket.getRemoteDevice(); 576 } 577 if (mRemoteDevice == null) { 578 Log.i(TAG, "getRemoteDevice() = null"); 579 break; 580 } 581 sRemoteDeviceName = mRemoteDevice.getName(); 582 // In case getRemoteName failed and return null 583 if (TextUtils.isEmpty(sRemoteDeviceName)) { 584 sRemoteDeviceName = getString(R.string.defaultname); 585 } 586 int permission = mRemoteDevice.getPhonebookAccessPermission(); 587 if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission); 588 589 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 590 try { 591 if (VERBOSE) { 592 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName 593 + " automatically as already allowed device"); 594 } 595 startObexServerSession(); 596 } catch (IOException ex) { 597 Log.e(TAG, "Caught exception starting obex server session" 598 + ex.toString()); 599 } 600 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 601 if (VERBOSE) { 602 Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName 603 + " automatically as already rejected device"); 604 } 605 stopObexServerSession(); 606 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 607 // Send an Intent to Settings app to ask user preference. 608 Intent intent = 609 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 610 intent.setPackage(getString(R.string.pairing_ui_package)); 611 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 612 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 613 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 614 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 615 intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, 616 BluetoothPbapReceiver.class.getName()); 617 618 mIsWaitingAuthorization = true; 619 sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM); 620 621 if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: " 622 + sRemoteDeviceName); 623 624 // In case car kit time out and try to use HFP for 625 // phonebook 626 // access, while UI still there waiting for user to 627 // confirm 628 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 629 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 630 // We will continue the process when we receive 631 // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. 632 } 633 stopped = true; // job done ,close this thread; 634 } catch (IOException ex) { 635 stopped=true; 636 /* 637 if (stopped) { 638 break; 639 } 640 */ 641 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 642 } 643 } 644 } 645 646 void shutdown() { 647 stopped = true; 648 interrupt(); 649 } 650 } 651 652 private final Handler mSessionStatusHandler = new Handler() { 653 @Override 654 public void handleMessage(Message msg) { 655 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 656 657 switch (msg.what) { 658 case START_LISTENER: 659 if (mAdapter.isEnabled()) { 660 startRfcommSocketListener(); 661 } else { 662 closeService();// release all resources 663 } 664 break; 665 case USER_TIMEOUT: 666 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 667 intent.setPackage(getString(R.string.pairing_ui_package)); 668 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 669 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 670 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 671 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 672 mIsWaitingAuthorization = false; 673 stopObexServerSession(); 674 break; 675 case AUTH_TIMEOUT: 676 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 677 sendBroadcast(i); 678 removePbapNotification(NOTIFICATION_ID_AUTH); 679 notifyAuthCancelled(); 680 break; 681 case MSG_SERVERSESSION_CLOSE: 682 stopObexServerSession(); 683 break; 684 case MSG_SESSION_ESTABLISHED: 685 break; 686 case MSG_SESSION_DISCONNECTED: 687 // case MSG_SERVERSESSION_CLOSE will handle ,so just skip 688 break; 689 case MSG_OBEX_AUTH_CHALL: 690 createPbapNotification(AUTH_CHALL_ACTION); 691 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 692 .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 693 break; 694 case MSG_ACQUIRE_WAKE_LOCK: 695 if (mWakeLock == null) { 696 PowerManager pm = (PowerManager)getSystemService( 697 Context.POWER_SERVICE); 698 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 699 "StartingObexPbapTransaction"); 700 mWakeLock.setReferenceCounted(false); 701 mWakeLock.acquire(); 702 Log.w(TAG, "Acquire Wake Lock"); 703 } 704 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 705 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 706 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 707 break; 708 case MSG_RELEASE_WAKE_LOCK: 709 if (mWakeLock != null) { 710 mWakeLock.release(); 711 mWakeLock = null; 712 Log.w(TAG, "Release Wake Lock"); 713 } 714 break; 715 default: 716 break; 717 } 718 } 719 }; 720 721 private void setState(int state) { 722 setState(state, BluetoothPbap.RESULT_SUCCESS); 723 } 724 725 private synchronized void setState(int state, int result) { 726 if (state != mState) { 727 if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " 728 + result); 729 int prevState = mState; 730 mState = state; 731 Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); 732 intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState); 733 intent.putExtra(BluetoothPbap.PBAP_STATE, mState); 734 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 735 sendBroadcast(intent, BLUETOOTH_PERM); 736 } 737 } 738 739 private void createPbapNotification(String action) { 740 741 NotificationManager nm = (NotificationManager) 742 getSystemService(Context.NOTIFICATION_SERVICE); 743 744 // Create an intent triggered by clicking on the status icon. 745 Intent clickIntent = new Intent(); 746 clickIntent.setClass(this, BluetoothPbapActivity.class); 747 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 748 clickIntent.setAction(action); 749 750 // Create an intent triggered by clicking on the 751 // "Clear All Notifications" button 752 Intent deleteIntent = new Intent(); 753 deleteIntent.setClass(this, BluetoothPbapReceiver.class); 754 755 String name = getRemoteDeviceName(); 756 757 if (action.equals(AUTH_CHALL_ACTION)) { 758 Notification notification = new Notification.Builder(this) 759 .setWhen(System.currentTimeMillis()) 760 .setContentTitle(getString(R.string.auth_notif_title)) 761 .setContentText(getString(R.string.auth_notif_message, name)) 762 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 763 .setTicker(getString(R.string.auth_notif_ticker)) 764 .setColor(getResources().getColor( 765 com.android.internal.R.color.system_notification_accent_color, 766 this.getTheme())) 767 .setFlag(Notification.FLAG_AUTO_CANCEL, true) 768 .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true) 769 .setDefaults(Notification.DEFAULT_SOUND) 770 .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0)) 771 .setDeleteIntent(PendingIntent.getBroadcast(this, 0, 772 deleteIntent.setAction(AUTH_CANCELLED_ACTION), 0)) 773 .build(); 774 nm.notify(NOTIFICATION_ID_AUTH, notification); 775 } 776 } 777 778 private void removePbapNotification(int id) { 779 NotificationManager nm = (NotificationManager) 780 getSystemService(Context.NOTIFICATION_SERVICE); 781 nm.cancel(id); 782 } 783 784 public static String getLocalPhoneNum() { 785 return sLocalPhoneNum; 786 } 787 788 public static String getLocalPhoneName() { 789 return sLocalPhoneName; 790 } 791 792 public static String getRemoteDeviceName() { 793 return sRemoteDeviceName; 794 } 795 796 /** 797 * Handlers for incoming service calls 798 */ 799 private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() { 800 public int getState() { 801 if (DEBUG) Log.d(TAG, "getState " + mState); 802 803 if (!Utils.checkCaller()) { 804 Log.w(TAG,"getState(): not allowed for non-active user"); 805 return BluetoothPbap.STATE_DISCONNECTED; 806 } 807 808 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 809 return mState; 810 } 811 812 public BluetoothDevice getClient() { 813 if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice); 814 815 if (!Utils.checkCaller()) { 816 Log.w(TAG,"getClient(): not allowed for non-active user"); 817 return null; 818 } 819 820 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 821 if (mState == BluetoothPbap.STATE_DISCONNECTED) { 822 return null; 823 } 824 return mRemoteDevice; 825 } 826 827 public boolean isConnected(BluetoothDevice device) { 828 if (!Utils.checkCaller()) { 829 Log.w(TAG,"isConnected(): not allowed for non-active user"); 830 return false; 831 } 832 833 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 834 return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device); 835 } 836 837 public boolean connect(BluetoothDevice device) { 838 if (!Utils.checkCaller()) { 839 Log.w(TAG,"connect(): not allowed for non-active user"); 840 return false; 841 } 842 843 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 844 "Need BLUETOOTH_ADMIN permission"); 845 return false; 846 } 847 848 public void disconnect() { 849 if (DEBUG) Log.d(TAG, "disconnect"); 850 851 if (!Utils.checkCaller()) { 852 Log.w(TAG,"disconnect(): not allowed for non-active user"); 853 return; 854 } 855 856 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 857 "Need BLUETOOTH_ADMIN permission"); 858 synchronized (BluetoothPbapService.this) { 859 switch (mState) { 860 case BluetoothPbap.STATE_CONNECTED: 861 if (mServerSession != null) { 862 mServerSession.close(); 863 mServerSession = null; 864 } 865 866 closeConnectionSocket(); 867 868 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 869 break; 870 default: 871 break; 872 } 873 } 874 } 875 }; 876} 877