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.app.AlarmManager; 36 import android.app.Notification; 37 import android.app.NotificationChannel; 38 import android.app.NotificationManager; 39 import android.app.PendingIntent; 40 import android.app.Service; 41 import android.bluetooth.BluetoothAdapter; 42 import android.bluetooth.BluetoothDevice; 43 import android.bluetooth.BluetoothPbap; 44 import android.bluetooth.BluetoothProfile; 45 import android.bluetooth.BluetoothServerSocket; 46 import android.bluetooth.BluetoothSocket; 47 import android.bluetooth.BluetoothUuid; 48 import android.bluetooth.IBluetoothPbap; 49 import android.database.sqlite.SQLiteException; 50 import android.content.BroadcastReceiver; 51 import android.content.Context; 52 import android.content.ContentResolver; 53 import android.database.ContentObserver; 54 import android.content.Intent; 55 import android.content.IntentFilter; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.Message; 59 import android.os.PowerManager; 60 import android.telephony.TelephonyManager; 61 import android.text.TextUtils; 62 import android.util.Log; 63 64 import com.android.bluetooth.BluetoothObexTransport; 65 import com.android.bluetooth.btservice.ProfileService; 66 import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; 67 import com.android.bluetooth.IObexConnectionHandler; 68 import com.android.bluetooth.ObexServerSockets; 69 import com.android.bluetooth.R; 70 import com.android.bluetooth.sdp.SdpManager; 71 import com.android.bluetooth.Utils; 72 import com.android.bluetooth.util.DevicePolicyUtils; 73 74 import java.io.IOException; 75 import java.util.Calendar; 76 import java.util.concurrent.atomic.AtomicLong; 77 import java.util.HashMap; 78 79 import javax.obex.ServerSession; 80 81 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler { 82 private static final String TAG = "BluetoothPbapService"; 83 84 /** 85 * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 86 * restart com.android.bluetooth process. only enable DEBUG log: 87 * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and 88 * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE" 89 */ 90 91 public static final boolean DEBUG = true; 92 93 public static final boolean VERBOSE = false; 94 95 /** 96 * Intent indicating incoming obex authentication request which is from 97 * PCE(Carkit) 98 */ 99 public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall"; 100 101 /** 102 * Intent indicating obex session key input complete by user which is sent 103 * from BluetoothPbapActivity 104 */ 105 public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse"; 106 107 /** 108 * Intent indicating user canceled obex authentication session key input 109 * which is sent from BluetoothPbapActivity 110 */ 111 public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled"; 112 113 /** 114 * Intent indicating timeout for user confirmation, which is sent to 115 * BluetoothPbapActivity 116 */ 117 public static final String USER_CONFIRM_TIMEOUT_ACTION = 118 "com.android.bluetooth.pbap.userconfirmtimeout"; 119 120 /** 121 * Intent Extra name indicating session key which is sent from 122 * BluetoothPbapActivity 123 */ 124 public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey"; 125 126 public static final String THIS_PACKAGE_NAME = "com.android.bluetooth"; 127 128 public static final int MSG_SERVERSESSION_CLOSE = 5000; 129 130 public static final int MSG_SESSION_ESTABLISHED = 5001; 131 132 public static final int MSG_SESSION_DISCONNECTED = 5002; 133 134 public static final int MSG_OBEX_AUTH_CHALL = 5003; 135 136 public static final int MSG_ACQUIRE_WAKE_LOCK = 5004; 137 138 public static final int MSG_RELEASE_WAKE_LOCK = 5005; 139 140 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 141 142 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 143 144 private static final int START_LISTENER = 1; 145 146 private static final int USER_TIMEOUT = 2; 147 148 private static final int AUTH_TIMEOUT = 3; 149 150 private static final int SHUTDOWN = 4; 151 152 protected static final int LOAD_CONTACTS = 5; 153 154 private static final int CHECK_SECONDARY_VERSION_COUNTER = 6; 155 156 protected static final int ROLLOVER_COUNTERS = 7; 157 158 private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000; 159 160 private static final int RELEASE_WAKE_LOCK_DELAY = 10000; 161 162 // Ensure not conflict with Opp notification ID 163 private static final int NOTIFICATION_ID_ACCESS = -1000001; 164 165 private static final int NOTIFICATION_ID_AUTH = -1000002; 166 167 private static final String PBAP_NOTIFICATION_CHANNEL = "pbap_notification_channel"; 168 169 private PowerManager.WakeLock mWakeLock = null; 170 171 private BluetoothPbapAuthenticator mAuth = null; 172 173 private BluetoothPbapObexServer mPbapServer; 174 175 private ServerSession mServerSession = null; 176 177 private BluetoothServerSocket mServerSocket = null; 178 179 private BluetoothSocket mConnSocket = null; 180 181 private BluetoothDevice mRemoteDevice = null; 182 183 private static String sLocalPhoneNum = null; 184 185 private static String sLocalPhoneName = null; 186 187 private static String sRemoteDeviceName = null; 188 189 private volatile boolean mInterrupted; 190 191 private int mState; 192 193 private boolean mIsWaitingAuthorization = false; 194 195 private ObexServerSockets mServerSockets = null; 196 197 private static final int SDP_PBAP_SERVER_VERSION = 0x0102; 198 199 private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0003; 200 201 private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F; 202 203 private AlarmManager mAlarmManager = null; 204 205 private int mSdpHandle = -1; 206 207 private boolean mRemoveTimeoutMsg = false; 208 209 private int mPermission = BluetoothDevice.ACCESS_UNKNOWN; 210 211 private boolean mSdpSearchInitiated = false; 212 213 private boolean isRegisteredObserver = false; 214 215 protected Context mContext; 216 217 // package and class name to which we send intent to check phone book access permission 218 private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings"; 219 private static final String ACCESS_AUTHORITY_CLASS = 220 "com.android.settings.bluetooth.BluetoothPermissionRequest"; 221 222 private class BluetoothPbapContentObserver extends ContentObserver { BluetoothPbapContentObserver()223 public BluetoothPbapContentObserver() { 224 super(new Handler()); 225 } 226 227 @Override onChange(boolean selfChange)228 public void onChange(boolean selfChange) { 229 Log.d(TAG, " onChange on contact uri "); 230 if (BluetoothPbapUtils.contactsLoaded) { 231 if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) { 232 mSessionStatusHandler.sendMessage( 233 mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER)); 234 } 235 } 236 } 237 } 238 239 private BluetoothPbapContentObserver mContactChangeObserver; 240 BluetoothPbapService()241 public BluetoothPbapService() { 242 mState = BluetoothPbap.STATE_DISCONNECTED; 243 mContext = this; 244 } 245 246 // process the intent from receiver parseIntent(final Intent intent)247 private void parseIntent(final Intent intent) { 248 String action = intent.getAction(); 249 if (DEBUG) Log.d(TAG, "action: " + action); 250 if (action == null) return; // Nothing to do 251 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 252 if (DEBUG) Log.d(TAG, "state: " + state); 253 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 254 if (state == BluetoothAdapter.STATE_TURNING_OFF) { 255 // Send any pending timeout now, as this service will be destroyed. 256 if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) { 257 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 258 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 259 } 260 // Release all resources 261 closeService(); 262 } else if (state == BluetoothAdapter.STATE_ON) { 263 // start RFCOMM listener 264 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 265 } 266 return; 267 } 268 269 if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) { 270 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 271 272 if (mRemoteDevice == null) return; 273 if (DEBUG) Log.d(TAG,"ACL disconnected for "+ device); 274 if (mRemoteDevice.equals(device)) { 275 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 276 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 277 } 278 return; 279 } 280 281 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 282 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 283 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 284 285 if ((!mIsWaitingAuthorization) 286 || (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) { 287 // this reply is not for us 288 return; 289 } 290 291 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 292 mIsWaitingAuthorization = false; 293 294 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 295 BluetoothDevice.CONNECTION_ACCESS_NO) 296 == BluetoothDevice.CONNECTION_ACCESS_YES) { 297 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 298 boolean result = mRemoteDevice.setPhonebookAccessPermission( 299 BluetoothDevice.ACCESS_ALLOWED); 300 if (VERBOSE) { 301 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)=" + result); 302 } 303 } 304 try { 305 if (mConnSocket != null) { 306 startObexServerSession(); 307 } else { 308 stopObexServerSession(); 309 } 310 } catch (IOException ex) { 311 Log.e(TAG, "Caught the error: " + ex.toString()); 312 } 313 } else { 314 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 315 boolean result = mRemoteDevice.setPhonebookAccessPermission( 316 BluetoothDevice.ACCESS_REJECTED); 317 if (VERBOSE) { 318 Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)=" + result); 319 } 320 } 321 stopObexServerSession(); 322 } 323 return; 324 } 325 326 if (action.equals(AUTH_RESPONSE_ACTION)) { 327 String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY); 328 notifyAuthKeyInput(sessionkey); 329 } else if (action.equals(AUTH_CANCELLED_ACTION)) { 330 notifyAuthCancelled(); 331 } else { 332 Log.w(TAG, "Unrecognized intent!"); 333 return; 334 } 335 336 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 337 } 338 339 private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() { 340 @Override 341 public void onReceive(Context context, Intent intent) { 342 parseIntent(intent); 343 } 344 }; 345 initSocket()346 private final boolean initSocket() { 347 if (VERBOSE) Log.v(TAG, "Pbap Service initSocket"); 348 349 boolean initSocketOK = false; 350 final int CREATE_RETRY_TIME = 10; 351 352 // It's possible that create will fail in some cases. retry for 10 times 353 for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) { 354 initSocketOK = true; 355 try { 356 // It is mandatory for PSE to support initiation of bonding and 357 // encryption. 358 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord 359 ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid()); 360 361 } catch (IOException e) { 362 Log.e(TAG, "Error create RfcommServerSocket " + e.toString()); 363 initSocketOK = false; 364 } 365 if (!initSocketOK) { 366 // Need to break out of this loop if BT is being turned off. 367 if (mAdapter == null) break; 368 int state = mAdapter.getState(); 369 if ((state != BluetoothAdapter.STATE_TURNING_ON) && 370 (state != BluetoothAdapter.STATE_ON)) { 371 Log.w(TAG, "initServerSocket failed as BT is (being) turned off"); 372 break; 373 } 374 try { 375 if (VERBOSE) Log.v(TAG, "wait 300 ms"); 376 Thread.sleep(300); 377 } catch (InterruptedException e) { 378 Log.e(TAG, "socketAcceptThread thread was interrupted (3)"); 379 break; 380 } 381 } else { 382 break; 383 } 384 } 385 386 if (mInterrupted) { 387 initSocketOK = false; 388 // close server socket to avoid resource leakage 389 closeServerSocket(); 390 } 391 392 if (initSocketOK) { 393 if (VERBOSE) Log.v(TAG, "Succeed to create listening socket "); 394 395 } else { 396 Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try"); 397 } 398 return initSocketOK; 399 } 400 closeServerSocket()401 private final synchronized void closeServerSocket() { 402 // exit SocketAcceptThread early 403 if (mServerSocket != null) { 404 try { 405 // this will cause mServerSocket.accept() return early with IOException 406 mServerSocket.close(); 407 mServerSocket = null; 408 } catch (IOException ex) { 409 Log.e(TAG, "Close Server Socket error: " + ex); 410 } 411 } 412 } 413 closeConnectionSocket()414 private final synchronized void closeConnectionSocket() { 415 if (mConnSocket != null) { 416 try { 417 mConnSocket.close(); 418 mConnSocket = null; 419 } catch (IOException e) { 420 Log.e(TAG, "Close Connection Socket error: " + e.toString()); 421 } 422 } 423 } 424 closeService()425 private final void closeService() { 426 if (VERBOSE) Log.v(TAG, "Pbap Service closeService in"); 427 428 BluetoothPbapUtils.savePbapParams(this, BluetoothPbapUtils.primaryVersionCounter, 429 BluetoothPbapUtils.secondaryVersionCounter, BluetoothPbapUtils.mDbIdentifier.get(), 430 BluetoothPbapUtils.contactsLastUpdated, BluetoothPbapUtils.totalFields, 431 BluetoothPbapUtils.totalSvcFields, BluetoothPbapUtils.totalContacts); 432 433 // exit initSocket early 434 mInterrupted = true; 435 if (mWakeLock != null) { 436 mWakeLock.release(); 437 mWakeLock = null; 438 } 439 440 // Step 1: clean up active server session 441 if (mServerSession != null) { 442 mServerSession.close(); 443 mServerSession = null; 444 } 445 // Step 2: clean up existing connection socket 446 closeConnectionSocket(); 447 // Step 3: clean up SDP record 448 cleanUpSdpRecord(); 449 // Step 4: clean up existing server socket(s) 450 closeServerSocket(); 451 if (mServerSockets != null) { 452 mServerSockets.shutdown(false); 453 mServerSockets = null; 454 } 455 if (mSessionStatusHandler != null) mSessionStatusHandler.removeCallbacksAndMessages(null); 456 if (VERBOSE) Log.v(TAG, "Pbap Service closeService out"); 457 } 458 cleanUpSdpRecord()459 private void cleanUpSdpRecord() { 460 if (mSdpHandle < 0) { 461 if (VERBOSE) Log.v(TAG, "cleanUpSdpRecord, SDP record never created"); 462 return; 463 } 464 int sdpHandle = mSdpHandle; 465 mSdpHandle = -1; 466 SdpManager sdpManager = SdpManager.getDefaultManager(); 467 if (sdpManager == null) { 468 Log.e(TAG, "cleanUpSdpRecord failed, sdpManager is null, sdpHandle=" + sdpHandle); 469 return; 470 } 471 Log.i(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle); 472 if (!sdpManager.removeSdpRecord(sdpHandle)) { 473 Log.e(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle); 474 } 475 } 476 startObexServerSession()477 private final void startObexServerSession() throws IOException { 478 if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession"); 479 480 // acquire the wakeLock before start Obex transaction thread 481 if (mWakeLock == null) { 482 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 483 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 484 "StartingObexPbapTransaction"); 485 mWakeLock.setReferenceCounted(false); 486 mWakeLock.acquire(); 487 } 488 TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); 489 if (tm != null) { 490 sLocalPhoneNum = tm.getLine1Number(); 491 sLocalPhoneName = tm.getLine1AlphaTag(); 492 if (TextUtils.isEmpty(sLocalPhoneName)) { 493 sLocalPhoneName = this.getString(R.string.localPhoneName); 494 } 495 } 496 497 mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this); 498 synchronized (this) { 499 mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler); 500 mAuth.setChallenged(false); 501 mAuth.setCancelled(false); 502 } 503 BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket); 504 mServerSession = new ServerSession(transport, mPbapServer, mAuth); 505 setState(BluetoothPbap.STATE_CONNECTED); 506 507 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 508 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 509 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 510 511 if (VERBOSE) { 512 Log.v(TAG, "startObexServerSession() success!"); 513 } 514 } 515 stopObexServerSession()516 private void stopObexServerSession() { 517 if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession"); 518 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 519 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 520 // Release the wake lock if obex transaction is over 521 if (mWakeLock != null) { 522 mWakeLock.release(); 523 mWakeLock = null; 524 } 525 526 if (mServerSession != null) { 527 mServerSession.close(); 528 mServerSession = null; 529 } 530 closeConnectionSocket(); 531 532 // Last obex transaction is finished, we start to listen for incoming 533 // connection again 534 if (mAdapter != null && mAdapter.isEnabled()) { 535 startSocketListeners(); 536 } 537 setState(BluetoothPbap.STATE_DISCONNECTED); 538 } 539 notifyAuthKeyInput(final String key)540 private void notifyAuthKeyInput(final String key) { 541 synchronized (mAuth) { 542 if (key != null) { 543 mAuth.setSessionKey(key); 544 } 545 mAuth.setChallenged(true); 546 mAuth.notify(); 547 } 548 } 549 notifyAuthCancelled()550 private void notifyAuthCancelled() { 551 synchronized (mAuth) { 552 mAuth.setCancelled(true); 553 mAuth.notify(); 554 } 555 } 556 557 /** 558 * A thread that runs in the background waiting for remote rfcomm 559 * connect.Once a remote socket connected, this thread shall be 560 * shutdown.When the remote disconnect,this thread shall run again waiting 561 * for next request. 562 */ 563 private class SocketAcceptThread extends Thread { 564 565 private boolean stopped = false; 566 567 @Override run()568 public void run() { 569 BluetoothServerSocket serverSocket; 570 if (mServerSocket == null) { 571 if (!initSocket()) { 572 return; 573 } 574 } 575 576 while (!stopped) { 577 try { 578 if (VERBOSE) Log.v(TAG, "Accepting socket connection..."); 579 serverSocket = mServerSocket; 580 if (serverSocket == null) { 581 Log.w(TAG, "mServerSocket is null"); 582 break; 583 } 584 mConnSocket = serverSocket.accept(); 585 if (VERBOSE) Log.v(TAG, "Accepted socket connection..."); 586 587 synchronized (BluetoothPbapService.this) { 588 if (mConnSocket == null) { 589 Log.w(TAG, "mConnSocket is null"); 590 break; 591 } 592 mRemoteDevice = mConnSocket.getRemoteDevice(); 593 } 594 if (mRemoteDevice == null) { 595 Log.i(TAG, "getRemoteDevice() = null"); 596 break; 597 } 598 sRemoteDeviceName = mRemoteDevice.getName(); 599 // In case getRemoteName failed and return null 600 if (TextUtils.isEmpty(sRemoteDeviceName)) { 601 sRemoteDeviceName = getString(R.string.defaultname); 602 } 603 int permission = mRemoteDevice.getPhonebookAccessPermission(); 604 if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission); 605 606 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 607 try { 608 if (VERBOSE) { 609 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName 610 + " automatically as already allowed device"); 611 } 612 startObexServerSession(); 613 } catch (IOException ex) { 614 Log.e(TAG, "Caught exception starting obex server session" 615 + ex.toString()); 616 } 617 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 618 if (VERBOSE) { 619 Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName 620 + " automatically as already rejected device"); 621 } 622 stopObexServerSession(); 623 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 624 // Send an Intent to Settings app to ask user preference. 625 Intent intent = 626 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 627 intent.setPackage(getString(R.string.pairing_ui_package)); 628 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 629 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 630 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 631 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 632 intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, getName()); 633 634 mIsWaitingAuthorization = true; 635 sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM); 636 637 if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: " 638 + sRemoteDeviceName); 639 640 // In case car kit time out and try to use HFP for 641 // phonebook 642 // access, while UI still there waiting for user to 643 // confirm 644 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 645 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 646 // We will continue the process when we receive 647 // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. 648 } 649 stopped = true; // job done ,close this thread; 650 } catch (IOException ex) { 651 stopped=true; 652 /* 653 if (stopped) { 654 break; 655 } 656 */ 657 if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString()); 658 } 659 } 660 } 661 shutdown()662 void shutdown() { 663 stopped = true; 664 interrupt(); 665 } 666 } 667 668 protected final Handler mSessionStatusHandler = new Handler() { 669 @Override 670 public void handleMessage(Message msg) { 671 if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what); 672 673 switch (msg.what) { 674 case START_LISTENER: 675 if (mAdapter.isEnabled()) { 676 startSocketListeners(); 677 } 678 break; 679 case USER_TIMEOUT: 680 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 681 intent.setPackage(getString(R.string.pairing_ui_package)); 682 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 683 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 684 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 685 sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); 686 mIsWaitingAuthorization = false; 687 stopObexServerSession(); 688 break; 689 case AUTH_TIMEOUT: 690 Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 691 sendBroadcast(i); 692 removePbapNotification(NOTIFICATION_ID_AUTH); 693 notifyAuthCancelled(); 694 break; 695 case MSG_SERVERSESSION_CLOSE: 696 stopObexServerSession(); 697 break; 698 case MSG_SESSION_ESTABLISHED: 699 break; 700 case MSG_SESSION_DISCONNECTED: 701 // case MSG_SERVERSESSION_CLOSE will handle ,so just skip 702 break; 703 case MSG_OBEX_AUTH_CHALL: 704 createPbapNotification(AUTH_CHALL_ACTION); 705 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 706 .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 707 break; 708 case MSG_ACQUIRE_WAKE_LOCK: 709 if (mWakeLock == null) { 710 PowerManager pm = (PowerManager)getSystemService( 711 Context.POWER_SERVICE); 712 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 713 "StartingObexPbapTransaction"); 714 mWakeLock.setReferenceCounted(false); 715 mWakeLock.acquire(); 716 Log.w(TAG, "Acquire Wake Lock"); 717 } 718 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 719 mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler 720 .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY); 721 break; 722 case MSG_RELEASE_WAKE_LOCK: 723 if (mWakeLock != null) { 724 mWakeLock.release(); 725 mWakeLock = null; 726 Log.w(TAG, "Release Wake Lock"); 727 } 728 break; 729 case SHUTDOWN: 730 closeService(); 731 break; 732 case LOAD_CONTACTS: 733 BluetoothPbapUtils.loadAllContacts(mContext, this); 734 break; 735 case CHECK_SECONDARY_VERSION_COUNTER: 736 BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, this); 737 break; 738 case ROLLOVER_COUNTERS: 739 BluetoothPbapUtils.rolloverCounters(); 740 break; 741 default: 742 break; 743 } 744 } 745 }; 746 setState(int state)747 private void setState(int state) { 748 setState(state, BluetoothPbap.RESULT_SUCCESS); 749 } 750 setState(int state, int result)751 private synchronized void setState(int state, int result) { 752 if (state != mState) { 753 if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " 754 + result); 755 int prevState = mState; 756 mState = state; 757 Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); 758 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 759 intent.putExtra(BluetoothProfile.EXTRA_STATE, mState); 760 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 761 sendBroadcast(intent, BLUETOOTH_PERM); 762 } 763 } 764 getState()765 protected int getState() { 766 return mState; 767 } 768 getRemoteDevice()769 protected BluetoothDevice getRemoteDevice() { 770 return mRemoteDevice; 771 } 772 createPbapNotification(String action)773 private void createPbapNotification(String action) { 774 775 NotificationManager nm = (NotificationManager) 776 getSystemService(Context.NOTIFICATION_SERVICE); 777 NotificationChannel notificationChannel = new NotificationChannel(PBAP_NOTIFICATION_CHANNEL, 778 getString(R.string.pbap_notification_group), NotificationManager.IMPORTANCE_HIGH); 779 nm.createNotificationChannel(notificationChannel); 780 781 // Create an intent triggered by clicking on the status icon. 782 Intent clickIntent = new Intent(); 783 clickIntent.setClass(this, BluetoothPbapActivity.class); 784 clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 785 clickIntent.setAction(action); 786 787 // Create an intent triggered by clicking on the 788 // "Clear All Notifications" button 789 Intent deleteIntent = new Intent(); 790 deleteIntent.setClass(this, BluetoothPbapService.class); 791 deleteIntent.setAction(AUTH_CANCELLED_ACTION); 792 793 String name = getRemoteDeviceName(); 794 795 if (action.equals(AUTH_CHALL_ACTION)) { 796 Notification notification = 797 new Notification.Builder(this, PBAP_NOTIFICATION_CHANNEL) 798 .setWhen(System.currentTimeMillis()) 799 .setContentTitle(getString(R.string.auth_notif_title)) 800 .setContentText(getString(R.string.auth_notif_message, name)) 801 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 802 .setTicker(getString(R.string.auth_notif_ticker)) 803 .setColor(getResources().getColor( 804 com.android.internal.R.color.system_notification_accent_color, 805 this.getTheme())) 806 .setFlag(Notification.FLAG_AUTO_CANCEL, true) 807 .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true) 808 .setDefaults(Notification.DEFAULT_SOUND) 809 .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0)) 810 .setDeleteIntent(PendingIntent.getBroadcast(this, 0, deleteIntent, 0)) 811 .build(); 812 nm.notify(NOTIFICATION_ID_AUTH, notification); 813 } 814 } 815 removePbapNotification(int id)816 private void removePbapNotification(int id) { 817 NotificationManager nm = (NotificationManager) 818 getSystemService(Context.NOTIFICATION_SERVICE); 819 nm.cancel(id); 820 } 821 getLocalPhoneNum()822 public static String getLocalPhoneNum() { 823 return sLocalPhoneNum; 824 } 825 getLocalPhoneName()826 public static String getLocalPhoneName() { 827 return sLocalPhoneName; 828 } 829 getRemoteDeviceName()830 public static String getRemoteDeviceName() { 831 return sRemoteDeviceName; 832 } 833 834 @Override initBinder()835 protected IProfileServiceBinder initBinder() { 836 return new PbapBinder(this); 837 } 838 839 @Override start()840 protected boolean start() { 841 Log.v(TAG, "start()"); 842 IntentFilter filter = new IntentFilter(); 843 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 844 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 845 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 846 filter.addAction(AUTH_RESPONSE_ACTION); 847 filter.addAction(AUTH_CANCELLED_ACTION); 848 mInterrupted = false; 849 BluetoothPbapConfig.init(this); 850 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER)); 851 if (mContactChangeObserver == null) { 852 registerReceiver(mPbapReceiver, filter); 853 try { 854 if (DEBUG) Log.d(TAG, "Registering observer"); 855 mContactChangeObserver = new BluetoothPbapContentObserver(); 856 getContentResolver().registerContentObserver( 857 DevicePolicyUtils.getEnterprisePhoneUri(this), false, 858 mContactChangeObserver); 859 } catch (SQLiteException e) { 860 Log.e(TAG, "SQLite exception: " + e); 861 } catch (IllegalStateException e) { 862 Log.e(TAG, "Illegal state exception, content observer is already registered"); 863 } 864 } 865 return true; 866 } 867 868 @Override stop()869 protected boolean stop() { 870 Log.v(TAG, "stop()"); 871 if (mContactChangeObserver == null) { 872 Log.i(TAG, "Avoid unregister when receiver it is not registered"); 873 return true; 874 } 875 try { 876 unregisterReceiver(mPbapReceiver); 877 getContentResolver().unregisterContentObserver(mContactChangeObserver); 878 mContactChangeObserver = null; 879 } catch (Exception e) { 880 Log.w(TAG, "Unable to unregister pbap receiver", e); 881 } 882 mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget(); 883 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 884 return true; 885 } 886 disconnect()887 protected void disconnect() { 888 synchronized (this) { 889 if (mState == BluetoothPbap.STATE_CONNECTED) { 890 if (mServerSession != null) { 891 mServerSession.close(); 892 mServerSession = null; 893 } 894 895 closeConnectionSocket(); 896 897 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED); 898 } 899 } 900 } 901 902 // Has to be a static class or a memory leak can occur. 903 private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { 904 private BluetoothPbapService mService; 905 getService(String perm)906 private BluetoothPbapService getService(String perm) { 907 if (!Utils.checkCaller()) { 908 Log.w(TAG, "not allowed for non-active user"); 909 return null; 910 } 911 if (mService != null && mService.isAvailable()) { 912 mService.enforceCallingOrSelfPermission(perm, "Need " + perm + " permission"); 913 return mService; 914 } 915 return null; 916 } 917 PbapBinder(BluetoothPbapService service)918 PbapBinder(BluetoothPbapService service) { 919 Log.v(TAG, "PbapBinder()"); 920 mService = service; 921 } 922 cleanup()923 public boolean cleanup() { 924 mService = null; 925 return true; 926 } 927 getState()928 public int getState() { 929 if (DEBUG) Log.d(TAG, "getState = " + mService.getState()); 930 BluetoothPbapService service = getService(BLUETOOTH_PERM); 931 if (service == null) return BluetoothPbap.STATE_DISCONNECTED; 932 933 return service.getState(); 934 } 935 getClient()936 public BluetoothDevice getClient() { 937 if (DEBUG) Log.d(TAG, "getClient = " + mService.getRemoteDevice()); 938 BluetoothPbapService service = getService(BLUETOOTH_PERM); 939 if (service == null) return null; 940 return service.getRemoteDevice(); 941 } 942 isConnected(BluetoothDevice device)943 public boolean isConnected(BluetoothDevice device) { 944 if (DEBUG) Log.d(TAG, "isConnected " + device); 945 BluetoothPbapService service = getService(BLUETOOTH_PERM); 946 if (service == null) return false; 947 return service.getState() == BluetoothPbap.STATE_CONNECTED 948 && service.getRemoteDevice().equals(device); 949 } 950 connect(BluetoothDevice device)951 public boolean connect(BluetoothDevice device) { 952 BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM); 953 return false; 954 } 955 disconnect()956 public void disconnect() { 957 if (DEBUG) Log.d(TAG, "disconnect"); 958 BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM); 959 if (service == null) return; 960 service.disconnect(); 961 } 962 } 963 964 /** 965 * Start server side socket listeners. Caller should make sure that adapter is in a ready state 966 * and SDP record is cleaned up. Otherwise, this method will fail. 967 */ startSocketListeners()968 synchronized private void startSocketListeners() { 969 if (DEBUG) Log.d(TAG, "startsocketListener"); 970 if (mServerSession != null) { 971 if (DEBUG) Log.d(TAG, "mServerSession exists - shutting it down..."); 972 mServerSession.close(); 973 mServerSession = null; 974 } 975 closeConnectionSocket(); 976 if (mServerSockets != null) { 977 mServerSockets.prepareForNewConnect(); 978 } else { 979 mServerSockets = ObexServerSockets.create(this); 980 if (mServerSockets == null) { 981 // TODO: Handle - was not handled before 982 Log.e(TAG, "Failed to start the listeners"); 983 return; 984 } 985 if (mSdpHandle >= 0) { 986 Log.e(TAG, "SDP handle was not cleaned up, mSdpHandle=" + mSdpHandle); 987 return; 988 } 989 mSdpHandle = SdpManager.getDefaultManager().createPbapPseRecord( 990 "OBEX Phonebook Access Server", mServerSockets.getRfcommChannel(), 991 mServerSockets.getL2capPsm(), SDP_PBAP_SERVER_VERSION, 992 SDP_PBAP_SUPPORTED_REPOSITORIES, SDP_PBAP_SUPPORTED_FEATURES); 993 // fetch Pbap Params to check if significant change has happened to Database 994 BluetoothPbapUtils.fetchPbapParams(mContext); 995 996 if (DEBUG) Log.d(TAG, "PBAP server with handle:" + mSdpHandle); 997 } 998 } 999 getDbIdentifier()1000 long getDbIdentifier() { 1001 return BluetoothPbapUtils.mDbIdentifier.get(); 1002 } 1003 setUserTimeoutAlarm()1004 private void setUserTimeoutAlarm() { 1005 if (DEBUG) Log.d(TAG, "SetUserTimeOutAlarm()"); 1006 if (mAlarmManager == null) { 1007 mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 1008 } 1009 mRemoveTimeoutMsg = true; 1010 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 1011 PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 1012 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 1013 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent); 1014 } 1015 1016 @Override onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket)1017 public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) { 1018 mRemoteDevice = remoteDevice; 1019 if (mRemoteDevice == null || socket == null) { 1020 Log.i(TAG, "mRemoteDevice :" + mRemoteDevice + " socket :" + socket); 1021 return false; 1022 } 1023 mConnSocket = socket; 1024 sRemoteDeviceName = mRemoteDevice.getName(); 1025 // In case getRemoteName failed and return null 1026 if (TextUtils.isEmpty(sRemoteDeviceName)) { 1027 sRemoteDeviceName = getString(R.string.defaultname); 1028 } 1029 int permission = mRemoteDevice.getPhonebookAccessPermission(); 1030 if (DEBUG) Log.d(TAG, "getPhonebookAccessPermission() = " + permission); 1031 1032 if (permission == BluetoothDevice.ACCESS_ALLOWED) { 1033 try { 1034 startObexServerSession(); 1035 } catch (IOException ex) { 1036 Log.e(TAG, "Caught exception starting obex server session" + ex.toString()); 1037 } 1038 1039 if (!BluetoothPbapUtils.contactsLoaded) { 1040 mSessionStatusHandler.sendMessage( 1041 mSessionStatusHandler.obtainMessage(LOAD_CONTACTS)); 1042 } 1043 1044 } else if (permission == BluetoothDevice.ACCESS_REJECTED) { 1045 if (DEBUG) { 1046 Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName 1047 + " automatically as already rejected device"); 1048 } 1049 return false; 1050 } else { // permission == BluetoothDevice.ACCESS_UNKNOWN 1051 // Send an Intent to Settings app to ask user preference. 1052 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 1053 intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS); 1054 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 1055 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 1056 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice); 1057 intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName()); 1058 mIsWaitingAuthorization = true; 1059 sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM); 1060 if (VERBOSE) 1061 Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName); 1062 /* In case car kit time out and try to use HFP for phonebook 1063 * access, while UI still there waiting for user to confirm */ 1064 mSessionStatusHandler.sendMessageDelayed( 1065 mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE); 1066 /* We will continue the process when we receive 1067 * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */ 1068 } 1069 return true; 1070 }; 1071 1072 /** 1073 * Called when an unrecoverable error occurred in an accept thread. 1074 * Close down the server socket, and restart. 1075 * TODO: Change to message, to call start in correct context. 1076 */ 1077 @Override onAcceptFailed()1078 public synchronized void onAcceptFailed() { 1079 // Clean up SDP record first 1080 cleanUpSdpRecord(); 1081 // Force socket listener to restart 1082 if (mServerSockets != null) { 1083 mServerSockets.shutdown(false); 1084 mServerSockets = null; 1085 } 1086 if (!mInterrupted && mAdapter != null && mAdapter.isEnabled()) { 1087 startSocketListeners(); 1088 } 1089 } 1090 } 1091