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