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