BluetoothPbapService.java revision f2c447b81c6c03cb1c8a3e64a10381e23934834a
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.telephony.TelephonyManager; 55import android.util.Log; 56import android.widget.Toast; 57 58import java.io.IOException; 59import java.util.ArrayList; 60 61import javax.obex.ServerSession; 62 63public class BluetoothPbapService extends Service { 64 65 private static final String TAG = "BluetoothPbapService"; 66 67 /** 68 * To enable PBAP debug logging - run below cmd in adb shell, and restart 69 * com.android.bluetooth process. only enable VERBOSE log: 70 * "setprop log.tag.BluetoothPbapService VERBOSE"; enable both VERBOSE and DEBUG log: 71 * "setprop log.tag.BluetoothPbapService DEBUG" 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 || Log.isLoggable(TAG, Log.DEBUG); 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 static BluetoothPbapVcardManager sVcardManager; 177 178 private ServerSession mServerSession = null; 179 180 private BluetoothServerSocket mServerSocket = null; 181 182 private BluetoothSocket mConnSocket = null; 183 184 private BluetoothDevice mRemoteDevice = null; 185 186 private static String sLocalPhoneNum = null; 187 188 private static String sLocalPhoneName = null; 189 190 private static String sRemoteDeviceName = null; 191 192 private boolean mHasStarted = false; 193 194 private volatile boolean mInterrupted; 195 196 private int mState; 197 198 private int mStartId = -1; 199 200 public BluetoothPbapService() { 201 mState = BluetoothPbap.STATE_DISCONNECTED; 202 } 203 204 @Override 205 public void onCreate() { 206 super.onCreate(); 207 if (VERBOSE) Log.v(TAG, "Pbap Service onCreate"); 208 209 mInterrupted = false; 210 mAdapter = (BluetoothAdapter)getSystemService(Context.BLUETOOTH_SERVICE); 211 sVcardManager = new BluetoothPbapVcardManager(BluetoothPbapService.this); 212 if (!mHasStarted) { 213 mHasStarted = true; 214 if (VERBOSE) Log.v(TAG, "Starting PBAP service"); 215 216 int state = mAdapter.getState(); 217 if (state == BluetoothAdapter.STATE_ON) { 218 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 219 .obtainMessage(START_LISTENER), TIME_TO_WAIT_VALUE); 220 } 221 } 222 } 223 224 @Override 225 public void onStart(Intent intent, int startId) { 226 if (VERBOSE) Log.v(TAG, "Pbap Service onStart"); 227 228 mStartId = startId; 229 if (mAdapter == null) { 230 Log.w(TAG, "Stopping BluetoothPbapService: " 231 + "device does not have BT or device is not ready"); 232 // Release all resources 233 closeService(); 234 } else { 235 parseIntent(intent); 236 } 237 } 238 239 // process the intent from receiver 240 private void parseIntent(final Intent intent) { 241 String action = intent.getExtras().getString("action"); 242 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 243 boolean removeTimeoutMsg = true; 244 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 245 removeTimeoutMsg = false; 246 if (state == BluetoothAdapter.STATE_OFF) { 247 // Release all resources 248 closeService(); 249 } 250 } else if (action.equals(ACCESS_ALLOWED_ACTION)) { 251 if (intent.getBooleanExtra(EXTRA_ALWAYS_ALLOWED, false)) { 252 boolean result = mRemoteDevice.setTrust(true); 253 if (VERBOSE) Log.v(TAG, "setTrust() result=" + result); 254 } 255 try { 256 if (mConnSocket != null) { 257 startObexServerSession(); 258 } else { 259 stopObexServerSession(); 260 } 261 } catch (IOException ex) { 262 Log.e(TAG, "Caught the error: " + ex.toString()); 263 } 264 } else if (action.equals(ACCESS_DISALLOWED_ACTION)) { 265 stopObexServerSession(); 266 } else if (action.equals(AUTH_RESPONSE_ACTION)) { 267 String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); 268 notifyAuthKeyInput(sessionkey); 269 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 270 notifyAuthCancelled(); 271 } else { 272 removeTimeoutMsg = false; 273 } 274 275 if (removeTimeoutMsg) { 276 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 277 } 278 } 279 280 @Override 281 public void onDestroy() { 282 if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy"); 283 284 super.onDestroy(); 285 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 286 if (mWakeLock != null) { 287 mWakeLock.release(); 288 mWakeLock = null; 289 } 290 } 291 292 @Override 293 public IBinder onBind(Intent intent) { 294 if (VERBOSE) Log.v(TAG, "Pbap Service onBind"); 295 return mBinder; 296 } 297 298 private void startRfcommSocketListener() { 299 if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener"); 300 301 if (mServerSocket == null) { 302 if (!initSocket()) { 303 closeService(); 304 return; 305 } 306 } 307 if (mAcceptThread == null) { 308 mAcceptThread = new SocketAcceptThread(); 309 mAcceptThread.setName("BluetoothPbapAcceptThread"); 310 mAcceptThread.start(); 311 } 312 } 313 314 private final boolean initSocket() { 315 if (VERBOSE) Log.v(TAG, "Pbap Service initSocket"); 316 317 boolean initSocketOK = true; 318 final int CREATE_RETRY_TIME = 10; 319 320 // It's possible that create will fail in some cases. retry for 10 times 321 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 322 try { 323 // It is mandatory for PSE to support initiation of bonding and 324 // encryption. 325 mServerSocket = mAdapter.listenUsingRfcommOn(PORT_NUM); 326 } catch (IOException e) { 327 Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); 328 initSocketOK = false; 329 } 330 if (!initSocketOK) { 331 synchronized (this) { 332 try { 333 if (VERBOSE) Log.v(TAG, "wait 3 seconds"); 334 Thread.sleep(3000); 335 } catch (InterruptedException e) { 336 Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); 337 mInterrupted = true; 338 } 339 } 340 } else { 341 break; 342 } 343 } 344 345 if (initSocketOK) { 346 if (VERBOSE) Log.v(TAG, "Succeed to create listening socket on channel " + PORT_NUM); 347 348 } else { 349 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 350 } 351 return initSocketOK; 352 } 353 354 private final void closeSocket(boolean server, boolean accept) throws IOException { 355 if (server == true) { 356 // Stop the possible trying to init serverSocket 357 mInterrupted = true; 358 359 if (mServerSocket != null) { 360 mServerSocket.close(); 361 } 362 mServerSocket = null; 363 } 364 365 if (accept == true) { 366 if (mConnSocket != null) { 367 mConnSocket.close(); 368 } 369 mConnSocket = null; 370 } 371 } 372 373 private final void closeService() { 374 if (VERBOSE) Log.v(TAG, "Pbap Service closeService"); 375 376 try { 377 closeSocket(true, true); 378 } catch (IOException ex) { 379 Log.e(TAG, "CloseSocket error: " + ex); 380 } 381 382 if (mAcceptThread != null) { 383 try { 384 mAcceptThread.shutdown(); 385 mAcceptThread.join(); 386 mAcceptThread = null; 387 } catch (InterruptedException ex) { 388 Log.w(TAG, "mAcceptThread close error" + ex); 389 } 390 } 391 if (mServerSession != null) { 392 mServerSession.close(); 393 mServerSession = null; 394 } 395 396 mHasStarted = false; 397 if (stopSelfResult(mStartId)) { 398 if (VERBOSE) Log.v(TAG, "successfully stopped pbap service"); 399 } 400 } 401 402 private final void startObexServerSession() throws IOException { 403 if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession"); 404 405 // acquire the wakeLock before start Obex transaction thread 406 if (mWakeLock == null) { 407 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 408 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 409 "StartingObexPbapTransaction"); 410 mWakeLock.setReferenceCounted(false); 411 mWakeLock.acquire(); 412 } 413 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 414 if (tm != null) { 415 sLocalPhoneNum = tm.getLine1Number(); 416 sLocalPhoneName = tm.getLine1AlphaTag(); 417 } 418 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler); 419 synchronized (this) { 420 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 421 mAuth.setChallenged(false); 422 mAuth.setCancelled(false); 423 } 424 BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket); 425 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 426 setState(BluetoothPbap.STATE_CONNECTED); 427 if (VERBOSE) { 428 Log.v(TAG, "startObexServerSession() success!"); 429 } 430 } 431 432 private void stopObexServerSession() { 433 if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession"); 434 435 // Release the wake lock if obex transaction is over 436 if (mWakeLock != null) { 437 mWakeLock.release(); 438 mWakeLock = null; 439 } 440 441 if (mServerSession != null) { 442 mServerSession.close(); 443 mServerSession = null; 444 } 445 446 mAcceptThread = null; 447 448 try { 449 closeSocket(false, true); 450 } catch (IOException e) { 451 Log.e(TAG, "closeSocket error: " + e.toString()); 452 } 453 // Last obex transaction is finished, we start to listen for incoming 454 // connection again 455 if (mAdapter.isEnabled()) { 456 startRfcommSocketListener(); 457 } 458 setState(BluetoothPbap.STATE_DISCONNECTED); 459 } 460 461 private void notifyAuthKeyInput(final String key) { 462 synchronized (this) { 463 mAuth.setSessionKey(key); 464 mAuth.setChallenged(true); 465 mAuth.notify(); 466 } 467 } 468 469 private void notifyAuthCancelled() { 470 synchronized (this) { 471 mAuth.setCancelled(true); 472 mAuth.notify(); 473 } 474 } 475 476 /** 477 * A thread that runs in the background waiting for remote rfcomm 478 * connect.Once a remote socket connected, this thread shall be 479 * shutdown.When the remote disconnect,this thread shall run again waiting 480 * for next request. 481 */ 482 private class SocketAcceptThread extends Thread { 483 484 private boolean stopped = false; 485 486 @Override 487 public void run() { 488 while (!stopped) { 489 try { 490 mConnSocket = mServerSocket.accept(); 491 492 mRemoteDevice = mConnSocket.getRemoteDevice(); 493 if (mRemoteDevice != null) { 494 sRemoteDeviceName = mRemoteDevice.getName(); 495 // In case getRemoteName failed and return null 496 if (sRemoteDeviceName == null) { 497 sRemoteDeviceName = getString(R.string.defaultname); 498 } 499 } 500 boolean trust = mRemoteDevice.getTrustState(); 501 if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust); 502 503 if (trust) { 504 try { 505 if (VERBOSE) Log.v(TAG, "incomming connection accepted from: " 506 + sRemoteDeviceName + " automatically as trusted device"); 507 startObexServerSession(); 508 } catch (IOException ex) { 509 Log.e(TAG, "catch exception starting obex server session" 510 + ex.toString()); 511 } 512 } else { 513 createPbapNotification(ACCESS_REQUEST_ACTION); 514 if (VERBOSE) Log.v(TAG, "incomming connection accepted from: " 515 + sRemoteDeviceName); 516 517 // In case car kit time out and try to use HFP for 518 // phonebook 519 // access, while UI still there waiting for user to 520 // confirm 521 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 522 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 523 } 524 stopped = true; // job done ,close this thread; 525 } catch (IOException ex) { 526 if (stopped) { 527 break; 528 } 529 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 530 } 531 } 532 } 533 534 void shutdown() { 535 stopped = true; 536 interrupt(); 537 } 538 } 539 540 private final Handler mSessionStatusHandler = new Handler() { 541 @Override 542 public void handleMessage(Message msg) { 543 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 544 545 CharSequence tmpTxt; 546 switch (msg.what) { 547 case START_LISTENER: 548 if (mAdapter.isEnabled()) { 549 startRfcommSocketListener(); 550 } else { 551 closeService();// release all resources 552 } 553 break; 554 case USER_TIMEOUT: 555 Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 556 sendBroadcast(intent); 557 removePbapNotification(NOTIFICATION_ID_ACCESS); 558 stopObexServerSession(); 559 break; 560 case AUTH_TIMEOUT: 561 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 562 sendBroadcast(i); 563 removePbapNotification(NOTIFICATION_ID_AUTH); 564 notifyAuthCancelled(); 565 break; 566 case MSG_SERVERSESSION_CLOSE: 567 stopObexServerSession(); 568 tmpTxt = getString(R.string.toast_disconnected, sRemoteDeviceName); 569 Toast.makeText(BluetoothPbapService.this, tmpTxt, Toast.LENGTH_SHORT).show(); 570 break; 571 case MSG_SESSION_ESTABLISHED: 572 tmpTxt = getString(R.string.toast_connected, sRemoteDeviceName); 573 Toast.makeText(BluetoothPbapService.this, tmpTxt, Toast.LENGTH_SHORT).show(); 574 break; 575 case MSG_SESSION_DISCONNECTED: 576 // case MSG_SERVERSESSION_CLOSE will handle ,so just skip 577 break; 578 case MSG_OBEX_AUTH_CHALL: 579 createPbapNotification(AUTH_CHALL_ACTION); 580 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 581 .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 582 break; 583 default: 584 break; 585 } 586 } 587 }; 588 589 private void setState(int state) { 590 setState(state, BluetoothPbap.RESULT_SUCCESS); 591 } 592 593 private synchronized void setState(int state, int result) { 594 if (state != mState) { 595 if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " 596 + result); 597 Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); 598 intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, mState); 599 mState = state; 600 intent.putExtra(BluetoothPbap.PBAP_STATE, mState); 601 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 602 sendBroadcast(intent, BLUETOOTH_PERM); 603 } 604 } 605 606 private void createPbapNotification(String action) { 607 Context context = getApplicationContext(); 608 609 NotificationManager nm = (NotificationManager)context 610 .getSystemService(Context.NOTIFICATION_SERVICE); 611 612 // Create an intent triggered by clicking on the status icon. 613 Intent clickIntent = new Intent(); 614 clickIntent.setClass(context, BluetoothPbapActivity.class); 615 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 616 clickIntent.setAction(action); 617 618 // Create an intent triggered by clicking on the 619 // "Clear All Notifications" button 620 Intent deleteIntent = new Intent(); 621 deleteIntent.setClass(context, BluetoothPbapReceiver.class); 622 623 Notification notification = null; 624 String name = getRemoteDeviceName(); 625 if (action.equals(ACCESS_REQUEST_ACTION)) { 626 deleteIntent.setAction(ACCESS_DISALLOWED_ACTION); 627 notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context 628 .getString(R.string.pbap_notif_ticker), System.currentTimeMillis()); 629 notification.setLatestEventInfo(context, context.getString(R.string.pbap_notif_title), 630 context.getString(R.string.pbap_notif_message, name), PendingIntent 631 .getActivity(context, 0, clickIntent, 0)); 632 633 notification.flags |= Notification.FLAG_AUTO_CANCEL; 634 notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE; 635 notification.defaults = Notification.DEFAULT_SOUND; 636 notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); 637 nm.notify(NOTIFICATION_ID_ACCESS, notification); 638 } else if (action.equals(AUTH_CHALL_ACTION)) { 639 deleteIntent.setAction(AUTH_CANCELLED_ACTION); 640 notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context 641 .getString(R.string.auth_notif_ticker), System.currentTimeMillis()); 642 notification.setLatestEventInfo(context, context.getString(R.string.auth_notif_title), 643 context.getString(R.string.auth_notif_message, name), PendingIntent 644 .getActivity(context, 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(context, 0, deleteIntent, 0); 650 nm.notify(NOTIFICATION_ID_AUTH, notification); 651 } 652 } 653 654 private void removePbapNotification(int id) { 655 Context context = getApplicationContext(); 656 NotificationManager nm = (NotificationManager)context 657 .getSystemService(Context.NOTIFICATION_SERVICE); 658 nm.cancel(id); 659 } 660 661 public static int getPhonebookSize(final int type) { 662 int size; 663 switch (type) { 664 case BluetoothPbapObexServer.ContentType.PHONEBOOK: 665 size = sVcardManager.getPhonebookSize(); 666 break; 667 default: 668 size = sVcardManager.getCallHistorySize(type); 669 break; 670 } 671 if (VERBOSE) Log.v(TAG, "getPhonebookSzie size = " + size + " type = " + type); 672 return size; 673 } 674 675 public static void closeContactsCursor(final int type) { 676 if (VERBOSE) Log.v(TAG, "closeContactsCursor: type=" + type); 677 678 switch (type) { 679 case BluetoothPbapObexServer.ContentType.PHONEBOOK: 680 sVcardManager.closeContactsCursor(); 681 break; 682 default: 683 sVcardManager.closeCallLogCursor(); 684 break; 685 } 686 } 687 688 public static int queryDataFromContactsDB(final int type) { 689 if (VERBOSE) Log.v(TAG, "queryDataFromContactsDB: type=" + type); 690 691 int queryResult = 0; 692 switch (type) { 693 case BluetoothPbapObexServer.ContentType.PHONEBOOK: 694 queryResult = sVcardManager.queryDataFromContactsDB(); 695 break; 696 default: 697 queryResult = sVcardManager.queryDataFromCallLogDB(type); 698 break; 699 } 700 return queryResult; 701 } 702 703 public static String getPhonebook(final int type, final int pos, final boolean vcardType) { 704 if (VERBOSE) Log.v(TAG, "getPhonebook type=" + type + " pos=" + pos + " vcardType=" 705 + vcardType); 706 707 String vCardStr; 708 switch (type) { 709 case BluetoothPbapObexServer.ContentType.PHONEBOOK: 710 vCardStr = sVcardManager.getPhonebook(pos, vcardType); 711 break; 712 default: 713 vCardStr = sVcardManager.getCallHistory(pos, vcardType); 714 break; 715 } 716 return vCardStr; 717 } 718 719 public static String getLocalPhoneNum() { 720 return sLocalPhoneNum; 721 } 722 723 public static String getLocalPhoneName() { 724 return sLocalPhoneName; 725 } 726 727 public static String getRemoteDeviceName() { 728 return sRemoteDeviceName; 729 } 730 731 // Used for phone book listing by name 732 public static ArrayList<String> getPhonebookNameList() { 733 return sVcardManager.loadNameList(); 734 } 735 736 // Used for phone book listing by number 737 public static ArrayList<String> getPhonebookNumberList() { 738 return sVcardManager.loadNumberList(); 739 } 740 741 // Used for call history listing 742 public static ArrayList<String> getCallLogList(final int type) { 743 return sVcardManager.loadCallHistoryList(type); 744 } 745 746 /** 747 * Handlers for incoming service calls 748 */ 749 private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() { 750 public int getState() { 751 if (DEBUG) Log.d(TAG, "getState " + mState); 752 753 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 754 return mState; 755 } 756 757 public BluetoothDevice getClient() { 758 if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice); 759 760 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 761 if (mState == BluetoothPbap.STATE_DISCONNECTED) { 762 return null; 763 } 764 return mRemoteDevice; 765 } 766 767 public boolean isConnected(BluetoothDevice device) { 768 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 769 return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device); 770 } 771 772 public boolean connect(BluetoothDevice device) { 773 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 774 "Need BLUETOOTH_ADMIN permission"); 775 return false; 776 } 777 778 public void disconnect() { 779 if (DEBUG) Log.d(TAG, "disconnect"); 780 781 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 782 "Need BLUETOOTH_ADMIN permission"); 783 synchronized (BluetoothPbapService.this) { 784 switch (mState) { 785 case BluetoothPbap.STATE_CONNECTED: 786 if (mServerSession != null) { 787 mServerSession.close(); 788 mServerSession = null; 789 } 790 try { 791 closeSocket(false, true); 792 } catch (IOException ex) { 793 Log.e(TAG, "Caught the error: " + ex); 794 } 795 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 796 break; 797 default: 798 break; 799 } 800 } 801 } 802 }; 803} 804