BluetoothPbapService.java revision 773ca65d26f0eb0eda3ec44c2a788043387981cf
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 com.android.bluetooth.R; 36 37import android.app.Notification; 38import android.app.NotificationManager; 39import android.app.PendingIntent; 40import android.app.Service; 41import android.content.Context; 42import android.content.Intent; 43import android.content.res.Resources; 44import android.bluetooth.BluetoothAdapter; 45import android.bluetooth.BluetoothDevice; 46import android.bluetooth.BluetoothPbap; 47import android.bluetooth.BluetoothSocket; 48import android.bluetooth.BluetoothServerSocket; 49import android.bluetooth.IBluetoothPbap; 50import android.os.Handler; 51import android.os.IBinder; 52import android.os.Message; 53import android.os.PowerManager; 54import android.provider.ContactsContract.RawContacts; 55import android.telephony.TelephonyManager; 56import android.text.TextUtils; 57import android.util.Log; 58 59import java.io.IOException; 60import java.util.ArrayList; 61 62import javax.obex.ServerSession; 63 64public 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 = Log.isLoggable(TAG, Log.DEBUG); 75 76 public static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 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)getSystemService(Context.BLUETOOTH_SERVICE); 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 if (TextUtils.isEmpty(sLocalPhoneNum)) { 425 // Default number (000000) should be ok 426 sLocalPhoneNum = this.getString(R.string.defaultnumber); 427 } 428 sLocalPhoneName = tm.getLine1AlphaTag(); 429 if (TextUtils.isEmpty(sLocalPhoneName)) { 430 sLocalPhoneName = this.getString(R.string.unknownName); 431 } 432 } 433 434 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this); 435 synchronized (this) { 436 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 437 mAuth.setChallenged(false); 438 mAuth.setCancelled(false); 439 } 440 BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket); 441 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 442 setState(BluetoothPbap.STATE_CONNECTED); 443 if (VERBOSE) { 444 Log.v(TAG, "startObexServerSession() success!"); 445 } 446 } 447 448 private void stopObexServerSession() { 449 if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession"); 450 451 // Release the wake lock if obex transaction is over 452 if (mWakeLock != null) { 453 mWakeLock.release(); 454 mWakeLock = null; 455 } 456 457 if (mServerSession != null) { 458 mServerSession.close(); 459 mServerSession = null; 460 } 461 462 mAcceptThread = null; 463 464 try { 465 closeSocket(false, true); 466 mConnSocket = null; 467 } catch (IOException e) { 468 Log.e(TAG, "closeSocket error: " + e.toString()); 469 } 470 // Last obex transaction is finished, we start to listen for incoming 471 // connection again 472 if (mAdapter.isEnabled()) { 473 startRfcommSocketListener(); 474 } 475 setState(BluetoothPbap.STATE_DISCONNECTED); 476 } 477 478 private void notifyAuthKeyInput(final String key) { 479 synchronized (mAuth) { 480 if (key != null) { 481 mAuth.setSessionKey(key); 482 } 483 mAuth.setChallenged(true); 484 mAuth.notify(); 485 } 486 } 487 488 private void notifyAuthCancelled() { 489 synchronized (mAuth) { 490 mAuth.setCancelled(true); 491 mAuth.notify(); 492 } 493 } 494 495 /** 496 * A thread that runs in the background waiting for remote rfcomm 497 * connect.Once a remote socket connected, this thread shall be 498 * shutdown.When the remote disconnect,this thread shall run again waiting 499 * for next request. 500 */ 501 private class SocketAcceptThread extends Thread { 502 503 private boolean stopped = false; 504 505 @Override 506 public void run() { 507 while (!stopped) { 508 try { 509 mConnSocket = mServerSocket.accept(); 510 511 mRemoteDevice = mConnSocket.getRemoteDevice(); 512 if (mRemoteDevice == null) { 513 Log.i(TAG, "getRemoteDevice() = null"); 514 break; 515 } 516 sRemoteDeviceName = mRemoteDevice.getName(); 517 // In case getRemoteName failed and return null 518 if (TextUtils.isEmpty(sRemoteDeviceName)) { 519 sRemoteDeviceName = getString(R.string.defaultname); 520 } 521 boolean trust = mRemoteDevice.getTrustState(); 522 if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust); 523 524 if (trust) { 525 try { 526 if (VERBOSE) Log.v(TAG, "incomming connection accepted from: " 527 + sRemoteDeviceName + " automatically as trusted device"); 528 startObexServerSession(); 529 } catch (IOException ex) { 530 Log.e(TAG, "catch exception starting obex server session" 531 + ex.toString()); 532 } 533 } else { 534 createPbapNotification(ACCESS_REQUEST_ACTION); 535 if (VERBOSE) Log.v(TAG, "incomming connection accepted from: " 536 + sRemoteDeviceName); 537 538 // In case car kit time out and try to use HFP for 539 // phonebook 540 // access, while UI still there waiting for user to 541 // confirm 542 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 543 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 544 } 545 stopped = true; // job done ,close this thread; 546 } catch (IOException ex) { 547 if (stopped) { 548 break; 549 } 550 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 551 } 552 } 553 } 554 555 void shutdown() { 556 stopped = true; 557 interrupt(); 558 } 559 } 560 561 private final Handler mSessionStatusHandler = new Handler() { 562 @Override 563 public void handleMessage(Message msg) { 564 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 565 566 CharSequence tmpTxt; 567 switch (msg.what) { 568 case START_LISTENER: 569 if (mAdapter.isEnabled()) { 570 startRfcommSocketListener(); 571 } else { 572 closeService();// release all resources 573 } 574 break; 575 case USER_TIMEOUT: 576 Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 577 sendBroadcast(intent); 578 removePbapNotification(NOTIFICATION_ID_ACCESS); 579 stopObexServerSession(); 580 break; 581 case AUTH_TIMEOUT: 582 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 583 sendBroadcast(i); 584 removePbapNotification(NOTIFICATION_ID_AUTH); 585 notifyAuthCancelled(); 586 break; 587 case MSG_SERVERSESSION_CLOSE: 588 stopObexServerSession(); 589 break; 590 case MSG_SESSION_ESTABLISHED: 591 break; 592 case MSG_SESSION_DISCONNECTED: 593 // case MSG_SERVERSESSION_CLOSE will handle ,so just skip 594 break; 595 case MSG_OBEX_AUTH_CHALL: 596 createPbapNotification(AUTH_CHALL_ACTION); 597 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 598 .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 599 break; 600 default: 601 break; 602 } 603 } 604 }; 605 606 private void setState(int state) { 607 setState(state, BluetoothPbap.RESULT_SUCCESS); 608 } 609 610 private synchronized void setState(int state, int result) { 611 if (state != mState) { 612 if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " 613 + result); 614 Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); 615 intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, mState); 616 mState = state; 617 intent.putExtra(BluetoothPbap.PBAP_STATE, mState); 618 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 619 sendBroadcast(intent, BLUETOOTH_PERM); 620 } 621 } 622 623 private void createPbapNotification(String action) { 624 Context context = getApplicationContext(); 625 626 NotificationManager nm = (NotificationManager)context 627 .getSystemService(Context.NOTIFICATION_SERVICE); 628 629 // Create an intent triggered by clicking on the status icon. 630 Intent clickIntent = new Intent(); 631 clickIntent.setClass(context, BluetoothPbapActivity.class); 632 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 633 clickIntent.setAction(action); 634 635 // Create an intent triggered by clicking on the 636 // "Clear All Notifications" button 637 Intent deleteIntent = new Intent(); 638 deleteIntent.setClass(context, BluetoothPbapReceiver.class); 639 640 Notification notification = null; 641 String name = getRemoteDeviceName(); 642 if (action.equals(ACCESS_REQUEST_ACTION)) { 643 deleteIntent.setAction(ACCESS_DISALLOWED_ACTION); 644 notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context 645 .getString(R.string.pbap_notif_ticker), System.currentTimeMillis()); 646 notification.setLatestEventInfo(context, context.getString(R.string.pbap_notif_title), 647 context.getString(R.string.pbap_notif_message, name), PendingIntent 648 .getActivity(context, 0, clickIntent, 0)); 649 650 notification.flags |= Notification.FLAG_AUTO_CANCEL; 651 notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; 652 notification.defaults = Notification.DEFAULT_SOUND; 653 notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); 654 nm.notify(NOTIFICATION_ID_ACCESS, notification); 655 } else if (action.equals(AUTH_CHALL_ACTION)) { 656 deleteIntent.setAction(AUTH_CANCELLED_ACTION); 657 notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context 658 .getString(R.string.auth_notif_ticker), System.currentTimeMillis()); 659 notification.setLatestEventInfo(context, context.getString(R.string.auth_notif_title), 660 context.getString(R.string.auth_notif_message, name), PendingIntent 661 .getActivity(context, 0, clickIntent, 0)); 662 663 notification.flags |= Notification.FLAG_AUTO_CANCEL; 664 notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; 665 notification.defaults = Notification.DEFAULT_SOUND; 666 notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); 667 nm.notify(NOTIFICATION_ID_AUTH, notification); 668 } 669 } 670 671 private void removePbapNotification(int id) { 672 Context context = getApplicationContext(); 673 NotificationManager nm = (NotificationManager)context 674 .getSystemService(Context.NOTIFICATION_SERVICE); 675 nm.cancel(id); 676 } 677 678 public static String getLocalPhoneNum() { 679 return sLocalPhoneNum; 680 } 681 682 public static String getLocalPhoneName() { 683 return sLocalPhoneName; 684 } 685 686 public static String getRemoteDeviceName() { 687 return sRemoteDeviceName; 688 } 689 690 /** 691 * Handlers for incoming service calls 692 */ 693 private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() { 694 public int getState() { 695 if (DEBUG) Log.d(TAG, "getState " + mState); 696 697 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 698 return mState; 699 } 700 701 public BluetoothDevice getClient() { 702 if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice); 703 704 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 705 if (mState == BluetoothPbap.STATE_DISCONNECTED) { 706 return null; 707 } 708 return mRemoteDevice; 709 } 710 711 public boolean isConnected(BluetoothDevice device) { 712 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 713 return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device); 714 } 715 716 public boolean connect(BluetoothDevice device) { 717 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 718 "Need BLUETOOTH_ADMIN permission"); 719 return false; 720 } 721 722 public void disconnect() { 723 if (DEBUG) Log.d(TAG, "disconnect"); 724 725 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 726 "Need BLUETOOTH_ADMIN permission"); 727 synchronized (BluetoothPbapService.this) { 728 switch (mState) { 729 case BluetoothPbap.STATE_CONNECTED: 730 if (mServerSession != null) { 731 mServerSession.close(); 732 mServerSession = null; 733 } 734 try { 735 closeSocket(false, true); 736 mConnSocket = null; 737 } catch (IOException ex) { 738 Log.e(TAG, "Caught the error: " + ex); 739 } 740 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 741 break; 742 default: 743 break; 744 } 745 } 746 } 747 }; 748} 749