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