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