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