BluetoothPbapService.java revision 0ce48bf302e010af811c903f8a986df567562ecf
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.IBluetooth; 46import android.bluetooth.IBluetoothPbap; 47import android.bluetooth.BluetoothUuid; 48import android.content.Context; 49import android.content.Intent; 50import android.os.Handler; 51import android.os.IBinder; 52import android.os.Message; 53import android.os.PowerManager; 54import android.os.RemoteException; 55import android.os.ServiceManager; 56import android.telephony.TelephonyManager; 57import android.text.TextUtils; 58import android.util.Log; 59import com.android.bluetooth.Utils; 60 61 62import com.android.bluetooth.R; 63import com.android.bluetooth.btservice.AdapterService; 64 65import java.io.IOException; 66 67import javax.obex.ServerSession; 68 69public 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 isWaitingAuthorization = 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 isWaitingAuthorization) { 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 isWaitingAuthorization = 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 ((!isWaitingAuthorization) || 284 (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) { 285 // this reply is not for us 286 return; 287 } 288 289 isWaitingAuthorization = false; 290 291 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 292 BluetoothDevice.CONNECTION_ACCESS_NO) == 293 BluetoothDevice.CONNECTION_ACCESS_YES) { 294 295 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 296 boolean result = mRemoteDevice.setTrust(true); 297 if (VERBOSE) Log.v(TAG, "setTrust() result=" + result); 298 } 299 try { 300 if (mConnSocket != null) { 301 startObexServerSession(); 302 } else { 303 stopObexServerSession(); 304 } 305 } catch (IOException ex) { 306 Log.e(TAG, "Caught the error: " + ex.toString()); 307 } 308 } else { 309 stopObexServerSession(); 310 } 311 } else if (action.equals(AUTH_RESPONSE_ACTION)) { 312 String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); 313 notifyAuthKeyInput(sessionkey); 314 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 315 notifyAuthCancelled(); 316 } else { 317 removeTimeoutMsg = false; 318 } 319 320 if (removeTimeoutMsg) { 321 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 322 } 323 } 324 325 @Override 326 public void onDestroy() { 327 if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy"); 328 329 super.onDestroy(); 330 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 331 closeService(); 332 if(mSessionStatusHandler != null) { 333 mSessionStatusHandler.removeCallbacksAndMessages(null); 334 } 335 } 336 337 @Override 338 public IBinder onBind(Intent intent) { 339 if (VERBOSE) Log.v(TAG, "Pbap Service onBind"); 340 return mBinder; 341 } 342 343 private void startRfcommSocketListener() { 344 if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener"); 345 346 if (mAcceptThread == null) { 347 mAcceptThread = new SocketAcceptThread(); 348 mAcceptThread.setName("BluetoothPbapAcceptThread"); 349 mAcceptThread.start(); 350 } 351 } 352 353 private final boolean initSocket() { 354 if (VERBOSE) Log.v(TAG, "Pbap Service initSocket"); 355 356 boolean initSocketOK = false; 357 final int CREATE_RETRY_TIME = 10; 358 359 // It's possible that create will fail in some cases. retry for 10 times 360 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 361 initSocketOK = true; 362 try { 363 // It is mandatory for PSE to support initiation of bonding and 364 // encryption. 365 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord 366 ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid()); 367 368 } catch (IOException e) { 369 Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); 370 initSocketOK = false; 371 } 372 if (!initSocketOK) { 373 // Need to break out of this loop if BT is being turned off. 374 if (mAdapter == null) break; 375 int state = mAdapter.getState(); 376 if ((state != BluetoothAdapter.STATE_TURNING_ON) && 377 (state != BluetoothAdapter.STATE_ON)) { 378 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 379 break; 380 } 381 try { 382 if (VERBOSE) Log.v(TAG, "wait 300 ms"); 383 Thread.sleep(300); 384 } catch (InterruptedException e) { 385 Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); 386 break; 387 } 388 } else { 389 break; 390 } 391 } 392 393 if (mInterrupted) { 394 initSocketOK = false; 395 // close server socket to avoid resource leakage 396 closeServerSocket(); 397 } 398 399 if (initSocketOK) { 400 if (VERBOSE) Log.v(TAG, "Succeed to create listening socket "); 401 402 } else { 403 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 404 } 405 return initSocketOK; 406 } 407 408 private final synchronized void closeServerSocket() { 409 // exit SocketAcceptThread early 410 if (mServerSocket != null) { 411 try { 412 // this will cause mServerSocket.accept() return early with IOException 413 mServerSocket.close(); 414 mServerSocket = null; 415 } catch (IOException ex) { 416 Log.e(TAG, "Close Server Socket error: " + ex); 417 } 418 } 419 } 420 421 private final synchronized void closeConnectionSocket() { 422 if (mConnSocket != null) { 423 try { 424 mConnSocket.close(); 425 mConnSocket = null; 426 } catch (IOException e) { 427 Log.e(TAG, "Close Connection Socket error: " + e.toString()); 428 } 429 } 430 } 431 432 private final void closeService() { 433 if (VERBOSE) Log.v(TAG, "Pbap Service closeService in"); 434 435 // exit initSocket early 436 mInterrupted = true; 437 closeServerSocket(); 438 439 if (mAcceptThread != null) { 440 try { 441 mAcceptThread.shutdown(); 442 mAcceptThread.join(); 443 mAcceptThread = null; 444 } catch (InterruptedException ex) { 445 Log.w(TAG, "mAcceptThread close error" + ex); 446 } 447 } 448 449 if (mWakeLock != null) { 450 mWakeLock.release(); 451 mWakeLock = null; 452 } 453 454 if (mServerSession != null) { 455 mServerSession.close(); 456 mServerSession = null; 457 } 458 459 closeConnectionSocket(); 460 461 mHasStarted = false; 462 if (mStartId != -1 && stopSelfResult(mStartId)) { 463 if (VERBOSE) Log.v(TAG, "successfully stopped pbap service"); 464 mStartId = -1; 465 } 466 if (VERBOSE) Log.v(TAG, "Pbap Service closeService out"); 467 } 468 469 private final void startObexServerSession() throws IOException { 470 if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession"); 471 472 // acquire the wakeLock before start Obex transaction thread 473 if (mWakeLock == null) { 474 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 475 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 476 "StartingObexPbapTransaction"); 477 mWakeLock.setReferenceCounted(false); 478 mWakeLock.acquire(); 479 } 480 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 481 if (tm != null) { 482 sLocalPhoneNum = tm.getLine1Number(); 483 sLocalPhoneName = tm.getLine1AlphaTag(); 484 if (TextUtils.isEmpty(sLocalPhoneName)) { 485 sLocalPhoneName = this.getString(R.string.localPhoneName); 486 } 487 } 488 489 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this); 490 synchronized (this) { 491 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 492 mAuth.setChallenged(false); 493 mAuth.setCancelled(false); 494 } 495 BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket); 496 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 497 setState(BluetoothPbap.STATE_CONNECTED); 498 499 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 500 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 501 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 502 503 if (VERBOSE) { 504 Log.v(TAG, "startObexServerSession() success!"); 505 } 506 } 507 508 private void stopObexServerSession() { 509 if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession"); 510 511 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 512 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 513 // Release the wake lock if obex transaction is over 514 if (mWakeLock != null) { 515 mWakeLock.release(); 516 mWakeLock = null; 517 } 518 519 if (mServerSession != null) { 520 mServerSession.close(); 521 mServerSession = null; 522 } 523 524 mAcceptThread = null; 525 526 closeConnectionSocket(); 527 528 // Last obex transaction is finished, we start to listen for incoming 529 // connection again 530 if (mAdapter.isEnabled()) { 531 startRfcommSocketListener(); 532 } 533 setState(BluetoothPbap.STATE_DISCONNECTED); 534 } 535 536 private void notifyAuthKeyInput(final String key) { 537 synchronized (mAuth) { 538 if (key != null) { 539 mAuth.setSessionKey(key); 540 } 541 mAuth.setChallenged(true); 542 mAuth.notify(); 543 } 544 } 545 546 private void notifyAuthCancelled() { 547 synchronized (mAuth) { 548 mAuth.setCancelled(true); 549 mAuth.notify(); 550 } 551 } 552 553 /** 554 * A thread that runs in the background waiting for remote rfcomm 555 * connect.Once a remote socket connected, this thread shall be 556 * shutdown.When the remote disconnect,this thread shall run again waiting 557 * for next request. 558 */ 559 private class SocketAcceptThread extends Thread { 560 561 private boolean stopped = false; 562 563 @Override 564 public void run() { 565 BluetoothServerSocket serverSocket; 566 if (mServerSocket == null) { 567 if (!initSocket()) { 568 return; 569 } 570 } 571 572 while (!stopped) { 573 try { 574 if (VERBOSE) Log.v(TAG, "Accepting socket connection..."); 575 serverSocket = mServerSocket; 576 if (serverSocket == null) { 577 Log.w(TAG, "mServerSocket is null"); 578 break; 579 } 580 mConnSocket = serverSocket.accept(); 581 if (VERBOSE) Log.v(TAG, "Accepted socket connection..."); 582 583 synchronized (BluetoothPbapService.this) { 584 if (mConnSocket == null) { 585 Log.w(TAG, "mConnSocket is null"); 586 break; 587 } 588 mRemoteDevice = mConnSocket.getRemoteDevice(); 589 } 590 if (mRemoteDevice == null) { 591 Log.i(TAG, "getRemoteDevice() = null"); 592 break; 593 } 594 sRemoteDeviceName = mRemoteDevice.getName(); 595 // In case getRemoteName failed and return null 596 if (TextUtils.isEmpty(sRemoteDeviceName)) { 597 sRemoteDeviceName = getString(R.string.defaultname); 598 } 599 boolean trust = mRemoteDevice.getTrustState(); 600 if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust); 601 602 if (trust) { 603 try { 604 if (VERBOSE) Log.v(TAG, "incoming connection accepted from: " 605 + sRemoteDeviceName + " automatically as trusted device"); 606 startObexServerSession(); 607 } catch (IOException ex) { 608 Log.e(TAG, "catch exception starting obex server session" 609 + ex.toString()); 610 } 611 } else { 612 Intent intent = new 613 Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 614 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 615 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 616 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 617 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 618 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 619 intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, 620 BluetoothPbapReceiver.class.getName()); 621 622 isWaitingAuthorization = true; 623 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 624 625 if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: " 626 + sRemoteDeviceName); 627 628 // In case car kit time out and try to use HFP for 629 // phonebook 630 // access, while UI still there waiting for user to 631 // confirm 632 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 633 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 634 } 635 stopped = true; // job done ,close this thread; 636 } catch (IOException ex) { 637 stopped=true; 638 /* 639 if (stopped) { 640 break; 641 } 642 */ 643 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 644 } 645 } 646 } 647 648 void shutdown() { 649 stopped = true; 650 interrupt(); 651 } 652 } 653 654 private final Handler mSessionStatusHandler = new Handler() { 655 @Override 656 public void handleMessage(Message msg) { 657 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 658 659 switch (msg.what) { 660 case START_LISTENER: 661 if (mAdapter.isEnabled()) { 662 startRfcommSocketListener(); 663 } else { 664 closeService();// release all resources 665 } 666 break; 667 case USER_TIMEOUT: 668 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 669 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 670 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 671 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 672 sendBroadcast(intent); 673 isWaitingAuthorization = false; 674 stopObexServerSession(); 675 break; 676 case AUTH_TIMEOUT: 677 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 678 sendBroadcast(i); 679 removePbapNotification(NOTIFICATION_ID_AUTH); 680 notifyAuthCancelled(); 681 break; 682 case MSG_SERVERSESSION_CLOSE: 683 stopObexServerSession(); 684 break; 685 case MSG_SESSION_ESTABLISHED: 686 break; 687 case MSG_SESSION_DISCONNECTED: 688 // case MSG_SERVERSESSION_CLOSE will handle ,so just skip 689 break; 690 case MSG_OBEX_AUTH_CHALL: 691 createPbapNotification(AUTH_CHALL_ACTION); 692 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 693 .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 694 break; 695 case MSG_ACQUIRE_WAKE_LOCK: 696 if (mWakeLock == null) { 697 PowerManager pm = (PowerManager)getSystemService( 698 Context.POWER_SERVICE); 699 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 700 "StartingObexPbapTransaction"); 701 mWakeLock.setReferenceCounted(false); 702 mWakeLock.acquire(); 703 Log.w(TAG, "Acquire Wake Lock"); 704 } 705 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 706 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 707 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 708 break; 709 case MSG_RELEASE_WAKE_LOCK: 710 if (mWakeLock != null) { 711 mWakeLock.release(); 712 mWakeLock = null; 713 Log.w(TAG, "Release Wake Lock"); 714 } 715 break; 716 default: 717 break; 718 } 719 } 720 }; 721 722 private void setState(int state) { 723 setState(state, BluetoothPbap.RESULT_SUCCESS); 724 } 725 726 private synchronized void setState(int state, int result) { 727 if (state != mState) { 728 if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " 729 + result); 730 int prevState = mState; 731 mState = state; 732 Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); 733 intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState); 734 intent.putExtra(BluetoothPbap.PBAP_STATE, mState); 735 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 736 sendBroadcast(intent, BLUETOOTH_PERM); 737 AdapterService s = AdapterService.getAdapterService(); 738 if (s != null) { 739 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP, 740 mState, prevState); 741 } 742 } 743 } 744 745 private void createPbapNotification(String action) { 746 747 NotificationManager nm = (NotificationManager) 748 getSystemService(Context.NOTIFICATION_SERVICE); 749 750 // Create an intent triggered by clicking on the status icon. 751 Intent clickIntent = new Intent(); 752 clickIntent.setClass(this, BluetoothPbapActivity.class); 753 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 754 clickIntent.setAction(action); 755 756 // Create an intent triggered by clicking on the 757 // "Clear All Notifications" button 758 Intent deleteIntent = new Intent(); 759 deleteIntent.setClass(this, BluetoothPbapReceiver.class); 760 761 Notification notification = null; 762 String name = getRemoteDeviceName(); 763 764 if (action.equals(AUTH_CHALL_ACTION)) { 765 deleteIntent.setAction(AUTH_CANCELLED_ACTION); 766 notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, 767 getString(R.string.auth_notif_ticker), System.currentTimeMillis()); 768 notification.color = getResources().getColor( 769 com.android.internal.R.color.system_notification_accent_color); 770 notification.setLatestEventInfo(this, getString(R.string.auth_notif_title), 771 getString(R.string.auth_notif_message, name), PendingIntent 772 .getActivity(this, 0, clickIntent, 0)); 773 774 notification.flags |= Notification.FLAG_AUTO_CANCEL; 775 notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; 776 notification.defaults = Notification.DEFAULT_SOUND; 777 notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0); 778 nm.notify(NOTIFICATION_ID_AUTH, notification); 779 } 780 } 781 782 private void removePbapNotification(int id) { 783 NotificationManager nm = (NotificationManager) 784 getSystemService(Context.NOTIFICATION_SERVICE); 785 nm.cancel(id); 786 } 787 788 public static String getLocalPhoneNum() { 789 return sLocalPhoneNum; 790 } 791 792 public static String getLocalPhoneName() { 793 return sLocalPhoneName; 794 } 795 796 public static String getRemoteDeviceName() { 797 return sRemoteDeviceName; 798 } 799 800 /** 801 * Handlers for incoming service calls 802 */ 803 private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() { 804 public int getState() { 805 if (DEBUG) Log.d(TAG, "getState " + mState); 806 807 if (!Utils.checkCaller()) { 808 Log.w(TAG,"getState(): not allowed for non-active user"); 809 return BluetoothPbap.STATE_DISCONNECTED; 810 } 811 812 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 813 return mState; 814 } 815 816 public BluetoothDevice getClient() { 817 if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice); 818 819 if (!Utils.checkCaller()) { 820 Log.w(TAG,"getClient(): not allowed for non-active user"); 821 return null; 822 } 823 824 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 825 if (mState == BluetoothPbap.STATE_DISCONNECTED) { 826 return null; 827 } 828 return mRemoteDevice; 829 } 830 831 public boolean isConnected(BluetoothDevice device) { 832 if (!Utils.checkCaller()) { 833 Log.w(TAG,"isConnected(): not allowed for non-active user"); 834 return false; 835 } 836 837 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 838 return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device); 839 } 840 841 public boolean connect(BluetoothDevice device) { 842 if (!Utils.checkCaller()) { 843 Log.w(TAG,"connect(): not allowed for non-active user"); 844 return false; 845 } 846 847 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 848 "Need BLUETOOTH_ADMIN permission"); 849 return false; 850 } 851 852 public void disconnect() { 853 if (DEBUG) Log.d(TAG, "disconnect"); 854 855 if (!Utils.checkCaller()) { 856 Log.w(TAG,"disconnect(): not allowed for non-active user"); 857 return; 858 } 859 860 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 861 "Need BLUETOOTH_ADMIN permission"); 862 synchronized (BluetoothPbapService.this) { 863 switch (mState) { 864 case BluetoothPbap.STATE_CONNECTED: 865 if (mServerSession != null) { 866 mServerSession.close(); 867 mServerSession = null; 868 } 869 870 closeConnectionSocket(); 871 872 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 873 break; 874 default: 875 break; 876 } 877 } 878 } 879 }; 880} 881