BluetoothPbapService.java revision 776a51b650f0b0239758ee28a20af749b13e17a3
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.bluetooth.BluetoothAdapter; 36import android.bluetooth.BluetoothDevice; 37import android.bluetooth.BluetoothProfile; 38import android.bluetooth.BluetoothSocket; 39import android.bluetooth.IBluetoothPbap; 40import android.content.BroadcastReceiver; 41import android.content.Context; 42import android.content.Intent; 43import android.content.IntentFilter; 44import android.database.ContentObserver; 45import android.database.sqlite.SQLiteException; 46import android.os.Handler; 47import android.os.HandlerThread; 48import android.os.Looper; 49import android.os.Message; 50import android.os.PowerManager; 51import android.support.annotation.VisibleForTesting; 52import android.telephony.TelephonyManager; 53import android.text.TextUtils; 54import android.util.Log; 55 56import com.android.bluetooth.IObexConnectionHandler; 57import com.android.bluetooth.ObexServerSockets; 58import com.android.bluetooth.R; 59import com.android.bluetooth.Utils; 60import com.android.bluetooth.btservice.ProfileService; 61import com.android.bluetooth.sdp.SdpManager; 62import com.android.bluetooth.util.DevicePolicyUtils; 63 64import java.util.ArrayList; 65import java.util.HashMap; 66import java.util.List; 67 68public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { 69 private static final String TAG = "BluetoothPbapService"; 70 71 /** 72 * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 73 * restart com.android.bluetooth process. only enable DEBUG log: 74 * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and 75 * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" 76 */ 77 78 public static final boolean DEBUG = true; 79 80 public static final boolean VERBOSE = true; 81 82 /** 83 * Intent indicating incoming obex authentication request which is from 84 * PCE(Carkit) 85 */ 86 static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall"; 87 88 /** 89 * Intent indicating obex session key input complete by user which is sent 90 * from BluetoothPbapActivity 91 */ 92 static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse"; 93 94 /** 95 * Intent indicating user canceled obex authentication session key input 96 * which is sent from BluetoothPbapActivity 97 */ 98 static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled"; 99 100 /** 101 * Intent indicating timeout for user confirmation, which is sent to 102 * BluetoothPbapActivity 103 */ 104 static final String USER_CONFIRM_TIMEOUT_ACTION = 105 "com.android.bluetooth.pbap.userconfirmtimeout"; 106 107 /** 108 * Intent Extra name indicating session key which is sent from 109 * BluetoothPbapActivity 110 */ 111 static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey"; 112 113 static final String EXTRA_DEVICE = "com.android.bluetooth.pbap.device"; 114 115 static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; 116 117 static final int MSG_ACQUIRE_WAKE_LOCK = 5004; 118 119 static final int MSG_RELEASE_WAKE_LOCK = 5005; 120 121 static final int MSG_STATE_MACHINE_DONE = 5006; 122 123 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 124 125 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 126 127 private static final int START_LISTENER = 1; 128 129 static final int USER_TIMEOUT = 2; 130 131 private static final int SHUTDOWN = 4; 132 133 static final int LOAD_CONTACTS = 5; 134 135 private static final int CHECK_SECONDARY_VERSION_COUNTER = 6; 136 137 static final int ROLLOVER_COUNTERS = 7; 138 139 static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; 140 141 static final int RELEASE_WAKE_LOCK_DELAY = 10000; 142 143 private PowerManager.WakeLock mWakeLock; 144 145 private static String sLocalPhoneNum; 146 147 private static String sLocalPhoneName; 148 149 private ObexServerSockets mServerSockets = null; 150 151 private static final int SDP_PBAP_SERVER_VERSION = 0x0102; 152 153 private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0001; 154 155 private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; 156 157 /* PBAP will use Bluetooth notification ID from 1000000 (included) to 2000000 (excluded). 158 The notification ID should be unique in Bluetooth package. */ 159 private static final int PBAP_NOTIFICATION_ID_START = 1000000; 160 private static final int PBAP_NOTIFICATION_ID_END = 2000000; 161 162 private int mSdpHandle = -1; 163 164 protected Context mContext; 165 166 private PbapHandler mSessionStatusHandler; 167 private HandlerThread mHandlerThread; 168 private final HashMap<BluetoothDevice, PbapStateMachine> mPbapStateMachineMap = new HashMap<>(); 169 private volatile int mNextNotificationId = PBAP_NOTIFICATION_ID_START; 170 171 // package and class name to which we send intent to check phone book access permission 172 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 173 private static final String ACCESS_AUTHORITY_CLASS = 174 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 175 176 private Thread mThreadLoadContacts; 177 178 private Thread mThreadUpdateSecVersionCounter; 179 180 private class BluetoothPbapContentObserver extends ContentObserver { 181 BluetoothPbapContentObserver() { 182 super(new Handler()); 183 } 184 185 @Override 186 public void onChange(boolean selfChange) { 187 Log.d(TAG, " onChange on contact uri "); 188 if (BluetoothPbapUtils.contactsLoaded) { 189 if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) { 190 mSessionStatusHandler.sendMessage( 191 mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER)); 192 } 193 } 194 } 195 } 196 197 private BluetoothPbapContentObserver mContactChangeObserver; 198 199 private void parseIntent(final Intent intent) { 200 String action = intent.getAction(); 201 if (DEBUG) { 202 Log.d(TAG, "action: " + action); 203 } 204 if (action == null) { 205 return; 206 } 207 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 208 if (DEBUG) { 209 Log.d(TAG, "state: " + state); 210 } 211 212 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 213 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 214 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 215 if (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 216 return; 217 } 218 219 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 220 synchronized (mPbapStateMachineMap) { 221 PbapStateMachine sm = mPbapStateMachineMap.get(device); 222 if (sm == null) { 223 Log.w(TAG, "device not connected! device=" + device); 224 return; 225 } 226 mSessionStatusHandler.removeMessages(USER_TIMEOUT, sm); 227 int access = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 228 BluetoothDevice.CONNECTION_ACCESS_NO); 229 boolean savePreference = intent.getBooleanExtra( 230 BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); 231 232 if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { 233 if (savePreference) { 234 device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 235 if (VERBOSE) { 236 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); 237 } 238 } 239 sm.sendMessage(PbapStateMachine.AUTHORIZED); 240 } else { 241 if (savePreference) { 242 device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); 243 if (VERBOSE) { 244 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); 245 } 246 } 247 sm.sendMessage(PbapStateMachine.REJECTED); 248 } 249 return; 250 } 251 } 252 253 if (action.equals(AUTH_RESPONSE_ACTION)) { 254 String sessionKey = intent.getStringExtra(EXTRA_SESSION_KEY); 255 BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE); 256 synchronized (mPbapStateMachineMap) { 257 PbapStateMachine sm = mPbapStateMachineMap.get(device); 258 if (sm == null) { 259 return; 260 } 261 Message msg = sm.obtainMessage(PbapStateMachine.AUTH_KEY_INPUT, sessionKey); 262 sm.sendMessage(msg); 263 } 264 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 265 BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE); 266 synchronized (mPbapStateMachineMap) { 267 PbapStateMachine sm = mPbapStateMachineMap.get(device); 268 if (sm == null) { 269 return; 270 } 271 sm.sendMessage(PbapStateMachine.AUTH_CANCELLED); 272 } 273 } else { 274 Log.w(TAG, "Unrecognized intent!"); 275 } 276 } 277 278 private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() { 279 @Override 280 public void onReceive(Context context, Intent intent) { 281 parseIntent(intent); 282 } 283 }; 284 285 private void closeService() { 286 if (VERBOSE) { 287 Log.v(TAG, "Pbap Service closeService"); 288 } 289 290 BluetoothPbapUtils.savePbapParams(this, BluetoothPbapUtils.sPrimaryVersionCounter, 291 BluetoothPbapUtils.sSecondaryVersionCounter, BluetoothPbapUtils.sDbIdentifier.get(), 292 BluetoothPbapUtils.contactsLastUpdated, BluetoothPbapUtils.totalFields, 293 BluetoothPbapUtils.totalSvcFields, BluetoothPbapUtils.totalContacts); 294 295 if (mWakeLock != null) { 296 mWakeLock.release(); 297 mWakeLock = null; 298 } 299 300 cleanUpServerSocket(); 301 302 if (mSessionStatusHandler != null) { 303 mSessionStatusHandler.removeCallbacksAndMessages(null); 304 } 305 } 306 307 private void cleanUpServerSocket() { 308 // Step 1, 2: clean up active server session and connection socket 309 synchronized (mPbapStateMachineMap) { 310 for (PbapStateMachine stateMachine : mPbapStateMachineMap.values()) { 311 stateMachine.sendMessage(PbapStateMachine.DISCONNECT); 312 } 313 } 314 // Step 3: clean up SDP record 315 cleanUpSdpRecord(); 316 // Step 4: clean up existing server sockets 317 if (mServerSockets != null) { 318 mServerSockets.shutdown(false); 319 mServerSockets = null; 320 } 321 } 322 323 private void createSdpRecord() { 324 if (mSdpHandle > -1) { 325 Log.w(TAG, "createSdpRecord, SDP record already created"); 326 } 327 mSdpHandle = SdpManager.getDefaultManager() 328 .createPbapPseRecord("OBEX Phonebook Access Server", 329 mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(), 330 SDP_PBAP_SERVER_VERSION, SDP_PBAP_SUPPORTED_REPOSITORIES, 331 SDP_PBAP_SUPPORTED_FEATURES); 332 if (DEBUG) { 333 Log.d(TAG, "created Sdp record, mSdpHandle=" + mSdpHandle); 334 } 335 } 336 337 private void cleanUpSdpRecord() { 338 if (mSdpHandle < 0) { 339 Log.w(TAG, "cleanUpSdpRecord, SDP record never created"); 340 return; 341 } 342 int sdpHandle = mSdpHandle; 343 mSdpHandle = -1; 344 SdpManager sdpManager = SdpManager.getDefaultManager(); 345 if (DEBUG) { 346 Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle); 347 } 348 if (sdpManager == null) { 349 Log.e(TAG, "sdpManager is null"); 350 } else if (!sdpManager.removeSdpRecord(sdpHandle)) { 351 Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle); 352 } 353 } 354 355 private class PbapHandler extends Handler { 356 private PbapHandler(Looper looper) { 357 super(looper); 358 } 359 360 @Override 361 public void handleMessage(Message msg) { 362 if (VERBOSE) { 363 Log.v(TAG, "Handler(): got msg=" + msg.what); 364 } 365 366 switch (msg.what) { 367 case START_LISTENER: 368 mServerSockets = ObexServerSockets.create(BluetoothPbapService.this); 369 if (mServerSockets == null) { 370 Log.w(TAG, "ObexServerSockets.create() returned null"); 371 break; 372 } 373 createSdpRecord(); 374 // fetch Pbap Params to check if significant change has happened to Database 375 BluetoothPbapUtils.fetchPbapParams(mContext); 376 break; 377 case USER_TIMEOUT: 378 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 379 intent.setPackage(getString(R.string.pairing_ui_package)); 380 PbapStateMachine stateMachine = (PbapStateMachine) msg.obj; 381 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, stateMachine.getRemoteDevice()); 382 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 383 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 384 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 385 stateMachine.sendMessage(PbapStateMachine.REJECTED); 386 break; 387 case MSG_ACQUIRE_WAKE_LOCK: 388 if (mWakeLock == null) { 389 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 390 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 391 "StartingObexPbapTransaction"); 392 mWakeLock.setReferenceCounted(false); 393 mWakeLock.acquire(); 394 Log.w(TAG, "Acquire Wake Lock"); 395 } 396 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 397 mSessionStatusHandler.sendMessageDelayed( 398 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 399 RELEASE_WAKE_LOCK_DELAY); 400 break; 401 case MSG_RELEASE_WAKE_LOCK: 402 if (mWakeLock != null) { 403 mWakeLock.release(); 404 mWakeLock = null; 405 } 406 break; 407 case SHUTDOWN: 408 closeService(); 409 break; 410 case LOAD_CONTACTS: 411 loadAllContacts(); 412 break; 413 case CHECK_SECONDARY_VERSION_COUNTER: 414 updateSecondaryVersion(); 415 break; 416 case ROLLOVER_COUNTERS: 417 BluetoothPbapUtils.rolloverCounters(); 418 break; 419 case MSG_STATE_MACHINE_DONE: 420 PbapStateMachine sm = (PbapStateMachine) msg.obj; 421 BluetoothDevice remoteDevice = sm.getRemoteDevice(); 422 sm.quitNow(); 423 synchronized (mPbapStateMachineMap) { 424 mPbapStateMachineMap.remove(remoteDevice); 425 } 426 break; 427 default: 428 break; 429 } 430 } 431 } 432 433 int getConnectionState(BluetoothDevice device) { 434 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 435 if (mPbapStateMachineMap == null) { 436 return BluetoothProfile.STATE_DISCONNECTED; 437 } 438 439 synchronized (mPbapStateMachineMap) { 440 PbapStateMachine sm = mPbapStateMachineMap.get(device); 441 if (sm == null) { 442 return BluetoothProfile.STATE_DISCONNECTED; 443 } 444 return sm.getConnectionState(); 445 } 446 } 447 448 List<BluetoothDevice> getConnectedDevices() { 449 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 450 if (mPbapStateMachineMap == null) { 451 return new ArrayList<>(); 452 } 453 synchronized (mPbapStateMachineMap) { 454 return new ArrayList<>(mPbapStateMachineMap.keySet()); 455 } 456 } 457 458 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 459 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 460 List<BluetoothDevice> devices = new ArrayList<>(); 461 if (mPbapStateMachineMap == null || states == null) { 462 return devices; 463 } 464 synchronized (mPbapStateMachineMap) { 465 for (int state : states) { 466 for (BluetoothDevice device : mPbapStateMachineMap.keySet()) { 467 if (state == mPbapStateMachineMap.get(device).getConnectionState()) { 468 devices.add(device); 469 } 470 } 471 } 472 } 473 return devices; 474 } 475 476 void disconnect(BluetoothDevice device) { 477 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 478 synchronized (mPbapStateMachineMap) { 479 PbapStateMachine sm = mPbapStateMachineMap.get(device); 480 if (sm != null) { 481 sm.sendMessage(PbapStateMachine.DISCONNECT); 482 } 483 } 484 } 485 486 static String getLocalPhoneNum() { 487 return sLocalPhoneNum; 488 } 489 490 static String getLocalPhoneName() { 491 return sLocalPhoneName; 492 } 493 494 @Override 495 protected IProfileServiceBinder initBinder() { 496 return new PbapBinder(this); 497 } 498 499 @Override 500 protected boolean start() { 501 if (VERBOSE) { 502 Log.v(TAG, "start()"); 503 } 504 mContext = this; 505 mHandlerThread = new HandlerThread("PbapHandlerThread"); 506 mHandlerThread.start(); 507 mSessionStatusHandler = new PbapHandler(mHandlerThread.getLooper()); 508 IntentFilter filter = new IntentFilter(); 509 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 510 filter.addAction(AUTH_RESPONSE_ACTION); 511 filter.addAction(AUTH_CANCELLED_ACTION); 512 BluetoothPbapConfig.init(this); 513 registerReceiver(mPbapReceiver, filter); 514 try { 515 mContactChangeObserver = new BluetoothPbapContentObserver(); 516 getContentResolver().registerContentObserver( 517 DevicePolicyUtils.getEnterprisePhoneUri(this), false, 518 mContactChangeObserver); 519 } catch (SQLiteException e) { 520 Log.e(TAG, "SQLite exception: " + e); 521 } catch (IllegalStateException e) { 522 Log.e(TAG, "Illegal state exception, content observer is already registered"); 523 } 524 525 TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 526 if (tm != null) { 527 sLocalPhoneNum = tm.getLine1Number(); 528 sLocalPhoneName = tm.getLine1AlphaTag(); 529 if (TextUtils.isEmpty(sLocalPhoneName)) { 530 sLocalPhoneName = this.getString(R.string.localPhoneName); 531 } 532 } 533 534 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); 535 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 536 return true; 537 } 538 539 @Override 540 protected boolean stop() { 541 if (VERBOSE) { 542 Log.v(TAG, "stop()"); 543 } 544 if (mSessionStatusHandler != null) { 545 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 546 } 547 if (mHandlerThread != null) { 548 mHandlerThread.quitSafely(); 549 } 550 if (mContactChangeObserver == null) { 551 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 552 return true; 553 } 554 try { 555 unregisterReceiver(mPbapReceiver); 556 getContentResolver().unregisterContentObserver(mContactChangeObserver); 557 mContactChangeObserver = null; 558 } catch (Exception e) { 559 Log.w(TAG, "Unable to unregister pbap receiver", e); 560 } 561 return true; 562 } 563 564 private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { 565 private BluetoothPbapService mService; 566 567 private BluetoothPbapService getService() { 568 if (!Utils.checkCaller()) { 569 Log.w(TAG, "not allowed for non-active user"); 570 return null; 571 } 572 if (mService != null && mService.isAvailable()) { 573 return mService; 574 } 575 return null; 576 } 577 578 PbapBinder(BluetoothPbapService service) { 579 if (VERBOSE) { 580 Log.v(TAG, "PbapBinder()"); 581 } 582 mService = service; 583 } 584 585 @Override 586 public void cleanup() { 587 mService = null; 588 } 589 590 @Override 591 public List<BluetoothDevice> getConnectedDevices() { 592 if (DEBUG) { 593 Log.d(TAG, "getConnectedDevices"); 594 } 595 BluetoothPbapService service = getService(); 596 if (service == null) { 597 return new ArrayList<>(0); 598 } 599 return service.getConnectedDevices(); 600 } 601 602 @Override 603 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 604 if (DEBUG) { 605 Log.d(TAG, "getDevicesMatchingConnectionStates"); 606 } 607 BluetoothPbapService service = getService(); 608 if (service == null) { 609 return new ArrayList<>(0); 610 } 611 return service.getDevicesMatchingConnectionStates(states); 612 } 613 614 @Override 615 public int getConnectionState(BluetoothDevice device) { 616 if (DEBUG) { 617 Log.d(TAG, "getConnectionState: " + device); 618 } 619 BluetoothPbapService service = getService(); 620 if (service == null) { 621 return BluetoothAdapter.STATE_DISCONNECTED; 622 } 623 return service.getConnectionState(device); 624 } 625 626 @Override 627 public void disconnect(BluetoothDevice device) { 628 if (DEBUG) { 629 Log.d(TAG, "disconnect"); 630 } 631 BluetoothPbapService service = getService(); 632 if (service == null) { 633 return; 634 } 635 service.disconnect(device); 636 } 637 } 638 639 @Override 640 public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) { 641 if (remoteDevice == null || socket == null) { 642 Log.e(TAG, "onConnect(): Unexpected null. remoteDevice=" + remoteDevice 643 + " socket=" + socket); 644 return false; 645 } 646 647 PbapStateMachine sm = PbapStateMachine.make(this, mHandlerThread.getLooper(), remoteDevice, 648 socket, this, mSessionStatusHandler, mNextNotificationId); 649 mNextNotificationId++; 650 if (mNextNotificationId == PBAP_NOTIFICATION_ID_END) { 651 mNextNotificationId = PBAP_NOTIFICATION_ID_START; 652 } 653 synchronized (mPbapStateMachineMap) { 654 mPbapStateMachineMap.put(remoteDevice, sm); 655 } 656 sm.sendMessage(PbapStateMachine.REQUEST_PERMISSION); 657 return true; 658 } 659 660 /** 661 * Get the phonebook access permission for the device; if unknown, ask the user. 662 * Send the result to the state machine. 663 * @param stateMachine PbapStateMachine which sends the request 664 */ 665 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 666 public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { 667 BluetoothDevice device = stateMachine.getRemoteDevice(); 668 int permission = device.getPhonebookAccessPermission(); 669 if (DEBUG) { 670 Log.d(TAG, "getPhonebookAccessPermission() = " + permission); 671 } 672 673 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 674 stateMachine.sendMessage(PbapStateMachine.AUTHORIZED); 675 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 676 stateMachine.sendMessage(PbapStateMachine.REJECTED); 677 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 678 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 679 intent.setClassName(BluetoothPbapService.ACCESS_AUTHORITY_PACKAGE, 680 BluetoothPbapService.ACCESS_AUTHORITY_CLASS); 681 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 682 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 683 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 684 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, this.getPackageName()); 685 this.sendOrderedBroadcast(intent, BluetoothPbapService.BLUETOOTH_ADMIN_PERM); 686 if (VERBOSE) { 687 Log.v(TAG, "waiting for authorization for connection from: " + device); 688 } 689 /* In case car kit time out and try to use HFP for phonebook 690 * access, while UI still there waiting for user to confirm */ 691 Message msg = mSessionStatusHandler.obtainMessage(BluetoothPbapService.USER_TIMEOUT, 692 stateMachine); 693 mSessionStatusHandler.sendMessageDelayed(msg, USER_CONFIRM_TIMEOUT_VALUE); 694 /* We will continue the process when we receive 695 * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */ 696 } 697 } 698 699 /** 700 * Called when an unrecoverable error occurred in an accept thread. 701 * Close down the server socket, and restart. 702 */ 703 @Override 704 public synchronized void onAcceptFailed() { 705 Log.w(TAG, "PBAP server socket accept thread failed. Restarting the server socket"); 706 707 if (mWakeLock != null) { 708 mWakeLock.release(); 709 mWakeLock = null; 710 } 711 712 cleanUpServerSocket(); 713 714 if (mSessionStatusHandler != null) { 715 mSessionStatusHandler.removeCallbacksAndMessages(null); 716 } 717 718 synchronized (mPbapStateMachineMap) { 719 mPbapStateMachineMap.clear(); 720 } 721 722 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 723 } 724 725 private void loadAllContacts() { 726 if (mThreadLoadContacts == null) { 727 Runnable r = new Runnable() { 728 @Override 729 public void run() { 730 BluetoothPbapUtils.loadAllContacts(mContext, 731 mSessionStatusHandler); 732 mThreadLoadContacts = null; 733 } 734 }; 735 mThreadLoadContacts = new Thread(r); 736 mThreadLoadContacts.start(); 737 } 738 } 739 740 private void updateSecondaryVersion() { 741 if (mThreadUpdateSecVersionCounter == null) { 742 Runnable r = new Runnable() { 743 @Override 744 public void run() { 745 BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, 746 mSessionStatusHandler); 747 mThreadUpdateSecVersionCounter = null; 748 } 749 }; 750 mThreadUpdateSecVersionCounter = new Thread(r); 751 mThreadUpdateSecVersionCounter.start(); 752 } 753 } 754} 755