1 /* 2 * Copyright (c) 2017, The Linux Foundation. 3 * Copyright (c) 2008-2009, Motorola, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * - Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * - Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * - Neither the name of the Motorola, Inc. nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 package com.android.bluetooth.pbap; 35 36 import static android.Manifest.permission.BLUETOOTH_CONNECT; 37 38 import android.annotation.RequiresPermission; 39 import android.app.Activity; 40 import android.app.Notification; 41 import android.app.NotificationChannel; 42 import android.app.NotificationManager; 43 import android.bluetooth.BluetoothAdapter; 44 import android.bluetooth.BluetoothDevice; 45 import android.bluetooth.BluetoothProfile; 46 import android.bluetooth.BluetoothSocket; 47 import android.bluetooth.IBluetoothPbap; 48 import android.content.AttributionSource; 49 import android.content.BroadcastReceiver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.IntentFilter; 53 import android.database.ContentObserver; 54 import android.database.sqlite.SQLiteException; 55 import android.os.Handler; 56 import android.os.HandlerThread; 57 import android.os.Looper; 58 import android.os.Message; 59 import android.os.PowerManager; 60 import android.os.SystemProperties; 61 import android.os.UserHandle; 62 import android.os.UserManager; 63 import android.sysprop.BluetoothProperties; 64 import android.telephony.TelephonyManager; 65 import android.util.Log; 66 67 import com.android.bluetooth.IObexConnectionHandler; 68 import com.android.bluetooth.ObexServerSockets; 69 import com.android.bluetooth.R; 70 import com.android.bluetooth.Utils; 71 import com.android.bluetooth.btservice.AdapterService; 72 import com.android.bluetooth.btservice.InteropUtil; 73 import com.android.bluetooth.btservice.ProfileService; 74 import com.android.bluetooth.btservice.storage.DatabaseManager; 75 import com.android.bluetooth.sdp.SdpManager; 76 import com.android.bluetooth.util.DevicePolicyUtils; 77 import com.android.internal.annotations.VisibleForTesting; 78 79 import java.util.ArrayList; 80 import java.util.HashMap; 81 import java.util.List; 82 import java.util.Objects; 83 84 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { 85 private static final String TAG = "BluetoothPbapService"; 86 87 /** 88 * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 89 * restart com.android.bluetooth process. only enable DEBUG log: 90 * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and 91 * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" 92 */ 93 94 public static final boolean DEBUG = true; 95 96 public static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 97 98 /** 99 * The component name of the owned BluetoothPbapActivity 100 */ 101 private static final String PBAP_ACTIVITY = BluetoothPbapActivity.class.getCanonicalName(); 102 103 /** 104 * Intent indicating incoming obex authentication request which is from 105 * PCE(Carkit) 106 */ 107 static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall"; 108 109 /** 110 * Intent indicating obex session key input complete by user which is sent 111 * from BluetoothPbapActivity 112 */ 113 static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse"; 114 115 /** 116 * Intent indicating user canceled obex authentication session key input 117 * which is sent from BluetoothPbapActivity 118 */ 119 static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled"; 120 121 /** 122 * Intent indicating timeout for user confirmation, which is sent to 123 * BluetoothPbapActivity 124 */ 125 static final String USER_CONFIRM_TIMEOUT_ACTION = 126 "com.android.bluetooth.pbap.userconfirmtimeout"; 127 128 /** 129 * Intent Extra name indicating session key which is sent from 130 * BluetoothPbapActivity 131 */ 132 static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey"; 133 static final String EXTRA_DEVICE = "com.android.bluetooth.pbap.device"; 134 135 static final int MSG_ACQUIRE_WAKE_LOCK = 5004; 136 static final int MSG_RELEASE_WAKE_LOCK = 5005; 137 static final int MSG_STATE_MACHINE_DONE = 5006; 138 139 static final int START_LISTENER = 1; 140 static final int USER_TIMEOUT = 2; 141 static final int SHUTDOWN = 3; 142 static final int LOAD_CONTACTS = 4; 143 static final int CONTACTS_LOADED = 5; 144 static final int CHECK_SECONDARY_VERSION_COUNTER = 6; 145 static final int ROLLOVER_COUNTERS = 7; 146 static final int GET_LOCAL_TELEPHONY_DETAILS = 8; 147 static final int HANDLE_VERSION_UPDATE_NOTIFICATION = 9; 148 149 static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; 150 static final int RELEASE_WAKE_LOCK_DELAY = 10000; 151 152 private PowerManager.WakeLock mWakeLock; 153 154 private static String sLocalPhoneNum; 155 private static String sLocalPhoneName; 156 157 private ObexServerSockets mServerSockets = null; 158 private DatabaseManager mDatabaseManager; 159 160 private static final int SDP_PBAP_SERVER_VERSION_1_2 = 0x0102; 161 // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites 162 private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0009; 163 private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; 164 165 /* PBAP will use Bluetooth notification ID from 1000000 (included) to 2000000 (excluded). 166 The notification ID should be unique in Bluetooth package. */ 167 private static final int PBAP_NOTIFICATION_ID_START = 1000000; 168 private static final int PBAP_NOTIFICATION_ID_END = 2000000; 169 static final int VERSION_UPDATE_NOTIFICATION_DELAY = 500; //in ms 170 171 private int mSdpHandle = -1; 172 173 protected Context mContext; 174 175 private PbapHandler mSessionStatusHandler; 176 private HandlerThread mHandlerThread; 177 @VisibleForTesting 178 final HashMap<BluetoothDevice, PbapStateMachine> mPbapStateMachineMap = new HashMap<>(); 179 private volatile int mNextNotificationId = PBAP_NOTIFICATION_ID_START; 180 181 // package and class name to which we send intent to check phone book access permission 182 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 183 private static final String ACCESS_AUTHORITY_CLASS = 184 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 185 186 private Thread mThreadLoadContacts; 187 private boolean mContactsLoaded = false; 188 189 private Thread mThreadUpdateSecVersionCounter; 190 191 private static BluetoothPbapService sBluetoothPbapService; 192 193 private static final String PBAP_NOTIFICATION_ID = "pbap_notification"; 194 private static final String PBAP_NOTIFICATION_NAME = "BT_PBAP_ADVANCE_SUPPORT"; 195 private static final int PBAP_ADV_VERSION = 0x0102; 196 private static NotificationManager sNotificationManager; 197 198 private static boolean sIsPseDynamicVersionUpgradeEnabled; 199 isEnabled()200 public static boolean isEnabled() { 201 return BluetoothProperties.isProfilePbapServerEnabled().orElse(false); 202 } 203 204 private class BluetoothPbapContentObserver extends ContentObserver { BluetoothPbapContentObserver()205 BluetoothPbapContentObserver() { 206 super(new Handler()); 207 } 208 209 @Override onChange(boolean selfChange)210 public void onChange(boolean selfChange) { 211 Log.d(TAG, " onChange on contact uri "); 212 sendUpdateRequest(); 213 } 214 } 215 sendUpdateRequest()216 private void sendUpdateRequest() { 217 if (mContactsLoaded) { 218 if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) { 219 mSessionStatusHandler.sendMessage( 220 mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER)); 221 } 222 } 223 } 224 225 private BluetoothPbapContentObserver mContactChangeObserver; 226 parseIntent(final Intent intent)227 private void parseIntent(final Intent intent) { 228 String action = intent.getAction(); 229 if (DEBUG) { 230 Log.d(TAG, "action: " + action); 231 } 232 if (BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY.equals(action)) { 233 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 234 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 235 if (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 236 return; 237 } 238 239 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 240 synchronized (mPbapStateMachineMap) { 241 PbapStateMachine sm = mPbapStateMachineMap.get(device); 242 if (sm == null) { 243 Log.w(TAG, "device not connected! device=" + device); 244 return; 245 } 246 mSessionStatusHandler.removeMessages(USER_TIMEOUT, sm); 247 int access = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 248 BluetoothDevice.CONNECTION_ACCESS_NO); 249 boolean savePreference = intent.getBooleanExtra( 250 BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false); 251 252 if (access == BluetoothDevice.CONNECTION_ACCESS_YES) { 253 if (savePreference) { 254 device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED); 255 if (VERBOSE) { 256 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)"); 257 } 258 } 259 sm.sendMessage(PbapStateMachine.AUTHORIZED); 260 } else { 261 if (savePreference) { 262 device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); 263 if (VERBOSE) { 264 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)"); 265 } 266 } 267 sm.sendMessage(PbapStateMachine.REJECTED); 268 } 269 } 270 } else if (AUTH_RESPONSE_ACTION.equals(action)) { 271 String sessionKey = intent.getStringExtra(EXTRA_SESSION_KEY); 272 BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE); 273 synchronized (mPbapStateMachineMap) { 274 PbapStateMachine sm = mPbapStateMachineMap.get(device); 275 if (sm == null) { 276 return; 277 } 278 Message msg = sm.obtainMessage(PbapStateMachine.AUTH_KEY_INPUT, sessionKey); 279 sm.sendMessage(msg); 280 } 281 } else if (AUTH_CANCELLED_ACTION.equals(action)) { 282 BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE); 283 synchronized (mPbapStateMachineMap) { 284 PbapStateMachine sm = mPbapStateMachineMap.get(device); 285 if (sm == null) { 286 return; 287 } 288 sm.sendMessage(PbapStateMachine.AUTH_CANCELLED); 289 } 290 } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { 291 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 292 BluetoothDevice.ERROR); 293 if (bondState == BluetoothDevice.BOND_BONDED && sIsPseDynamicVersionUpgradeEnabled) { 294 BluetoothDevice remoteDevice = intent.getParcelableExtra( 295 BluetoothDevice.EXTRA_DEVICE); 296 mSessionStatusHandler.sendMessageDelayed( 297 mSessionStatusHandler.obtainMessage( 298 HANDLE_VERSION_UPDATE_NOTIFICATION, remoteDevice), 299 VERSION_UPDATE_NOTIFICATION_DELAY); 300 } 301 } else { 302 Log.w(TAG, "Unhandled intent action: " + action); 303 } 304 } 305 306 @VisibleForTesting 307 BroadcastReceiver mPbapReceiver = new BroadcastReceiver() { 308 @Override 309 public void onReceive(Context context, Intent intent) { 310 parseIntent(intent); 311 } 312 }; 313 closeService()314 private void closeService() { 315 if (VERBOSE) { 316 Log.v(TAG, "Pbap Service closeService"); 317 } 318 319 BluetoothPbapUtils.savePbapParams(this); 320 321 if (mWakeLock != null) { 322 mWakeLock.release(); 323 mWakeLock = null; 324 } 325 326 cleanUpServerSocket(); 327 328 if (mSessionStatusHandler != null) { 329 mSessionStatusHandler.removeCallbacksAndMessages(null); 330 } 331 } 332 cleanUpServerSocket()333 private void cleanUpServerSocket() { 334 // Step 1, 2: clean up active server session and connection socket 335 synchronized (mPbapStateMachineMap) { 336 for (PbapStateMachine stateMachine : mPbapStateMachineMap.values()) { 337 stateMachine.sendMessage(PbapStateMachine.DISCONNECT); 338 } 339 } 340 // Step 3: clean up SDP record 341 cleanUpSdpRecord(); 342 // Step 4: clean up existing server sockets 343 if (mServerSockets != null) { 344 mServerSockets.shutdown(false); 345 mServerSockets = null; 346 } 347 } 348 createSdpRecord()349 private void createSdpRecord() { 350 if (mSdpHandle > -1) { 351 Log.w(TAG, "createSdpRecord, SDP record already created"); 352 return; 353 } 354 355 mSdpHandle = SdpManager.getDefaultManager() 356 .createPbapPseRecord("OBEX Phonebook Access Server", 357 mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(), 358 SDP_PBAP_SERVER_VERSION_1_2, SDP_PBAP_SUPPORTED_REPOSITORIES, 359 SDP_PBAP_SUPPORTED_FEATURES); 360 361 if (DEBUG) { 362 Log.d(TAG, "created Sdp record, mSdpHandle=" + mSdpHandle); 363 } 364 365 } 366 cleanUpSdpRecord()367 private void cleanUpSdpRecord() { 368 if (mSdpHandle < 0) { 369 Log.w(TAG, "cleanUpSdpRecord, SDP record never created"); 370 return; 371 } 372 int sdpHandle = mSdpHandle; 373 mSdpHandle = -1; 374 SdpManager sdpManager = SdpManager.getDefaultManager(); 375 if (DEBUG) { 376 Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle); 377 } 378 if (sdpManager == null) { 379 Log.e(TAG, "sdpManager is null"); 380 } else if (!sdpManager.removeSdpRecord(sdpHandle)) { 381 Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle); 382 } 383 } 384 385 /*Creates Notification for PBAP version upgrade */ createNotification(BluetoothPbapService context)386 protected static void createNotification(BluetoothPbapService context) { 387 if (VERBOSE) Log.v(TAG, "Create PBAP Notification for Upgrade"); 388 // create Notification channel. 389 sNotificationManager = context.getSystemService(NotificationManager.class); 390 if (sNotificationManager != null) { 391 NotificationChannel mChannel = new NotificationChannel(PBAP_NOTIFICATION_ID, 392 PBAP_NOTIFICATION_NAME, NotificationManager.IMPORTANCE_DEFAULT); 393 sNotificationManager.createNotificationChannel(mChannel); 394 // create notification 395 String title = context.getString(R.string.phonebook_advance_feature_support); 396 String contentText = context.getString(R.string.repair_for_adv_phonebook_feature); 397 int notificationId = android.R.drawable.stat_sys_data_bluetooth; 398 Notification notification = new Notification.Builder(context, PBAP_NOTIFICATION_ID) 399 .setContentTitle(title) 400 .setContentText(contentText) 401 .setSmallIcon(notificationId) 402 .setAutoCancel(true) 403 .build(); 404 sNotificationManager.notify(notificationId, notification); 405 } else { 406 Log.e(TAG, "sNotificationManager is null"); 407 } 408 409 } 410 411 /* Checks if notification for Version Upgrade is required */ handleNotificationTask(BluetoothPbapService service, BluetoothDevice remoteDevice)412 protected static void handleNotificationTask(BluetoothPbapService service, 413 BluetoothDevice remoteDevice) { 414 int pce_version = 0; 415 416 AdapterService adapterService = AdapterService.getAdapterService(); 417 if (adapterService != null) { 418 pce_version = adapterService.getRemotePbapPceVersion(remoteDevice.getAddress()); 419 Log.d(TAG, "pce_version: " + pce_version); 420 } 421 422 boolean matched = InteropUtil.interopMatchAddrOrName( 423 InteropUtil.InteropFeature.INTEROP_ADV_PBAP_VER_1_2, remoteDevice.getAddress()); 424 Log.d(TAG, "INTEROP_ADV_PBAP_VER_1_2: matched=" + matched); 425 426 if (pce_version == PBAP_ADV_VERSION && !matched) { 427 Log.d(TAG, "Remote Supports PBAP 1.2. Notify user"); 428 createNotification(service); 429 } else { 430 Log.d(TAG, "Notification Not Required."); 431 if (sNotificationManager != null) { 432 sNotificationManager.cancelAll(); 433 } 434 } 435 436 } 437 private class PbapHandler extends Handler { PbapHandler(Looper looper)438 private PbapHandler(Looper looper) { 439 super(looper); 440 } 441 442 @Override handleMessage(Message msg)443 public void handleMessage(Message msg) { 444 if (VERBOSE) { 445 Log.v(TAG, "Handler(): got msg=" + msg.what); 446 } 447 448 switch (msg.what) { 449 case START_LISTENER: 450 mServerSockets = ObexServerSockets.create(BluetoothPbapService.this); 451 if (mServerSockets == null) { 452 Log.w(TAG, "ObexServerSockets.create() returned null"); 453 break; 454 } 455 createSdpRecord(); 456 // fetch Pbap Params to check if significant change has happened to Database 457 BluetoothPbapUtils.fetchPbapParams(mContext); 458 break; 459 case USER_TIMEOUT: 460 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 461 intent.setPackage(SystemProperties.get( 462 Utils.PAIRING_UI_PROPERTY, 463 getString(R.string.pairing_ui_package))); 464 PbapStateMachine stateMachine = (PbapStateMachine) msg.obj; 465 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, stateMachine.getRemoteDevice()); 466 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 467 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 468 Utils.sendBroadcast(BluetoothPbapService.this, intent, BLUETOOTH_CONNECT, 469 Utils.getTempAllowlistBroadcastOptions()); 470 stateMachine.sendMessage(PbapStateMachine.REJECTED); 471 break; 472 case MSG_ACQUIRE_WAKE_LOCK: 473 if (mWakeLock == null) { 474 PowerManager pm = getSystemService(PowerManager.class); 475 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 476 "StartingObexPbapTransaction"); 477 mWakeLock.setReferenceCounted(false); 478 mWakeLock.acquire(); 479 Log.w(TAG, "Acquire Wake Lock"); 480 } 481 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 482 mSessionStatusHandler.sendMessageDelayed( 483 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 484 RELEASE_WAKE_LOCK_DELAY); 485 break; 486 case MSG_RELEASE_WAKE_LOCK: 487 if (mWakeLock != null) { 488 mWakeLock.release(); 489 mWakeLock = null; 490 } 491 break; 492 case SHUTDOWN: 493 closeService(); 494 break; 495 case LOAD_CONTACTS: 496 loadAllContacts(); 497 break; 498 case CONTACTS_LOADED: 499 mContactsLoaded = true; 500 break; 501 case CHECK_SECONDARY_VERSION_COUNTER: 502 updateSecondaryVersion(); 503 break; 504 case ROLLOVER_COUNTERS: 505 BluetoothPbapUtils.rolloverCounters(); 506 break; 507 case MSG_STATE_MACHINE_DONE: 508 PbapStateMachine sm = (PbapStateMachine) msg.obj; 509 BluetoothDevice remoteDevice = sm.getRemoteDevice(); 510 sm.quitNow(); 511 synchronized (mPbapStateMachineMap) { 512 mPbapStateMachineMap.remove(remoteDevice); 513 } 514 break; 515 case GET_LOCAL_TELEPHONY_DETAILS: 516 getLocalTelephonyDetails(); 517 break; 518 case HANDLE_VERSION_UPDATE_NOTIFICATION: 519 BluetoothDevice remoteDev = (BluetoothDevice) msg.obj; 520 521 handleNotificationTask(sBluetoothPbapService, remoteDev); 522 break; 523 default: 524 break; 525 } 526 } 527 } 528 529 /** 530 * Get the current connection state of PBAP with the passed in device 531 * 532 * @param device is the device whose connection state to PBAP we are trying to get 533 * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED}, 534 * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or 535 * {@link BluetoothProfile#STATE_DISCONNECTING} 536 */ 537 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectionState(BluetoothDevice device)538 public int getConnectionState(BluetoothDevice device) { 539 enforceCallingOrSelfPermission( 540 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 541 synchronized (mPbapStateMachineMap) { 542 PbapStateMachine sm = mPbapStateMachineMap.get(device); 543 if (sm == null) { 544 return BluetoothProfile.STATE_DISCONNECTED; 545 } 546 return sm.getConnectionState(); 547 } 548 } 549 getConnectedDevices()550 List<BluetoothDevice> getConnectedDevices() { 551 synchronized (mPbapStateMachineMap) { 552 return new ArrayList<>(mPbapStateMachineMap.keySet()); 553 } 554 } 555 getDevicesMatchingConnectionStates(int[] states)556 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 557 List<BluetoothDevice> devices = new ArrayList<>(); 558 if (states == null) { 559 return devices; 560 } 561 synchronized (mPbapStateMachineMap) { 562 for (int state : states) { 563 for (BluetoothDevice device : mPbapStateMachineMap.keySet()) { 564 if (state == mPbapStateMachineMap.get(device).getConnectionState()) { 565 devices.add(device); 566 } 567 } 568 } 569 } 570 return devices; 571 } 572 573 /** 574 * Set connection policy of the profile and tries to disconnect it if connectionPolicy is 575 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 576 * 577 * <p> The device should already be paired. 578 * Connection policy can be one of: 579 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 580 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 581 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 582 * 583 * @param device Paired bluetooth device 584 * @param connectionPolicy is the connection policy to set to for this profile 585 * @return true if connectionPolicy is set, false on error 586 */ 587 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)588 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 589 enforceCallingOrSelfPermission( 590 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 591 if (DEBUG) { 592 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 593 } 594 595 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.PBAP, 596 connectionPolicy)) { 597 return false; 598 } 599 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 600 disconnect(device); 601 } 602 return true; 603 } 604 605 /** 606 * Get the connection policy of the profile. 607 * 608 * <p> The connection policy can be any of: 609 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 610 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 611 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 612 * 613 * @param device Bluetooth device 614 * @return connection policy of the device 615 * @hide 616 */ getConnectionPolicy(BluetoothDevice device)617 public int getConnectionPolicy(BluetoothDevice device) { 618 if (device == null) { 619 throw new IllegalArgumentException("Null device"); 620 } 621 return mDatabaseManager 622 .getProfileConnectionPolicy(device, BluetoothProfile.PBAP); 623 } 624 625 /** 626 * Disconnects pbap server profile with device 627 * @param device is the remote bluetooth device 628 */ disconnect(BluetoothDevice device)629 public void disconnect(BluetoothDevice device) { 630 synchronized (mPbapStateMachineMap) { 631 PbapStateMachine sm = mPbapStateMachineMap.get(device); 632 if (sm != null) { 633 sm.sendMessage(PbapStateMachine.DISCONNECT); 634 } 635 } 636 } 637 getLocalPhoneNum()638 static String getLocalPhoneNum() { 639 return sLocalPhoneNum; 640 } 641 642 @VisibleForTesting setLocalPhoneName(String localPhoneName)643 static void setLocalPhoneName(String localPhoneName) { 644 sLocalPhoneName = localPhoneName; 645 } 646 getLocalPhoneName()647 static String getLocalPhoneName() { 648 return sLocalPhoneName; 649 } 650 651 @Override initBinder()652 protected IProfileServiceBinder initBinder() { 653 return new PbapBinder(this); 654 } 655 656 @Override start()657 protected boolean start() { 658 if (VERBOSE) { 659 Log.v(TAG, "start()"); 660 } 661 mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), 662 "DatabaseManager cannot be null when PbapService starts"); 663 664 // Enable owned Activity component 665 setComponentAvailable(PBAP_ACTIVITY, true); 666 667 mContext = this; 668 mContactsLoaded = false; 669 mHandlerThread = new HandlerThread("PbapHandlerThread"); 670 mHandlerThread.start(); 671 mSessionStatusHandler = new PbapHandler(mHandlerThread.getLooper()); 672 IntentFilter filter = new IntentFilter(); 673 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 674 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 675 filter.addAction(AUTH_RESPONSE_ACTION); 676 filter.addAction(AUTH_CANCELLED_ACTION); 677 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 678 BluetoothPbapConfig.init(this); 679 registerReceiver(mPbapReceiver, filter); 680 try { 681 mContactChangeObserver = new BluetoothPbapContentObserver(); 682 getContentResolver().registerContentObserver( 683 DevicePolicyUtils.getEnterprisePhoneUri(this), false, 684 mContactChangeObserver); 685 } catch (SQLiteException e) { 686 Log.e(TAG, "SQLite exception: " + e); 687 } catch (IllegalStateException e) { 688 Log.e(TAG, "Illegal state exception, content observer is already registered"); 689 } 690 691 setBluetoothPbapService(this); 692 693 mSessionStatusHandler.sendMessage( 694 mSessionStatusHandler.obtainMessage(GET_LOCAL_TELEPHONY_DETAILS)); 695 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); 696 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 697 698 AdapterService adapterService = AdapterService.getAdapterService(); 699 if (adapterService != null) { 700 sIsPseDynamicVersionUpgradeEnabled = 701 adapterService.pbapPseDynamicVersionUpgradeIsEnabled(); 702 Log.d(TAG, "sIsPseDynamicVersionUpgradeEnabled: " + sIsPseDynamicVersionUpgradeEnabled); 703 } 704 return true; 705 } 706 707 @Override stop()708 protected boolean stop() { 709 if (VERBOSE) { 710 Log.v(TAG, "stop()"); 711 } 712 setBluetoothPbapService(null); 713 if (mSessionStatusHandler != null) { 714 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 715 } 716 if (mHandlerThread != null) { 717 mHandlerThread.quitSafely(); 718 } 719 mContactsLoaded = false; 720 if (mContactChangeObserver == null) { 721 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 722 return true; 723 } 724 unregisterReceiver(mPbapReceiver); 725 getContentResolver().unregisterContentObserver(mContactChangeObserver); 726 mContactChangeObserver = null; 727 setComponentAvailable(PBAP_ACTIVITY, false); 728 mPbapStateMachineMap.clear(); 729 return true; 730 } 731 732 /** 733 * Get the current instance of {@link BluetoothPbapService} 734 * 735 * @return current instance of {@link BluetoothPbapService} 736 */ 737 @VisibleForTesting getBluetoothPbapService()738 public static synchronized BluetoothPbapService getBluetoothPbapService() { 739 if (sBluetoothPbapService == null) { 740 Log.w(TAG, "getBluetoothPbapService(): service is null"); 741 return null; 742 } 743 if (!sBluetoothPbapService.isAvailable()) { 744 Log.w(TAG, "getBluetoothPbapService(): service is not available"); 745 return null; 746 } 747 return sBluetoothPbapService; 748 } 749 setBluetoothPbapService(BluetoothPbapService instance)750 private static synchronized void setBluetoothPbapService(BluetoothPbapService instance) { 751 if (DEBUG) { 752 Log.d(TAG, "setBluetoothPbapService(): set to: " + instance); 753 } 754 sBluetoothPbapService = instance; 755 } 756 757 @Override setCurrentUser(int userId)758 protected void setCurrentUser(int userId) { 759 Log.i(TAG, "setCurrentUser(" + userId + ")"); 760 UserManager userManager = getSystemService(UserManager.class); 761 if (userManager.isUserUnlocked(UserHandle.of(userId))) { 762 setUserUnlocked(userId); 763 } 764 } 765 766 @Override setUserUnlocked(int userId)767 protected void setUserUnlocked(int userId) { 768 Log.i(TAG, "setUserUnlocked(" + userId + ")"); 769 sendUpdateRequest(); 770 } 771 772 @VisibleForTesting 773 static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { 774 private BluetoothPbapService mService; 775 776 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)777 private BluetoothPbapService getService(AttributionSource source) { 778 if (Utils.isInstrumentationTestMode()) { 779 return mService; 780 } 781 if (!Utils.checkServiceAvailable(mService, TAG) 782 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 783 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 784 return null; 785 } 786 return mService; 787 } 788 PbapBinder(BluetoothPbapService service)789 PbapBinder(BluetoothPbapService service) { 790 if (VERBOSE) { 791 Log.v(TAG, "PbapBinder()"); 792 } 793 mService = service; 794 } 795 796 @Override cleanup()797 public void cleanup() { 798 mService = null; 799 } 800 801 @Override getConnectedDevices(AttributionSource source)802 public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { 803 if (DEBUG) { 804 Log.d(TAG, "getConnectedDevices"); 805 } 806 BluetoothPbapService service = getService(source); 807 if (service == null) { 808 return new ArrayList<>(0); 809 } 810 return service.getConnectedDevices(); 811 } 812 813 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source)814 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states, 815 AttributionSource source) { 816 if (DEBUG) { 817 Log.d(TAG, "getDevicesMatchingConnectionStates"); 818 } 819 BluetoothPbapService service = getService(source); 820 if (service == null) { 821 return new ArrayList<>(0); 822 } 823 return service.getDevicesMatchingConnectionStates(states); 824 } 825 826 @Override getConnectionState(BluetoothDevice device, AttributionSource source)827 public int getConnectionState(BluetoothDevice device, AttributionSource source) { 828 if (DEBUG) { 829 Log.d(TAG, "getConnectionState: " + device); 830 } 831 BluetoothPbapService service = getService(source); 832 if (service == null) { 833 return BluetoothAdapter.STATE_DISCONNECTED; 834 } 835 return service.getConnectionState(device); 836 } 837 838 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source)839 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 840 AttributionSource source) { 841 if (DEBUG) { 842 Log.d(TAG, "setConnectionPolicy for device: " + device + ", policy:" 843 + connectionPolicy); 844 } 845 BluetoothPbapService service = getService(source); 846 if (service == null) { 847 return false; 848 } 849 return service.setConnectionPolicy(device, connectionPolicy); 850 } 851 852 @Override disconnect(BluetoothDevice device, AttributionSource source)853 public void disconnect(BluetoothDevice device, AttributionSource source) { 854 if (DEBUG) { 855 Log.d(TAG, "disconnect"); 856 } 857 BluetoothPbapService service = getService(source); 858 if (service == null) { 859 return; 860 } 861 service.disconnect(device); 862 } 863 } 864 865 @Override onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket)866 public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) { 867 if (remoteDevice == null || socket == null) { 868 Log.e(TAG, "onConnect(): Unexpected null. remoteDevice=" + remoteDevice 869 + " socket=" + socket); 870 return false; 871 } 872 873 PbapStateMachine sm = PbapStateMachine.make(this, mHandlerThread.getLooper(), remoteDevice, 874 socket, this, mSessionStatusHandler, mNextNotificationId); 875 mNextNotificationId++; 876 if (mNextNotificationId == PBAP_NOTIFICATION_ID_END) { 877 mNextNotificationId = PBAP_NOTIFICATION_ID_START; 878 } 879 synchronized (mPbapStateMachineMap) { 880 mPbapStateMachineMap.put(remoteDevice, sm); 881 } 882 sm.sendMessage(PbapStateMachine.REQUEST_PERMISSION); 883 return true; 884 } 885 886 /** 887 * Get the phonebook access permission for the device; if unknown, ask the user. 888 * Send the result to the state machine. 889 * @param stateMachine PbapStateMachine which sends the request 890 */ 891 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 892 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) checkOrGetPhonebookPermission(PbapStateMachine stateMachine)893 public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) { 894 BluetoothDevice device = stateMachine.getRemoteDevice(); 895 int permission = device.getPhonebookAccessPermission(); 896 if (DEBUG) { 897 Log.d(TAG, "getPhonebookAccessPermission() = " + permission); 898 } 899 900 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 901 setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 902 stateMachine.sendMessage(PbapStateMachine.AUTHORIZED); 903 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 904 stateMachine.sendMessage(PbapStateMachine.REJECTED); 905 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 906 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 907 intent.setClassName(BluetoothPbapService.ACCESS_AUTHORITY_PACKAGE, 908 BluetoothPbapService.ACCESS_AUTHORITY_CLASS); 909 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 910 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 911 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 912 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, this.getPackageName()); 913 Utils.sendOrderedBroadcast(this, intent, BLUETOOTH_CONNECT, 914 Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */, 915 null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */, 916 null/* initialExtras */); 917 if (VERBOSE) { 918 Log.v(TAG, "waiting for authorization for connection from: " + device); 919 } 920 /* In case car kit time out and try to use HFP for phonebook 921 * access, while UI still there waiting for user to confirm */ 922 Message msg = mSessionStatusHandler.obtainMessage(BluetoothPbapService.USER_TIMEOUT, 923 stateMachine); 924 mSessionStatusHandler.sendMessageDelayed(msg, USER_CONFIRM_TIMEOUT_VALUE); 925 /* We will continue the process when we receive 926 * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */ 927 } 928 } 929 930 /** 931 * Called when an unrecoverable error occurred in an accept thread. 932 * Close down the server socket, and restart. 933 */ 934 @Override onAcceptFailed()935 public synchronized void onAcceptFailed() { 936 Log.w(TAG, "PBAP server socket accept thread failed. Restarting the server socket"); 937 938 if (mWakeLock != null) { 939 mWakeLock.release(); 940 mWakeLock = null; 941 } 942 943 cleanUpServerSocket(); 944 945 if (mSessionStatusHandler != null) { 946 mSessionStatusHandler.removeCallbacksAndMessages(null); 947 } 948 949 synchronized (mPbapStateMachineMap) { 950 mPbapStateMachineMap.clear(); 951 } 952 953 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 954 } 955 loadAllContacts()956 private void loadAllContacts() { 957 if (mThreadLoadContacts == null) { 958 Runnable r = new Runnable() { 959 @Override 960 public void run() { 961 BluetoothPbapUtils.loadAllContacts(mContext, 962 mSessionStatusHandler); 963 mThreadLoadContacts = null; 964 } 965 }; 966 mThreadLoadContacts = new Thread(r); 967 mThreadLoadContacts.start(); 968 } 969 } 970 updateSecondaryVersion()971 private void updateSecondaryVersion() { 972 if (mThreadUpdateSecVersionCounter == null) { 973 Runnable r = new Runnable() { 974 @Override 975 public void run() { 976 BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, 977 mSessionStatusHandler); 978 mThreadUpdateSecVersionCounter = null; 979 } 980 }; 981 mThreadUpdateSecVersionCounter = new Thread(r); 982 mThreadUpdateSecVersionCounter.start(); 983 } 984 } 985 getLocalTelephonyDetails()986 private void getLocalTelephonyDetails() { 987 TelephonyManager tm = getSystemService(TelephonyManager.class); 988 if (tm != null) { 989 sLocalPhoneNum = tm.getLine1Number(); 990 sLocalPhoneName = this.getString(R.string.localPhoneName); 991 } 992 if (VERBOSE) 993 Log.v(TAG, "Local Phone Details- Number:" + sLocalPhoneNum 994 + ", Name:" + sLocalPhoneName); 995 } 996 } 997