BluetoothPbapService.java revision 00bac0c867db92336ac808fc8c6f845fa2849023
1/* 2 * Copyright (c) 2008-2009, Motorola, Inc. 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * - Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * - Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * - Neither the name of the Motorola, Inc. nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.android.bluetooth.pbap; 34 35import android.app.Notification; 36import android.app.NotificationChannel; 37import android.app.NotificationManager; 38import android.app.PendingIntent; 39import android.bluetooth.BluetoothAdapter; 40import android.bluetooth.BluetoothDevice; 41import android.bluetooth.BluetoothPbap; 42import android.bluetooth.BluetoothProfile; 43import android.bluetooth.BluetoothSocket; 44import android.bluetooth.IBluetoothPbap; 45import android.content.BroadcastReceiver; 46import android.content.Context; 47import android.content.Intent; 48import android.content.IntentFilter; 49import android.database.ContentObserver; 50import android.database.sqlite.SQLiteException; 51import android.os.Handler; 52import android.os.Message; 53import android.os.PowerManager; 54import android.telephony.TelephonyManager; 55import android.text.TextUtils; 56import android.util.Log; 57 58import com.android.bluetooth.BluetoothObexTransport; 59import com.android.bluetooth.IObexConnectionHandler; 60import com.android.bluetooth.ObexServerSockets; 61import com.android.bluetooth.R; 62import com.android.bluetooth.Utils; 63import com.android.bluetooth.btservice.ProfileService; 64import com.android.bluetooth.sdp.SdpManager; 65import com.android.bluetooth.util.DevicePolicyUtils; 66 67import java.io.IOException; 68import java.util.ArrayList; 69import java.util.List; 70 71import javax.obex.ServerSession; 72 73public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { 74 private static final String TAG = "BluetoothPbapService"; 75 76 /** 77 * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 78 * restart com.android.bluetooth process. only enable DEBUG log: 79 * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and 80 * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" 81 */ 82 83 public static final boolean DEBUG = true; 84 85 public static final boolean VERBOSE = false; 86 87 /** 88 * Intent indicating incoming obex authentication request which is from 89 * PCE(Carkit) 90 */ 91 static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall"; 92 93 /** 94 * Intent indicating obex session key input complete by user which is sent 95 * from BluetoothPbapActivity 96 */ 97 static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse"; 98 99 /** 100 * Intent indicating user canceled obex authentication session key input 101 * which is sent from BluetoothPbapActivity 102 */ 103 static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled"; 104 105 /** 106 * Intent indicating timeout for user confirmation, which is sent to 107 * BluetoothPbapActivity 108 */ 109 static final String USER_CONFIRM_TIMEOUT_ACTION = 110 "com.android.bluetooth.pbap.userconfirmtimeout"; 111 112 /** 113 * Intent Extra name indicating session key which is sent from 114 * BluetoothPbapActivity 115 */ 116 static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey"; 117 118 static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; 119 120 static final int MSG_SERVERSESSION_CLOSE = 5000; 121 122 static final int MSG_SESSION_ESTABLISHED = 5001; 123 124 static final int MSG_OBEX_AUTH_CHALL = 5003; 125 126 static final int MSG_ACQUIRE_WAKE_LOCK = 5004; 127 128 private static final int MSG_RELEASE_WAKE_LOCK = 5005; 129 130 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 131 132 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 133 134 private static final int START_LISTENER = 1; 135 136 private static final int USER_TIMEOUT = 2; 137 138 private static final int AUTH_TIMEOUT = 3; 139 140 private static final int SHUTDOWN = 4; 141 142 static final int LOAD_CONTACTS = 5; 143 144 private static final int CHECK_SECONDARY_VERSION_COUNTER = 6; 145 146 static final int ROLLOVER_COUNTERS = 7; 147 148 private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; 149 150 private static final int RELEASE_WAKE_LOCK_DELAY = 10000; 151 152 private static final int NOTIFICATION_ID_AUTH = -1000002; 153 154 private static final String PBAP_NOTIFICATION_CHANNEL = "pbap_notification_channel"; 155 156 private PowerManager.WakeLock mWakeLock; 157 158 private BluetoothPbapAuthenticator mAuth; 159 160 private BluetoothPbapObexServer mPbapServer; 161 162 private ServerSession mServerSession; 163 164 private BluetoothSocket mConnSocket; 165 166 private BluetoothDevice mRemoteDevice; 167 168 private static String sLocalPhoneNum; 169 170 private static String sLocalPhoneName; 171 172 private static String sRemoteDeviceName; 173 174 private volatile boolean mInterrupted; 175 176 private int mState; 177 178 private boolean mIsWaitingAuthorization = false; 179 180 private ObexServerSockets mServerSockets; 181 182 private static final int SDP_PBAP_SERVER_VERSION = 0x0102; 183 184 private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0001; 185 186 private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; 187 188 private int mSdpHandle = -1; 189 190 protected Context mContext; 191 192 private PbapHandler mSessionStatusHandler; 193 194 // package and class name to which we send intent to check phone book access permission 195 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 196 private static final String ACCESS_AUTHORITY_CLASS = 197 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 198 199 private Thread mThreadLoadContacts; 200 201 private Thread mThreadUpdateSecVersionCounter; 202 203 private class BluetoothPbapContentObserver extends ContentObserver { 204 BluetoothPbapContentObserver() { 205 super(new Handler()); 206 } 207 208 @Override 209 public void onChange(boolean selfChange) { 210 Log.d(TAG, " onChange on contact uri "); 211 if (BluetoothPbapUtils.contactsLoaded) { 212 if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) { 213 mSessionStatusHandler.sendMessage( 214 mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER)); 215 } 216 } 217 } 218 } 219 220 private BluetoothPbapContentObserver mContactChangeObserver; 221 222 // process the intent from receiver 223 private void parseIntent(final Intent intent) { 224 String action = intent.getAction(); 225 if (DEBUG) { 226 Log.d(TAG, "action: " + action); 227 } 228 if (action == null) { 229 return; // Nothing to do 230 } 231 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 232 if (DEBUG) { 233 Log.d(TAG, "state: " + state); 234 } 235 236 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { 237 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 238 239 if (mRemoteDevice == null) { 240 return; 241 } 242 if (DEBUG) { 243 Log.d(TAG, "ACL disconnected for " + device); 244 } 245 if (mRemoteDevice.equals(device)) { 246 if (mIsWaitingAuthorization) { 247 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 248 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 249 } 250 mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE) 251 .sendToTarget(); 252 } 253 return; 254 } 255 256 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 257 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 258 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 259 260 if ((!mIsWaitingAuthorization) || (requestType 261 != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) { 262 // this reply is not for us 263 return; 264 } 265 266 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 267 mIsWaitingAuthorization = false; 268 269 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 270 BluetoothDevice.CONNECTION_ACCESS_NO) 271 == BluetoothDevice.CONNECTION_ACCESS_YES) { 272 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 273 boolean result = mRemoteDevice.setPhonebookAccessPermission( 274 BluetoothDevice.ACCESS_ALLOWED); 275 if (VERBOSE) { 276 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)=" + result); 277 } 278 } 279 try { 280 if (mConnSocket != null) { 281 startObexServerSession(); 282 } else { 283 mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE).sendToTarget(); 284 } 285 } catch (IOException ex) { 286 Log.e(TAG, "Caught the error: " + ex.toString()); 287 } 288 } else { 289 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 290 boolean result = mRemoteDevice.setPhonebookAccessPermission( 291 BluetoothDevice.ACCESS_REJECTED); 292 if (VERBOSE) { 293 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)=" + result); 294 } 295 } 296 mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE).sendToTarget(); 297 } 298 return; 299 } 300 301 if (action.equals(AUTH_RESPONSE_ACTION)) { 302 String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); 303 notifyAuthKeyInput(sessionkey); 304 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 305 notifyAuthCancelled(); 306 } else { 307 Log.w(TAG, "Unrecognized intent!"); 308 return; 309 } 310 311 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 312 } 313 314 private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() { 315 @Override 316 public void onReceive(Context context, Intent intent) { 317 parseIntent(intent); 318 } 319 }; 320 321 private synchronized void closeConnectionSocket() { 322 if (mConnSocket != null) { 323 try { 324 mConnSocket.close(); 325 mConnSocket = null; 326 } catch (IOException e) { 327 Log.e(TAG, "Close Connection Socket error: " + e.toString()); 328 } 329 } 330 } 331 332 private void closeService() { 333 if (VERBOSE) { 334 Log.v(TAG, "Pbap Service closeService"); 335 } 336 337 BluetoothPbapUtils.savePbapParams(this, BluetoothPbapUtils.sPrimaryVersionCounter, 338 BluetoothPbapUtils.sSecondaryVersionCounter, BluetoothPbapUtils.sDbIdentifier.get(), 339 BluetoothPbapUtils.contactsLastUpdated, BluetoothPbapUtils.totalFields, 340 BluetoothPbapUtils.totalSvcFields, BluetoothPbapUtils.totalContacts); 341 342 // exit initSocket early 343 mInterrupted = true; 344 if (mWakeLock != null) { 345 mWakeLock.release(); 346 mWakeLock = null; 347 } 348 349 cleanUpServerSocket(); 350 351 if (mSessionStatusHandler != null) { 352 mSessionStatusHandler.removeCallbacksAndMessages(null); 353 } 354 mRemoteDevice = null; 355 } 356 357 private void cleanUpServerSocket() { 358 // Step 1: clean up active server session 359 if (mServerSession != null) { 360 mServerSession.close(); 361 mServerSession = null; 362 } 363 // Step 2: clean up existing connection socket 364 closeConnectionSocket(); 365 // Step 3: clean up SDP record 366 cleanUpSdpRecord(); 367 // Step 4: clean up existing server sockets 368 if (mServerSockets != null) { 369 mServerSockets.shutdown(false); 370 mServerSockets = null; 371 } 372 } 373 374 private void cleanUpSdpRecord() { 375 if (mSdpHandle < 0) { 376 Log.w(TAG, "cleanUpSdpRecord, SDP record never created"); 377 return; 378 } 379 int sdpHandle = mSdpHandle; 380 mSdpHandle = -1; 381 SdpManager sdpManager = SdpManager.getDefaultManager(); 382 if (DEBUG) { 383 Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle); 384 } 385 if (sdpManager == null) { 386 Log.e(TAG, "sdpManager is null"); 387 } else if (!sdpManager.removeSdpRecord(sdpHandle)) { 388 Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle); 389 } 390 } 391 392 private void startObexServerSession() throws IOException { 393 if (VERBOSE) { 394 Log.v(TAG, "Pbap Service startObexServerSession"); 395 } 396 397 // acquire the wakeLock before start Obex transaction thread 398 if (mWakeLock == null) { 399 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 400 mWakeLock = 401 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingObexPbapTransaction"); 402 mWakeLock.setReferenceCounted(false); 403 mWakeLock.acquire(); 404 } 405 TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 406 if (tm != null) { 407 sLocalPhoneNum = tm.getLine1Number(); 408 sLocalPhoneName = tm.getLine1AlphaTag(); 409 if (TextUtils.isEmpty(sLocalPhoneName)) { 410 sLocalPhoneName = this.getString(R.string.localPhoneName); 411 } 412 } 413 414 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this); 415 synchronized (this) { 416 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 417 mAuth.setChallenged(false); 418 mAuth.setCancelled(false); 419 } 420 BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket); 421 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 422 setState(BluetoothProfile.STATE_CONNECTED); 423 424 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 425 mSessionStatusHandler.sendMessageDelayed( 426 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 427 RELEASE_WAKE_LOCK_DELAY); 428 429 if (VERBOSE) { 430 Log.v(TAG, "startObexServerSession() success!"); 431 } 432 } 433 434 private void stopObexServerSession() { 435 if (VERBOSE) { 436 Log.v(TAG, "Pbap Service stopObexServerSession"); 437 } 438 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 439 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 440 // Release the wake lock if obex transaction is over 441 if (mWakeLock != null) { 442 mWakeLock.release(); 443 mWakeLock = null; 444 } 445 446 if (mServerSession != null) { 447 mServerSession.close(); 448 mServerSession = null; 449 } 450 closeConnectionSocket(); 451 452 // Last obex transaction is finished, we start to listen for incoming 453 // connection again 454 startSocketListeners(); 455 setState(BluetoothProfile.STATE_DISCONNECTED); 456 } 457 458 private void notifyAuthKeyInput(final String key) { 459 synchronized (mAuth) { 460 if (key != null) { 461 mAuth.setSessionKey(key); 462 } 463 mAuth.setChallenged(true); 464 mAuth.notify(); 465 } 466 } 467 468 private void notifyAuthCancelled() { 469 synchronized (mAuth) { 470 mAuth.setCancelled(true); 471 mAuth.notify(); 472 } 473 } 474 475 private class PbapHandler extends Handler { 476 @Override 477 public void handleMessage(Message msg) { 478 if (VERBOSE) { 479 Log.v(TAG, "Handler(): got msg=" + msg.what); 480 } 481 482 switch (msg.what) { 483 case START_LISTENER: 484 startSocketListeners(); 485 break; 486 case USER_TIMEOUT: 487 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 488 intent.setPackage(getString(R.string.pairing_ui_package)); 489 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 490 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 491 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 492 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 493 mIsWaitingAuthorization = false; 494 stopObexServerSession(); 495 break; 496 case AUTH_TIMEOUT: 497 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 498 sendBroadcast(i); 499 removePbapNotification(NOTIFICATION_ID_AUTH); 500 notifyAuthCancelled(); 501 break; 502 case MSG_SERVERSESSION_CLOSE: 503 stopObexServerSession(); 504 break; 505 case MSG_OBEX_AUTH_CHALL: 506 createPbapNotification(AUTH_CHALL_ACTION); 507 mSessionStatusHandler.sendMessageDelayed( 508 mSessionStatusHandler.obtainMessage(AUTH_TIMEOUT), 509 USER_CONFIRM_TIMEOUT_VALUE); 510 break; 511 case MSG_ACQUIRE_WAKE_LOCK: 512 if (mWakeLock == null) { 513 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 514 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 515 "StartingObexPbapTransaction"); 516 mWakeLock.setReferenceCounted(false); 517 mWakeLock.acquire(); 518 Log.w(TAG, "Acquire Wake Lock"); 519 } 520 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 521 mSessionStatusHandler.sendMessageDelayed( 522 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 523 RELEASE_WAKE_LOCK_DELAY); 524 break; 525 case MSG_RELEASE_WAKE_LOCK: 526 if (mWakeLock != null) { 527 mWakeLock.release(); 528 mWakeLock = null; 529 Log.w(TAG, "Release Wake Lock"); 530 } 531 break; 532 case SHUTDOWN: 533 closeService(); 534 break; 535 case LOAD_CONTACTS: 536 loadAllContacts(); 537 break; 538 case CHECK_SECONDARY_VERSION_COUNTER: 539 updateSecondaryVersion(); 540 break; 541 case ROLLOVER_COUNTERS: 542 BluetoothPbapUtils.rolloverCounters(); 543 break; 544 default: 545 break; 546 } 547 } 548 } 549 550 private void setState(int state) { 551 setState(state, BluetoothPbap.RESULT_SUCCESS); 552 } 553 554 private synchronized void setState(int state, int result) { 555 if (state != mState) { 556 if (DEBUG) { 557 Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " + result); 558 } 559 int prevState = mState; 560 mState = state; 561 Intent intent = new Intent(BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED); 562 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 563 intent.putExtra(BluetoothProfile.EXTRA_STATE, mState); 564 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 565 sendBroadcast(intent, BLUETOOTH_PERM); 566 } 567 } 568 569 int getConnectionState(BluetoothDevice device) { 570 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 571 return mState; 572 } 573 574 List<BluetoothDevice> getConnectedDevices() { 575 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 576 List<BluetoothDevice> devices = new ArrayList<>(); 577 if (mRemoteDevice != null) { 578 devices.add(mRemoteDevice); 579 } 580 return devices; 581 } 582 583 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 584 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 585 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 586 587 if (mRemoteDevice != null) { 588 for (int state : states) { 589 if (state == mState) { 590 devices.add(mRemoteDevice); 591 break; 592 } 593 } 594 } 595 return devices; 596 } 597 598 private void createPbapNotification(String action) { 599 600 NotificationManager nm = 601 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 602 NotificationChannel notificationChannel = new NotificationChannel(PBAP_NOTIFICATION_CHANNEL, 603 getString(R.string.pbap_notification_group), NotificationManager.IMPORTANCE_HIGH); 604 nm.createNotificationChannel(notificationChannel); 605 606 // Create an intent triggered by clicking on the status icon. 607 Intent clickIntent = new Intent(); 608 clickIntent.setClass(this, BluetoothPbapActivity.class); 609 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 610 clickIntent.setAction(action); 611 612 // Create an intent triggered by clicking on the 613 // "Clear All Notifications" button 614 Intent deleteIntent = new Intent(); 615 deleteIntent.setClass(this, BluetoothPbapService.class); 616 deleteIntent.setAction(AUTH_CANCELLED_ACTION); 617 618 String name = getRemoteDeviceName(); 619 620 if (action.equals(AUTH_CHALL_ACTION)) { 621 Notification notification = 622 new Notification.Builder(this, PBAP_NOTIFICATION_CHANNEL).setWhen( 623 System.currentTimeMillis()) 624 .setContentTitle(getString(R.string.auth_notif_title)) 625 .setContentText(getString(R.string.auth_notif_message, name)) 626 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 627 .setTicker(getString(R.string.auth_notif_ticker)) 628 .setColor(getResources().getColor( 629 com.android.internal.R.color.system_notification_accent_color, 630 this.getTheme())) 631 .setFlag(Notification.FLAG_AUTO_CANCEL, true) 632 .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true) 633 .setDefaults(Notification.DEFAULT_SOUND) 634 .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0)) 635 .setDeleteIntent(PendingIntent.getBroadcast(this, 0, deleteIntent, 0)) 636 .build(); 637 nm.notify(NOTIFICATION_ID_AUTH, notification); 638 } 639 } 640 641 private void removePbapNotification(int id) { 642 NotificationManager nm = 643 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 644 nm.cancel(id); 645 } 646 647 public static String getLocalPhoneNum() { 648 return sLocalPhoneNum; 649 } 650 651 public static String getLocalPhoneName() { 652 return sLocalPhoneName; 653 } 654 655 public static String getRemoteDeviceName() { 656 return sRemoteDeviceName; 657 } 658 659 @Override 660 protected IProfileServiceBinder initBinder() { 661 return new PbapBinder(this); 662 } 663 664 @Override 665 protected boolean start() { 666 Log.v(TAG, "start()"); 667 mState = BluetoothProfile.STATE_DISCONNECTED; 668 mContext = this; 669 mSessionStatusHandler = new PbapHandler(); 670 IntentFilter filter = new IntentFilter(); 671 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 672 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 673 filter.addAction(AUTH_RESPONSE_ACTION); 674 filter.addAction(AUTH_CANCELLED_ACTION); 675 mInterrupted = false; 676 BluetoothPbapConfig.init(this); 677 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 678 registerReceiver(mPbapReceiver, filter); 679 try { 680 mContactChangeObserver = new BluetoothPbapContentObserver(); 681 getContentResolver().registerContentObserver( 682 DevicePolicyUtils.getEnterprisePhoneUri(this), false, 683 mContactChangeObserver); 684 } catch (SQLiteException e) { 685 Log.e(TAG, "SQLite exception: " + e); 686 } catch (IllegalStateException e) { 687 Log.e(TAG, "Illegal state exception, content observer is already registered"); 688 } 689 return true; 690 } 691 692 @Override 693 protected boolean stop() { 694 Log.v(TAG, "stop()"); 695 if (mContactChangeObserver == null) { 696 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 697 return true; 698 } 699 try { 700 unregisterReceiver(mPbapReceiver); 701 getContentResolver().unregisterContentObserver(mContactChangeObserver); 702 mContactChangeObserver = null; 703 } catch (Exception e) { 704 Log.w(TAG, "Unable to unregister pbap receiver", e); 705 } 706 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 707 setState(BluetoothProfile.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 708 return true; 709 } 710 711 void disconnect(BluetoothDevice device) { 712 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 713 // TODO: disconnect the specified device. 714 synchronized (this) { 715 if (mState == BluetoothProfile.STATE_CONNECTED) { 716 if (mServerSession != null) { 717 mServerSession.close(); 718 mServerSession = null; 719 } 720 721 closeConnectionSocket(); 722 723 setState(BluetoothProfile.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 724 } 725 } 726 } 727 728 // Has to be a static class or a memory leak can occur. 729 private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { 730 private BluetoothPbapService mService; 731 732 private BluetoothPbapService getService() { 733 if (!Utils.checkCaller()) { 734 Log.w(TAG, "not allowed for non-active user"); 735 return null; 736 } 737 if (mService != null && mService.isAvailable()) { 738 return mService; 739 } 740 return null; 741 } 742 743 PbapBinder(BluetoothPbapService service) { 744 Log.v(TAG, "PbapBinder()"); 745 mService = service; 746 } 747 748 @Override 749 public boolean cleanup() { 750 mService = null; 751 return true; 752 } 753 754 @Override 755 public List<BluetoothDevice> getConnectedDevices() { 756 if (DEBUG) { 757 Log.d(TAG, "getConnectedDevices"); 758 } 759 BluetoothPbapService service = getService(); 760 if (service == null) { 761 return new ArrayList<>(0); 762 } 763 return service.getConnectedDevices(); 764 } 765 766 @Override 767 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 768 if (DEBUG) { 769 Log.d(TAG, "getDevicesMatchingConnectionStates"); 770 } 771 BluetoothPbapService service = getService(); 772 if (service == null) { 773 return new ArrayList<>(0); 774 } 775 return service.getDevicesMatchingConnectionStates(states); 776 } 777 778 @Override 779 public int getConnectionState(BluetoothDevice device) { 780 if (DEBUG) { 781 Log.d(TAG, "getConnectionState: " + device); 782 } 783 BluetoothPbapService service = getService(); 784 if (service == null) { 785 return BluetoothAdapter.STATE_DISCONNECTED; 786 } 787 return service.getConnectionState(device); 788 } 789 790 @Override 791 public void disconnect(BluetoothDevice device) { 792 if (DEBUG) { 793 Log.d(TAG, "disconnect"); 794 } 795 BluetoothPbapService service = getService(); 796 if (service == null) { 797 return; 798 } 799 service.disconnect(device); 800 } 801 } 802 803 /** 804 * Start server side socket listeners. Caller should make sure that adapter is in a ready state 805 * and SDP record is cleaned up. Otherwise, this method will fail. 806 */ 807 private synchronized void startSocketListeners() { 808 if (DEBUG) { 809 Log.d(TAG, "startsocketListener"); 810 } 811 if (mServerSession != null) { 812 if (DEBUG) { 813 Log.d(TAG, "mServerSession exists - shutting it down..."); 814 } 815 mServerSession.close(); 816 mServerSession = null; 817 } 818 closeConnectionSocket(); 819 if (mServerSockets != null) { 820 mServerSockets.prepareForNewConnect(); 821 } else { 822 mServerSockets = ObexServerSockets.create(this); 823 if (mServerSockets == null) { 824 // TODO: Handle - was not handled before 825 Log.e(TAG, "Failed to start the listeners"); 826 return; 827 } 828 if (mSdpHandle >= 0) { 829 Log.e(TAG, "SDP handle was not cleaned up, mSdpHandle=" + mSdpHandle); 830 return; 831 } 832 mSdpHandle = SdpManager.getDefaultManager() 833 .createPbapPseRecord("OBEX Phonebook Access Server", 834 mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(), 835 SDP_PBAP_SERVER_VERSION, SDP_PBAP_SUPPORTED_REPOSITORIES, 836 SDP_PBAP_SUPPORTED_FEATURES); 837 // fetch Pbap Params to check if significant change has happened to Database 838 BluetoothPbapUtils.fetchPbapParams(mContext); 839 840 if (DEBUG) { 841 Log.d(TAG, "PBAP server with handle:" + mSdpHandle); 842 } 843 } 844 } 845 846 long getDbIdentifier() { 847 return BluetoothPbapUtils.sDbIdentifier.get(); 848 } 849 850 @Override 851 public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) { 852 mRemoteDevice = remoteDevice; 853 if (DEBUG) { 854 Log.d(TAG, "onConnect(): mRemoteDevice=" + mRemoteDevice + " socket=" + socket); 855 } 856 if (mRemoteDevice == null || socket == null) { 857 Log.e(TAG, "onConnect(): Unexpected null. mRemoteDevice=" + mRemoteDevice 858 + " socket=" + socket); 859 return false; 860 } 861 mConnSocket = socket; 862 sRemoteDeviceName = mRemoteDevice.getName(); 863 // In case getRemoteName failed and return null 864 if (TextUtils.isEmpty(sRemoteDeviceName)) { 865 sRemoteDeviceName = getString(R.string.defaultname); 866 } 867 int permission = mRemoteDevice.getPhonebookAccessPermission(); 868 if (DEBUG) { 869 Log.d(TAG, "getPhonebookAccessPermission() = " + permission); 870 } 871 872 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 873 try { 874 startObexServerSession(); 875 } catch (IOException ex) { 876 Log.e(TAG, "Caught exception starting obex server session" + ex.toString()); 877 } 878 879 if (!BluetoothPbapUtils.contactsLoaded) { 880 mSessionStatusHandler.sendMessage( 881 mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); 882 } 883 884 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 885 if (DEBUG) { 886 Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName 887 + " automatically as already rejected device"); 888 } 889 return false; 890 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 891 // Send an Intent to Settings app to ask user preference. 892 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 893 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 894 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 895 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 896 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 897 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 898 mIsWaitingAuthorization = true; 899 sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM); 900 if (VERBOSE) { 901 Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName); 902 } 903 /* In case car kit time out and try to use HFP for phonebook 904 * access, while UI still there waiting for user to confirm */ 905 mSessionStatusHandler.sendMessageDelayed( 906 mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 907 /* We will continue the process when we receive 908 * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */ 909 } 910 return true; 911 } 912 913 /** 914 * Called when an unrecoverable error occurred in an accept thread. 915 * Close down the server socket, and restart. 916 * TODO: Change to message, to call start in correct context. 917 */ 918 @Override 919 public synchronized void onAcceptFailed() { 920 Log.w(TAG, "PBAP server socket accept thread failed. Restarting the server socket"); 921 922 if (mWakeLock != null) { 923 mWakeLock.release(); 924 mWakeLock = null; 925 } 926 927 cleanUpServerSocket(); 928 929 if (mSessionStatusHandler != null) { 930 mSessionStatusHandler.removeCallbacksAndMessages(null); 931 } 932 933 if (!mInterrupted) { 934 startSocketListeners(); 935 } 936 } 937 938 private void loadAllContacts() { 939 if (mThreadLoadContacts == null) { 940 Runnable r = new Runnable() { 941 @Override 942 public void run() { 943 BluetoothPbapUtils.loadAllContacts(mContext, 944 mSessionStatusHandler); 945 mThreadLoadContacts = null; 946 } 947 }; 948 mThreadLoadContacts = new Thread(r); 949 mThreadLoadContacts.start(); 950 } 951 } 952 953 private void updateSecondaryVersion() { 954 if (mThreadUpdateSecVersionCounter == null) { 955 Runnable r = new Runnable() { 956 @Override 957 public void run() { 958 BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, 959 mSessionStatusHandler); 960 mThreadUpdateSecVersionCounter = null; 961 } 962 }; 963 mThreadUpdateSecVersionCounter = new Thread(r); 964 mThreadUpdateSecVersionCounter.start(); 965 } 966 } 967 968} 969