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