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