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