• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
BluetoothPbapContentObserver()223         public BluetoothPbapContentObserver() {
224             super(new Handler());
225         }
226 
227         @Override
onChange(boolean selfChange)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 
BluetoothPbapService()241     public BluetoothPbapService() {
242         mState = BluetoothPbap.STATE_DISCONNECTED;
243         mContext = this;
244     }
245 
246     // process the intent from receiver
parseIntent(final Intent intent)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 
initSocket()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 
closeServerSocket()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 
closeConnectionSocket()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 
closeService()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 
cleanUpSdpRecord()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 
startObexServerSession()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 
stopObexServerSession()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 
notifyAuthKeyInput(final String key)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 
notifyAuthCancelled()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
run()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 
shutdown()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 
setState(int state)747     private void setState(int state) {
748         setState(state, BluetoothPbap.RESULT_SUCCESS);
749     }
750 
setState(int state, int result)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 
getState()765     protected int getState() {
766         return mState;
767     }
768 
getRemoteDevice()769     protected BluetoothDevice getRemoteDevice() {
770         return mRemoteDevice;
771     }
772 
createPbapNotification(String action)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 
removePbapNotification(int id)816     private void removePbapNotification(int id) {
817         NotificationManager nm = (NotificationManager)
818             getSystemService(Context.NOTIFICATION_SERVICE);
819         nm.cancel(id);
820     }
821 
getLocalPhoneNum()822     public static String getLocalPhoneNum() {
823         return sLocalPhoneNum;
824     }
825 
getLocalPhoneName()826     public static String getLocalPhoneName() {
827         return sLocalPhoneName;
828     }
829 
getRemoteDeviceName()830     public static String getRemoteDeviceName() {
831         return sRemoteDeviceName;
832     }
833 
834     @Override
initBinder()835     protected IProfileServiceBinder initBinder() {
836         return new PbapBinder(this);
837     }
838 
839     @Override
start()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
stop()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 
disconnect()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 
getService(String perm)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 
PbapBinder(BluetoothPbapService service)918         PbapBinder(BluetoothPbapService service) {
919             Log.v(TAG, "PbapBinder()");
920             mService = service;
921         }
922 
cleanup()923         public boolean cleanup() {
924             mService = null;
925             return true;
926         }
927 
getState()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 
getClient()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 
isConnected(BluetoothDevice device)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 
connect(BluetoothDevice device)951         public boolean connect(BluetoothDevice device) {
952             BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM);
953             return false;
954         }
955 
disconnect()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      */
startSocketListeners()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 
getDbIdentifier()1000     long getDbIdentifier() {
1001         return BluetoothPbapUtils.mDbIdentifier.get();
1002     }
1003 
setUserTimeoutAlarm()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
onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket)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
onAcceptFailed()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