• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2013 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 java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Set;
22 
23 import javax.obex.ServerSession;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.Service;
28 import android.bluetooth.BluetoothAdapter;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothProfile;
31 import android.bluetooth.BluetoothServerSocket;
32 import android.bluetooth.IBluetooth;
33 import android.bluetooth.IBluetoothMap;
34 import android.bluetooth.BluetoothUuid;
35 import android.bluetooth.BluetoothMap;
36 import android.bluetooth.BluetoothSocket;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.Message;
42 import android.os.PowerManager;
43 import android.os.ParcelUuid;
44 import android.text.TextUtils;
45 import android.util.Log;
46 import android.provider.Settings;
47 import android.content.IntentFilter;
48 import android.content.BroadcastReceiver;
49 
50 import com.android.bluetooth.R;
51 import com.android.bluetooth.Utils;
52 import com.android.bluetooth.btservice.AdapterService;
53 import com.android.bluetooth.btservice.ProfileService;
54 import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
55 
56 
57 public class BluetoothMapService extends ProfileService {
58     private static final String TAG = "BluetoothMapService";
59 
60     /**
61      * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
62      * restart com.android.bluetooth process. only enable DEBUG log:
63      * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and
64      * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE"
65      */
66 
67     public static final boolean DEBUG = true;
68 
69     public static final boolean VERBOSE = false;
70 
71     /**
72      * Intent indicating incoming obex authentication request which is from
73      * PCE(Carkit)
74      */
75     public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.map.authchall";
76 
77     /**
78      * Intent indicating timeout for user confirmation, which is sent to
79      * BluetoothMapActivity
80      */
81     public static final String USER_CONFIRM_TIMEOUT_ACTION =
82             "com.android.bluetooth.map.userconfirmtimeout";
83 
84     /**
85      * Intent Extra name indicating session key which is sent from
86      * BluetoothMapActivity
87      */
88     public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.map.sessionkey";
89 
90     public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
91 
92     public static final int MSG_SERVERSESSION_CLOSE = 5000;
93 
94     public static final int MSG_SESSION_ESTABLISHED = 5001;
95 
96     public static final int MSG_SESSION_DISCONNECTED = 5002;
97 
98     public static final int MSG_OBEX_AUTH_CHALL = 5003;
99 
100     public static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
101 
102     public static final int MSG_RELEASE_WAKE_LOCK = 5005;
103 
104     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
105 
106     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
107 
108     private static final int START_LISTENER = 1;
109 
110     private static final int USER_TIMEOUT = 2;
111 
112     private static final int DISCONNECT_MAP = 3;
113 
114     private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
115 
116     private PowerManager.WakeLock mWakeLock = null;
117 
118     private BluetoothAdapter mAdapter;
119 
120     private SocketAcceptThread mAcceptThread = null;
121 
122     private BluetoothMapAuthenticator mAuth = null;
123 
124     private BluetoothMapObexServer mMapServer;
125 
126     private ServerSession mServerSession = null;
127 
128     private BluetoothMnsObexClient mBluetoothMnsObexClient = null;
129 
130     private BluetoothServerSocket mServerSocket = null;
131 
132     private BluetoothSocket mConnSocket = null;
133 
134     private BluetoothDevice mRemoteDevice = null;
135 
136     private static String sRemoteDeviceName = null;
137 
138     private volatile boolean mInterrupted;
139 
140     private int mState;
141 
142     private boolean isWaitingAuthorization = false;
143 
144     // package and class name to which we send intent to check message access access permission
145     private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
146     private static final String ACCESS_AUTHORITY_CLASS =
147         "com.android.settings.bluetooth.BluetoothPermissionRequest";
148 
149     private static final ParcelUuid[] MAP_UUIDS = {
150         BluetoothUuid.MAP,
151         BluetoothUuid.MNS,
152     };
153 
BluetoothMapService()154     public BluetoothMapService() {
155         mState = BluetoothMap.STATE_DISCONNECTED;
156     }
157 
startRfcommSocketListener()158     private void startRfcommSocketListener() {
159         if (DEBUG) Log.d(TAG, "Map Service startRfcommSocketListener");
160 
161         if (mAcceptThread == null) {
162             mAcceptThread = new SocketAcceptThread();
163             mAcceptThread.setName("BluetoothMapAcceptThread");
164             mAcceptThread.start();
165         }
166     }
167 
initSocket()168     private final boolean initSocket() {
169         if (DEBUG) Log.d(TAG, "Map Service initSocket");
170 
171         boolean initSocketOK = false;
172         final int CREATE_RETRY_TIME = 10;
173 
174         // It's possible that create will fail in some cases. retry for 10 times
175         for (int i = 0; (i < CREATE_RETRY_TIME) && !mInterrupted; i++) {
176             initSocketOK = true;
177             try {
178                 // It is mandatory for MSE to support initiation of bonding and
179                 // encryption.
180                 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
181                     ("MAP SMS/MMS", BluetoothUuid.MAS.getUuid());
182 
183             } catch (IOException e) {
184                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
185                 initSocketOK = false;
186             }
187             if (!initSocketOK) {
188                 // Need to break out of this loop if BT is being turned off.
189                 if (mAdapter == null) break;
190                 int state = mAdapter.getState();
191                 if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
192                     (state != BluetoothAdapter.STATE_ON)) {
193                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
194                     break;
195                 }
196                 try {
197                     if (VERBOSE) Log.v(TAG, "wait 300 ms");
198                     Thread.sleep(300);
199                 } catch (InterruptedException e) {
200                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
201                 }
202             } else {
203                 break;
204             }
205         }
206         if (mInterrupted) {
207             initSocketOK = false;
208             // close server socket to avoid resource leakage
209             closeServerSocket();
210         }
211 
212         if (initSocketOK) {
213             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
214 
215         } else {
216             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
217         }
218         return initSocketOK;
219     }
220 
closeServerSocket()221     private final synchronized void closeServerSocket() {
222         // exit SocketAcceptThread early
223         if (mServerSocket != null) {
224             try {
225                 // this will cause mServerSocket.accept() return early with IOException
226                 mServerSocket.close();
227                 mServerSocket = null;
228             } catch (IOException ex) {
229                 Log.e(TAG, "Close Server Socket error: " + ex);
230             }
231         }
232     }
closeConnectionSocket()233     private final synchronized void closeConnectionSocket() {
234         if (mConnSocket != null) {
235             try {
236                 mConnSocket.close();
237                 mConnSocket = null;
238             } catch (IOException e) {
239                 Log.e(TAG, "Close Connection Socket error: " + e.toString());
240             }
241         }
242     }
243 
closeService()244     private final void closeService() {
245         if (DEBUG) Log.d(TAG, "MAP Service closeService in");
246 
247         // exit initSocket early
248         mInterrupted = true;
249         closeServerSocket();
250 
251         if (mAcceptThread != null) {
252             try {
253                 mAcceptThread.shutdown();
254                 mAcceptThread.join();
255                 mAcceptThread = null;
256             } catch (InterruptedException ex) {
257                 Log.w(TAG, "mAcceptThread close error" + ex);
258             }
259         }
260 
261         if (mWakeLock != null) {
262             mWakeLock.release();
263             mWakeLock = null;
264         }
265 
266         if (mServerSession != null) {
267             mServerSession.close();
268             mServerSession = null;
269         }
270 
271         if (mBluetoothMnsObexClient != null) {
272             mBluetoothMnsObexClient.shutdown();
273             mBluetoothMnsObexClient = null;
274         }
275 
276         closeConnectionSocket();
277 
278         if (mSessionStatusHandler != null) {
279             mSessionStatusHandler.removeCallbacksAndMessages(null);
280         }
281         isWaitingAuthorization = false;
282 
283         if (VERBOSE) Log.v(TAG, "MAP Service closeService out");
284     }
285 
startObexServerSession()286     private final void startObexServerSession() throws IOException {
287         if (DEBUG) Log.d(TAG, "Map Service startObexServerSession");
288 
289         // acquire the wakeLock before start Obex transaction thread
290         if (mWakeLock == null) {
291             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
292             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
293                     "StartingObexMapTransaction");
294             mWakeLock.setReferenceCounted(false);
295             mWakeLock.acquire();
296         }
297 
298         mBluetoothMnsObexClient = new BluetoothMnsObexClient(this, mRemoteDevice,
299                                                              mSessionStatusHandler);
300         mMapServer = new BluetoothMapObexServer(mSessionStatusHandler, this,
301                                                 mBluetoothMnsObexClient);
302         synchronized (this) {
303             // We need to get authentication now that obex server is up
304             mAuth = new BluetoothMapAuthenticator(mSessionStatusHandler);
305             mAuth.setChallenged(false);
306             mAuth.setCancelled(false);
307         }
308         // setup RFCOMM transport
309         BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(mConnSocket);
310         mServerSession = new ServerSession(transport, mMapServer, mAuth);
311         setState(BluetoothMap.STATE_CONNECTED);
312 
313         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
314         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
315             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
316 
317         if (VERBOSE) {
318             Log.v(TAG, "startObexServerSession() success!");
319         }
320     }
321 
stopObexServerSession()322     private void stopObexServerSession() {
323         if (DEBUG) Log.d(TAG, "MAP Service stopObexServerSession");
324 
325         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
326         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
327 
328         // Release the wake lock if obex transaction is over
329         if (mWakeLock != null) {
330             mWakeLock.release();
331             mWakeLock = null;
332         }
333 
334         if (mServerSession != null) {
335             mServerSession.close();
336             mServerSession = null;
337         }
338 
339         mAcceptThread = null;
340 
341         if(mBluetoothMnsObexClient != null) {
342             mBluetoothMnsObexClient.shutdown();
343             mBluetoothMnsObexClient = null;
344         }
345         closeConnectionSocket();
346 
347         // Last obex transaction is finished, we start to listen for incoming
348         // connection again
349         if (mAdapter.isEnabled()) {
350             startRfcommSocketListener();
351         }
352         setState(BluetoothMap.STATE_DISCONNECTED);
353     }
354 
355 
356 
357     /**
358      * A thread that runs in the background waiting for remote rfcomm
359      * connect.Once a remote socket connected, this thread shall be
360      * shutdown.When the remote disconnect,this thread shall run again waiting
361      * for next request.
362      */
363     private class SocketAcceptThread extends Thread {
364 
365         private boolean stopped = false;
366 
367         @Override
run()368         public void run() {
369             BluetoothServerSocket serverSocket;
370             if (mServerSocket == null) {
371                 if (!initSocket()) {
372                     return;
373                 }
374             }
375 
376             while (!stopped) {
377                 try {
378                     if (DEBUG) Log.d(TAG, "Accepting socket connection...");
379                     serverSocket = mServerSocket;
380                     if(serverSocket == null) {
381                         Log.w(TAG, "mServerSocket is null");
382                         break;
383                     }
384                     mConnSocket = serverSocket.accept();
385                     if (DEBUG) Log.d(TAG, "Accepted socket connection...");
386                     synchronized (BluetoothMapService.this) {
387                         if (mConnSocket == null) {
388                             Log.w(TAG, "mConnSocket is null");
389                             break;
390                         }
391                         mRemoteDevice = mConnSocket.getRemoteDevice();
392                     }
393                     if (mRemoteDevice == null) {
394                         Log.i(TAG, "getRemoteDevice() = null");
395                         break;
396                     }
397 
398                     sRemoteDeviceName = mRemoteDevice.getName();
399                     // In case getRemoteName failed and return null
400                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
401                         sRemoteDeviceName = getString(R.string.defaultname);
402                     }
403                     boolean trust = mRemoteDevice.getTrustState();
404                     if (DEBUG) Log.d(TAG, "GetTrustState() = " + trust);
405 
406 
407                     if (trust) {
408                         try {
409                             if (DEBUG) Log.d(TAG, "incoming connection accepted from: "
410                                 + sRemoteDeviceName + " automatically as trusted device");
411                             startObexServerSession();
412                         } catch (IOException ex) {
413                             Log.e(TAG, "catch exception starting obex server session"
414                                     + ex.toString());
415                         }
416                     } else {
417                         Intent intent = new
418                             Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
419                         intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
420                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
421                                         BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS);
422                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
423 
424                         isWaitingAuthorization = true;
425                         sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
426 
427                         if (DEBUG) Log.d(TAG, "waiting for authorization for connection from: "
428                                 + sRemoteDeviceName);
429 
430                     }
431                     stopped = true; // job done ,close this thread;
432                 } catch (IOException ex) {
433                     stopped=true;
434                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
435                 }
436             }
437         }
438 
shutdown()439         void shutdown() {
440             stopped = true;
441             interrupt();
442         }
443     }
444 
445     private final Handler mSessionStatusHandler = new Handler() {
446         @Override
447         public void handleMessage(Message msg) {
448             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
449 
450             switch (msg.what) {
451                 case START_LISTENER:
452                     if (mAdapter.isEnabled()) {
453                         startRfcommSocketListener();
454                     }
455                     break;
456                 case USER_TIMEOUT:
457                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
458                     intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
459                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
460                                     BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS);
461                     sendBroadcast(intent);
462                     isWaitingAuthorization = false;
463                     stopObexServerSession();
464                     break;
465                 case MSG_SERVERSESSION_CLOSE:
466                     stopObexServerSession();
467                     break;
468                 case MSG_SESSION_ESTABLISHED:
469                     break;
470                 case MSG_SESSION_DISCONNECTED:
471                     // handled elsewhere
472                     break;
473                 case DISCONNECT_MAP:
474                     disconnectMap((BluetoothDevice)msg.obj);
475                     break;
476                 case MSG_ACQUIRE_WAKE_LOCK:
477                     if (mWakeLock == null) {
478                         PowerManager pm = (PowerManager)getSystemService(
479                                           Context.POWER_SERVICE);
480                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
481                                     "StartingObexMapTransaction");
482                         mWakeLock.setReferenceCounted(false);
483                         mWakeLock.acquire();
484                         Log.w(TAG, "Acquire Wake Lock");
485                     }
486                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
487                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
488                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
489                     break;
490                 case MSG_RELEASE_WAKE_LOCK:
491                     if (mWakeLock != null) {
492                         mWakeLock.release();
493                         mWakeLock = null;
494                         Log.w(TAG, "Release Wake Lock");
495                     }
496                     break;
497                 default:
498                     break;
499             }
500         }
501     };
502 
503 
getState()504    public int getState() {
505         return mState;
506     }
507 
getRemoteDevice()508     public BluetoothDevice getRemoteDevice() {
509         return mRemoteDevice;
510     }
setState(int state)511     private void setState(int state) {
512         setState(state, BluetoothMap.RESULT_SUCCESS);
513     }
514 
setState(int state, int result)515     private synchronized void setState(int state, int result) {
516         if (state != mState) {
517             if (DEBUG) Log.d(TAG, "Map state " + mState + " -> " + state + ", result = "
518                     + result);
519             int prevState = mState;
520             mState = state;
521             Intent intent = new Intent(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
522             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
523             intent.putExtra(BluetoothProfile.EXTRA_STATE, mState);
524             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
525             sendBroadcast(intent, BLUETOOTH_PERM);
526             AdapterService s = AdapterService.getAdapterService();
527             if (s != null) {
528                 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.MAP,
529                         mState, prevState);
530             }
531         }
532     }
533 
getRemoteDeviceName()534     public static String getRemoteDeviceName() {
535         return sRemoteDeviceName;
536     }
537 
disconnect(BluetoothDevice device)538     public boolean disconnect(BluetoothDevice device) {
539         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(DISCONNECT_MAP, 0, 0, device));
540         return true;
541     }
542 
disconnectMap(BluetoothDevice device)543     public boolean disconnectMap(BluetoothDevice device) {
544         boolean result = false;
545         if (DEBUG) Log.d(TAG, "disconnectMap");
546         if (getRemoteDevice().equals(device)) {
547             switch (mState) {
548                 case BluetoothMap.STATE_CONNECTED:
549                     if (mServerSession != null) {
550                         mServerSession.close();
551                         mServerSession = null;
552                     }
553                     if(mBluetoothMnsObexClient != null) {
554                         mBluetoothMnsObexClient.shutdown();
555                         mBluetoothMnsObexClient = null;
556                     }
557                     closeConnectionSocket();
558 
559                     setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED);
560                     result = true;
561                     break;
562                 default:
563                     break;
564                 }
565         }
566         return result;
567     }
568 
getConnectedDevices()569     public List<BluetoothDevice> getConnectedDevices() {
570         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
571         synchronized(this) {
572             if (mState == BluetoothMap.STATE_CONNECTED && mRemoteDevice != null) {
573                 devices.add(mRemoteDevice);
574             }
575         }
576         return devices;
577     }
578 
getDevicesMatchingConnectionStates(int[] states)579     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
580         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
581         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
582         int connectionState;
583         synchronized (this) {
584             for (BluetoothDevice device : bondedDevices) {
585                 ParcelUuid[] featureUuids = device.getUuids();
586                 if (!BluetoothUuid.containsAnyUuid(featureUuids, MAP_UUIDS)) {
587                     continue;
588                 }
589                 connectionState = getConnectionState(device);
590                 for(int i = 0; i < states.length; i++) {
591                     if (connectionState == states[i]) {
592                         deviceList.add(device);
593                     }
594                 }
595             }
596         }
597         return deviceList;
598     }
599 
getConnectionState(BluetoothDevice device)600     public int getConnectionState(BluetoothDevice device) {
601         synchronized(this) {
602             if (getState() == BluetoothMap.STATE_CONNECTED && getRemoteDevice().equals(device)) {
603                 return BluetoothProfile.STATE_CONNECTED;
604             } else {
605                 return BluetoothProfile.STATE_DISCONNECTED;
606             }
607         }
608     }
609 
setPriority(BluetoothDevice device, int priority)610     public boolean setPriority(BluetoothDevice device, int priority) {
611         Settings.Global.putInt(getContentResolver(),
612             Settings.Global.getBluetoothMapPriorityKey(device.getAddress()),
613             priority);
614         if (DEBUG) Log.d(TAG, "Saved priority " + device + " = " + priority);
615         return true;
616     }
617 
getPriority(BluetoothDevice device)618     public int getPriority(BluetoothDevice device) {
619         int priority = Settings.Global.getInt(getContentResolver(),
620             Settings.Global.getBluetoothMapPriorityKey(device.getAddress()),
621             BluetoothProfile.PRIORITY_UNDEFINED);
622         return priority;
623     }
624 
625     @Override
initBinder()626     protected IProfileServiceBinder initBinder() {
627         return new BluetoothMapBinder(this);
628     }
629 
630     @Override
start()631     protected boolean start() {
632         if (DEBUG) Log.d(TAG, "start()");
633         IntentFilter filter = new IntentFilter();
634         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
635         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
636         try {
637             registerReceiver(mMapReceiver, filter);
638         } catch (Exception e) {
639             Log.w(TAG,"Unable to register map receiver",e);
640         }
641         mInterrupted = false;
642         mAdapter = BluetoothAdapter.getDefaultAdapter();
643         // start RFCOMM listener
644         mSessionStatusHandler.sendMessage(mSessionStatusHandler
645                 .obtainMessage(START_LISTENER));
646         return true;
647     }
648 
649     @Override
stop()650     protected boolean stop() {
651         if (DEBUG) Log.d(TAG, "stop()");
652         try {
653             unregisterReceiver(mMapReceiver);
654         } catch (Exception e) {
655             Log.w(TAG,"Unable to unregister map receiver",e);
656         }
657 
658         setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED);
659         closeService();
660         return true;
661     }
662 
cleanup()663     public boolean cleanup()  {
664         if (DEBUG) Log.d(TAG, "cleanup()");
665         setState(BluetoothMap.STATE_DISCONNECTED, BluetoothMap.RESULT_CANCELED);
666         closeService();
667         return true;
668     }
669 
670     private MapBroadcastReceiver mMapReceiver = new MapBroadcastReceiver();
671 
672     private class MapBroadcastReceiver extends BroadcastReceiver {
673         @Override
onReceive(Context context, Intent intent)674         public void onReceive(Context context, Intent intent) {
675             if (DEBUG) Log.d(TAG, "onReceive");
676             String action = intent.getAction();
677             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
678                 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
679                                                BluetoothAdapter.ERROR);
680                 if (state == BluetoothAdapter.STATE_TURNING_OFF) {
681                     if (DEBUG) Log.d(TAG, "STATE_TURNING_OFF");
682                     // Release all resources
683                     closeService();
684                 } else if (state == BluetoothAdapter.STATE_ON) {
685                     if (DEBUG) Log.d(TAG, "STATE_ON");
686                     mInterrupted = false;
687                     // start RFCOMM listener
688                     mSessionStatusHandler.sendMessage(mSessionStatusHandler
689                                   .obtainMessage(START_LISTENER));
690                 }
691             } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
692                 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
693                                                BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
694                 if (DEBUG) Log.d(TAG, "Received ACTION_CONNECTION_ACCESS_REPLY:" +
695                            requestType + ":" + isWaitingAuthorization);
696                 if ((!isWaitingAuthorization) ||
697                     (requestType != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS)) {
698                     // this reply is not for us
699                     return;
700                 }
701 
702                 isWaitingAuthorization = false;
703 
704                 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
705                                        BluetoothDevice.CONNECTION_ACCESS_NO) ==
706                     BluetoothDevice.CONNECTION_ACCESS_YES) {
707                     //bluetooth connection accepted by user
708                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
709                         boolean result = mRemoteDevice.setTrust(true);
710                         if (DEBUG) Log.d(TAG, "setTrust() result=" + result);
711                     }
712                     try {
713                         if (mConnSocket != null) {
714                             // start obex server and rfcomm connection
715                             startObexServerSession();
716                         } else {
717                             stopObexServerSession();
718                         }
719                     } catch (IOException ex) {
720                         Log.e(TAG, "Caught the error: " + ex.toString());
721                     }
722                 } else {
723                     stopObexServerSession();
724                 }
725             }
726         }
727     };
728 
729     //Binder object: Must be static class or memory leak may occur
730     /**
731      * This class implements the IBluetoothMap interface - or actually it validates the
732      * preconditions for calling the actual functionality in the MapService, and calls it.
733      */
734     private static class BluetoothMapBinder extends IBluetoothMap.Stub
735         implements IProfileServiceBinder {
736         private BluetoothMapService mService;
737 
getService()738         private BluetoothMapService getService() {
739             if (!Utils.checkCaller()) {
740                 Log.w(TAG,"MAP call not allowed for non-active user");
741                 return null;
742             }
743 
744             if (mService != null && mService.isAvailable()) {
745                 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
746                 return mService;
747             }
748             return null;
749         }
750 
BluetoothMapBinder(BluetoothMapService service)751         BluetoothMapBinder(BluetoothMapService service) {
752             if (VERBOSE) Log.v(TAG, "BluetoothMapBinder()");
753             mService = service;
754         }
755 
cleanup()756         public boolean cleanup()  {
757             mService = null;
758             return true;
759         }
760 
getState()761         public int getState() {
762             if (VERBOSE) Log.v(TAG, "getState()");
763             BluetoothMapService service = getService();
764             if (service == null) return BluetoothMap.STATE_DISCONNECTED;
765             return getService().getState();
766         }
767 
getClient()768         public BluetoothDevice getClient() {
769             if (VERBOSE) Log.v(TAG, "getClient()");
770             BluetoothMapService service = getService();
771             if (service == null) return null;
772             Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
773             return service.getRemoteDevice();
774         }
775 
isConnected(BluetoothDevice device)776         public boolean isConnected(BluetoothDevice device) {
777             if (VERBOSE) Log.v(TAG, "isConnected()");
778             BluetoothMapService service = getService();
779             if (service == null) return false;
780             return service.getState() == BluetoothMap.STATE_CONNECTED && service.getRemoteDevice().equals(device);
781         }
782 
connect(BluetoothDevice device)783         public boolean connect(BluetoothDevice device) {
784             if (VERBOSE) Log.v(TAG, "connect()");
785             BluetoothMapService service = getService();
786             if (service == null) return false;
787             return false;
788         }
789 
disconnect(BluetoothDevice device)790         public boolean disconnect(BluetoothDevice device) {
791             if (VERBOSE) Log.v(TAG, "disconnect()");
792             BluetoothMapService service = getService();
793             if (service == null) return false;
794             return service.disconnect(device);
795         }
796 
getConnectedDevices()797         public List<BluetoothDevice> getConnectedDevices() {
798             if (VERBOSE) Log.v(TAG, "getConnectedDevices()");
799             BluetoothMapService service = getService();
800             if (service == null) return new ArrayList<BluetoothDevice>(0);
801             return service.getConnectedDevices();
802         }
803 
getDevicesMatchingConnectionStates(int[] states)804         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
805             if (VERBOSE) Log.v(TAG, "getDevicesMatchingConnectionStates()");
806             BluetoothMapService service = getService();
807             if (service == null) return new ArrayList<BluetoothDevice>(0);
808             return service.getDevicesMatchingConnectionStates(states);
809         }
810 
getConnectionState(BluetoothDevice device)811         public int getConnectionState(BluetoothDevice device) {
812             if (VERBOSE) Log.v(TAG, "getConnectionState()");
813             BluetoothMapService service = getService();
814             if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
815             return service.getConnectionState(device);
816         }
817 
setPriority(BluetoothDevice device, int priority)818         public boolean setPriority(BluetoothDevice device, int priority) {
819             BluetoothMapService service = getService();
820             if (service == null) return false;
821             return service.setPriority(device, priority);
822         }
823 
getPriority(BluetoothDevice device)824         public int getPriority(BluetoothDevice device) {
825             BluetoothMapService service = getService();
826             if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
827             return service.getPriority(device);
828         }
829     };
830 }
831