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