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