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 mIsWaitingAuthorization = 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_ACL_DISCONNECTED)
    260                 && mIsWaitingAuthorization) {
    261             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    262 
    263             if (mRemoteDevice == null || device == null) {
    264                 Log.e(TAG, "Unexpected error!");
    265                 return;
    266             }
    267 
    268             if (DEBUG) Log.d(TAG,"ACL disconnected for "+ device);
    269 
    270             if (mRemoteDevice.equals(device)) {
    271                 Intent cancelIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    272                 cancelIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    273                 cancelIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    274                                       BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    275                 sendBroadcast(cancelIntent);
    276                 mIsWaitingAuthorization = false;
    277                 stopObexServerSession();
    278             }
    279         } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
    280             int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    281                                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    282 
    283             if ((!mIsWaitingAuthorization)
    284                     || (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
    285                 // this reply is not for us
    286                 return;
    287             }
    288 
    289             mIsWaitingAuthorization = false;
    290 
    291             if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
    292                                    BluetoothDevice.CONNECTION_ACCESS_NO)
    293                     == BluetoothDevice.CONNECTION_ACCESS_YES) {
    294                 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    295                     boolean result = mRemoteDevice.setPhonebookAccessPermission(
    296                             BluetoothDevice.ACCESS_ALLOWED);
    297                     if (VERBOSE) {
    298                         Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED) result=" + result);
    299                     }
    300                 }
    301                 try {
    302                     if (mConnSocket != null) {
    303                         startObexServerSession();
    304                     } else {
    305                         stopObexServerSession();
    306                     }
    307                 } catch (IOException ex) {
    308                     Log.e(TAG, "Caught the error: " + ex.toString());
    309                 }
    310             } else {
    311                 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    312                     boolean result = mRemoteDevice.setPhonebookAccessPermission(
    313                             BluetoothDevice.ACCESS_REJECTED);
    314                     if (VERBOSE) {
    315                         Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED) result="
    316                                 + result);
    317                     }
    318                 }
    319                 stopObexServerSession();
    320             }
    321         } else if (action.equals(AUTH_RESPONSE_ACTION)) {
    322             String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
    323             notifyAuthKeyInput(sessionkey);
    324         } else if (action.equals(AUTH_CANCELLED_ACTION)) {
    325             notifyAuthCancelled();
    326         } else {
    327             removeTimeoutMsg = false;
    328         }
    329 
    330         if (removeTimeoutMsg) {
    331             mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    332         }
    333     }
    334 
    335     @Override
    336     public void onDestroy() {
    337         if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
    338 
    339         super.onDestroy();
    340         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    341         closeService();
    342         if(mSessionStatusHandler != null) {
    343             mSessionStatusHandler.removeCallbacksAndMessages(null);
    344         }
    345     }
    346 
    347     @Override
    348     public IBinder onBind(Intent intent) {
    349         if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
    350         return mBinder;
    351     }
    352 
    353     private void startRfcommSocketListener() {
    354         if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
    355 
    356         if (mAcceptThread == null) {
    357             mAcceptThread = new SocketAcceptThread();
    358             mAcceptThread.setName("BluetoothPbapAcceptThread");
    359             mAcceptThread.start();
    360         }
    361     }
    362 
    363     private final boolean initSocket() {
    364         if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
    365 
    366         boolean initSocketOK = false;
    367         final int CREATE_RETRY_TIME = 10;
    368 
    369         // It's possible that create will fail in some cases. retry for 10 times
    370         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
    371             initSocketOK = true;
    372             try {
    373                 // It is mandatory for PSE to support initiation of bonding and
    374                 // encryption.
    375                 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
    376                     ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid());
    377 
    378             } catch (IOException e) {
    379                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
    380                 initSocketOK = false;
    381             }
    382             if (!initSocketOK) {
    383                 // Need to break out of this loop if BT is being turned off.
    384                 if (mAdapter == null) break;
    385                 int state = mAdapter.getState();
    386                 if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
    387                     (state != BluetoothAdapter.STATE_ON)) {
    388                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
    389                     break;
    390                 }
    391                 try {
    392                     if (VERBOSE) Log.v(TAG, "wait 300 ms");
    393                     Thread.sleep(300);
    394                 } catch (InterruptedException e) {
    395                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
    396                     break;
    397                 }
    398             } else {
    399                 break;
    400             }
    401         }
    402 
    403         if (mInterrupted) {
    404             initSocketOK = false;
    405             // close server socket to avoid resource leakage
    406             closeServerSocket();
    407         }
    408 
    409         if (initSocketOK) {
    410             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
    411 
    412         } else {
    413             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
    414         }
    415         return initSocketOK;
    416     }
    417 
    418     private final synchronized void closeServerSocket() {
    419         // exit SocketAcceptThread early
    420         if (mServerSocket != null) {
    421             try {
    422                 // this will cause mServerSocket.accept() return early with IOException
    423                 mServerSocket.close();
    424                 mServerSocket = null;
    425             } catch (IOException ex) {
    426                 Log.e(TAG, "Close Server Socket error: " + ex);
    427             }
    428         }
    429     }
    430 
    431     private final synchronized void closeConnectionSocket() {
    432         if (mConnSocket != null) {
    433             try {
    434                 mConnSocket.close();
    435                 mConnSocket = null;
    436             } catch (IOException e) {
    437                 Log.e(TAG, "Close Connection Socket error: " + e.toString());
    438             }
    439         }
    440     }
    441 
    442     private final void closeService() {
    443         if (VERBOSE) Log.v(TAG, "Pbap Service closeService in");
    444 
    445         // exit initSocket early
    446         mInterrupted = true;
    447         closeServerSocket();
    448 
    449         if (mAcceptThread != null) {
    450             try {
    451                 mAcceptThread.shutdown();
    452                 mAcceptThread.join();
    453                 mAcceptThread = null;
    454             } catch (InterruptedException ex) {
    455                 Log.w(TAG, "mAcceptThread close error" + ex);
    456             }
    457         }
    458 
    459         if (mWakeLock != null) {
    460             mWakeLock.release();
    461             mWakeLock = null;
    462         }
    463 
    464         if (mServerSession != null) {
    465             mServerSession.close();
    466             mServerSession = null;
    467         }
    468 
    469         closeConnectionSocket();
    470 
    471         mHasStarted = false;
    472         if (mStartId != -1 && stopSelfResult(mStartId)) {
    473             if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
    474             mStartId = -1;
    475         }
    476         if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
    477     }
    478 
    479     private final void startObexServerSession() throws IOException {
    480         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
    481 
    482         // acquire the wakeLock before start Obex transaction thread
    483         if (mWakeLock == null) {
    484             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    485             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    486                     "StartingObexPbapTransaction");
    487             mWakeLock.setReferenceCounted(false);
    488             mWakeLock.acquire();
    489         }
    490         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    491         if (tm != null) {
    492             sLocalPhoneNum = tm.getLine1Number();
    493             sLocalPhoneName = tm.getLine1AlphaTag();
    494             if (TextUtils.isEmpty(sLocalPhoneName)) {
    495                 sLocalPhoneName = this.getString(R.string.localPhoneName);
    496             }
    497         }
    498 
    499         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
    500         synchronized (this) {
    501             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
    502             mAuth.setChallenged(false);
    503             mAuth.setCancelled(false);
    504         }
    505         BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket);
    506         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
    507         setState(BluetoothPbap.STATE_CONNECTED);
    508 
    509         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    510         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    511             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    512 
    513         if (VERBOSE) {
    514             Log.v(TAG, "startObexServerSession() success!");
    515         }
    516     }
    517 
    518     private void stopObexServerSession() {
    519         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
    520 
    521         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
    522         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    523         // Release the wake lock if obex transaction is over
    524         if (mWakeLock != null) {
    525             mWakeLock.release();
    526             mWakeLock = null;
    527         }
    528 
    529         if (mServerSession != null) {
    530             mServerSession.close();
    531             mServerSession = null;
    532         }
    533 
    534         mAcceptThread = null;
    535 
    536         closeConnectionSocket();
    537 
    538         // Last obex transaction is finished, we start to listen for incoming
    539         // connection again
    540         if (mAdapter.isEnabled()) {
    541             startRfcommSocketListener();
    542         }
    543         setState(BluetoothPbap.STATE_DISCONNECTED);
    544     }
    545 
    546     private void notifyAuthKeyInput(final String key) {
    547         synchronized (mAuth) {
    548             if (key != null) {
    549                 mAuth.setSessionKey(key);
    550             }
    551             mAuth.setChallenged(true);
    552             mAuth.notify();
    553         }
    554     }
    555 
    556     private void notifyAuthCancelled() {
    557         synchronized (mAuth) {
    558             mAuth.setCancelled(true);
    559             mAuth.notify();
    560         }
    561     }
    562 
    563     /**
    564      * A thread that runs in the background waiting for remote rfcomm
    565      * connect.Once a remote socket connected, this thread shall be
    566      * shutdown.When the remote disconnect,this thread shall run again waiting
    567      * for next request.
    568      */
    569     private class SocketAcceptThread extends Thread {
    570 
    571         private boolean stopped = false;
    572 
    573         @Override
    574         public void run() {
    575             BluetoothServerSocket serverSocket;
    576             if (mServerSocket == null) {
    577                 if (!initSocket()) {
    578                     return;
    579                 }
    580             }
    581 
    582             while (!stopped) {
    583                 try {
    584                     if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
    585                     serverSocket = mServerSocket;
    586                     if (serverSocket == null) {
    587                         Log.w(TAG, "mServerSocket is null");
    588                         break;
    589                     }
    590                     mConnSocket = serverSocket.accept();
    591                     if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
    592 
    593                     synchronized (BluetoothPbapService.this) {
    594                         if (mConnSocket == null) {
    595                             Log.w(TAG, "mConnSocket is null");
    596                             break;
    597                         }
    598                         mRemoteDevice = mConnSocket.getRemoteDevice();
    599                     }
    600                     if (mRemoteDevice == null) {
    601                         Log.i(TAG, "getRemoteDevice() = null");
    602                         break;
    603                     }
    604                     sRemoteDeviceName = mRemoteDevice.getName();
    605                     // In case getRemoteName failed and return null
    606                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
    607                         sRemoteDeviceName = getString(R.string.defaultname);
    608                     }
    609                     int permission = mRemoteDevice.getPhonebookAccessPermission();
    610                     if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission);
    611 
    612                     if (permission == BluetoothDevice.ACCESS_ALLOWED) {
    613                         try {
    614                             if (VERBOSE) {
    615                                 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName
    616                                         + " automatically as already allowed device");
    617                             }
    618                             startObexServerSession();
    619                         } catch (IOException ex) {
    620                             Log.e(TAG, "Caught exception starting obex server session"
    621                                     + ex.toString());
    622                         }
    623                     } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
    624                         if (VERBOSE) {
    625                             Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName
    626                                     + " automatically as already rejected device");
    627                         }
    628                         stopObexServerSession();
    629                     } else {  // permission == BluetoothDevice.ACCESS_UNKNOWN
    630                         // Send an Intent to Settings app to ask user preference.
    631                         Intent intent =
    632                                 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
    633                         intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
    634                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    635                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    636                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    637                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
    638                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME,
    639                                         BluetoothPbapReceiver.class.getName());
    640 
    641                         mIsWaitingAuthorization = true;
    642                         sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    643 
    644                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
    645                                 + sRemoteDeviceName);
    646 
    647                         // In case car kit time out and try to use HFP for
    648                         // phonebook
    649                         // access, while UI still there waiting for user to
    650                         // confirm
    651                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    652                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    653                         // We will continue the process when we receive
    654                         // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app.
    655                     }
    656                     stopped = true; // job done ,close this thread;
    657                 } catch (IOException ex) {
    658                     stopped=true;
    659                     /*
    660                     if (stopped) {
    661                         break;
    662                     }
    663                     */
    664                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
    665                 }
    666             }
    667         }
    668 
    669         void shutdown() {
    670             stopped = true;
    671             interrupt();
    672         }
    673     }
    674 
    675     private final Handler mSessionStatusHandler = new Handler() {
    676         @Override
    677         public void handleMessage(Message msg) {
    678             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
    679 
    680             switch (msg.what) {
    681                 case START_LISTENER:
    682                     if (mAdapter.isEnabled()) {
    683                         startRfcommSocketListener();
    684                     } else {
    685                         closeService();// release all resources
    686                     }
    687                     break;
    688                 case USER_TIMEOUT:
    689                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    690                     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    691                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    692                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    693                     sendBroadcast(intent);
    694                     mIsWaitingAuthorization = false;
    695                     stopObexServerSession();
    696                     break;
    697                 case AUTH_TIMEOUT:
    698                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    699                     sendBroadcast(i);
    700                     removePbapNotification(NOTIFICATION_ID_AUTH);
    701                     notifyAuthCancelled();
    702                     break;
    703                 case MSG_SERVERSESSION_CLOSE:
    704                     stopObexServerSession();
    705                     break;
    706                 case MSG_SESSION_ESTABLISHED:
    707                     break;
    708                 case MSG_SESSION_DISCONNECTED:
    709                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
    710                     break;
    711                 case MSG_OBEX_AUTH_CHALL:
    712                     createPbapNotification(AUTH_CHALL_ACTION);
    713                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    714                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    715                     break;
    716                 case MSG_ACQUIRE_WAKE_LOCK:
    717                     if (mWakeLock == null) {
    718                         PowerManager pm = (PowerManager)getSystemService(
    719                                           Context.POWER_SERVICE);
    720                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    721                                     "StartingObexPbapTransaction");
    722                         mWakeLock.setReferenceCounted(false);
    723                         mWakeLock.acquire();
    724                         Log.w(TAG, "Acquire Wake Lock");
    725                     }
    726                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    727                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    728                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    729                     break;
    730                 case MSG_RELEASE_WAKE_LOCK:
    731                     if (mWakeLock != null) {
    732                         mWakeLock.release();
    733                         mWakeLock = null;
    734                         Log.w(TAG, "Release Wake Lock");
    735                     }
    736                     break;
    737                 default:
    738                     break;
    739             }
    740         }
    741     };
    742 
    743     private void setState(int state) {
    744         setState(state, BluetoothPbap.RESULT_SUCCESS);
    745     }
    746 
    747     private synchronized void setState(int state, int result) {
    748         if (state != mState) {
    749             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
    750                     + result);
    751             int prevState = mState;
    752             mState = state;
    753             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
    754             intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
    755             intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
    756             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    757             sendBroadcast(intent, BLUETOOTH_PERM);
    758             AdapterService s = AdapterService.getAdapterService();
    759             if (s != null) {
    760                 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP,
    761                         mState, prevState);
    762             }
    763         }
    764     }
    765 
    766     private void createPbapNotification(String action) {
    767 
    768         NotificationManager nm = (NotificationManager)
    769             getSystemService(Context.NOTIFICATION_SERVICE);
    770 
    771         // Create an intent triggered by clicking on the status icon.
    772         Intent clickIntent = new Intent();
    773         clickIntent.setClass(this, BluetoothPbapActivity.class);
    774         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    775         clickIntent.setAction(action);
    776 
    777         // Create an intent triggered by clicking on the
    778         // "Clear All Notifications" button
    779         Intent deleteIntent = new Intent();
    780         deleteIntent.setClass(this, BluetoothPbapReceiver.class);
    781 
    782         Notification notification = null;
    783         String name = getRemoteDeviceName();
    784 
    785         if (action.equals(AUTH_CHALL_ACTION)) {
    786             deleteIntent.setAction(AUTH_CANCELLED_ACTION);
    787             notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
    788                 getString(R.string.auth_notif_ticker), System.currentTimeMillis());
    789             notification.color = getResources().getColor(
    790                     com.android.internal.R.color.system_notification_accent_color);
    791             notification.setLatestEventInfo(this, getString(R.string.auth_notif_title),
    792                     getString(R.string.auth_notif_message, name), PendingIntent
    793                             .getActivity(this, 0, clickIntent, 0));
    794 
    795             notification.flags |= Notification.FLAG_AUTO_CANCEL;
    796             notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
    797             notification.defaults = Notification.DEFAULT_SOUND;
    798             notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0);
    799             nm.notify(NOTIFICATION_ID_AUTH, notification);
    800         }
    801     }
    802 
    803     private void removePbapNotification(int id) {
    804         NotificationManager nm = (NotificationManager)
    805             getSystemService(Context.NOTIFICATION_SERVICE);
    806         nm.cancel(id);
    807     }
    808 
    809     public static String getLocalPhoneNum() {
    810         return sLocalPhoneNum;
    811     }
    812 
    813     public static String getLocalPhoneName() {
    814         return sLocalPhoneName;
    815     }
    816 
    817     public static String getRemoteDeviceName() {
    818         return sRemoteDeviceName;
    819     }
    820 
    821     /**
    822      * Handlers for incoming service calls
    823      */
    824     private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
    825         public int getState() {
    826             if (DEBUG) Log.d(TAG, "getState " + mState);
    827 
    828             if (!Utils.checkCaller()) {
    829                 Log.w(TAG,"getState(): not allowed for non-active user");
    830                 return BluetoothPbap.STATE_DISCONNECTED;
    831             }
    832 
    833             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    834             return mState;
    835         }
    836 
    837         public BluetoothDevice getClient() {
    838             if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
    839 
    840             if (!Utils.checkCaller()) {
    841                 Log.w(TAG,"getClient(): not allowed for non-active user");
    842                 return null;
    843             }
    844 
    845             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    846             if (mState == BluetoothPbap.STATE_DISCONNECTED) {
    847                 return null;
    848             }
    849             return mRemoteDevice;
    850         }
    851 
    852         public boolean isConnected(BluetoothDevice device) {
    853             if (!Utils.checkCaller()) {
    854                 Log.w(TAG,"isConnected(): not allowed for non-active user");
    855                 return false;
    856             }
    857 
    858             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    859             return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
    860         }
    861 
    862         public boolean connect(BluetoothDevice device) {
    863             if (!Utils.checkCaller()) {
    864                 Log.w(TAG,"connect(): not allowed for non-active user");
    865                 return false;
    866             }
    867 
    868             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    869                     "Need BLUETOOTH_ADMIN permission");
    870             return false;
    871         }
    872 
    873         public void disconnect() {
    874             if (DEBUG) Log.d(TAG, "disconnect");
    875 
    876             if (!Utils.checkCaller()) {
    877                 Log.w(TAG,"disconnect(): not allowed for non-active user");
    878                 return;
    879             }
    880 
    881             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
    882                     "Need BLUETOOTH_ADMIN permission");
    883             synchronized (BluetoothPbapService.this) {
    884                 switch (mState) {
    885                     case BluetoothPbap.STATE_CONNECTED:
    886                         if (mServerSession != null) {
    887                             mServerSession.close();
    888                             mServerSession = null;
    889                         }
    890 
    891                         closeConnectionSocket();
    892 
    893                         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    894                         break;
    895                     default:
    896                         break;
    897                 }
    898             }
    899         }
    900     };
    901 }
    902