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