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