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.bluetooth.BluetoothUuid;
     48 import android.content.Context;
     49 import android.content.Intent;
     50 import android.os.Handler;
     51 import android.os.IBinder;
     52 import android.os.Message;
     53 import android.os.PowerManager;
     54 import android.os.RemoteException;
     55 import android.os.ServiceManager;
     56 import android.telephony.TelephonyManager;
     57 import android.text.TextUtils;
     58 import android.util.Log;
     59 import com.android.bluetooth.Utils;
     60 
     61 
     62 import com.android.bluetooth.R;
     63 import com.android.bluetooth.btservice.AdapterService;
     64 
     65 import java.io.IOException;
     66 
     67 import javax.obex.ServerSession;
     68 
     69 public class BluetoothPbapService extends Service {
     70     private static final String TAG = "BluetoothPbapService";
     71 
     72     /**
     73      * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
     74      * restart com.android.bluetooth process. only enable DEBUG log:
     75      * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
     76      * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
     77      */
     78 
     79     public static final boolean DEBUG = true;
     80 
     81     public static final boolean VERBOSE = false;
     82 
     83     /**
     84      * Intent indicating incoming obex authentication request which is from
     85      * PCE(Carkit)
     86      */
     87     public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
     88 
     89     /**
     90      * Intent indicating obex session key input complete by user which is sent
     91      * from BluetoothPbapActivity
     92      */
     93     public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
     94 
     95     /**
     96      * Intent indicating user canceled obex authentication session key input
     97      * which is sent from BluetoothPbapActivity
     98      */
     99     public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
    100 
    101     /**
    102      * Intent indicating timeout for user confirmation, which is sent to
    103      * BluetoothPbapActivity
    104      */
    105     public static final String USER_CONFIRM_TIMEOUT_ACTION =
    106             "com.android.bluetooth.pbap.userconfirmtimeout";
    107 
    108     /**
    109      * Intent Extra name indicating session key which is sent from
    110      * BluetoothPbapActivity
    111      */
    112     public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
    113 
    114     public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
    115 
    116     public static final int MSG_SERVERSESSION_CLOSE = 5000;
    117 
    118     public static final int MSG_SESSION_ESTABLISHED = 5001;
    119 
    120     public static final int MSG_SESSION_DISCONNECTED = 5002;
    121 
    122     public static final int MSG_OBEX_AUTH_CHALL = 5003;
    123 
    124     public static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
    125 
    126     public static final int MSG_RELEASE_WAKE_LOCK = 5005;
    127 
    128     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
    129 
    130     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
    131 
    132     private static final int START_LISTENER = 1;
    133 
    134     private static final int USER_TIMEOUT = 2;
    135 
    136     private static final int AUTH_TIMEOUT = 3;
    137 
    138 
    139     private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
    140 
    141     private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
    142 
    143     // Ensure not conflict with Opp notification ID
    144     private static final int NOTIFICATION_ID_ACCESS = -1000001;
    145 
    146     private static final int NOTIFICATION_ID_AUTH = -1000002;
    147 
    148     private PowerManager.WakeLock mWakeLock = null;
    149 
    150     private BluetoothAdapter mAdapter;
    151 
    152     private SocketAcceptThread mAcceptThread = null;
    153 
    154     private BluetoothPbapAuthenticator mAuth = null;
    155 
    156     private BluetoothPbapObexServer mPbapServer;
    157 
    158     private ServerSession mServerSession = null;
    159 
    160     private BluetoothServerSocket mServerSocket = null;
    161 
    162     private BluetoothSocket mConnSocket = null;
    163 
    164     private BluetoothDevice mRemoteDevice = null;
    165 
    166     private static String sLocalPhoneNum = null;
    167 
    168     private static String sLocalPhoneName = null;
    169 
    170     private static String sRemoteDeviceName = null;
    171 
    172     private boolean mHasStarted = false;
    173 
    174     private volatile boolean mInterrupted;
    175 
    176     private int mState;
    177 
    178     private int mStartId = -1;
    179 
    180     //private IBluetooth mBluetoothService;
    181 
    182     private boolean isWaitingAuthorization = false;
    183 
    184     // package and class name to which we send intent to check phone book access permission
    185     private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
    186     private static final String ACCESS_AUTHORITY_CLASS =
    187         "com.android.settings.bluetooth.BluetoothPermissionRequest";
    188 
    189     public BluetoothPbapService() {
    190         mState = BluetoothPbap.STATE_DISCONNECTED;
    191     }
    192 
    193     @Override
    194     public void onCreate() {
    195         super.onCreate();
    196         if (VERBOSE) Log.v(TAG, "Pbap Service onCreate");
    197 
    198         mInterrupted = false;
    199         mAdapter = BluetoothAdapter.getDefaultAdapter();
    200 
    201         if (!mHasStarted) {
    202             mHasStarted = true;
    203             if (VERBOSE) Log.v(TAG, "Starting PBAP service");
    204             BluetoothPbapConfig.init(this);
    205             int state = mAdapter.getState();
    206             if (state == BluetoothAdapter.STATE_ON) {
    207                 mSessionStatusHandler.sendMessage(mSessionStatusHandler
    208                         .obtainMessage(START_LISTENER));
    209             }
    210         }
    211     }
    212 
    213     @Override
    214     public int onStartCommand(Intent intent, int flags, int startId) {
    215         //int retCode = super.onStartCommand(intent, flags, startId);
    216         //if (retCode == START_STICKY) {
    217             mStartId = startId;
    218             if (mAdapter == null) {
    219                 Log.w(TAG, "Stopping BluetoothPbapService: "
    220                         + "device does not have BT or device is not ready");
    221                 // Release all resources
    222                 closeService();
    223             } else {
    224                 // No need to handle the null intent case, because we have
    225                 // all restart work done in onCreate()
    226                 if (intent != null) {
    227                     parseIntent(intent);
    228                 }
    229             }
    230         //}
    231         return START_NOT_STICKY;
    232     }
    233 
    234     // process the intent from receiver
    235     private void parseIntent(final Intent intent) {
    236         String action = intent.getStringExtra("action");
    237         if (VERBOSE) Log.v(TAG, "action: " + action);
    238 
    239         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
    240         if (VERBOSE) Log.v(TAG, "state: " + state);
    241 
    242         boolean removeTimeoutMsg = true;
    243         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    244             if (state == BluetoothAdapter.STATE_TURNING_OFF) {
    245                 // Send any pending timeout now, as this service will be destroyed.
    246                 if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) {
    247                     Intent timeoutIntent =
    248                         new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    249                     timeoutIntent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    250                     timeoutIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    251                                      BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    252                     sendBroadcast(timeoutIntent, BLUETOOTH_ADMIN_PERM);
    253                 }
    254                 // Release all resources
    255                 closeService();
    256             } else {
    257                 removeTimeoutMsg = false;
    258             }
    259         } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
    260             int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    261                                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    262 
    263             if ((!isWaitingAuthorization) ||
    264                 (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
    265                 // this reply is not for us
    266                 return;
    267             }
    268 
    269             isWaitingAuthorization = false;
    270 
    271             if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
    272                                    BluetoothDevice.CONNECTION_ACCESS_NO) ==
    273                 BluetoothDevice.CONNECTION_ACCESS_YES) {
    274 
    275                 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    276                     boolean result = mRemoteDevice.setTrust(true);
    277                     if (VERBOSE) Log.v(TAG, "setTrust() result=" + result);
    278                 }
    279                 try {
    280                     if (mConnSocket != null) {
    281                         startObexServerSession();
    282                     } else {
    283                         stopObexServerSession();
    284                     }
    285                 } catch (IOException ex) {
    286                     Log.e(TAG, "Caught the error: " + ex.toString());
    287                 }
    288             } else {
    289                 stopObexServerSession();
    290             }
    291         } else if (action.equals(AUTH_RESPONSE_ACTION)) {
    292             String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
    293             notifyAuthKeyInput(sessionkey);
    294         } else if (action.equals(AUTH_CANCELLED_ACTION)) {
    295             notifyAuthCancelled();
    296         } else {
    297             removeTimeoutMsg = false;
    298         }
    299 
    300         if (removeTimeoutMsg) {
    301             mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    302         }
    303     }
    304 
    305     @Override
    306     public void onDestroy() {
    307         if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
    308 
    309         super.onDestroy();
    310         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    311         closeService();
    312         if(mSessionStatusHandler != null) {
    313             mSessionStatusHandler.removeCallbacksAndMessages(null);
    314         }
    315     }
    316 
    317     @Override
    318     public IBinder onBind(Intent intent) {
    319         if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
    320         return mBinder;
    321     }
    322 
    323     private void startRfcommSocketListener() {
    324         if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
    325 
    326         if (mAcceptThread == null) {
    327             mAcceptThread = new SocketAcceptThread();
    328             mAcceptThread.setName("BluetoothPbapAcceptThread");
    329             mAcceptThread.start();
    330         }
    331     }
    332 
    333     private final boolean initSocket() {
    334         if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
    335 
    336         boolean initSocketOK = false;
    337         final int CREATE_RETRY_TIME = 10;
    338 
    339         // It's possible that create will fail in some cases. retry for 10 times
    340         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
    341             initSocketOK = true;
    342             try {
    343                 // It is mandatory for PSE to support initiation of bonding and
    344                 // encryption.
    345                 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
    346                     ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid());
    347 
    348             } catch (IOException e) {
    349                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
    350                 initSocketOK = false;
    351             }
    352             if (!initSocketOK) {
    353                 // Need to break out of this loop if BT is being turned off.
    354                 if (mAdapter == null) break;
    355                 int state = mAdapter.getState();
    356                 if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
    357                     (state != BluetoothAdapter.STATE_ON)) {
    358                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
    359                     break;
    360                 }
    361                 try {
    362                     if (VERBOSE) Log.v(TAG, "wait 300 ms");
    363                     Thread.sleep(300);
    364                 } catch (InterruptedException e) {
    365                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
    366                     break;
    367                 }
    368             } else {
    369                 break;
    370             }
    371         }
    372 
    373         if (mInterrupted) {
    374             initSocketOK = false;
    375             // close server socket to avoid resource leakage
    376             closeServerSocket();
    377         }
    378 
    379         if (initSocketOK) {
    380             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
    381 
    382         } else {
    383             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
    384         }
    385         return initSocketOK;
    386     }
    387 
    388     private final synchronized void closeServerSocket() {
    389         // exit SocketAcceptThread early
    390         if (mServerSocket != null) {
    391             try {
    392                 // this will cause mServerSocket.accept() return early with IOException
    393                 mServerSocket.close();
    394                 mServerSocket = null;
    395             } catch (IOException ex) {
    396                 Log.e(TAG, "Close Server Socket error: " + ex);
    397             }
    398         }
    399     }
    400 
    401     private final synchronized void closeConnectionSocket() {
    402         if (mConnSocket != null) {
    403             try {
    404                 mConnSocket.close();
    405                 mConnSocket = null;
    406             } catch (IOException e) {
    407                 Log.e(TAG, "Close Connection Socket error: " + e.toString());
    408             }
    409         }
    410     }
    411 
    412     private final void closeService() {
    413         if (VERBOSE) Log.v(TAG, "Pbap Service closeService in");
    414 
    415         // exit initSocket early
    416         mInterrupted = true;
    417         closeServerSocket();
    418 
    419         if (mAcceptThread != null) {
    420             try {
    421                 mAcceptThread.shutdown();
    422                 mAcceptThread.join();
    423                 mAcceptThread = null;
    424             } catch (InterruptedException ex) {
    425                 Log.w(TAG, "mAcceptThread close error" + ex);
    426             }
    427         }
    428 
    429         if (mWakeLock != null) {
    430             mWakeLock.release();
    431             mWakeLock = null;
    432         }
    433 
    434         if (mServerSession != null) {
    435             mServerSession.close();
    436             mServerSession = null;
    437         }
    438 
    439         closeConnectionSocket();
    440 
    441         mHasStarted = false;
    442         if (mStartId != -1 && stopSelfResult(mStartId)) {
    443             if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
    444             mStartId = -1;
    445         }
    446         if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
    447     }
    448 
    449     private final void startObexServerSession() throws IOException {
    450         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
    451 
    452         // acquire the wakeLock before start Obex transaction thread
    453         if (mWakeLock == null) {
    454             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    455             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    456                     "StartingObexPbapTransaction");
    457             mWakeLock.setReferenceCounted(false);
    458             mWakeLock.acquire();
    459         }
    460         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    461         if (tm != null) {
    462             sLocalPhoneNum = tm.getLine1Number();
    463             sLocalPhoneName = tm.getLine1AlphaTag();
    464             if (TextUtils.isEmpty(sLocalPhoneName)) {
    465                 sLocalPhoneName = this.getString(R.string.localPhoneName);
    466             }
    467         }
    468 
    469         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
    470         synchronized (this) {
    471             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
    472             mAuth.setChallenged(false);
    473             mAuth.setCancelled(false);
    474         }
    475         BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket);
    476         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
    477         setState(BluetoothPbap.STATE_CONNECTED);
    478 
    479         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    480         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    481             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    482 
    483         if (VERBOSE) {
    484             Log.v(TAG, "startObexServerSession() success!");
    485         }
    486     }
    487 
    488     private void stopObexServerSession() {
    489         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
    490 
    491         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
    492         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    493         // Release the wake lock if obex transaction is over
    494         if (mWakeLock != null) {
    495             mWakeLock.release();
    496             mWakeLock = null;
    497         }
    498 
    499         if (mServerSession != null) {
    500             mServerSession.close();
    501             mServerSession = null;
    502         }
    503 
    504         mAcceptThread = null;
    505 
    506         closeConnectionSocket();
    507 
    508         // Last obex transaction is finished, we start to listen for incoming
    509         // connection again
    510         if (mAdapter.isEnabled()) {
    511             startRfcommSocketListener();
    512         }
    513         setState(BluetoothPbap.STATE_DISCONNECTED);
    514     }
    515 
    516     private void notifyAuthKeyInput(final String key) {
    517         synchronized (mAuth) {
    518             if (key != null) {
    519                 mAuth.setSessionKey(key);
    520             }
    521             mAuth.setChallenged(true);
    522             mAuth.notify();
    523         }
    524     }
    525 
    526     private void notifyAuthCancelled() {
    527         synchronized (mAuth) {
    528             mAuth.setCancelled(true);
    529             mAuth.notify();
    530         }
    531     }
    532 
    533     /**
    534      * A thread that runs in the background waiting for remote rfcomm
    535      * connect.Once a remote socket connected, this thread shall be
    536      * shutdown.When the remote disconnect,this thread shall run again waiting
    537      * for next request.
    538      */
    539     private class SocketAcceptThread extends Thread {
    540 
    541         private boolean stopped = false;
    542 
    543         @Override
    544         public void run() {
    545             BluetoothServerSocket serverSocket;
    546             if (mServerSocket == null) {
    547                 if (!initSocket()) {
    548                     return;
    549                 }
    550             }
    551 
    552             while (!stopped) {
    553                 try {
    554                     if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
    555                     serverSocket = mServerSocket;
    556                     if (serverSocket == null) {
    557                         Log.w(TAG, "mServerSocket is null");
    558                         break;
    559                     }
    560                     mConnSocket = serverSocket.accept();
    561                     if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
    562 
    563                     synchronized (BluetoothPbapService.this) {
    564                         if (mConnSocket == null) {
    565                             Log.w(TAG, "mConnSocket is null");
    566                             break;
    567                         }
    568                         mRemoteDevice = mConnSocket.getRemoteDevice();
    569                     }
    570                     if (mRemoteDevice == null) {
    571                         Log.i(TAG, "getRemoteDevice() = null");
    572                         break;
    573                     }
    574                     sRemoteDeviceName = mRemoteDevice.getName();
    575                     // In case getRemoteName failed and return null
    576                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
    577                         sRemoteDeviceName = getString(R.string.defaultname);
    578                     }
    579                     boolean trust = mRemoteDevice.getTrustState();
    580                     if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
    581 
    582                     if (trust) {
    583                         try {
    584                             if (VERBOSE) Log.v(TAG, "incoming connection accepted from: "
    585                                 + sRemoteDeviceName + " automatically as trusted device");
    586                             startObexServerSession();
    587                         } catch (IOException ex) {
    588                             Log.e(TAG, "catch exception starting obex server session"
    589                                     + ex.toString());
    590                         }
    591                     } else {
    592                         Intent intent = new
    593                             Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
    594                         intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    595                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    596                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    597                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    598                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
    599                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME,
    600                                         BluetoothPbapReceiver.class.getName());
    601 
    602                         isWaitingAuthorization = true;
    603                         sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    604 
    605                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
    606                                 + sRemoteDeviceName);
    607 
    608                         // In case car kit time out and try to use HFP for
    609                         // phonebook
    610                         // access, while UI still there waiting for user to
    611                         // confirm
    612                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    613                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    614                     }
    615                     stopped = true; // job done ,close this thread;
    616                 } catch (IOException ex) {
    617                     stopped=true;
    618                     /*
    619                     if (stopped) {
    620                         break;
    621                     }
    622                     */
    623                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
    624                 }
    625             }
    626         }
    627 
    628         void shutdown() {
    629             stopped = true;
    630             interrupt();
    631         }
    632     }
    633 
    634     private final Handler mSessionStatusHandler = new Handler() {
    635         @Override
    636         public void handleMessage(Message msg) {
    637             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
    638 
    639             switch (msg.what) {
    640                 case START_LISTENER:
    641                     if (mAdapter.isEnabled()) {
    642                         startRfcommSocketListener();
    643                     } else {
    644                         closeService();// release all resources
    645                     }
    646                     break;
    647                 case USER_TIMEOUT:
    648                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    649                     intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    650                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    651                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    652                     sendBroadcast(intent);
    653                     isWaitingAuthorization = false;
    654                     stopObexServerSession();
    655                     break;
    656                 case AUTH_TIMEOUT:
    657                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    658                     sendBroadcast(i);
    659                     removePbapNotification(NOTIFICATION_ID_AUTH);
    660                     notifyAuthCancelled();
    661                     break;
    662                 case MSG_SERVERSESSION_CLOSE:
    663                     stopObexServerSession();
    664                     break;
    665                 case MSG_SESSION_ESTABLISHED:
    666                     break;
    667                 case MSG_SESSION_DISCONNECTED:
    668                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
    669                     break;
    670                 case MSG_OBEX_AUTH_CHALL:
    671                     createPbapNotification(AUTH_CHALL_ACTION);
    672                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    673                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    674                     break;
    675                 case MSG_ACQUIRE_WAKE_LOCK:
    676                     if (mWakeLock == null) {
    677                         PowerManager pm = (PowerManager)getSystemService(
    678                                           Context.POWER_SERVICE);
    679                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    680                                     "StartingObexPbapTransaction");
    681                         mWakeLock.setReferenceCounted(false);
    682                         mWakeLock.acquire();
    683                         Log.w(TAG, "Acquire Wake Lock");
    684                     }
    685                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    686                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    687                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    688                     break;
    689                 case MSG_RELEASE_WAKE_LOCK:
    690                     if (mWakeLock != null) {
    691                         mWakeLock.release();
    692                         mWakeLock = null;
    693                         Log.w(TAG, "Release Wake Lock");
    694                     }
    695                     break;
    696                 default:
    697                     break;
    698             }
    699         }
    700     };
    701 
    702     private void setState(int state) {
    703         setState(state, BluetoothPbap.RESULT_SUCCESS);
    704     }
    705 
    706     private synchronized void setState(int state, int result) {
    707         if (state != mState) {
    708             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
    709                     + result);
    710             int prevState = mState;
    711             mState = state;
    712             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
    713             intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
    714             intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
    715             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    716             sendBroadcast(intent, BLUETOOTH_PERM);
    717             AdapterService s = AdapterService.getAdapterService();
    718             if (s != null) {
    719                 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP,
    720                         mState, prevState);
    721             }
    722         }
    723     }
    724 
    725     private void createPbapNotification(String action) {
    726 
    727         NotificationManager nm = (NotificationManager)
    728             getSystemService(Context.NOTIFICATION_SERVICE);
    729 
    730         // Create an intent triggered by clicking on the status icon.
    731         Intent clickIntent = new Intent();
    732         clickIntent.setClass(this, BluetoothPbapActivity.class);
    733         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    734         clickIntent.setAction(action);
    735 
    736         // Create an intent triggered by clicking on the
    737         // "Clear All Notifications" button
    738         Intent deleteIntent = new Intent();
    739         deleteIntent.setClass(this, BluetoothPbapReceiver.class);
    740 
    741         Notification notification = null;
    742         String name = getRemoteDeviceName();
    743 
    744         if (action.equals(AUTH_CHALL_ACTION)) {
    745             deleteIntent.setAction(AUTH_CANCELLED_ACTION);
    746             notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
    747                 getString(R.string.auth_notif_ticker), System.currentTimeMillis());
    748             notification.setLatestEventInfo(this, getString(R.string.auth_notif_title),
    749                     getString(R.string.auth_notif_message, name), PendingIntent
    750                             .getActivity(this, 0, clickIntent, 0));
    751 
    752             notification.flags |= Notification.FLAG_AUTO_CANCEL;
    753             notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
    754             notification.defaults = Notification.DEFAULT_SOUND;
    755             notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0);
    756             nm.notify(NOTIFICATION_ID_AUTH, notification);
    757         }
    758     }
    759 
    760     private void removePbapNotification(int id) {
    761         NotificationManager nm = (NotificationManager)
    762             getSystemService(Context.NOTIFICATION_SERVICE);
    763         nm.cancel(id);
    764     }
    765 
    766     public static String getLocalPhoneNum() {
    767         return sLocalPhoneNum;
    768     }
    769 
    770     public static String getLocalPhoneName() {
    771         return sLocalPhoneName;
    772     }
    773 
    774     public static String getRemoteDeviceName() {
    775         return sRemoteDeviceName;
    776     }
    777 
    778     /**
    779      * Handlers for incoming service calls
    780      */
    781     private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
    782         public int getState() {
    783             if (DEBUG) Log.d(TAG, "getState " + mState);
    784 
    785             if (!Utils.checkCaller()) {
    786                 Log.w(TAG,"getState(): not allowed for non-active user");
    787                 return BluetoothPbap.STATE_DISCONNECTED;
    788             }
    789 
    790             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    791             return mState;
    792         }
    793 
    794         public BluetoothDevice getClient() {
    795             if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
    796 
    797             if (!Utils.checkCaller()) {
    798                 Log.w(TAG,"getClient(): not allowed for non-active user");
    799                 return null;
    800             }
    801 
    802             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    803             if (mState == BluetoothPbap.STATE_DISCONNECTED) {
    804                 return null;
    805             }
    806             return mRemoteDevice;
    807         }
    808 
    809         public boolean isConnected(BluetoothDevice device) {
    810             if (!Utils.checkCaller()) {
    811                 Log.w(TAG,"isConnected(): not allowed for non-active user");
    812                 return false;
    813             }
    814 
    815             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    816             return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
    817         }
    818 
    819         public boolean connect(BluetoothDevice device) {
    820             if (!Utils.checkCaller()) {
    821                 Log.w(TAG,"connect(): not allowed for non-active user");
    822                 return false;
    823             }
    824 
    825             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    826                     "Need BLUETOOTH_ADMIN permission");
    827             return false;
    828         }
    829 
    830         public void disconnect() {
    831             if (DEBUG) Log.d(TAG, "disconnect");
    832 
    833             if (!Utils.checkCaller()) {
    834                 Log.w(TAG,"disconnect(): not allowed for non-active user");
    835                 return;
    836             }
    837 
    838             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    839                     "Need BLUETOOTH_ADMIN permission");
    840             synchronized (BluetoothPbapService.this) {
    841                 switch (mState) {
    842                     case BluetoothPbap.STATE_CONNECTED:
    843                         if (mServerSession != null) {
    844                             mServerSession.close();
    845                             mServerSession = null;
    846                         }
    847 
    848                         closeConnectionSocket();
    849 
    850                         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    851                         break;
    852                     default:
    853                         break;
    854                 }
    855             }
    856         }
    857     };
    858 }
    859