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         if (mServerSession != null) {
441             mServerSession.close();
442             mServerSession = null;
443         }
444 
445         closeConnectionSocket();
446         closeServerSocket();
447         if (mSessionStatusHandler != null) mSessionStatusHandler.removeCallbacksAndMessages(null);
448         if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
449     }
450 
startObexServerSession()451     private final void startObexServerSession() throws IOException {
452         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
453 
454         // acquire the wakeLock before start Obex transaction thread
455         if (mWakeLock == null) {
456             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
457             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
458                     "StartingObexPbapTransaction");
459             mWakeLock.setReferenceCounted(false);
460             mWakeLock.acquire();
461         }
462         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
463         if (tm != null) {
464             sLocalPhoneNum = tm.getLine1Number();
465             sLocalPhoneName = tm.getLine1AlphaTag();
466             if (TextUtils.isEmpty(sLocalPhoneName)) {
467                 sLocalPhoneName = this.getString(R.string.localPhoneName);
468             }
469         }
470 
471         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
472         synchronized (this) {
473             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
474             mAuth.setChallenged(false);
475             mAuth.setCancelled(false);
476         }
477         BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket);
478         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
479         setState(BluetoothPbap.STATE_CONNECTED);
480 
481         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
482         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
483             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
484 
485         if (VERBOSE) {
486             Log.v(TAG, "startObexServerSession() success!");
487         }
488     }
489 
stopObexServerSession()490     private void stopObexServerSession() {
491         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
492         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
493         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
494         // Release the wake lock if obex transaction is over
495         if (mWakeLock != null) {
496             mWakeLock.release();
497             mWakeLock = null;
498         }
499 
500         if (mServerSession != null) {
501             mServerSession.close();
502             mServerSession = null;
503         }
504         closeConnectionSocket();
505 
506         // Last obex transaction is finished, we start to listen for incoming
507         // connection again
508         if (mAdapter != null && mAdapter.isEnabled()) {
509             startSocketListeners();
510         }
511         setState(BluetoothPbap.STATE_DISCONNECTED);
512     }
513 
notifyAuthKeyInput(final String key)514     private void notifyAuthKeyInput(final String key) {
515         synchronized (mAuth) {
516             if (key != null) {
517                 mAuth.setSessionKey(key);
518             }
519             mAuth.setChallenged(true);
520             mAuth.notify();
521         }
522     }
523 
notifyAuthCancelled()524     private void notifyAuthCancelled() {
525         synchronized (mAuth) {
526             mAuth.setCancelled(true);
527             mAuth.notify();
528         }
529     }
530 
531     /**
532      * A thread that runs in the background waiting for remote rfcomm
533      * connect.Once a remote socket connected, this thread shall be
534      * shutdown.When the remote disconnect,this thread shall run again waiting
535      * for next request.
536      */
537     private class SocketAcceptThread extends Thread {
538 
539         private boolean stopped = false;
540 
541         @Override
run()542         public void run() {
543             BluetoothServerSocket serverSocket;
544             if (mServerSocket == null) {
545                 if (!initSocket()) {
546                     return;
547                 }
548             }
549 
550             while (!stopped) {
551                 try {
552                     if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
553                     serverSocket = mServerSocket;
554                     if (serverSocket == null) {
555                         Log.w(TAG, "mServerSocket is null");
556                         break;
557                     }
558                     mConnSocket = serverSocket.accept();
559                     if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
560 
561                     synchronized (BluetoothPbapService.this) {
562                         if (mConnSocket == null) {
563                             Log.w(TAG, "mConnSocket is null");
564                             break;
565                         }
566                         mRemoteDevice = mConnSocket.getRemoteDevice();
567                     }
568                     if (mRemoteDevice == null) {
569                         Log.i(TAG, "getRemoteDevice() = null");
570                         break;
571                     }
572                     sRemoteDeviceName = mRemoteDevice.getName();
573                     // In case getRemoteName failed and return null
574                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
575                         sRemoteDeviceName = getString(R.string.defaultname);
576                     }
577                     int permission = mRemoteDevice.getPhonebookAccessPermission();
578                     if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission);
579 
580                     if (permission == BluetoothDevice.ACCESS_ALLOWED) {
581                         try {
582                             if (VERBOSE) {
583                                 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName
584                                         + " automatically as already allowed device");
585                             }
586                             startObexServerSession();
587                         } catch (IOException ex) {
588                             Log.e(TAG, "Caught exception starting obex server session"
589                                     + ex.toString());
590                         }
591                     } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
592                         if (VERBOSE) {
593                             Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName
594                                     + " automatically as already rejected device");
595                         }
596                         stopObexServerSession();
597                     } else {  // permission == BluetoothDevice.ACCESS_UNKNOWN
598                         // Send an Intent to Settings app to ask user preference.
599                         Intent intent =
600                                 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
601                         intent.setPackage(getString(R.string.pairing_ui_package));
602                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
603                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
604                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
605                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
606                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, getName());
607 
608                         mIsWaitingAuthorization = true;
609                         sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
610 
611                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
612                                 + sRemoteDeviceName);
613 
614                         // In case car kit time out and try to use HFP for
615                         // phonebook
616                         // access, while UI still there waiting for user to
617                         // confirm
618                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
619                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
620                         // We will continue the process when we receive
621                         // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app.
622                     }
623                     stopped = true; // job done ,close this thread;
624                 } catch (IOException ex) {
625                     stopped=true;
626                     /*
627                     if (stopped) {
628                         break;
629                     }
630                     */
631                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
632                 }
633             }
634         }
635 
shutdown()636         void shutdown() {
637             stopped = true;
638             interrupt();
639         }
640     }
641 
642     protected final Handler mSessionStatusHandler = new Handler() {
643         @Override
644         public void handleMessage(Message msg) {
645             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
646 
647             switch (msg.what) {
648                 case START_LISTENER:
649                     if (mAdapter.isEnabled()) {
650                         startSocketListeners();
651                     }
652                     break;
653                 case USER_TIMEOUT:
654                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
655                     intent.setPackage(getString(R.string.pairing_ui_package));
656                     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
657                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
658                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
659                     sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
660                     mIsWaitingAuthorization = false;
661                     stopObexServerSession();
662                     break;
663                 case AUTH_TIMEOUT:
664                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
665                     sendBroadcast(i);
666                     removePbapNotification(NOTIFICATION_ID_AUTH);
667                     notifyAuthCancelled();
668                     break;
669                 case MSG_SERVERSESSION_CLOSE:
670                     stopObexServerSession();
671                     break;
672                 case MSG_SESSION_ESTABLISHED:
673                     break;
674                 case MSG_SESSION_DISCONNECTED:
675                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
676                     break;
677                 case MSG_OBEX_AUTH_CHALL:
678                     createPbapNotification(AUTH_CHALL_ACTION);
679                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
680                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
681                     break;
682                 case MSG_ACQUIRE_WAKE_LOCK:
683                     if (mWakeLock == null) {
684                         PowerManager pm = (PowerManager)getSystemService(
685                                           Context.POWER_SERVICE);
686                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
687                                     "StartingObexPbapTransaction");
688                         mWakeLock.setReferenceCounted(false);
689                         mWakeLock.acquire();
690                         Log.w(TAG, "Acquire Wake Lock");
691                     }
692                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
693                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
694                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
695                     break;
696                 case MSG_RELEASE_WAKE_LOCK:
697                     if (mWakeLock != null) {
698                         mWakeLock.release();
699                         mWakeLock = null;
700                         Log.w(TAG, "Release Wake Lock");
701                     }
702                     break;
703                 case SHUTDOWN:
704                     closeService();
705                     break;
706                 case LOAD_CONTACTS:
707                     BluetoothPbapUtils.loadAllContacts(mContext, this);
708                     break;
709                 case CHECK_SECONDARY_VERSION_COUNTER:
710                     BluetoothPbapUtils.updateSecondaryVersionCounter(mContext, this);
711                     break;
712                 case ROLLOVER_COUNTERS:
713                     BluetoothPbapUtils.rolloverCounters();
714                     break;
715                 default:
716                     break;
717             }
718         }
719     };
720 
setState(int state)721     private void setState(int state) {
722         setState(state, BluetoothPbap.RESULT_SUCCESS);
723     }
724 
setState(int state, int result)725     private synchronized void setState(int state, int result) {
726         if (state != mState) {
727             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
728                     + result);
729             int prevState = mState;
730             mState = state;
731             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
732             intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
733             intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
734             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
735             sendBroadcast(intent, BLUETOOTH_PERM);
736         }
737     }
738 
getState()739     protected int getState() {
740         return mState;
741     }
742 
getRemoteDevice()743     protected BluetoothDevice getRemoteDevice() {
744         return mRemoteDevice;
745     }
746 
createPbapNotification(String action)747     private void createPbapNotification(String action) {
748 
749         NotificationManager nm = (NotificationManager)
750             getSystemService(Context.NOTIFICATION_SERVICE);
751         NotificationChannel notificationChannel = new NotificationChannel(PBAP_NOTIFICATION_CHANNEL,
752                 getString(R.string.pbap_notification_group), NotificationManager.IMPORTANCE_HIGH);
753         nm.createNotificationChannel(notificationChannel);
754 
755         // Create an intent triggered by clicking on the status icon.
756         Intent clickIntent = new Intent();
757         clickIntent.setClass(this, BluetoothPbapActivity.class);
758         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
759         clickIntent.setAction(action);
760 
761         // Create an intent triggered by clicking on the
762         // "Clear All Notifications" button
763         Intent deleteIntent = new Intent();
764         deleteIntent.setClass(this, BluetoothPbapService.class);
765         deleteIntent.setAction(AUTH_CANCELLED_ACTION);
766 
767         String name = getRemoteDeviceName();
768 
769         if (action.equals(AUTH_CHALL_ACTION)) {
770             Notification notification =
771                     new Notification.Builder(this, PBAP_NOTIFICATION_CHANNEL)
772                             .setWhen(System.currentTimeMillis())
773                             .setContentTitle(getString(R.string.auth_notif_title))
774                             .setContentText(getString(R.string.auth_notif_message, name))
775                             .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
776                             .setTicker(getString(R.string.auth_notif_ticker))
777                             .setColor(getResources().getColor(
778                                     com.android.internal.R.color.system_notification_accent_color,
779                                     this.getTheme()))
780                             .setFlag(Notification.FLAG_AUTO_CANCEL, true)
781                             .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true)
782                             .setDefaults(Notification.DEFAULT_SOUND)
783                             .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0))
784                             .setDeleteIntent(PendingIntent.getBroadcast(this, 0, deleteIntent, 0))
785                             .build();
786             nm.notify(NOTIFICATION_ID_AUTH, notification);
787         }
788     }
789 
removePbapNotification(int id)790     private void removePbapNotification(int id) {
791         NotificationManager nm = (NotificationManager)
792             getSystemService(Context.NOTIFICATION_SERVICE);
793         nm.cancel(id);
794     }
795 
getLocalPhoneNum()796     public static String getLocalPhoneNum() {
797         return sLocalPhoneNum;
798     }
799 
getLocalPhoneName()800     public static String getLocalPhoneName() {
801         return sLocalPhoneName;
802     }
803 
getRemoteDeviceName()804     public static String getRemoteDeviceName() {
805         return sRemoteDeviceName;
806     }
807 
808     @Override
initBinder()809     protected IProfileServiceBinder initBinder() {
810         return new PbapBinder(this);
811     }
812 
813     @Override
start()814     protected boolean start() {
815         Log.v(TAG, "start()");
816         IntentFilter filter = new IntentFilter();
817         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
818         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
819         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
820         filter.addAction(AUTH_RESPONSE_ACTION);
821         filter.addAction(AUTH_CANCELLED_ACTION);
822         mInterrupted = false;
823         BluetoothPbapConfig.init(this);
824         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
825         if (mContactChangeObserver == null) {
826             registerReceiver(mPbapReceiver, filter);
827             try {
828                 if (DEBUG) Log.d(TAG, "Registering observer");
829                 mContactChangeObserver = new BluetoothPbapContentObserver();
830                 getContentResolver().registerContentObserver(
831                         DevicePolicyUtils.getEnterprisePhoneUri(this), false,
832                         mContactChangeObserver);
833             } catch (SQLiteException e) {
834                 Log.e(TAG, "SQLite exception: " + e);
835             } catch (IllegalStateException e) {
836                 Log.e(TAG, "Illegal state exception, content observer is already registered");
837             }
838         }
839         return true;
840     }
841 
842     @Override
stop()843     protected boolean stop() {
844         Log.v(TAG, "stop()");
845         if (mContactChangeObserver == null) {
846             Log.i(TAG, "Avoid unregister when receiver it is not registered");
847             return true;
848         }
849         try {
850             unregisterReceiver(mPbapReceiver);
851             getContentResolver().unregisterContentObserver(mContactChangeObserver);
852             mContactChangeObserver = null;
853         } catch (Exception e) {
854             Log.w(TAG, "Unable to unregister pbap receiver", e);
855         }
856         mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
857         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
858         return true;
859     }
860 
disconnect()861     protected void disconnect() {
862         synchronized (this) {
863             if (mState == BluetoothPbap.STATE_CONNECTED) {
864                 if (mServerSession != null) {
865                     mServerSession.close();
866                     mServerSession = null;
867                 }
868 
869                 closeConnectionSocket();
870 
871                 setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
872             }
873         }
874     }
875 
876     // Has to be a static class or a memory leak can occur.
877     private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
878         private BluetoothPbapService mService;
879 
getService(String perm)880         private BluetoothPbapService getService(String perm) {
881             if (!Utils.checkCaller()) {
882                 Log.w(TAG, "not allowed for non-active user");
883                 return null;
884             }
885             if (mService != null && mService.isAvailable()) {
886                 mService.enforceCallingOrSelfPermission(perm, "Need " + perm + " permission");
887                 return mService;
888             }
889             return null;
890         }
891 
PbapBinder(BluetoothPbapService service)892         PbapBinder(BluetoothPbapService service) {
893             Log.v(TAG, "PbapBinder()");
894             mService = service;
895         }
896 
cleanup()897         public boolean cleanup() {
898             mService = null;
899             return true;
900         }
901 
getState()902         public int getState() {
903             if (DEBUG) Log.d(TAG, "getState = " + mService.getState());
904             BluetoothPbapService service = getService(BLUETOOTH_PERM);
905             if (service == null) return BluetoothPbap.STATE_DISCONNECTED;
906 
907             return service.getState();
908         }
909 
getClient()910         public BluetoothDevice getClient() {
911             if (DEBUG) Log.d(TAG, "getClient = " + mService.getRemoteDevice());
912             BluetoothPbapService service = getService(BLUETOOTH_PERM);
913             if (service == null) return null;
914             return service.getRemoteDevice();
915         }
916 
isConnected(BluetoothDevice device)917         public boolean isConnected(BluetoothDevice device) {
918             if (DEBUG) Log.d(TAG, "isConnected " + device);
919             BluetoothPbapService service = getService(BLUETOOTH_PERM);
920             if (service == null) return false;
921             return service.getState() == BluetoothPbap.STATE_CONNECTED
922                     && service.getRemoteDevice().equals(device);
923         }
924 
connect(BluetoothDevice device)925         public boolean connect(BluetoothDevice device) {
926             BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM);
927             return false;
928         }
929 
disconnect()930         public void disconnect() {
931             if (DEBUG) Log.d(TAG, "disconnect");
932             BluetoothPbapService service = getService(BLUETOOTH_ADMIN_PERM);
933             if (service == null) return;
934             service.disconnect();
935         }
936     }
937 
startSocketListeners()938     synchronized private void startSocketListeners() {
939         if (DEBUG) Log.d(TAG, "startsocketListener");
940         if (mServerSession != null) {
941             if (DEBUG) Log.d(TAG, "mServerSession exists - shutting it down...");
942             mServerSession.close();
943             mServerSession = null;
944         }
945         closeConnectionSocket();
946         if (mServerSockets != null) {
947             mServerSockets.prepareForNewConnect();
948         } else {
949             mServerSockets = ObexServerSockets.create(this);
950             if (mServerSockets == null) {
951                 // TODO: Handle - was not handled before
952                 Log.e(TAG, "Failed to start the listeners");
953                 return;
954             }
955             SdpManager sdpManager = SdpManager.getDefaultManager();
956             if (sdpManager == null) {
957                 Log.e(TAG, "Failed to start the listeners sdp null ");
958                 return;
959             }
960             if (mAdapter != null && mSdpHandle >= 0) {
961                 Log.d(TAG, "Removing SDP record for PBAP with SDP handle:" + mSdpHandle);
962                 boolean status = sdpManager.removeSdpRecord(mSdpHandle);
963                 Log.d(TAG, "RemoveSDPrecord returns " + status);
964                 mSdpHandle = -1;
965             }
966             mSdpHandle = SdpManager.getDefaultManager().createPbapPseRecord(
967                     "OBEX Phonebook Access Server", mServerSockets.getRfcommChannel(),
968                     mServerSockets.getL2capPsm(), SDP_PBAP_SERVER_VERSION,
969                     SDP_PBAP_SUPPORTED_REPOSITORIES, SDP_PBAP_SUPPORTED_FEATURES);
970             // fetch Pbap Params to check if significant change has happened to Database
971             BluetoothPbapUtils.fetchPbapParams(mContext);
972 
973             if (DEBUG) Log.d(TAG, "PBAP server with handle:" + mSdpHandle);
974         }
975     }
976 
getDbIdentifier()977     long getDbIdentifier() {
978         return BluetoothPbapUtils.mDbIdentifier.get();
979     }
980 
setUserTimeoutAlarm()981     private void setUserTimeoutAlarm() {
982         if (DEBUG) Log.d(TAG, "SetUserTimeOutAlarm()");
983         if (mAlarmManager == null) {
984             mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
985         }
986         mRemoveTimeoutMsg = true;
987         Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
988         PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
989         mAlarmManager.set(AlarmManager.RTC_WAKEUP,
990                 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent);
991     }
992 
993     @Override
onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket)994     public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
995         mRemoteDevice = remoteDevice;
996         if (mRemoteDevice == null || socket == null) {
997             Log.i(TAG, "mRemoteDevice :" + mRemoteDevice + " socket :" + socket);
998             return false;
999         }
1000         mConnSocket = socket;
1001         sRemoteDeviceName = mRemoteDevice.getName();
1002         // In case getRemoteName failed and return null
1003         if (TextUtils.isEmpty(sRemoteDeviceName)) {
1004             sRemoteDeviceName = getString(R.string.defaultname);
1005         }
1006         int permission = mRemoteDevice.getPhonebookAccessPermission();
1007         if (DEBUG) Log.d(TAG, "getPhonebookAccessPermission() = " + permission);
1008 
1009         if (permission == BluetoothDevice.ACCESS_ALLOWED) {
1010             try {
1011                 startObexServerSession();
1012             } catch (IOException ex) {
1013                 Log.e(TAG, "Caught exception starting obex server session" + ex.toString());
1014             }
1015 
1016             if (!BluetoothPbapUtils.contactsLoaded) {
1017                 mSessionStatusHandler.sendMessage(
1018                         mSessionStatusHandler.obtainMessage(LOAD_CONTACTS));
1019             }
1020 
1021         } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
1022             if (DEBUG) {
1023                 Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName
1024                                 + " automatically as already rejected device");
1025             }
1026             return false;
1027         } else { // permission == BluetoothDevice.ACCESS_UNKNOWN
1028             // Send an Intent to Settings app to ask user preference.
1029             Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
1030             intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
1031             intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
1032                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
1033             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
1034             intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
1035             mIsWaitingAuthorization = true;
1036             sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
1037             if (VERBOSE)
1038                 Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName);
1039             /* In case car kit time out and try to use HFP for phonebook
1040              * access, while UI still there waiting for user to confirm */
1041             mSessionStatusHandler.sendMessageDelayed(
1042                     mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
1043             /* We will continue the process when we receive
1044              * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */
1045         }
1046         return true;
1047     };
1048 
1049     /**
1050      * Called when an unrecoverable error occurred in an accept thread.
1051      * Close down the server socket, and restart.
1052      * TODO: Change to message, to call start in correct context.
1053      */
1054     @Override
onAcceptFailed()1055     public synchronized void onAcceptFailed() {
1056         // Force socket listener to restart
1057         mServerSockets = null;
1058         if (!mInterrupted && mAdapter != null && mAdapter.isEnabled()) {
1059             startSocketListeners();
1060         }
1061     }
1062 }
1063