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