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.AlarmManager;
     36 import android.app.Notification;
     37 import android.app.NotificationChannel;
     38 import android.app.NotificationManager;
     39 import android.app.PendingIntent;
     40 import android.app.Service;
     41 import android.bluetooth.BluetoothAdapter;
     42 import android.bluetooth.BluetoothDevice;
     43 import android.bluetooth.BluetoothPbap;
     44 import android.bluetooth.BluetoothProfile;
     45 import android.bluetooth.BluetoothServerSocket;
     46 import android.bluetooth.BluetoothSocket;
     47 import android.bluetooth.BluetoothUuid;
     48 import android.bluetooth.IBluetoothPbap;
     49 import android.database.sqlite.SQLiteException;
     50 import android.content.BroadcastReceiver;
     51 import android.content.Context;
     52 import android.content.ContentResolver;
     53 import android.database.ContentObserver;
     54 import android.content.Intent;
     55 import android.content.IntentFilter;
     56 import android.os.Handler;
     57 import android.os.IBinder;
     58 import android.os.Message;
     59 import android.os.PowerManager;
     60 import android.telephony.TelephonyManager;
     61 import android.text.TextUtils;
     62 import android.util.Log;
     63 
     64 import com.android.bluetooth.BluetoothObexTransport;
     65 import com.android.bluetooth.btservice.ProfileService;
     66 import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
     67 import com.android.bluetooth.IObexConnectionHandler;
     68 import com.android.bluetooth.ObexServerSockets;
     69 import com.android.bluetooth.R;
     70 import com.android.bluetooth.sdp.SdpManager;
     71 import com.android.bluetooth.Utils;
     72 import com.android.bluetooth.util.DevicePolicyUtils;
     73 
     74 import java.io.IOException;
     75 import java.util.Calendar;
     76 import java.util.concurrent.atomic.AtomicLong;
     77 import java.util.HashMap;
     78 
     79 import javax.obex.ServerSession;
     80 
     81 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler {
     82     private static final String TAG = "BluetoothPbapService";
     83 
     84     /**
     85      * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
     86      * restart com.android.bluetooth process. only enable DEBUG log:
     87      * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
     88      * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
     89      */
     90 
     91     public static final boolean DEBUG = true;
     92 
     93     public static final boolean VERBOSE = false;
     94 
     95     /**
     96      * Intent indicating incoming obex authentication request which is from
     97      * PCE(Carkit)
     98      */
     99     public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
    100 
    101     /**
    102      * Intent indicating obex session key input complete by user which is sent
    103      * from BluetoothPbapActivity
    104      */
    105     public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
    106 
    107     /**
    108      * Intent indicating user canceled obex authentication session key input
    109      * which is sent from BluetoothPbapActivity
    110      */
    111     public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
    112 
    113     /**
    114      * Intent indicating timeout for user confirmation, which is sent to
    115      * BluetoothPbapActivity
    116      */
    117     public static final String USER_CONFIRM_TIMEOUT_ACTION =
    118             "com.android.bluetooth.pbap.userconfirmtimeout";
    119 
    120     /**
    121      * Intent Extra name indicating session key which is sent from
    122      * BluetoothPbapActivity
    123      */
    124     public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
    125 
    126     public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
    127 
    128     public static final int MSG_SERVERSESSION_CLOSE = 5000;
    129 
    130     public static final int MSG_SESSION_ESTABLISHED = 5001;
    131 
    132     public static final int MSG_SESSION_DISCONNECTED = 5002;
    133 
    134     public static final int MSG_OBEX_AUTH_CHALL = 5003;
    135 
    136     public static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
    137 
    138     public static final int MSG_RELEASE_WAKE_LOCK = 5005;
    139 
    140     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
    141 
    142     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
    143 
    144     private static final int START_LISTENER = 1;
    145 
    146     private static final int USER_TIMEOUT = 2;
    147 
    148     private static final int AUTH_TIMEOUT = 3;
    149 
    150     private static final int SHUTDOWN = 4;
    151 
    152     protected static final int LOAD_CONTACTS = 5;
    153 
    154     private static final int CHECK_SECONDARY_VERSION_COUNTER = 6;
    155 
    156     protected static final int ROLLOVER_COUNTERS = 7;
    157 
    158     private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
    159 
    160     private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
    161 
    162     // Ensure not conflict with Opp notification ID
    163     private static final int NOTIFICATION_ID_ACCESS = -1000001;
    164 
    165     private static final int NOTIFICATION_ID_AUTH = -1000002;
    166 
    167     private static final String PBAP_NOTIFICATION_CHANNEL = "pbap_notification_channel";
    168 
    169     private PowerManager.WakeLock mWakeLock = null;
    170 
    171     private BluetoothPbapAuthenticator mAuth = null;
    172 
    173     private BluetoothPbapObexServer mPbapServer;
    174 
    175     private ServerSession mServerSession = null;
    176 
    177     private BluetoothServerSocket mServerSocket = null;
    178 
    179     private BluetoothSocket mConnSocket = null;
    180 
    181     private BluetoothDevice mRemoteDevice = null;
    182 
    183     private static String sLocalPhoneNum = null;
    184 
    185     private static String sLocalPhoneName = null;
    186 
    187     private static String sRemoteDeviceName = null;
    188 
    189     private volatile boolean mInterrupted;
    190 
    191     private int mState;
    192 
    193     private boolean mIsWaitingAuthorization = false;
    194 
    195     private ObexServerSockets mServerSockets = null;
    196 
    197     private static final int SDP_PBAP_SERVER_VERSION = 0x0102;
    198 
    199     private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0003;
    200 
    201     private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F;
    202 
    203     private AlarmManager mAlarmManager = null;
    204 
    205     private int mSdpHandle = -1;
    206 
    207     private boolean mRemoveTimeoutMsg = false;
    208 
    209     private int mPermission = BluetoothDevice.ACCESS_UNKNOWN;
    210 
    211     private boolean mSdpSearchInitiated = false;
    212 
    213     private boolean isRegisteredObserver = false;
    214 
    215     protected Context mContext;
    216 
    217     // package and class name to which we send intent to check phone book access permission
    218     private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
    219     private static final String ACCESS_AUTHORITY_CLASS =
    220             "com.android.settings.bluetooth.BluetoothPermissionRequest";
    221 
    222     private class BluetoothPbapContentObserver extends ContentObserver {
    223         public BluetoothPbapContentObserver() {
    224             super(new Handler());
    225         }
    226 
    227         @Override
    228         public void onChange(boolean selfChange) {
    229             Log.d(TAG, " onChange on contact uri ");
    230             if (BluetoothPbapUtils.contactsLoaded) {
    231                 if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) {
    232                     mSessionStatusHandler.sendMessage(
    233                             mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER));
    234                 }
    235             }
    236         }
    237     }
    238 
    239     private BluetoothPbapContentObserver mContactChangeObserver;
    240 
    241     public BluetoothPbapService() {
    242         mState = BluetoothPbap.STATE_DISCONNECTED;
    243         mContext = this;
    244     }
    245 
    246     // process the intent from receiver
    247     private void parseIntent(final Intent intent) {
    248         String action = intent.getAction();
    249         if (DEBUG) Log.d(TAG, "action: " + action);
    250         if (action == null) return;             // Nothing to do
    251         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
    252         if (DEBUG) Log.d(TAG, "state: " + state);
    253         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    254             if (state == BluetoothAdapter.STATE_TURNING_OFF) {
    255                 // Send any pending timeout now, as this service will be destroyed.
    256                 if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) {
    257                     mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    258                     mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget();
    259                 }
    260                 // Release all resources
    261                 closeService();
    262             } else if (state == BluetoothAdapter.STATE_ON) {
    263                 // start RFCOMM listener
    264                 mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
    265             }
    266             return;
    267         }
    268 
    269         if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) {
    270             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    271 
    272             if (mRemoteDevice == null) return;
    273             if (DEBUG) Log.d(TAG,"ACL disconnected for "+ device);
    274             if (mRemoteDevice.equals(device)) {
    275                 mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    276                 mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget();
    277             }
    278             return;
    279         }
    280 
    281         if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
    282             int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    283                                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    284 
    285             if ((!mIsWaitingAuthorization)
    286                     || (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
    287                 // this reply is not for us
    288                 return;
    289             }
    290 
    291             mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    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);
    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                     }
    320                 }
    321                 stopObexServerSession();
    322             }
    323             return;
    324         }
    325 
    326         if (action.equals(AUTH_RESPONSE_ACTION)) {
    327             String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
    328             notifyAuthKeyInput(sessionkey);
    329         } else if (action.equals(AUTH_CANCELLED_ACTION)) {
    330             notifyAuthCancelled();
    331         } else {
    332             Log.w(TAG, "Unrecognized intent!");
    333             return;
    334         }
    335 
    336         mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    337     }
    338 
    339     private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() {
    340         @Override
    341         public void onReceive(Context context, Intent intent) {
    342             parseIntent(intent);
    343         }
    344     };
    345 
    346     private final boolean initSocket() {
    347         if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
    348 
    349         boolean initSocketOK = false;
    350         final int CREATE_RETRY_TIME = 10;
    351 
    352         // It's possible that create will fail in some cases. retry for 10 times
    353         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
    354             initSocketOK = true;
    355             try {
    356                 // It is mandatory for PSE to support initiation of bonding and
    357                 // encryption.
    358                 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
    359                     ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid());
    360 
    361             } catch (IOException e) {
    362                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
    363                 initSocketOK = false;
    364             }
    365             if (!initSocketOK) {
    366                 // Need to break out of this loop if BT is being turned off.
    367                 if (mAdapter == null) break;
    368                 int state = mAdapter.getState();
    369                 if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
    370                     (state != BluetoothAdapter.STATE_ON)) {
    371                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
    372                     break;
    373                 }
    374                 try {
    375                     if (VERBOSE) Log.v(TAG, "wait 300 ms");
    376                     Thread.sleep(300);
    377                 } catch (InterruptedException e) {
    378                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
    379                     break;
    380                 }
    381             } else {
    382                 break;
    383             }
    384         }
    385 
    386         if (mInterrupted) {
    387             initSocketOK = false;
    388             // close server socket to avoid resource leakage
    389             closeServerSocket();
    390         }
    391 
    392         if (initSocketOK) {
    393             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
    394 
    395         } else {
    396             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
    397         }
    398         return initSocketOK;
    399     }
    400 
    401     private final synchronized void closeServerSocket() {
    402         // exit SocketAcceptThread early
    403         if (mServerSocket != null) {
    404             try {
    405                 // this will cause mServerSocket.accept() return early with IOException
    406                 mServerSocket.close();
    407                 mServerSocket = null;
    408             } catch (IOException ex) {
    409                 Log.e(TAG, "Close Server Socket error: " + ex);
    410             }
    411         }
    412     }
    413 
    414     private final synchronized void closeConnectionSocket() {
    415         if (mConnSocket != null) {
    416             try {
    417                 mConnSocket.close();
    418                 mConnSocket = null;
    419             } catch (IOException e) {
    420                 Log.e(TAG, "Close Connection Socket error: " + e.toString());
    421             }
    422         }
    423     }
    424 
    425     private final void closeService() {
    426         if (VERBOSE) Log.v(TAG, "Pbap Service closeService in");
    427 
    428         BluetoothPbapUtils.savePbapParams(this, BluetoothPbapUtils.primaryVersionCounter,
    429                 BluetoothPbapUtils.secondaryVersionCounter, BluetoothPbapUtils.mDbIdentifier.get(),
    430                 BluetoothPbapUtils.contactsLastUpdated, BluetoothPbapUtils.totalFields,
    431                 BluetoothPbapUtils.totalSvcFields, BluetoothPbapUtils.totalContacts);
    432 
    433         // exit initSocket early
    434         mInterrupted = true;
    435         if (mWakeLock != null) {
    436             mWakeLock.release();
    437             mWakeLock = null;
    438         }
    439 
    440         // Step 1: clean up active server session
    441         if (mServerSession != null) {
    442             mServerSession.close();
    443             mServerSession = null;
    444         }
    445         // Step 2: clean up existing connection socket
    446         closeConnectionSocket();
    447         // Step 3: clean up SDP record
    448         cleanUpSdpRecord();
    449         // Step 4: clean up existing server socket(s)
    450         closeServerSocket();
    451         if (mServerSockets != null) {
    452             mServerSockets.shutdown(false);
    453             mServerSockets = null;
    454         }
    455         if (mSessionStatusHandler != null) mSessionStatusHandler.removeCallbacksAndMessages(null);
    456         if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
    457     }
    458 
    459     private void cleanUpSdpRecord() {
    460         if (mSdpHandle < 0) {
    461             if (VERBOSE) Log.v(TAG, "cleanUpSdpRecord, SDP record never created");
    462             return;
    463         }
    464         int sdpHandle = mSdpHandle;
    465         mSdpHandle = -1;
    466         SdpManager sdpManager = SdpManager.getDefaultManager();
    467         if (sdpManager == null) {
    468             Log.e(TAG, "cleanUpSdpRecord failed, sdpManager is null, sdpHandle=" + sdpHandle);
    469             return;
    470         }
    471         Log.i(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
    472         if (!sdpManager.removeSdpRecord(sdpHandle)) {
    473             Log.e(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
    474         }
    475     }
    476 
    477     private final void startObexServerSession() throws IOException {
    478         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
    479 
    480         // acquire the wakeLock before start Obex transaction thread
    481         if (mWakeLock == null) {
    482             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    483             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    484                     "StartingObexPbapTransaction");
    485             mWakeLock.setReferenceCounted(false);
    486             mWakeLock.acquire();
    487         }
    488         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    489         if (tm != null) {
    490             sLocalPhoneNum = tm.getLine1Number();
    491             sLocalPhoneName = tm.getLine1AlphaTag();
    492             if (TextUtils.isEmpty(sLocalPhoneName)) {
    493                 sLocalPhoneName = this.getString(R.string.localPhoneName);
    494             }
    495         }
    496 
    497         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
    498         synchronized (this) {
    499             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
    500             mAuth.setChallenged(false);
    501             mAuth.setCancelled(false);
    502         }
    503         BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket);
    504         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
    505         setState(BluetoothPbap.STATE_CONNECTED);
    506 
    507         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    508         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    509             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    510 
    511         if (VERBOSE) {
    512             Log.v(TAG, "startObexServerSession() success!");
    513         }
    514     }
    515 
    516     private void stopObexServerSession() {
    517         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
    518         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
    519         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    520         // Release the wake lock if obex transaction is over
    521         if (mWakeLock != null) {
    522             mWakeLock.release();
    523             mWakeLock = null;
    524         }
    525 
    526         if (mServerSession != null) {
    527             mServerSession.close();
    528             mServerSession = null;
    529         }
    530         closeConnectionSocket();
    531 
    532         // Last obex transaction is finished, we start to listen for incoming
    533         // connection again
    534         if (mAdapter != null && mAdapter.isEnabled()) {
    535             startSocketListeners();
    536         }
    537         setState(BluetoothPbap.STATE_DISCONNECTED);
    538     }
    539 
    540     private void notifyAuthKeyInput(final String key) {
    541         synchronized (mAuth) {
    542             if (key != null) {
    543                 mAuth.setSessionKey(key);
    544             }
    545             mAuth.setChallenged(true);
    546             mAuth.notify();
    547         }
    548     }
    549 
    550     private void notifyAuthCancelled() {
    551         synchronized (mAuth) {
    552             mAuth.setCancelled(true);
    553             mAuth.notify();
    554         }
    555     }
    556 
    557     /**
    558      * A thread that runs in the background waiting for remote rfcomm
    559      * connect.Once a remote socket connected, this thread shall be
    560      * shutdown.When the remote disconnect,this thread shall run again waiting
    561      * for next request.
    562      */
    563     private class SocketAcceptThread extends Thread {
    564 
    565         private boolean stopped = false;
    566 
    567         @Override
    568         public void run() {
    569             BluetoothServerSocket serverSocket;
    570             if (mServerSocket == null) {
    571                 if (!initSocket()) {
    572                     return;
    573                 }
    574             }
    575 
    576             while (!stopped) {
    577                 try {
    578                     if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
    579                     serverSocket = mServerSocket;
    580                     if (serverSocket == null) {
    581                         Log.w(TAG, "mServerSocket is null");
    582                         break;
    583                     }
    584                     mConnSocket = serverSocket.accept();
    585                     if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
    586 
    587                     synchronized (BluetoothPbapService.this) {
    588                         if (mConnSocket == null) {
    589                             Log.w(TAG, "mConnSocket is null");
    590                             break;
    591                         }
    592                         mRemoteDevice = mConnSocket.getRemoteDevice();
    593                     }
    594                     if (mRemoteDevice == null) {
    595                         Log.i(TAG, "getRemoteDevice() = null");
    596                         break;
    597                     }
    598                     sRemoteDeviceName = mRemoteDevice.getName();
    599                     // In case getRemoteName failed and return null
    600                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
    601                         sRemoteDeviceName = getString(R.string.defaultname);
    602                     }
    603                     int permission = mRemoteDevice.getPhonebookAccessPermission();
    604                     if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission);
    605 
    606                     if (permission == BluetoothDevice.ACCESS_ALLOWED) {
    607                         try {
    608                             if (VERBOSE) {
    609                                 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName
    610                                         + " automatically as already allowed device");
    611                             }
    612                             startObexServerSession();
    613                         } catch (IOException ex) {
    614                             Log.e(TAG, "Caught exception starting obex server session"
    615                                     + ex.toString());
    616                         }
    617                     } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
    618                         if (VERBOSE) {
    619                             Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName
    620                                     + " automatically as already rejected device");
    621                         }
    622                         stopObexServerSession();
    623                     } else {  // permission == BluetoothDevice.ACCESS_UNKNOWN
    624                         // Send an Intent to Settings app to ask user preference.
    625                         Intent intent =
    626                                 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
    627                         intent.setPackage(getString(R.string.pairing_ui_package));
    628                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    629                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    630                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    631                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
    632                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, getName());
    633 
    634                         mIsWaitingAuthorization = true;
    635                         sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    636 
    637                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
    638                                 + sRemoteDeviceName);
    639 
    640                         // In case car kit time out and try to use HFP for
    641                         // phonebook
    642                         // access, while UI still there waiting for user to
    643                         // confirm
    644                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    645                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    646                         // We will continue the process when we receive
    647                         // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app.
    648                     }
    649                     stopped = true; // job done ,close this thread;
    650                 } catch (IOException ex) {
    651                     stopped=true;
    652                     /*
    653                     if (stopped) {
    654                         break;
    655                     }
    656                     */
    657                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
    658                 }
    659             }
    660         }
    661 
    662         void shutdown() {
    663             stopped = true;
    664             interrupt();
    665         }
    666     }
    667 
    668     protected final Handler mSessionStatusHandler = new Handler() {
    669         @Override
    670         public void handleMessage(Message msg) {
    671             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
    672 
    673             switch (msg.what) {
    674                 case START_LISTENER:
    675                     if (mAdapter.isEnabled()) {
    676                         startSocketListeners();
    677                     }
    678                     break;
    679                 case USER_TIMEOUT:
    680                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    681                     intent.setPackage(getString(R.string.pairing_ui_package));
    682                     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    683                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    684                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
    685                     sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    686                     mIsWaitingAuthorization = false;
    687                     stopObexServerSession();
    688                     break;
    689                 case AUTH_TIMEOUT:
    690                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    691                     sendBroadcast(i);
    692                     removePbapNotification(NOTIFICATION_ID_AUTH);
    693                     notifyAuthCancelled();
    694                     break;
    695                 case MSG_SERVERSESSION_CLOSE:
    696                     stopObexServerSession();
    697                     break;
    698                 case MSG_SESSION_ESTABLISHED:
    699                     break;
    700                 case MSG_SESSION_DISCONNECTED:
    701                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
    702                     break;
    703                 case MSG_OBEX_AUTH_CHALL:
    704                     createPbapNotification(AUTH_CHALL_ACTION);
    705                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    706                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
    707                     break;
    708                 case MSG_ACQUIRE_WAKE_LOCK:
    709                     if (mWakeLock == null) {
    710                         PowerManager pm = (PowerManager)getSystemService(
    711                                           Context.POWER_SERVICE);
    712                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    713                                     "StartingObexPbapTransaction");
    714                         mWakeLock.setReferenceCounted(false);
    715                         mWakeLock.acquire();
    716                         Log.w(TAG, "Acquire Wake Lock");
    717                     }
    718                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    719                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
    720                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
    721                     break;
    722                 case MSG_RELEASE_WAKE_LOCK:
    723                     if (mWakeLock != null) {
    724                         mWakeLock.release();
    725                         mWakeLock = null;
    726                         Log.w(TAG, "Release Wake Lock");
    727                     }
    728                     break;
    729                 case SHUTDOWN:
    730                     closeService();
    731                     break;
    732                 case LOAD_CONTACTS:
    733                     BluetoothPbapUtils.loadAllContacts(mContext, this);
    734                     break;
    735                 case CHECK_SECONDARY_VERSION_COUNTER:
    736                     BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, this);
    737                     break;
    738                 case ROLLOVER_COUNTERS:
    739                     BluetoothPbapUtils.rolloverCounters();
    740                     break;
    741                 default:
    742                     break;
    743             }
    744         }
    745     };
    746 
    747     private void setState(int state) {
    748         setState(state, BluetoothPbap.RESULT_SUCCESS);
    749     }
    750 
    751     private synchronized void setState(int state, int result) {
    752         if (state != mState) {
    753             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
    754                     + result);
    755             int prevState = mState;
    756             mState = state;
    757             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
    758             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    759             intent.putExtra(BluetoothProfile.EXTRA_STATE, mState);
    760             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    761             sendBroadcast(intent, BLUETOOTH_PERM);
    762         }
    763     }
    764 
    765     protected int getState() {
    766         return mState;
    767     }
    768 
    769     protected BluetoothDevice getRemoteDevice() {
    770         return mRemoteDevice;
    771     }
    772 
    773     private void createPbapNotification(String action) {
    774 
    775         NotificationManager nm = (NotificationManager)
    776             getSystemService(Context.NOTIFICATION_SERVICE);
    777         NotificationChannel notificationChannel = new NotificationChannel(PBAP_NOTIFICATION_CHANNEL,
    778                 getString(R.string.pbap_notification_group), NotificationManager.IMPORTANCE_HIGH);
    779         nm.createNotificationChannel(notificationChannel);
    780 
    781         // Create an intent triggered by clicking on the status icon.
    782         Intent clickIntent = new Intent();
    783         clickIntent.setClass(this, BluetoothPbapActivity.class);
    784         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    785         clickIntent.setAction(action);
    786 
    787         // Create an intent triggered by clicking on the
    788         // "Clear All Notifications" button
    789         Intent deleteIntent = new Intent();
    790         deleteIntent.setClass(this, BluetoothPbapService.class);
    791         deleteIntent.setAction(AUTH_CANCELLED_ACTION);
    792 
    793         String name = getRemoteDeviceName();
    794 
    795         if (action.equals(AUTH_CHALL_ACTION)) {
    796             Notification notification =
    797                     new Notification.Builder(this, PBAP_NOTIFICATION_CHANNEL)
    798                             .setWhen(System.currentTimeMillis())
    799                             .setContentTitle(getString(R.string.auth_notif_title))
    800                             .setContentText(getString(R.string.auth_notif_message, name))
    801                             .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
    802                             .setTicker(getString(R.string.auth_notif_ticker))
    803                             .setColor(getResources().getColor(
    804                                     com.android.internal.R.color.system_notification_accent_color,
    805                                     this.getTheme()))
    806                             .setFlag(Notification.FLAG_AUTO_CANCEL, true)
    807                             .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true)
    808                             .setDefaults(Notification.DEFAULT_SOUND)
    809                             .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0))
    810                             .setDeleteIntent(PendingIntent.getBroadcast(this, 0, deleteIntent, 0))
    811                             .build();
    812             nm.notify(NOTIFICATION_ID_AUTH, notification);
    813         }
    814     }
    815 
    816     private void removePbapNotification(int id) {
    817         NotificationManager nm = (NotificationManager)
    818             getSystemService(Context.NOTIFICATION_SERVICE);
    819         nm.cancel(id);
    820     }
    821 
    822     public static String getLocalPhoneNum() {
    823         return sLocalPhoneNum;
    824     }
    825 
    826     public static String getLocalPhoneName() {
    827         return sLocalPhoneName;
    828     }
    829 
    830     public static String getRemoteDeviceName() {
    831         return sRemoteDeviceName;
    832     }
    833 
    834     @Override
    835     protected IProfileServiceBinder initBinder() {
    836         return new PbapBinder(this);
    837     }
    838 
    839     @Override
    840     protected boolean start() {
    841         Log.v(TAG, "start()");
    842         IntentFilter filter = new IntentFilter();
    843         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
    844         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    845         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    846         filter.addAction(AUTH_RESPONSE_ACTION);
    847         filter.addAction(AUTH_CANCELLED_ACTION);
    848         mInterrupted = false;
    849         BluetoothPbapConfig.init(this);
    850         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
    851         if (mContactChangeObserver == null) {
    852             registerReceiver(mPbapReceiver, filter);
    853             try {
    854                 if (DEBUG) Log.d(TAG, "Registering observer");
    855                 mContactChangeObserver = new BluetoothPbapContentObserver();
    856                 getContentResolver().registerContentObserver(
    857                         DevicePolicyUtils.getEnterprisePhoneUri(this), false,
    858                         mContactChangeObserver);
    859             } catch (SQLiteException e) {
    860                 Log.e(TAG, "SQLite exception: " + e);
    861             } catch (IllegalStateException e) {
    862                 Log.e(TAG, "Illegal state exception, content observer is already registered");
    863             }
    864         }
    865         return true;
    866     }
    867 
    868     @Override
    869     protected boolean stop() {
    870         Log.v(TAG, "stop()");
    871         if (mContactChangeObserver == null) {
    872             Log.i(TAG, "Avoid unregister when receiver it is not registered");
    873             return true;
    874         }
    875         try {
    876             unregisterReceiver(mPbapReceiver);
    877             getContentResolver().unregisterContentObserver(mContactChangeObserver);
    878             mContactChangeObserver = null;
    879         } catch (Exception e) {
    880             Log.w(TAG, "Unable to unregister pbap receiver", e);
    881         }
    882         mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
    883         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    884         return true;
    885     }
    886 
    887     protected void disconnect() {
    888         synchronized (this) {
    889             if (mState == BluetoothPbap.STATE_CONNECTED) {
    890                 if (mServerSession != null) {
    891                     mServerSession.close();
    892                     mServerSession = null;
    893                 }
    894 
    895                 closeConnectionSocket();
    896 
    897                 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
    898             }
    899         }
    900     }
    901 
    902     // Has to be a static class or a memory leak can occur.
    903     private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
    904         private BluetoothPbapService mService;
    905 
    906         private BluetoothPbapService getService(String perm) {
    907             if (!Utils.checkCaller()) {
    908                 Log.w(TAG, "not allowed for non-active user");
    909                 return null;
    910             }
    911             if (mService != null && mService.isAvailable()) {
    912                 mService.enforceCallingOrSelfPermission(perm, "Need " + perm + " permission");
    913                 return mService;
    914             }
    915             return null;
    916         }
    917 
    918         PbapBinder(BluetoothPbapService service) {
    919             Log.v(TAG, "PbapBinder()");
    920             mService = service;
    921         }
    922 
    923         public boolean cleanup() {
    924             mService = null;
    925             return true;
    926         }
    927 
    928         public int getState() {
    929             if (DEBUG) Log.d(TAG, "getState = " + mService.getState());
    930             BluetoothPbapService service = getService(BLUETOOTH_PERM);
    931             if (service == null) return BluetoothPbap.STATE_DISCONNECTED;
    932 
    933             return service.getState();
    934         }
    935 
    936         public BluetoothDevice getClient() {
    937             if (DEBUG) Log.d(TAG, "getClient = " + mService.getRemoteDevice());
    938             BluetoothPbapService service = getService(BLUETOOTH_PERM);
    939             if (service == null) return null;
    940             return service.getRemoteDevice();
    941         }
    942 
    943         public boolean isConnected(BluetoothDevice device) {
    944             if (DEBUG) Log.d(TAG, "isConnected " + device);
    945             BluetoothPbapService service = getService(BLUETOOTH_PERM);
    946             if (service == null) return false;
    947             return service.getState() == BluetoothPbap.STATE_CONNECTED
    948                     && service.getRemoteDevice().equals(device);
    949         }
    950 
    951         public boolean connect(BluetoothDevice device) {
    952             BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM);
    953             return false;
    954         }
    955 
    956         public void disconnect() {
    957             if (DEBUG) Log.d(TAG, "disconnect");
    958             BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM);
    959             if (service == null) return;
    960             service.disconnect();
    961         }
    962     }
    963 
    964     /**
    965      * Start server side socket listeners. Caller should make sure that adapter is in a ready state
    966      * and SDP record is cleaned up. Otherwise, this method will fail.
    967      */
    968     synchronized private void startSocketListeners() {
    969         if (DEBUG) Log.d(TAG, "startsocketListener");
    970         if (mServerSession != null) {
    971             if (DEBUG) Log.d(TAG, "mServerSession exists - shutting it down...");
    972             mServerSession.close();
    973             mServerSession = null;
    974         }
    975         closeConnectionSocket();
    976         if (mServerSockets != null) {
    977             mServerSockets.prepareForNewConnect();
    978         } else {
    979             mServerSockets = ObexServerSockets.create(this);
    980             if (mServerSockets == null) {
    981                 // TODO: Handle - was not handled before
    982                 Log.e(TAG, "Failed to start the listeners");
    983                 return;
    984             }
    985             if (mSdpHandle >= 0) {
    986                 Log.e(TAG, "SDP handle was not cleaned up, mSdpHandle=" + mSdpHandle);
    987                 return;
    988             }
    989             mSdpHandle = SdpManager.getDefaultManager().createPbapPseRecord(
    990                     "OBEX Phonebook Access Server", mServerSockets.getRfcommChannel(),
    991                     mServerSockets.getL2capPsm(), SDP_PBAP_SERVER_VERSION,
    992                     SDP_PBAP_SUPPORTED_REPOSITORIES, SDP_PBAP_SUPPORTED_FEATURES);
    993             // fetch Pbap Params to check if significant change has happened to Database
    994             BluetoothPbapUtils.fetchPbapParams(mContext);
    995 
    996             if (DEBUG) Log.d(TAG, "PBAP server with handle:" + mSdpHandle);
    997         }
    998     }
    999 
   1000     long getDbIdentifier() {
   1001         return BluetoothPbapUtils.mDbIdentifier.get();
   1002     }
   1003 
   1004     private void setUserTimeoutAlarm() {
   1005         if (DEBUG) Log.d(TAG, "SetUserTimeOutAlarm()");
   1006         if (mAlarmManager == null) {
   1007             mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
   1008         }
   1009         mRemoveTimeoutMsg = true;
   1010         Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
   1011         PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
   1012         mAlarmManager.set(AlarmManager.RTC_WAKEUP,
   1013                 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent);
   1014     }
   1015 
   1016     @Override
   1017     public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
   1018         mRemoteDevice = remoteDevice;
   1019         if (mRemoteDevice == null || socket == null) {
   1020             Log.i(TAG, "mRemoteDevice :" + mRemoteDevice + " socket :" + socket);
   1021             return false;
   1022         }
   1023         mConnSocket = socket;
   1024         sRemoteDeviceName = mRemoteDevice.getName();
   1025         // In case getRemoteName failed and return null
   1026         if (TextUtils.isEmpty(sRemoteDeviceName)) {
   1027             sRemoteDeviceName = getString(R.string.defaultname);
   1028         }
   1029         int permission = mRemoteDevice.getPhonebookAccessPermission();
   1030         if (DEBUG) Log.d(TAG, "getPhonebookAccessPermission() = " + permission);
   1031 
   1032         if (permission == BluetoothDevice.ACCESS_ALLOWED) {
   1033             try {
   1034                 startObexServerSession();
   1035             } catch (IOException ex) {
   1036                 Log.e(TAG, "Caught exception starting obex server session" + ex.toString());
   1037             }
   1038 
   1039             if (!BluetoothPbapUtils.contactsLoaded) {
   1040                 mSessionStatusHandler.sendMessage(
   1041                         mSessionStatusHandler.obtainMessage(LOAD_CONTACTS));
   1042             }
   1043 
   1044         } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
   1045             if (DEBUG) {
   1046                 Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName
   1047                                 + " automatically as already rejected device");
   1048             }
   1049             return false;
   1050         } else { // permission == BluetoothDevice.ACCESS_UNKNOWN
   1051             // Send an Intent to Settings app to ask user preference.
   1052             Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
   1053             intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
   1054             intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
   1055                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
   1056             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
   1057             intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
   1058             mIsWaitingAuthorization = true;
   1059             sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
   1060             if (VERBOSE)
   1061                 Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName);
   1062             /* In case car kit time out and try to use HFP for phonebook
   1063              * access, while UI still there waiting for user to confirm */
   1064             mSessionStatusHandler.sendMessageDelayed(
   1065                     mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
   1066             /* We will continue the process when we receive
   1067              * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */
   1068         }
   1069         return true;
   1070     };
   1071 
   1072     /**
   1073      * Called when an unrecoverable error occurred in an accept thread.
   1074      * Close down the server socket, and restart.
   1075      * TODO: Change to message, to call start in correct context.
   1076      */
   1077     @Override
   1078     public synchronized void onAcceptFailed() {
   1079         // Clean up SDP record first
   1080         cleanUpSdpRecord();
   1081         // Force socket listener to restart
   1082         if (mServerSockets != null) {
   1083             mServerSockets.shutdown(false);
   1084             mServerSockets = null;
   1085         }
   1086         if (!mInterrupted && mAdapter != null && mAdapter.isEnabled()) {
   1087             startSocketListeners();
   1088         }
   1089     }
   1090 }
   1091