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