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