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