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