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