1 /* 2 * Copyright (C) 2014 Samsung System LSI 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.android.bluetooth.map; 17 18 import android.app.AlarmManager; 19 import android.app.PendingIntent; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothMap; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.BluetoothUuid; 25 import android.bluetooth.IBluetoothMap; 26 import android.bluetooth.SdpMnsRecord; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.IntentFilter.MalformedMimeTypeException; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.ParcelUuid; 37 import android.os.PowerManager; 38 import android.os.RemoteException; 39 import android.provider.Settings; 40 import android.support.annotation.VisibleForTesting; 41 import android.text.TextUtils; 42 import android.util.Log; 43 import android.util.SparseArray; 44 45 import com.android.bluetooth.BluetoothMetricsProto; 46 import com.android.bluetooth.R; 47 import com.android.bluetooth.Utils; 48 import com.android.bluetooth.btservice.MetricsLogger; 49 import com.android.bluetooth.btservice.ProfileService; 50 51 import java.io.IOException; 52 import java.util.ArrayList; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Set; 56 57 public class BluetoothMapService extends ProfileService { 58 private static final String TAG = "BluetoothMapService"; 59 60 /** 61 * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and 62 * restart com.android.bluetooth process. only enable DEBUG log: 63 * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and 64 * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE" 65 */ 66 67 public static final boolean DEBUG = true; //FIXME set to false; 68 69 public static final boolean VERBOSE = false; 70 71 /** 72 * Intent indicating timeout for user confirmation, which is sent to 73 * BluetoothMapActivity 74 */ 75 public static final String USER_CONFIRM_TIMEOUT_ACTION = 76 "com.android.bluetooth.map.USER_CONFIRM_TIMEOUT"; 77 private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000; 78 79 // Intent indicating that the email settings activity should be opened 80 static final String ACTION_SHOW_MAPS_SETTINGS = 81 "android.btmap.intent.action.SHOW_MAPS_SETTINGS"; 82 83 static final int MSG_SERVERSESSION_CLOSE = 5000; 84 static final int MSG_SESSION_ESTABLISHED = 5001; 85 static final int MSG_SESSION_DISCONNECTED = 5002; 86 static final int MSG_MAS_CONNECT = 5003; // Send at MAS connect, including the MAS_ID 87 static final int MSG_MAS_CONNECT_CANCEL = 5004; // Send at auth. declined 88 static final int MSG_ACQUIRE_WAKE_LOCK = 5005; 89 static final int MSG_RELEASE_WAKE_LOCK = 5006; 90 static final int MSG_MNS_SDP_SEARCH = 5007; 91 static final int MSG_OBSERVER_REGISTRATION = 5008; 92 93 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 94 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 95 96 private static final int START_LISTENER = 1; 97 private static final int USER_TIMEOUT = 2; 98 private static final int DISCONNECT_MAP = 3; 99 private static final int SHUTDOWN = 4; 100 private static final int UPDATE_MAS_INSTANCES = 5; 101 102 private static final int RELEASE_WAKE_LOCK_DELAY = 10000; 103 private PowerManager.WakeLock mWakeLock = null; 104 105 static final int UPDATE_MAS_INSTANCES_ACCOUNT_ADDED = 0; 106 static final int UPDATE_MAS_INSTANCES_ACCOUNT_REMOVED = 1; 107 static final int UPDATE_MAS_INSTANCES_ACCOUNT_RENAMED = 2; 108 static final int UPDATE_MAS_INSTANCES_ACCOUNT_DISCONNECT = 3; 109 110 private static final int MAS_ID_SMS_MMS = 0; 111 112 private BluetoothAdapter mAdapter; 113 114 private BluetoothMnsObexClient mBluetoothMnsObexClient = null; 115 116 // mMasInstances: A list of the active MasInstances using the MasId for the key 117 private SparseArray<BluetoothMapMasInstance> mMasInstances = 118 new SparseArray<BluetoothMapMasInstance>(1); 119 // mMasInstanceMap: A list of the active MasInstances using the account for the key 120 private HashMap<BluetoothMapAccountItem, BluetoothMapMasInstance> mMasInstanceMap = 121 new HashMap<BluetoothMapAccountItem, BluetoothMapMasInstance>(1); 122 123 // The remote connected device - protect access 124 private static BluetoothDevice sRemoteDevice = null; 125 126 private ArrayList<BluetoothMapAccountItem> mEnabledAccounts = null; 127 private static String sRemoteDeviceName = null; 128 129 private int mState; 130 private BluetoothMapAppObserver mAppObserver = null; 131 private AlarmManager mAlarmManager = null; 132 133 private boolean mIsWaitingAuthorization = false; 134 private boolean mRemoveTimeoutMsg = false; 135 private boolean mRegisteredMapReceiver = false; 136 private int mPermission = BluetoothDevice.ACCESS_UNKNOWN; 137 private boolean mAccountChanged = false; 138 private boolean mSdpSearchInitiated = false; 139 private SdpMnsRecord mMnsRecord = null; 140 private MapServiceMessageHandler mSessionStatusHandler; 141 private boolean mServiceStarted = false; 142 143 private static BluetoothMapService sBluetoothMapService; 144 145 private boolean mSmsCapable = true; 146 147 private static final ParcelUuid[] MAP_UUIDS = { 148 BluetoothUuid.MAP, BluetoothUuid.MNS, 149 }; 150 151 public BluetoothMapService() { 152 mState = BluetoothMap.STATE_DISCONNECTED; 153 } 154 155 private synchronized void closeService() { 156 if (DEBUG) { 157 Log.d(TAG, "closeService() in"); 158 } 159 if (mBluetoothMnsObexClient != null) { 160 mBluetoothMnsObexClient.shutdown(); 161 mBluetoothMnsObexClient = null; 162 } 163 int numMasInstances = mMasInstances.size(); 164 for (int i = 0; i < numMasInstances; i++) { 165 mMasInstances.valueAt(i).shutdown(); 166 } 167 mMasInstances.clear(); 168 169 mIsWaitingAuthorization = false; 170 mPermission = BluetoothDevice.ACCESS_UNKNOWN; 171 setState(BluetoothMap.STATE_DISCONNECTED); 172 173 if (mWakeLock != null) { 174 mWakeLock.release(); 175 if (VERBOSE) { 176 Log.v(TAG, "CloseService(): Release Wake Lock"); 177 } 178 mWakeLock = null; 179 } 180 181 sRemoteDevice = null; 182 183 if (mSessionStatusHandler == null) { 184 return; 185 } 186 187 // Perform cleanup in Handler running on worker Thread 188 mSessionStatusHandler.removeCallbacksAndMessages(null); 189 Looper looper = mSessionStatusHandler.getLooper(); 190 if (looper != null) { 191 looper.quit(); 192 if (VERBOSE) { 193 Log.i(TAG, "Quit looper"); 194 } 195 } 196 mSessionStatusHandler = null; 197 198 if (VERBOSE) { 199 Log.v(TAG, "MAP Service closeService out"); 200 } 201 } 202 203 /** 204 * Starts the Socket listener threads for each MAS 205 */ 206 private void startSocketListeners(int masId) { 207 if (masId == -1) { 208 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 209 mMasInstances.valueAt(i).startSocketListeners(); 210 } 211 } else { 212 BluetoothMapMasInstance masInst = mMasInstances.get(masId); // returns null for -1 213 if (masInst != null) { 214 masInst.startSocketListeners(); 215 } else { 216 Log.w(TAG, "startSocketListeners(): Invalid MasId: " + masId); 217 } 218 } 219 } 220 221 /** 222 * Start a MAS instance for SMS/MMS and each e-mail account. 223 */ 224 private void startObexServerSessions() { 225 if (DEBUG) { 226 Log.d(TAG, "Map Service START ObexServerSessions()"); 227 } 228 229 // Acquire the wakeLock before starting Obex transaction thread 230 if (mWakeLock == null) { 231 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 232 mWakeLock = 233 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingObexMapTransaction"); 234 mWakeLock.setReferenceCounted(false); 235 mWakeLock.acquire(); 236 if (VERBOSE) { 237 Log.v(TAG, "startObexSessions(): Acquire Wake Lock"); 238 } 239 } 240 241 if (mBluetoothMnsObexClient == null) { 242 mBluetoothMnsObexClient = 243 new BluetoothMnsObexClient(sRemoteDevice, mMnsRecord, mSessionStatusHandler); 244 } 245 246 boolean connected = false; 247 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 248 try { 249 if (mMasInstances.valueAt(i).startObexServerSession(mBluetoothMnsObexClient)) { 250 connected = true; 251 } 252 } catch (IOException e) { 253 Log.w(TAG, "IOException occured while starting an obexServerSession restarting" 254 + " the listener", e); 255 mMasInstances.valueAt(i).restartObexServerSession(); 256 } catch (RemoteException e) { 257 Log.w(TAG, "RemoteException occured while starting an obexServerSession restarting" 258 + " the listener", e); 259 mMasInstances.valueAt(i).restartObexServerSession(); 260 } 261 } 262 if (connected) { 263 setState(BluetoothMap.STATE_CONNECTED); 264 } 265 266 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 267 mSessionStatusHandler.sendMessageDelayed( 268 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 269 RELEASE_WAKE_LOCK_DELAY); 270 271 if (VERBOSE) { 272 Log.v(TAG, "startObexServerSessions() success!"); 273 } 274 } 275 276 public Handler getHandler() { 277 return mSessionStatusHandler; 278 } 279 280 /** 281 * Restart a MAS instances. 282 * @param masId use -1 to stop all instances 283 */ 284 private void stopObexServerSessions(int masId) { 285 if (DEBUG) { 286 Log.d(TAG, "MAP Service STOP ObexServerSessions()"); 287 } 288 289 boolean lastMasInst = true; 290 291 if (masId != -1) { 292 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 293 BluetoothMapMasInstance masInst = mMasInstances.valueAt(i); 294 if (masInst.getMasId() != masId && masInst.isStarted()) { 295 lastMasInst = false; 296 } 297 } 298 } // Else just close down it all 299 300 // Shutdown the MNS client - this must happen before MAS close 301 if (mBluetoothMnsObexClient != null && lastMasInst) { 302 mBluetoothMnsObexClient.shutdown(); 303 mBluetoothMnsObexClient = null; 304 } 305 306 BluetoothMapMasInstance masInst = mMasInstances.get(masId); // returns null for -1 307 if (masInst != null) { 308 masInst.restartObexServerSession(); 309 } else if (masId == -1) { 310 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 311 mMasInstances.valueAt(i).restartObexServerSession(); 312 } 313 } 314 315 if (lastMasInst) { 316 setState(BluetoothMap.STATE_DISCONNECTED); 317 mPermission = BluetoothDevice.ACCESS_UNKNOWN; 318 sRemoteDevice = null; 319 if (mAccountChanged) { 320 updateMasInstances(UPDATE_MAS_INSTANCES_ACCOUNT_DISCONNECT); 321 } 322 } 323 324 // Release the wake lock at disconnect 325 if (mWakeLock != null && lastMasInst) { 326 mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK); 327 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 328 mWakeLock.release(); 329 if (VERBOSE) { 330 Log.v(TAG, "stopObexServerSessions(): Release Wake Lock"); 331 } 332 } 333 } 334 335 private final class MapServiceMessageHandler extends Handler { 336 private MapServiceMessageHandler(Looper looper) { 337 super(looper); 338 } 339 340 @Override 341 public void handleMessage(Message msg) { 342 if (VERBOSE) { 343 Log.v(TAG, "Handler(): got msg=" + msg.what); 344 } 345 346 switch (msg.what) { 347 case UPDATE_MAS_INSTANCES: 348 updateMasInstancesHandler(); 349 break; 350 case START_LISTENER: 351 if (mAdapter.isEnabled()) { 352 startSocketListeners(msg.arg1); 353 } 354 break; 355 case MSG_MAS_CONNECT: 356 onConnectHandler(msg.arg1); 357 break; 358 case MSG_MAS_CONNECT_CANCEL: 359 /* TODO: We need to handle this by accepting the connection and reject at 360 * OBEX level, by using ObexRejectServer - add timeout to handle clients not 361 * closing the transport channel. 362 */ 363 stopObexServerSessions(-1); 364 break; 365 case USER_TIMEOUT: 366 if (mIsWaitingAuthorization) { 367 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL); 368 intent.setPackage(getString(R.string.pairing_ui_package)); 369 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, sRemoteDevice); 370 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 371 BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS); 372 sendBroadcast(intent); 373 cancelUserTimeoutAlarm(); 374 mIsWaitingAuthorization = false; 375 stopObexServerSessions(-1); 376 } 377 break; 378 case MSG_SERVERSESSION_CLOSE: 379 stopObexServerSessions(msg.arg1); 380 break; 381 case MSG_SESSION_ESTABLISHED: 382 break; 383 case MSG_SESSION_DISCONNECTED: 384 // handled elsewhere 385 break; 386 case DISCONNECT_MAP: 387 disconnectMap((BluetoothDevice) msg.obj); 388 break; 389 case SHUTDOWN: 390 // Call close from this handler to avoid starting because of pending messages 391 closeService(); 392 break; 393 case MSG_ACQUIRE_WAKE_LOCK: 394 if (VERBOSE) { 395 Log.v(TAG, "Acquire Wake Lock request message"); 396 } 397 if (mWakeLock == null) { 398 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 399 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 400 "StartingObexMapTransaction"); 401 mWakeLock.setReferenceCounted(false); 402 } 403 if (!mWakeLock.isHeld()) { 404 mWakeLock.acquire(); 405 if (DEBUG) { 406 Log.d(TAG, " Acquired Wake Lock by message"); 407 } 408 } 409 mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK); 410 mSessionStatusHandler.sendMessageDelayed( 411 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK), 412 RELEASE_WAKE_LOCK_DELAY); 413 break; 414 case MSG_RELEASE_WAKE_LOCK: 415 if (VERBOSE) { 416 Log.v(TAG, "Release Wake Lock request message"); 417 } 418 if (mWakeLock != null) { 419 mWakeLock.release(); 420 if (DEBUG) { 421 Log.d(TAG, " Released Wake Lock by message"); 422 } 423 } 424 break; 425 case MSG_MNS_SDP_SEARCH: 426 if (sRemoteDevice != null) { 427 if (DEBUG) { 428 Log.d(TAG, "MNS SDP Initiate Search .."); 429 } 430 sRemoteDevice.sdpSearch(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS); 431 } else { 432 Log.w(TAG, "remoteDevice info not available"); 433 } 434 break; 435 case MSG_OBSERVER_REGISTRATION: 436 if (DEBUG) { 437 Log.d(TAG, "ContentObserver Registration MASID: " + msg.arg1 + " Enable: " 438 + msg.arg2); 439 } 440 BluetoothMapMasInstance masInst = mMasInstances.get(msg.arg1); 441 if (masInst != null && masInst.mObserver != null) { 442 try { 443 if (msg.arg2 == BluetoothMapAppParams.NOTIFICATION_STATUS_YES) { 444 masInst.mObserver.registerObserver(); 445 } else { 446 masInst.mObserver.unregisterObserver(); 447 } 448 } catch (RemoteException e) { 449 Log.e(TAG, "ContentObserverRegistarion Failed: " + e); 450 } 451 } 452 break; 453 default: 454 break; 455 } 456 } 457 } 458 459 private void onConnectHandler(int masId) { 460 if (mIsWaitingAuthorization || sRemoteDevice == null || mSdpSearchInitiated) { 461 return; 462 } 463 BluetoothMapMasInstance masInst = mMasInstances.get(masId); 464 // Need to ensure we are still allowed. 465 if (DEBUG) { 466 Log.d(TAG, "mPermission = " + mPermission); 467 } 468 if (mPermission == BluetoothDevice.ACCESS_ALLOWED) { 469 try { 470 if (VERBOSE) { 471 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName 472 + " automatically as trusted device"); 473 } 474 if (mBluetoothMnsObexClient != null && masInst != null) { 475 masInst.startObexServerSession(mBluetoothMnsObexClient); 476 } else { 477 startObexServerSessions(); 478 } 479 } catch (IOException ex) { 480 Log.e(TAG, "catch IOException starting obex server session", ex); 481 } catch (RemoteException ex) { 482 Log.e(TAG, "catch RemoteException starting obex server session", ex); 483 } 484 } 485 } 486 487 public int getState() { 488 return mState; 489 } 490 491 public static BluetoothDevice getRemoteDevice() { 492 return sRemoteDevice; 493 } 494 495 private void setState(int state) { 496 setState(state, BluetoothMap.RESULT_SUCCESS); 497 } 498 499 private synchronized void setState(int state, int result) { 500 if (state != mState) { 501 if (DEBUG) { 502 Log.d(TAG, "Map state " + mState + " -> " + state + ", result = " + result); 503 } 504 int prevState = mState; 505 mState = state; 506 Intent intent = new Intent(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED); 507 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 508 intent.putExtra(BluetoothProfile.EXTRA_STATE, mState); 509 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, sRemoteDevice); 510 sendBroadcast(intent, BLUETOOTH_PERM); 511 } 512 } 513 514 void disconnect(BluetoothDevice device) { 515 mSessionStatusHandler.sendMessage( 516 mSessionStatusHandler.obtainMessage(DISCONNECT_MAP, 0, 0, device)); 517 } 518 519 void disconnectMap(BluetoothDevice device) { 520 if (DEBUG) { 521 Log.d(TAG, "disconnectMap"); 522 } 523 if (getRemoteDevice() != null && getRemoteDevice().equals(device)) { 524 switch (mState) { 525 case BluetoothMap.STATE_CONNECTED: 526 // Disconnect all connections and restart all MAS instances 527 stopObexServerSessions(-1); 528 break; 529 default: 530 break; 531 } 532 } 533 } 534 535 private List<BluetoothDevice> getConnectedDevices() { 536 List<BluetoothDevice> devices = new ArrayList<>(); 537 synchronized (this) { 538 if (mState == BluetoothMap.STATE_CONNECTED && sRemoteDevice != null) { 539 devices.add(sRemoteDevice); 540 } 541 } 542 return devices; 543 } 544 545 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 546 List<BluetoothDevice> deviceList = new ArrayList<>(); 547 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 548 if (bondedDevices == null) { 549 return deviceList; 550 } 551 synchronized (this) { 552 for (BluetoothDevice device : bondedDevices) { 553 ParcelUuid[] featureUuids = device.getUuids(); 554 if (!BluetoothUuid.containsAnyUuid(featureUuids, MAP_UUIDS)) { 555 continue; 556 } 557 int connectionState = getConnectionState(device); 558 for (int state : states) { 559 if (connectionState == state) { 560 deviceList.add(device); 561 } 562 } 563 } 564 } 565 return deviceList; 566 } 567 568 int getConnectionState(BluetoothDevice device) { 569 synchronized (this) { 570 if (getState() == BluetoothMap.STATE_CONNECTED && getRemoteDevice().equals(device)) { 571 return BluetoothProfile.STATE_CONNECTED; 572 } else { 573 return BluetoothProfile.STATE_DISCONNECTED; 574 } 575 } 576 } 577 578 boolean setPriority(BluetoothDevice device, int priority) { 579 if (VERBOSE) { 580 Log.v(TAG, "Saved priority " + device + " = " + priority); 581 } 582 return Settings.Global.putInt(getContentResolver(), 583 Settings.Global.getBluetoothMapPriorityKey(device.getAddress()), priority); 584 } 585 586 int getPriority(BluetoothDevice device) { 587 return Settings.Global.getInt(getContentResolver(), 588 Settings.Global.getBluetoothMapPriorityKey(device.getAddress()), 589 BluetoothProfile.PRIORITY_UNDEFINED); 590 } 591 592 @Override 593 protected IProfileServiceBinder initBinder() { 594 return new BluetoothMapBinder(this); 595 } 596 597 @Override 598 protected boolean start() { 599 if (DEBUG) { 600 Log.d(TAG, "start()"); 601 } 602 HandlerThread thread = new HandlerThread("BluetoothMapHandler"); 603 thread.start(); 604 Looper looper = thread.getLooper(); 605 mSessionStatusHandler = new MapServiceMessageHandler(looper); 606 607 IntentFilter filter = new IntentFilter(); 608 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 609 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 610 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 611 filter.addAction(BluetoothDevice.ACTION_SDP_RECORD); 612 filter.addAction(ACTION_SHOW_MAPS_SETTINGS); 613 filter.addAction(USER_CONFIRM_TIMEOUT_ACTION); 614 615 // We need two filters, since Type only applies to the ACTION_MESSAGE_SENT 616 IntentFilter filterMessageSent = new IntentFilter(); 617 filterMessageSent.addAction(BluetoothMapContentObserver.ACTION_MESSAGE_SENT); 618 try { 619 filterMessageSent.addDataType("message/*"); 620 } catch (MalformedMimeTypeException e) { 621 Log.e(TAG, "Wrong mime type!!!", e); 622 } 623 if (!mRegisteredMapReceiver) { 624 registerReceiver(mMapReceiver, filter); 625 registerReceiver(mMapReceiver, filterMessageSent); 626 mRegisteredMapReceiver = true; 627 } 628 mAdapter = BluetoothAdapter.getDefaultAdapter(); 629 mAppObserver = new BluetoothMapAppObserver(this, this); 630 631 mSmsCapable = getResources().getBoolean(com.android.internal.R.bool.config_sms_capable); 632 633 mEnabledAccounts = mAppObserver.getEnabledAccountItems(); 634 createMasInstances(); // Uses mEnabledAccounts 635 636 sendStartListenerMessage(-1); 637 setBluetoothMapService(this); 638 mServiceStarted = true; 639 return true; 640 } 641 642 /** 643 * Get the current instance of {@link BluetoothMapService} 644 * 645 * @return current instance of {@link BluetoothMapService} 646 */ 647 @VisibleForTesting 648 public static synchronized BluetoothMapService getBluetoothMapService() { 649 if (sBluetoothMapService == null) { 650 Log.w(TAG, "getBluetoothMapService(): service is null"); 651 return null; 652 } 653 if (!sBluetoothMapService.isAvailable()) { 654 Log.w(TAG, "getBluetoothMapService(): service is not available"); 655 return null; 656 } 657 return sBluetoothMapService; 658 } 659 660 private static synchronized void setBluetoothMapService(BluetoothMapService instance) { 661 if (DEBUG) { 662 Log.d(TAG, "setBluetoothMapService(): set to: " + instance); 663 } 664 sBluetoothMapService = instance; 665 } 666 667 /** 668 * Call this to trigger an update of the MAS instance list. 669 * No changes will be applied unless in disconnected state 670 */ 671 void updateMasInstances(int action) { 672 mSessionStatusHandler.obtainMessage(UPDATE_MAS_INSTANCES, action, 0).sendToTarget(); 673 } 674 675 /** 676 * Update the active MAS Instances according the difference between mEnabledDevices 677 * and the current list of accounts. 678 * Will only make changes if state is disconnected. 679 * 680 * How it works: 681 * 1) Build two lists of accounts 682 * newAccountList - all accounts from mAppObserver 683 * newAccounts - accounts that have been enabled since mEnabledAccounts 684 * was last updated. 685 * mEnabledAccounts - The accounts which are left 686 * 2) Stop and remove all MasInstances in mEnabledAccounts 687 * 3) Add and start MAS instances for accounts on the new list. 688 * Called at: 689 * - Each change in accounts 690 * - Each disconnect - before MasInstances restart. 691 */ 692 private void updateMasInstancesHandler() { 693 if (DEBUG) { 694 Log.d(TAG, "updateMasInstancesHandler() state = " + getState()); 695 } 696 697 if (getState() != BluetoothMap.STATE_DISCONNECTED) { 698 mAccountChanged = true; 699 return; 700 } 701 702 ArrayList<BluetoothMapAccountItem> newAccountList = mAppObserver.getEnabledAccountItems(); 703 ArrayList<BluetoothMapAccountItem> newAccounts = new ArrayList<>(); 704 705 for (BluetoothMapAccountItem account : newAccountList) { 706 if (!mEnabledAccounts.remove(account)) { 707 newAccounts.add(account); 708 } 709 } 710 711 // Remove all disabled/removed accounts 712 if (mEnabledAccounts.size() > 0) { 713 for (BluetoothMapAccountItem account : mEnabledAccounts) { 714 BluetoothMapMasInstance masInst = mMasInstanceMap.remove(account); 715 if (VERBOSE) { 716 Log.v(TAG, " Removing account: " + account + " masInst = " + masInst); 717 } 718 if (masInst != null) { 719 masInst.shutdown(); 720 mMasInstances.remove(masInst.getMasId()); 721 } 722 } 723 } 724 725 // Add any newly created accounts 726 for (BluetoothMapAccountItem account : newAccounts) { 727 if (VERBOSE) { 728 Log.v(TAG, " Adding account: " + account); 729 } 730 int masId = getNextMasId(); 731 BluetoothMapMasInstance newInst = 732 new BluetoothMapMasInstance(this, this, account, masId, false); 733 mMasInstances.append(masId, newInst); 734 mMasInstanceMap.put(account, newInst); 735 // Start the new instance 736 if (mAdapter.isEnabled()) { 737 newInst.startSocketListeners(); 738 } 739 } 740 741 mEnabledAccounts = newAccountList; 742 if (VERBOSE) { 743 Log.v(TAG, " Enabled accounts:"); 744 for (BluetoothMapAccountItem account : mEnabledAccounts) { 745 Log.v(TAG, " " + account); 746 } 747 Log.v(TAG, " Active MAS instances:"); 748 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 749 BluetoothMapMasInstance masInst = mMasInstances.valueAt(i); 750 Log.v(TAG, " " + masInst); 751 } 752 } 753 mAccountChanged = false; 754 } 755 756 /** 757 * Return a free key greater than the largest key in use. 758 * If the key 255 is in use, the first free masId will be returned. 759 * @return a free MasId 760 */ 761 private int getNextMasId() { 762 // Find the largest masId in use 763 int largestMasId = 0; 764 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 765 int masId = mMasInstances.keyAt(i); 766 if (masId > largestMasId) { 767 largestMasId = masId; 768 } 769 } 770 if (largestMasId < 0xff) { 771 return largestMasId + 1; 772 } 773 // If 0xff is already in use, wrap and choose the first free MasId. 774 for (int i = 1; i <= 0xff; i++) { 775 if (mMasInstances.get(i) == null) { 776 return i; 777 } 778 } 779 return 0xff; // This will never happen, as we only allow 10 e-mail accounts to be enabled 780 } 781 782 private void createMasInstances() { 783 int masId = MAS_ID_SMS_MMS; 784 785 if (mSmsCapable) { 786 // Add the SMS/MMS instance 787 BluetoothMapMasInstance smsMmsInst = 788 new BluetoothMapMasInstance(this, this, null, masId, true); 789 mMasInstances.append(masId, smsMmsInst); 790 mMasInstanceMap.put(null, smsMmsInst); 791 masId++; 792 } 793 794 // get list of accounts already set to be visible through MAP 795 for (BluetoothMapAccountItem account : mEnabledAccounts) { 796 BluetoothMapMasInstance newInst = 797 new BluetoothMapMasInstance(this, this, account, masId, false); 798 mMasInstances.append(masId, newInst); 799 mMasInstanceMap.put(account, newInst); 800 masId++; 801 } 802 } 803 804 @Override 805 protected boolean stop() { 806 if (DEBUG) { 807 Log.d(TAG, "stop()"); 808 } 809 if (!mServiceStarted) { 810 if (DEBUG) { 811 Log.d(TAG, "mServiceStarted is false - Ignoring"); 812 } 813 return true; 814 } 815 setBluetoothMapService(null); 816 mServiceStarted = false; 817 if (mRegisteredMapReceiver) { 818 mRegisteredMapReceiver = false; 819 unregisterReceiver(mMapReceiver); 820 mAppObserver.shutdown(); 821 } 822 sendShutdownMessage(); 823 return true; 824 } 825 826 /** 827 * Called from each MAS instance when a connection is received. 828 * @param remoteDevice The device connecting 829 * @param masInst a reference to the calling MAS instance. 830 * @return true if the connection was accepted, false otherwise 831 */ 832 public boolean onConnect(BluetoothDevice remoteDevice, BluetoothMapMasInstance masInst) { 833 boolean sendIntent = false; 834 boolean cancelConnection = false; 835 836 // As this can be called from each MasInstance, we need to lock access to member variables 837 synchronized (this) { 838 if (sRemoteDevice == null) { 839 sRemoteDevice = remoteDevice; 840 sRemoteDeviceName = sRemoteDevice.getName(); 841 // In case getRemoteName failed and return null 842 if (TextUtils.isEmpty(sRemoteDeviceName)) { 843 sRemoteDeviceName = getString(R.string.defaultname); 844 } 845 846 mPermission = sRemoteDevice.getMessageAccessPermission(); 847 if (mPermission == BluetoothDevice.ACCESS_UNKNOWN) { 848 sendIntent = true; 849 mIsWaitingAuthorization = true; 850 setUserTimeoutAlarm(); 851 } else if (mPermission == BluetoothDevice.ACCESS_REJECTED) { 852 cancelConnection = true; 853 } else if (mPermission == BluetoothDevice.ACCESS_ALLOWED) { 854 sRemoteDevice.sdpSearch(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS); 855 mSdpSearchInitiated = true; 856 } 857 } else if (!sRemoteDevice.equals(remoteDevice)) { 858 Log.w(TAG, "Unexpected connection from a second Remote Device received. name: " + ( 859 (remoteDevice == null) ? "unknown" : remoteDevice.getName())); 860 return false; 861 } // Else second connection to same device, just continue 862 } 863 864 if (sendIntent) { 865 // This will trigger Settings app's dialog. 866 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST); 867 intent.setPackage(getString(R.string.pairing_ui_package)); 868 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 869 BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS); 870 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, sRemoteDevice); 871 sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM); 872 873 if (VERBOSE) { 874 Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName); 875 } 876 //Queue USER_TIMEOUT to disconnect MAP OBEX session. If user doesn't 877 //accept or reject authorization request 878 } else if (cancelConnection) { 879 sendConnectCancelMessage(); 880 } else if (mPermission == BluetoothDevice.ACCESS_ALLOWED) { 881 // Signal to the service that we have a incoming connection. 882 sendConnectMessage(masInst.getMasId()); 883 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.MAP); 884 } 885 return true; 886 } 887 888 private void setUserTimeoutAlarm() { 889 if (DEBUG) { 890 Log.d(TAG, "SetUserTimeOutAlarm()"); 891 } 892 if (mAlarmManager == null) { 893 mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 894 } 895 mRemoveTimeoutMsg = true; 896 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 897 PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 898 mAlarmManager.set(AlarmManager.RTC_WAKEUP, 899 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent); 900 } 901 902 private void cancelUserTimeoutAlarm() { 903 if (DEBUG) { 904 Log.d(TAG, "cancelUserTimeOutAlarm()"); 905 } 906 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 907 PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0); 908 pIntent.cancel(); 909 910 AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); 911 alarmManager.cancel(pIntent); 912 mRemoveTimeoutMsg = false; 913 } 914 915 /** 916 * Start the incoming connection listeners for a MAS ID 917 * @param masId the MasID to start. Use -1 to start all listeners. 918 */ 919 void sendStartListenerMessage(int masId) { 920 if (mSessionStatusHandler != null && !mSessionStatusHandler.hasMessages(START_LISTENER)) { 921 Message msg = mSessionStatusHandler.obtainMessage(START_LISTENER, masId, 0); 922 /* We add a small delay here to ensure the call returns true before this message is 923 * handled. It seems wrong to add a delay, but the alternative is to build a lock 924 * system to handle synchronization, which isn't nice either... */ 925 mSessionStatusHandler.sendMessageDelayed(msg, 20); 926 } else if (mSessionStatusHandler != null) { 927 if (DEBUG) { 928 Log.w(TAG, "mSessionStatusHandler START_LISTENER message already in Queue"); 929 } 930 } 931 } 932 933 private void sendConnectMessage(int masId) { 934 if (mSessionStatusHandler != null) { 935 Message msg = mSessionStatusHandler.obtainMessage(MSG_MAS_CONNECT, masId, 0); 936 /* We add a small delay here to ensure onConnect returns true before this message is 937 * handled. It seems wrong, but the alternative is to store a reference to the 938 * connection in this message, which isn't nice either... */ 939 mSessionStatusHandler.sendMessageDelayed(msg, 20); 940 } // Can only be null during shutdown 941 } 942 943 private void sendConnectTimeoutMessage() { 944 if (DEBUG) { 945 Log.d(TAG, "sendConnectTimeoutMessage()"); 946 } 947 if (mSessionStatusHandler != null) { 948 Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT); 949 msg.sendToTarget(); 950 } // Can only be null during shutdown 951 } 952 953 private void sendConnectCancelMessage() { 954 if (mSessionStatusHandler != null) { 955 Message msg = mSessionStatusHandler.obtainMessage(MSG_MAS_CONNECT_CANCEL); 956 msg.sendToTarget(); 957 } // Can only be null during shutdown 958 } 959 960 private void sendShutdownMessage() { 961 // Pending messages are no longer valid. To speed up things, simply delete them. 962 if (mRemoveTimeoutMsg) { 963 Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION); 964 sendBroadcast(timeoutIntent, BLUETOOTH_PERM); 965 mIsWaitingAuthorization = false; 966 cancelUserTimeoutAlarm(); 967 } 968 if (mSessionStatusHandler == null) { 969 Log.w(TAG, "mSessionStatusHandler is null"); 970 return; 971 } 972 if (mSessionStatusHandler.hasMessages(SHUTDOWN)) { 973 if (DEBUG) { 974 Log.w(TAG, "mSessionStatusHandler shutdown message already in Queue"); 975 } 976 return; 977 } 978 mSessionStatusHandler.removeCallbacksAndMessages(null); 979 // Request release of all resources 980 Message msg = mSessionStatusHandler.obtainMessage(SHUTDOWN); 981 if (!mSessionStatusHandler.sendMessage(msg)) { 982 Log.w(TAG, "mSessionStatusHandler shutdown message could not be sent"); 983 } 984 } 985 986 private MapBroadcastReceiver mMapReceiver = new MapBroadcastReceiver(); 987 988 private class MapBroadcastReceiver extends BroadcastReceiver { 989 @Override 990 public void onReceive(Context context, Intent intent) { 991 if (DEBUG) { 992 Log.d(TAG, "onReceive"); 993 } 994 String action = intent.getAction(); 995 if (DEBUG) { 996 Log.d(TAG, "onReceive: " + action); 997 } 998 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 999 int state = 1000 intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 1001 if (state == BluetoothAdapter.STATE_TURNING_OFF) { 1002 if (DEBUG) { 1003 Log.d(TAG, "STATE_TURNING_OFF"); 1004 } 1005 sendShutdownMessage(); 1006 } else if (state == BluetoothAdapter.STATE_ON) { 1007 if (DEBUG) { 1008 Log.d(TAG, "STATE_ON"); 1009 } 1010 // start ServerSocket listener threads 1011 sendStartListenerMessage(-1); 1012 } 1013 1014 } else if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) { 1015 if (DEBUG) { 1016 Log.d(TAG, "USER_CONFIRM_TIMEOUT ACTION Received."); 1017 } 1018 sendConnectTimeoutMessage(); 1019 } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 1020 1021 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 1022 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 1023 1024 if (DEBUG) { 1025 Log.d(TAG, "Received ACTION_CONNECTION_ACCESS_REPLY:" + requestType 1026 + "isWaitingAuthorization:" + mIsWaitingAuthorization); 1027 } 1028 if ((!mIsWaitingAuthorization) || (requestType 1029 != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS)) { 1030 return; 1031 } 1032 1033 mIsWaitingAuthorization = false; 1034 if (mRemoveTimeoutMsg) { 1035 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 1036 cancelUserTimeoutAlarm(); 1037 setState(BluetoothMap.STATE_DISCONNECTED); 1038 } 1039 1040 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 1041 BluetoothDevice.CONNECTION_ACCESS_NO) 1042 == BluetoothDevice.CONNECTION_ACCESS_YES) { 1043 // Bluetooth connection accepted by user 1044 mPermission = BluetoothDevice.ACCESS_ALLOWED; 1045 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 1046 boolean result = sRemoteDevice.setMessageAccessPermission( 1047 BluetoothDevice.ACCESS_ALLOWED); 1048 if (DEBUG) { 1049 Log.d(TAG, 1050 "setMessageAccessPermission(ACCESS_ALLOWED) result=" + result); 1051 } 1052 } 1053 1054 sRemoteDevice.sdpSearch(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS); 1055 mSdpSearchInitiated = true; 1056 } else { 1057 // Auth. declined by user, serverSession should not be running, but 1058 // call stop anyway to restart listener. 1059 mPermission = BluetoothDevice.ACCESS_REJECTED; 1060 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) { 1061 boolean result = sRemoteDevice.setMessageAccessPermission( 1062 BluetoothDevice.ACCESS_REJECTED); 1063 if (DEBUG) { 1064 Log.d(TAG, 1065 "setMessageAccessPermission(ACCESS_REJECTED) result=" + result); 1066 } 1067 } 1068 sendConnectCancelMessage(); 1069 } 1070 } else if (action.equals(BluetoothDevice.ACTION_SDP_RECORD)) { 1071 if (DEBUG) { 1072 Log.d(TAG, "Received ACTION_SDP_RECORD."); 1073 } 1074 ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID); 1075 if (VERBOSE) { 1076 Log.v(TAG, "Received UUID: " + uuid.toString()); 1077 Log.v(TAG, "expected UUID: " 1078 + BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS.toString()); 1079 } 1080 if (uuid.equals(BluetoothMnsObexClient.BLUETOOTH_UUID_OBEX_MNS)) { 1081 mMnsRecord = intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD); 1082 int status = intent.getIntExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, -1); 1083 if (VERBOSE) { 1084 Log.v(TAG, " -> MNS Record:" + mMnsRecord); 1085 Log.v(TAG, " -> status: " + status); 1086 } 1087 if (mBluetoothMnsObexClient != null && !mSdpSearchInitiated) { 1088 mBluetoothMnsObexClient.setMnsRecord(mMnsRecord); 1089 } 1090 if (status != -1 && mMnsRecord != null) { 1091 for (int i = 0, c = mMasInstances.size(); i < c; i++) { 1092 mMasInstances.valueAt(i) 1093 .setRemoteFeatureMask(mMnsRecord.getSupportedFeatures()); 1094 } 1095 } 1096 if (mSdpSearchInitiated) { 1097 mSdpSearchInitiated = false; // done searching 1098 sendConnectMessage(-1); // -1 indicates all MAS instances 1099 } 1100 } 1101 } else if (action.equals(ACTION_SHOW_MAPS_SETTINGS)) { 1102 if (VERBOSE) { 1103 Log.v(TAG, "Received ACTION_SHOW_MAPS_SETTINGS."); 1104 } 1105 1106 Intent in = new Intent(context, BluetoothMapSettings.class); 1107 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 1108 context.startActivity(in); 1109 } else if (action.equals(BluetoothMapContentObserver.ACTION_MESSAGE_SENT)) { 1110 int result = getResultCode(); 1111 boolean handled = false; 1112 if (mSmsCapable && mMasInstances != null) { 1113 BluetoothMapMasInstance masInst = mMasInstances.get(MAS_ID_SMS_MMS); 1114 if (masInst != null) { 1115 intent.putExtra(BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_RESULT, 1116 result); 1117 handled = masInst.handleSmsSendIntent(context, intent); 1118 } 1119 } 1120 if (!handled) { 1121 // Move the SMS to the correct folder. 1122 BluetoothMapContentObserver.actionMessageSentDisconnected(context, intent, 1123 result); 1124 } 1125 } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) 1126 && mIsWaitingAuthorization) { 1127 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1128 1129 if (sRemoteDevice == null || device == null) { 1130 Log.e(TAG, "Unexpected error!"); 1131 return; 1132 } 1133 1134 if (VERBOSE) { 1135 Log.v(TAG, "ACL disconnected for " + device); 1136 } 1137 1138 if (sRemoteDevice.equals(device)) { 1139 // Send any pending timeout now, since ACL got disconnected 1140 mSessionStatusHandler.removeMessages(USER_TIMEOUT); 1141 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget(); 1142 } 1143 } 1144 } 1145 } 1146 1147 //Binder object: Must be static class or memory leak may occur 1148 1149 /** 1150 * This class implements the IBluetoothMap interface - or actually it validates the 1151 * preconditions for calling the actual functionality in the MapService, and calls it. 1152 */ 1153 private static class BluetoothMapBinder extends IBluetoothMap.Stub 1154 implements IProfileServiceBinder { 1155 private BluetoothMapService mService; 1156 1157 private BluetoothMapService getService() { 1158 if (!Utils.checkCaller()) { 1159 Log.w(TAG, "MAP call not allowed for non-active user"); 1160 return null; 1161 } 1162 1163 if (mService != null && mService.isAvailable()) { 1164 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 1165 "Need BLUETOOTH permission"); 1166 return mService; 1167 } 1168 return null; 1169 } 1170 1171 BluetoothMapBinder(BluetoothMapService service) { 1172 if (VERBOSE) { 1173 Log.v(TAG, "BluetoothMapBinder()"); 1174 } 1175 mService = service; 1176 } 1177 1178 @Override 1179 public synchronized void cleanup() { 1180 mService = null; 1181 } 1182 1183 @Override 1184 public int getState() { 1185 if (VERBOSE) { 1186 Log.v(TAG, "getState()"); 1187 } 1188 BluetoothMapService service = getService(); 1189 if (service == null) { 1190 return BluetoothMap.STATE_DISCONNECTED; 1191 } 1192 return getService().getState(); 1193 } 1194 1195 @Override 1196 public BluetoothDevice getClient() { 1197 if (VERBOSE) { 1198 Log.v(TAG, "getClient()"); 1199 } 1200 BluetoothMapService service = getService(); 1201 if (service == null) { 1202 return null; 1203 } 1204 BluetoothDevice client = BluetoothMapService.getRemoteDevice(); 1205 if (VERBOSE) { 1206 Log.v(TAG, "getClient() - returning " + client); 1207 } 1208 return client; 1209 } 1210 1211 @Override 1212 public boolean isConnected(BluetoothDevice device) { 1213 if (VERBOSE) { 1214 Log.v(TAG, "isConnected()"); 1215 } 1216 BluetoothMapService service = getService(); 1217 return service != null && service.getState() == BluetoothMap.STATE_CONNECTED 1218 && BluetoothMapService.getRemoteDevice().equals(device); 1219 } 1220 1221 @Override 1222 public boolean connect(BluetoothDevice device) { 1223 if (VERBOSE) { 1224 Log.v(TAG, "connect()"); 1225 } 1226 BluetoothMapService service = getService(); 1227 if (service == null) { 1228 return false; 1229 } 1230 return false; 1231 } 1232 1233 @Override 1234 public boolean disconnect(BluetoothDevice device) { 1235 if (VERBOSE) { 1236 Log.v(TAG, "disconnect()"); 1237 } 1238 BluetoothMapService service = getService(); 1239 if (service == null) { 1240 return false; 1241 } 1242 service.disconnect(device); 1243 return true; 1244 } 1245 1246 @Override 1247 public List<BluetoothDevice> getConnectedDevices() { 1248 if (VERBOSE) { 1249 Log.v(TAG, "getConnectedDevices()"); 1250 } 1251 BluetoothMapService service = getService(); 1252 if (service == null) { 1253 return new ArrayList<>(0); 1254 } 1255 return service.getConnectedDevices(); 1256 } 1257 1258 @Override 1259 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1260 if (VERBOSE) { 1261 Log.v(TAG, "getDevicesMatchingConnectionStates()"); 1262 } 1263 BluetoothMapService service = getService(); 1264 if (service == null) { 1265 return new ArrayList<>(0); 1266 } 1267 return service.getDevicesMatchingConnectionStates(states); 1268 } 1269 1270 @Override 1271 public int getConnectionState(BluetoothDevice device) { 1272 if (VERBOSE) { 1273 Log.v(TAG, "getConnectionState()"); 1274 } 1275 BluetoothMapService service = getService(); 1276 if (service == null) { 1277 return BluetoothProfile.STATE_DISCONNECTED; 1278 } 1279 return service.getConnectionState(device); 1280 } 1281 1282 @Override 1283 public boolean setPriority(BluetoothDevice device, int priority) { 1284 BluetoothMapService service = getService(); 1285 if (service == null) { 1286 return false; 1287 } 1288 return service.setPriority(device, priority); 1289 } 1290 1291 @Override 1292 public int getPriority(BluetoothDevice device) { 1293 BluetoothMapService service = getService(); 1294 if (service == null) { 1295 return BluetoothProfile.PRIORITY_UNDEFINED; 1296 } 1297 return service.getPriority(device); 1298 } 1299 } 1300 1301 @Override 1302 public void dump(StringBuilder sb) { 1303 super.dump(sb); 1304 println(sb, "mRemoteDevice: " + sRemoteDevice); 1305 println(sb, "sRemoteDeviceName: " + sRemoteDeviceName); 1306 println(sb, "mState: " + mState); 1307 println(sb, "mAppObserver: " + mAppObserver); 1308 println(sb, "mIsWaitingAuthorization: " + mIsWaitingAuthorization); 1309 println(sb, "mRemoveTimeoutMsg: " + mRemoveTimeoutMsg); 1310 println(sb, "mPermission: " + mPermission); 1311 println(sb, "mAccountChanged: " + mAccountChanged); 1312 println(sb, "mBluetoothMnsObexClient: " + mBluetoothMnsObexClient); 1313 println(sb, "mMasInstanceMap:"); 1314 for (BluetoothMapAccountItem key : mMasInstanceMap.keySet()) { 1315 println(sb, " " + key + " : " + mMasInstanceMap.get(key)); 1316 } 1317 println(sb, "mEnabledAccounts:"); 1318 for (BluetoothMapAccountItem account : mEnabledAccounts) { 1319 println(sb, " " + account); 1320 } 1321 } 1322 } 1323