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