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