Home | History | Annotate | Download | only in pbap
      1 /*
      2  * Copyright (c) 2008-2009, Motorola, Inc.
      3  *
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are met:
      8  *
      9  * - Redistributions of source code must retain the above copyright notice,
     10  * this list of conditions and the following disclaimer.
     11  *
     12  * - Redistributions in binary form must reproduce the above copyright notice,
     13  * this list of conditions and the following disclaimer in the documentation
     14  * and/or other materials provided with the distribution.
     15  *
     16  * - Neither the name of the Motorola, Inc. nor the names of its contributors
     17  * may be used to endorse or promote products derived from this software
     18  * without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.android.bluetooth.pbap;
     34 
     35 import android.app.Notification;
     36 import android.app.NotificationManager;
     37 import android.app.PendingIntent;
     38 import android.app.Service;
     39 import android.bluetooth.BluetoothAdapter;
     40 import android.bluetooth.BluetoothDevice;
     41 import android.bluetooth.BluetoothPbap;
     42 import android.bluetooth.BluetoothProfile;
     43 import android.bluetooth.BluetoothServerSocket;
     44 import android.bluetooth.BluetoothSocket;
     45 import android.bluetooth.IBluetooth;
     46 import android.bluetooth.IBluetoothPbap;
     47 import android.content.Context;
     48 import android.content.Intent;
     49 import android.os.Handler;
     50 import android.os.IBinder;
     51 import android.os.Message;
     52 import android.os.PowerManager;
     53 import android.os.RemoteException;
     54 import android.os.ServiceManager;
     55 import android.telephony.TelephonyManager;
     56 import android.text.TextUtils;
     57 import android.util.Log;
     58 
     59 import com.android.bluetooth.R;
     60 
     61 import java.io.IOException;
     62 
     63 import javax.obex.ServerSession;
     64 
     65 public class BluetoothPbapService extends Service {
     66     private static final String TAG = "BluetoothPbapService";
     67 
     68     /**
     69      * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
     70      * restart com.android.bluetooth process. only enable DEBUG log:
     71      * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
     72      * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
     73      */
     74 
     75     public static final boolean DEBUG = false;
     76 
     77     public static final boolean VERBOSE = false;
     78 
     79     /**
     80      * Intent indicating incoming obex authentication request which is from
     81      * PCE(Carkit)
     82      */
     83     public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
     84 
     85     /**
     86      * Intent indicating obex session key input complete by user which is sent
     87      * from BluetoothPbapActivity
     88      */
     89     public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
     90 
     91     /**
     92      * Intent indicating user canceled obex authentication session key input
     93      * which is sent from BluetoothPbapActivity
     94      */
     95     public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
     96 
     97     /**
     98      * Intent indicating timeout for user confirmation, which is sent to
     99      * BluetoothPbapActivity
    100      */
    101     public static final String USER_CONFIRM_TIMEOUT_ACTION =
    102             "com.android.bluetooth.pbap.userconfirmtimeout";
    103 
    104     /**
    105      * Intent Extra name indicating session key which is sent from
    106      * BluetoothPbapActivity
    107      */
    108     public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
    109 
    110     public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
    111 
    112     public static final int MSG_SERVERSESSION_CLOSE = 5000;
    113 
    114     public static final int MSG_SESSION_ESTABLISHED = 5001;
    115 
    116     public static final int MSG_SESSION_DISCONNECTED = 5002;
    117 
    118     public static final int MSG_OBEX_AUTH_CHALL = 5003;
    119 
    120     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
    121 
    122     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
    123 
    124     private static final int START_LISTENER = 1;
    125 
    126     private static final int USER_TIMEOUT = 2;
    127 
    128     private static final int AUTH_TIMEOUT = 3;
    129 
    130     private static final int PORT_NUM = 19;
    131 
    132     private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
    133 
    134 
    135     // Ensure not conflict with Opp notification ID
    136     private static final int NOTIFICATION_ID_ACCESS = -1000001;
    137 
    138     private static final int NOTIFICATION_ID_AUTH = -1000002;
    139 
    140     private PowerManager.WakeLock mWakeLock = null;
    141 
    142     private BluetoothAdapter mAdapter;
    143 
    144     private SocketAcceptThread mAcceptThread = null;
    145 
    146     private BluetoothPbapAuthenticator mAuth = null;
    147 
    148     private BluetoothPbapObexServer mPbapServer;
    149 
    150     private ServerSession mServerSession = null;
    151 
    152     private BluetoothServerSocket mServerSocket = null;
    153 
    154     private BluetoothSocket mConnSocket = null;
    155 
    156     private BluetoothDevice mRemoteDevice = null;
    157 
    158     private static String sLocalPhoneNum = null;
    159 
    160     private static String sLocalPhoneName = null;
    161 
    162     private static String sRemoteDeviceName = null;
    163 
    164     private boolean mHasStarted = false;
    165 
    166     private volatile boolean mInterrupted;
    167 
    168     private int mState;
    169 
    170     private int mStartId = -1;
    171 
    172     private IBluetooth mBluetoothService;
    173 
    174     private boolean isWaitingAuthorization = false;
    175 
    176     // package and class name to which we send intent to check phone book access permission
    177     private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
    178     private static final String ACCESS_AUTHORITY_CLASS =
    179         "com.android.settings.bluetooth.BluetoothPermissionRequest";
    180 
    181     public BluetoothPbapService() {
    182         mState = BluetoothPbap.STATE_DISCONNECTED;
    183         IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
    184         if (b == null) {
    185             throw new RuntimeException("Bluetooth service not available");
    186         }
    187         mBluetoothService = IBluetooth.Stub.asInterface(b);
    188     }
    189 
    190     @Override
    191     public void onCreate() {
    192         super.onCreate();
    193         if (VERBOSE) Log.v(TAG, "Pbap Service onCreate");
    194 
    195         mInterrupted = false;
    196         mAdapter = BluetoothAdapter.getDefaultAdapter();
    197 
    198         if (!mHasStarted) {
    199             mHasStarted = true;
    200             if (VERBOSE) Log.v(TAG, "Starting PBAP service");
    201 
    202             int state = mAdapter.getState();
    203             if (state == BluetoothAdapter.STATE_ON) {
    204                 mSessionStatusHandler.sendMessage(mSessionStatusHandler
    205                         .obtainMessage(START_LISTENER));
    206             }
    207         }
    208     }
    209 
    210     @Override
    211     public int onStartCommand(Intent intent, int flags, int startId) {
    212         if (VERBOSE) Log.v(TAG, "Pbap Service onStartCommand");
    213         int retCode = super.onStartCommand(intent, flags, startId);
    214         if (retCode == START_STICKY) {
    215             mStartId = startId;
    216             if (mAdapter == null) {
    217                 Log.w(TAG, "Stopping BluetoothPbapService: "
    218                         + "device does not have BT or device is not ready");
    219                 // Release all resources
    220                 closeService();
    221             } else {
    222                 // No need to handle the null intent case, because we have
    223                 // all restart work done in onCreate()
    224                 if (intent != null) {
    225                     parseIntent(intent);
    226                 }
    227             }
    228         }
    229         return retCode;
    230     }
    231 
    232     // process the intent from receiver
    233     private void parseIntent(final Intent intent) {
    234         String action = intent.getStringExtra("action");
    235         if (VERBOSE) Log.v(TAG, "action: " + action);
    236 
    237         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
    238         boolean removeTimeoutMsg = true;
    239         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    240             if (state == BluetoothAdapter.STATE_OFF) {
    241                 // Send any pending timeout now, as this service will be destroyed.
    242                 if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) {
    243                     Intent timeoutIntent =
    244                         new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    245                     timeoutIntent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    246                     sendBroadcast(timeoutIntent, BLUETOOTH_ADMIN_PERM);
    247                 }
    248                 // Release all resources
    249                 closeService();
    250             } else {
    251                 removeTimeoutMsg = false;
    252             }
    253         } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
    254             if (!isWaitingAuthorization) {
    255                 // this reply is not for us
    256                 return;
    257             }
    258 
    259             isWaitingAuthorization = false;
    260 
    261             if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
    262                                    BluetoothDevice.CONNECTION_ACCESS_NO) ==
    263                 BluetoothDevice.CONNECTION_ACCESS_YES) {
    264 
    265                 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    266                     boolean result = mRemoteDevice.setTrust(true);
    267                     if (VERBOSE) Log.v(TAG, "setTrust() result=" + result);
    268                 }
    269                 try {
    270                     if (mConnSocket != null) {
    271                         startObexServerSession();
    272                     } else {
    273                         stopObexServerSession();
    274                     }
    275                 } catch (IOException ex) {
    276                     Log.e(TAG, "Caught the error: " + ex.toString());
    277                 }
    278             } else {
    279                 stopObexServerSession();
    280             }
    281         } else if (action.equals(AUTH_RESPONSE_ACTION)) {
    282             String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
    283             notifyAuthKeyInput(sessionkey);
    284         } else if (action.equals(AUTH_CANCELLED_ACTION)) {
    285             notifyAuthCancelled();
    286         } else {
    287             removeTimeoutMsg = false;
    288         }
    289 
    290         if (removeTimeoutMsg) {
    291             mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    292         }
    293     }
    294 
    295     @Override
    296     public void onDestroy() {
    297         if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
    298 
    299         super.onDestroy();
    300         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    301         if (mWakeLock != null) {
    302             mWakeLock.release();
    303             mWakeLock = null;
    304         }
    305         closeService();
    306     }
    307 
    308     @Override
    309     public IBinder onBind(Intent intent) {
    310         if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
    311         return mBinder;
    312     }
    313 
    314     private void startRfcommSocketListener() {
    315         if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
    316 
    317         if (mServerSocket == null) {
    318             if (!initSocket()) {
    319                 closeService();
    320                 return;
    321             }
    322         }
    323         if (mAcceptThread == null) {
    324             mAcceptThread = new SocketAcceptThread();
    325             mAcceptThread.setName("BluetoothPbapAcceptThread");
    326             mAcceptThread.start();
    327         }
    328     }
    329 
    330     private final boolean initSocket() {
    331         if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
    332 
    333         boolean initSocketOK = true;
    334         final int CREATE_RETRY_TIME = 10;
    335 
    336         // It's possible that create will fail in some cases. retry for 10 times
    337         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
    338             try {
    339                 // It is mandatory for PSE to support initiation of bonding and
    340                 // encryption.
    341                 mServerSocket = mAdapter.listenUsingEncryptedRfcommOn(PORT_NUM);
    342             } catch (IOException e) {
    343                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
    344                 initSocketOK = false;
    345             }
    346             if (!initSocketOK) {
    347                 synchronized (this) {
    348                     try {
    349                         if (VERBOSE) Log.v(TAG, "wait 3 seconds");
    350                         Thread.sleep(3000);
    351                     } catch (InterruptedException e) {
    352                         Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
    353                         mInterrupted = true;
    354                     }
    355                 }
    356             } else {
    357                 break;
    358             }
    359         }
    360 
    361         if (initSocketOK) {
    362             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket on channel " + PORT_NUM);
    363 
    364         } else {
    365             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
    366         }
    367         return initSocketOK;
    368     }
    369 
    370     private final void closeSocket(boolean server, boolean accept) throws IOException {
    371         if (server == true) {
    372             // Stop the possible trying to init serverSocket
    373             mInterrupted = true;
    374 
    375             if (mServerSocket != null) {
    376                 mServerSocket.close();
    377             }
    378         }
    379 
    380         if (accept == true) {
    381             if (mConnSocket != null) {
    382                 mConnSocket.close();
    383             }
    384         }
    385     }
    386 
    387     private final void closeService() {
    388         if (VERBOSE) Log.v(TAG, "Pbap Service closeService");
    389 
    390         try {
    391             closeSocket(true, true);
    392         } catch (IOException ex) {
    393             Log.e(TAG, "CloseSocket error: " + ex);
    394         }
    395 
    396         if (mAcceptThread != null) {
    397             try {
    398                 mAcceptThread.shutdown();
    399                 mAcceptThread.join();
    400                 mAcceptThread = null;
    401             } catch (InterruptedException ex) {
    402                 Log.w(TAG, "mAcceptThread close error" + ex);
    403             }
    404         }
    405         mServerSocket = null;
    406         mConnSocket = null;
    407 
    408         if (mServerSession != null) {
    409             mServerSession.close();
    410             mServerSession = null;
    411         }
    412 
    413         mHasStarted = false;
    414         if (stopSelfResult(mStartId)) {
    415             if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
    416         }
    417     }
    418 
    419     private final void startObexServerSession() throws IOException {
    420         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
    421 
    422         // acquire the wakeLock before start Obex transaction thread
    423         if (mWakeLock == null) {
    424             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    425             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    426                     "StartingObexPbapTransaction");
    427             mWakeLock.setReferenceCounted(false);
    428             mWakeLock.acquire();
    429         }
    430         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    431         if (tm != null) {
    432             sLocalPhoneNum = tm.getLine1Number();
    433             sLocalPhoneName = tm.getLine1AlphaTag();
    434             if (TextUtils.isEmpty(sLocalPhoneName)) {
    435                 sLocalPhoneName = this.getString(R.string.localPhoneName);
    436             }
    437         }
    438 
    439         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
    440         synchronized (this) {
    441             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
    442             mAuth.setChallenged(false);
    443             mAuth.setCancelled(false);
    444         }
    445         BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket);
    446         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
    447         setState(BluetoothPbap.STATE_CONNECTED);
    448         if (VERBOSE) {
    449             Log.v(TAG, "startObexServerSession() success!");
    450         }
    451     }
    452 
    453     private void stopObexServerSession() {
    454         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
    455 
    456         // Release the wake lock if obex transaction is over
    457         if (mWakeLock != null) {
    458             mWakeLock.release();
    459             mWakeLock = null;
    460         }
    461 
    462         if (mServerSession != null) {
    463             mServerSession.close();
    464             mServerSession = null;
    465         }
    466 
    467         mAcceptThread = null;
    468 
    469         try {
    470             closeSocket(false, true);
    471             mConnSocket = null;
    472         } catch (IOException e) {
    473             Log.e(TAG, "closeSocket error: " + e.toString());
    474         }
    475         // Last obex transaction is finished, we start to listen for incoming
    476         // connection again
    477         if (mAdapter.isEnabled()) {
    478             startRfcommSocketListener();
    479         }
    480         setState(BluetoothPbap.STATE_DISCONNECTED);
    481     }
    482 
    483     private void notifyAuthKeyInput(final String key) {
    484         synchronized (mAuth) {
    485             if (key != null) {
    486                 mAuth.setSessionKey(key);
    487             }
    488             mAuth.setChallenged(true);
    489             mAuth.notify();
    490         }
    491     }
    492 
    493     private void notifyAuthCancelled() {
    494         synchronized (mAuth) {
    495             mAuth.setCancelled(true);
    496             mAuth.notify();
    497         }
    498     }
    499 
    500     /**
    501      * A thread that runs in the background waiting for remote rfcomm
    502      * connect.Once a remote socket connected, this thread shall be
    503      * shutdown.When the remote disconnect,this thread shall run again waiting
    504      * for next request.
    505      */
    506     private class SocketAcceptThread extends Thread {
    507 
    508         private boolean stopped = false;
    509 
    510         @Override
    511         public void run() {
    512             while (!stopped) {
    513                 try {
    514                     mConnSocket = mServerSocket.accept();
    515 
    516                     mRemoteDevice = mConnSocket.getRemoteDevice();
    517                     if (mRemoteDevice == null) {
    518                         Log.i(TAG, "getRemoteDevice() = null");
    519                         break;
    520                     }
    521                     sRemoteDeviceName = mRemoteDevice.getName();
    522                     // In case getRemoteName failed and return null
    523                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
    524                         sRemoteDeviceName = getString(R.string.defaultname);
    525                     }
    526                     boolean trust = mRemoteDevice.getTrustState();
    527                     if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
    528 
    529                     if (trust) {
    530                         try {
    531                             if (VERBOSE) Log.v(TAG, "incomming connection accepted from: "
    532                                 + sRemoteDeviceName + " automatically as trusted device");
    533                             startObexServerSession();
    534                         } catch (IOException ex) {
    535                             Log.e(TAG, "catch exception starting obex server session"
    536                                     + ex.toString());
    537                         }
    538                     } else {
    539                         Intent intent = new
    540                             Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
    541                         intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    542                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    543                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    544                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    545                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
    546                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME,
    547                                         BluetoothPbapReceiver.class.getName());
    548                         sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    549                         isWaitingAuthorization = true;
    550 
    551                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
    552                                 + sRemoteDeviceName);
    553 
    554                         // In case car kit time out and try to use HFP for
    555                         // phonebook
    556                         // access, while UI still there waiting for user to
    557                         // confirm
    558                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    559                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    560                     }
    561                     stopped = true; // job done ,close this thread;
    562                 } catch (IOException ex) {
    563                     if (stopped) {
    564                         break;
    565                     }
    566                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
    567                 }
    568             }
    569         }
    570 
    571         void shutdown() {
    572             stopped = true;
    573             interrupt();
    574         }
    575     }
    576 
    577     private final Handler mSessionStatusHandler = new Handler() {
    578         @Override
    579         public void handleMessage(Message msg) {
    580             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
    581 
    582             switch (msg.what) {
    583                 case START_LISTENER:
    584                     if (mAdapter.isEnabled()) {
    585                         startRfcommSocketListener();
    586                     } else {
    587                         closeService();// release all resources
    588                     }
    589                     break;
    590                 case USER_TIMEOUT:
    591                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    592                     intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    593                     sendBroadcast(intent);
    594                     isWaitingAuthorization = false;
    595                     stopObexServerSession();
    596                     break;
    597                 case AUTH_TIMEOUT:
    598                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    599                     sendBroadcast(i);
    600                     removePbapNotification(NOTIFICATION_ID_AUTH);
    601                     notifyAuthCancelled();
    602                     break;
    603                 case MSG_SERVERSESSION_CLOSE:
    604                     stopObexServerSession();
    605                     break;
    606                 case MSG_SESSION_ESTABLISHED:
    607                     break;
    608                 case MSG_SESSION_DISCONNECTED:
    609                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
    610                     break;
    611                 case MSG_OBEX_AUTH_CHALL:
    612                     createPbapNotification(AUTH_CHALL_ACTION);
    613                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    614                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    615                     break;
    616                 default:
    617                     break;
    618             }
    619         }
    620     };
    621 
    622     private void setState(int state) {
    623         setState(state, BluetoothPbap.RESULT_SUCCESS);
    624     }
    625 
    626     private synchronized void setState(int state, int result) {
    627         if (state != mState) {
    628             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
    629                     + result);
    630             int prevState = mState;
    631             mState = state;
    632             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
    633             intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
    634             intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
    635             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    636             sendBroadcast(intent, BLUETOOTH_PERM);
    637             try {
    638                 mBluetoothService.sendConnectionStateChange(mRemoteDevice, BluetoothProfile.PBAP,
    639                                                             mState, prevState);
    640             } catch (RemoteException e) {
    641                 Log.e(TAG, "RemoteException in sendConnectionStateChange");
    642             }
    643         }
    644     }
    645 
    646     private void createPbapNotification(String action) {
    647 
    648         NotificationManager nm = (NotificationManager)
    649             getSystemService(Context.NOTIFICATION_SERVICE);
    650 
    651         // Create an intent triggered by clicking on the status icon.
    652         Intent clickIntent = new Intent();
    653         clickIntent.setClass(this, BluetoothPbapActivity.class);
    654         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    655         clickIntent.setAction(action);
    656 
    657         // Create an intent triggered by clicking on the
    658         // "Clear All Notifications" button
    659         Intent deleteIntent = new Intent();
    660         deleteIntent.setClass(this, BluetoothPbapReceiver.class);
    661 
    662         Notification notification = null;
    663         String name = getRemoteDeviceName();
    664 
    665         if (action.equals(AUTH_CHALL_ACTION)) {
    666             deleteIntent.setAction(AUTH_CANCELLED_ACTION);
    667             notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
    668                 getString(R.string.auth_notif_ticker), System.currentTimeMillis());
    669             notification.setLatestEventInfo(this, getString(R.string.auth_notif_title),
    670                     getString(R.string.auth_notif_message, name), PendingIntent
    671                             .getActivity(this, 0, clickIntent, 0));
    672 
    673             notification.flags |= Notification.FLAG_AUTO_CANCEL;
    674             notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
    675             notification.defaults = Notification.DEFAULT_SOUND;
    676             notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0);
    677             nm.notify(NOTIFICATION_ID_AUTH, notification);
    678         }
    679     }
    680 
    681     private void removePbapNotification(int id) {
    682         NotificationManager nm = (NotificationManager)
    683             getSystemService(Context.NOTIFICATION_SERVICE);
    684         nm.cancel(id);
    685     }
    686 
    687     public static String getLocalPhoneNum() {
    688         return sLocalPhoneNum;
    689     }
    690 
    691     public static String getLocalPhoneName() {
    692         return sLocalPhoneName;
    693     }
    694 
    695     public static String getRemoteDeviceName() {
    696         return sRemoteDeviceName;
    697     }
    698 
    699     /**
    700      * Handlers for incoming service calls
    701      */
    702     private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
    703         public int getState() {
    704             if (DEBUG) Log.d(TAG, "getState " + mState);
    705 
    706             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    707             return mState;
    708         }
    709 
    710         public BluetoothDevice getClient() {
    711             if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
    712 
    713             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    714             if (mState == BluetoothPbap.STATE_DISCONNECTED) {
    715                 return null;
    716             }
    717             return mRemoteDevice;
    718         }
    719 
    720         public boolean isConnected(BluetoothDevice device) {
    721             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    722             return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
    723         }
    724 
    725         public boolean connect(BluetoothDevice device) {
    726             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    727                     "Need BLUETOOTH_ADMIN permission");
    728             return false;
    729         }
    730 
    731         public void disconnect() {
    732             if (DEBUG) Log.d(TAG, "disconnect");
    733 
    734             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    735                     "Need BLUETOOTH_ADMIN permission");
    736             synchronized (BluetoothPbapService.this) {
    737                 switch (mState) {
    738                     case BluetoothPbap.STATE_CONNECTED:
    739                         if (mServerSession != null) {
    740                             mServerSession.close();
    741                             mServerSession = null;
    742                         }
    743                         try {
    744                             closeSocket(false, true);
    745                             mConnSocket = null;
    746                         } catch (IOException ex) {
    747                             Log.e(TAG, "Caught the error: " + ex);
    748                         }
    749                         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    750                         break;
    751                     default:
    752                         break;
    753                 }
    754             }
    755         }
    756     };
    757 }
    758