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