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