• 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 static android.Manifest.permission.BLUETOOTH_CONNECT;
36 
37 import android.annotation.RequiresPermission;
38 import android.app.Activity;
39 import android.bluetooth.BluetoothAdapter;
40 import android.bluetooth.BluetoothDevice;
41 import android.bluetooth.BluetoothProfile;
42 import android.bluetooth.BluetoothSocket;
43 import android.bluetooth.IBluetoothPbap;
44 import android.content.Attributable;
45 import android.content.AttributionSource;
46 import android.content.BroadcastReceiver;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.IntentFilter;
50 import android.database.ContentObserver;
51 import android.database.sqlite.SQLiteException;
52 import android.os.Handler;
53 import android.os.HandlerThread;
54 import android.os.Looper;
55 import android.os.Message;
56 import android.os.PowerManager;
57 import android.os.UserManager;
58 import android.telephony.TelephonyManager;
59 import android.util.Log;
60 
61 import com.android.bluetooth.IObexConnectionHandler;
62 import com.android.bluetooth.ObexServerSockets;
63 import com.android.bluetooth.R;
64 import com.android.bluetooth.Utils;
65 import com.android.bluetooth.btservice.AdapterService;
66 import com.android.bluetooth.btservice.ProfileService;
67 import com.android.bluetooth.btservice.storage.DatabaseManager;
68 import com.android.bluetooth.sdp.SdpManager;
69 import com.android.bluetooth.util.DevicePolicyUtils;
70 import com.android.internal.annotations.VisibleForTesting;
71 
72 import java.util.ArrayList;
73 import java.util.HashMap;
74 import java.util.List;
75 import java.util.Objects;
76 
77 public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler {
78     private static final String TAG = "BluetoothPbapService";
79 
80     /**
81      * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
82      * restart com.android.bluetooth process. only enable DEBUG log:
83      * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
84      * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
85      */
86 
87     public static final boolean DEBUG = true;
88 
89     public static final boolean VERBOSE = false;
90 
91     /**
92      * Intent indicating incoming obex authentication request which is from
93      * PCE(Carkit)
94      */
95     static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
96 
97     /**
98      * Intent indicating obex session key input complete by user which is sent
99      * from BluetoothPbapActivity
100      */
101     static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
102 
103     /**
104      * Intent indicating user canceled obex authentication session key input
105      * which is sent from BluetoothPbapActivity
106      */
107     static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
108 
109     /**
110      * Intent indicating timeout for user confirmation, which is sent to
111      * BluetoothPbapActivity
112      */
113     static final String USER_CONFIRM_TIMEOUT_ACTION =
114             "com.android.bluetooth.pbap.userconfirmtimeout";
115 
116     /**
117      * Intent Extra name indicating session key which is sent from
118      * BluetoothPbapActivity
119      */
120     static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
121     static final String EXTRA_DEVICE = "com.android.bluetooth.pbap.device";
122     static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
123 
124     static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
125     static final int MSG_RELEASE_WAKE_LOCK = 5005;
126     static final int MSG_STATE_MACHINE_DONE = 5006;
127 
128     static final int START_LISTENER = 1;
129     static final int USER_TIMEOUT = 2;
130     static final int SHUTDOWN = 3;
131     static final int LOAD_CONTACTS = 4;
132     static final int CONTACTS_LOADED = 5;
133     static final int CHECK_SECONDARY_VERSION_COUNTER = 6;
134     static final int ROLLOVER_COUNTERS = 7;
135     static final int GET_LOCAL_TELEPHONY_DETAILS = 8;
136 
137     static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
138     static final int RELEASE_WAKE_LOCK_DELAY = 10000;
139 
140     private PowerManager.WakeLock mWakeLock;
141 
142     private static String sLocalPhoneNum;
143     private static String sLocalPhoneName;
144 
145     private ObexServerSockets mServerSockets = null;
146     private DatabaseManager mDatabaseManager;
147 
148     private static final int SDP_PBAP_SERVER_VERSION = 0x0102;
149     // PBAP v1.2.3, Sec. 7.1.2: local phonebook and favorites
150     private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0009;
151     private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F;
152 
153     /* PBAP will use Bluetooth notification ID from 1000000 (included) to 2000000 (excluded).
154        The notification ID should be unique in Bluetooth package. */
155     private static final int PBAP_NOTIFICATION_ID_START = 1000000;
156     private static final int PBAP_NOTIFICATION_ID_END = 2000000;
157 
158     private int mSdpHandle = -1;
159 
160     protected Context mContext;
161 
162     private PbapHandler mSessionStatusHandler;
163     private HandlerThread mHandlerThread;
164     private final HashMap<BluetoothDevice, PbapStateMachine> mPbapStateMachineMap = new HashMap<>();
165     private volatile int mNextNotificationId = PBAP_NOTIFICATION_ID_START;
166 
167     // package and class name to which we send intent to check phone book access permission
168     private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
169     private static final String ACCESS_AUTHORITY_CLASS =
170             "com.android.settings.bluetooth.BluetoothPermissionRequest";
171 
172     private Thread mThreadLoadContacts;
173     private boolean mContactsLoaded = false;
174 
175     private Thread mThreadUpdateSecVersionCounter;
176 
177     private static BluetoothPbapService sBluetoothPbapService;
178 
179     private class BluetoothPbapContentObserver extends ContentObserver {
BluetoothPbapContentObserver()180         BluetoothPbapContentObserver() {
181             super(new Handler());
182         }
183 
184         @Override
onChange(boolean selfChange)185         public void onChange(boolean selfChange) {
186             Log.d(TAG, " onChange on contact uri ");
187             sendUpdateRequest();
188         }
189     }
190 
sendUpdateRequest()191     private void sendUpdateRequest() {
192         if (mContactsLoaded) {
193             if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) {
194                 mSessionStatusHandler.sendMessage(
195                         mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER));
196             }
197         }
198     }
199 
200     private BluetoothPbapContentObserver mContactChangeObserver;
201 
parseIntent(final Intent intent)202     private void parseIntent(final Intent intent) {
203         String action = intent.getAction();
204         if (DEBUG) {
205             Log.d(TAG, "action: " + action);
206         }
207         if (BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY.equals(action)) {
208             int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
209                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
210             if (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
211                 return;
212             }
213 
214             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
215             synchronized (mPbapStateMachineMap) {
216                 PbapStateMachine sm = mPbapStateMachineMap.get(device);
217                 if (sm == null) {
218                     Log.w(TAG, "device not connected! device=" + device);
219                     return;
220                 }
221                 mSessionStatusHandler.removeMessages(USER_TIMEOUT, sm);
222                 int access = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
223                         BluetoothDevice.CONNECTION_ACCESS_NO);
224                 boolean savePreference = intent.getBooleanExtra(
225                         BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false);
226 
227                 if (access == BluetoothDevice.CONNECTION_ACCESS_YES) {
228                     if (savePreference) {
229                         device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
230                         if (VERBOSE) {
231                             Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)");
232                         }
233                     }
234                     sm.sendMessage(PbapStateMachine.AUTHORIZED);
235                 } else {
236                     if (savePreference) {
237                         device.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
238                         if (VERBOSE) {
239                             Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)");
240                         }
241                     }
242                     sm.sendMessage(PbapStateMachine.REJECTED);
243                 }
244             }
245         } else if (AUTH_RESPONSE_ACTION.equals(action)) {
246             String sessionKey = intent.getStringExtra(EXTRA_SESSION_KEY);
247             BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE);
248             synchronized (mPbapStateMachineMap) {
249                 PbapStateMachine sm = mPbapStateMachineMap.get(device);
250                 if (sm == null) {
251                     return;
252                 }
253                 Message msg = sm.obtainMessage(PbapStateMachine.AUTH_KEY_INPUT, sessionKey);
254                 sm.sendMessage(msg);
255             }
256         } else if (AUTH_CANCELLED_ACTION.equals(action)) {
257             BluetoothDevice device = intent.getParcelableExtra(EXTRA_DEVICE);
258             synchronized (mPbapStateMachineMap) {
259                 PbapStateMachine sm = mPbapStateMachineMap.get(device);
260                 if (sm == null) {
261                     return;
262                 }
263                 sm.sendMessage(PbapStateMachine.AUTH_CANCELLED);
264             }
265         } else {
266             Log.w(TAG, "Unhandled intent action: " + action);
267         }
268     }
269 
270     private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() {
271         @Override
272         public void onReceive(Context context, Intent intent) {
273             parseIntent(intent);
274         }
275     };
276 
closeService()277     private void closeService() {
278         if (VERBOSE) {
279             Log.v(TAG, "Pbap Service closeService");
280         }
281 
282         BluetoothPbapUtils.savePbapParams(this);
283 
284         if (mWakeLock != null) {
285             mWakeLock.release();
286             mWakeLock = null;
287         }
288 
289         cleanUpServerSocket();
290 
291         if (mSessionStatusHandler != null) {
292             mSessionStatusHandler.removeCallbacksAndMessages(null);
293         }
294     }
295 
cleanUpServerSocket()296     private void cleanUpServerSocket() {
297         // Step 1, 2: clean up active server session and connection socket
298         synchronized (mPbapStateMachineMap) {
299             for (PbapStateMachine stateMachine : mPbapStateMachineMap.values()) {
300                 stateMachine.sendMessage(PbapStateMachine.DISCONNECT);
301             }
302         }
303         // Step 3: clean up SDP record
304         cleanUpSdpRecord();
305         // Step 4: clean up existing server sockets
306         if (mServerSockets != null) {
307             mServerSockets.shutdown(false);
308             mServerSockets = null;
309         }
310     }
311 
createSdpRecord()312     private void createSdpRecord() {
313         if (mSdpHandle > -1) {
314             Log.w(TAG, "createSdpRecord, SDP record already created");
315         }
316         mSdpHandle = SdpManager.getDefaultManager()
317                 .createPbapPseRecord("OBEX Phonebook Access Server",
318                         mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(),
319                         SDP_PBAP_SERVER_VERSION, SDP_PBAP_SUPPORTED_REPOSITORIES,
320                         SDP_PBAP_SUPPORTED_FEATURES);
321         if (DEBUG) {
322             Log.d(TAG, "created Sdp record, mSdpHandle=" + mSdpHandle);
323         }
324     }
325 
cleanUpSdpRecord()326     private void cleanUpSdpRecord() {
327         if (mSdpHandle < 0) {
328             Log.w(TAG, "cleanUpSdpRecord, SDP record never created");
329             return;
330         }
331         int sdpHandle = mSdpHandle;
332         mSdpHandle = -1;
333         SdpManager sdpManager = SdpManager.getDefaultManager();
334         if (DEBUG) {
335             Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
336         }
337         if (sdpManager == null) {
338             Log.e(TAG, "sdpManager is null");
339         } else if (!sdpManager.removeSdpRecord(sdpHandle)) {
340             Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
341         }
342     }
343 
344     private class PbapHandler extends Handler {
PbapHandler(Looper looper)345         private PbapHandler(Looper looper) {
346             super(looper);
347         }
348 
349         @Override
handleMessage(Message msg)350         public void handleMessage(Message msg) {
351             if (VERBOSE) {
352                 Log.v(TAG, "Handler(): got msg=" + msg.what);
353             }
354 
355             switch (msg.what) {
356                 case START_LISTENER:
357                     mServerSockets = ObexServerSockets.create(BluetoothPbapService.this);
358                     if (mServerSockets == null) {
359                         Log.w(TAG, "ObexServerSockets.create() returned null");
360                         break;
361                     }
362                     createSdpRecord();
363                     // fetch Pbap Params to check if significant change has happened to Database
364                     BluetoothPbapUtils.fetchPbapParams(mContext);
365                     break;
366                 case USER_TIMEOUT:
367                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
368                     intent.setPackage(getString(R.string.pairing_ui_package));
369                     PbapStateMachine stateMachine = (PbapStateMachine) msg.obj;
370                     intent.putExtra(BluetoothDevice.EXTRA_DEVICE, stateMachine.getRemoteDevice());
371                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
372                             BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
373                     sendBroadcast(intent, BLUETOOTH_CONNECT,
374                             Utils.getTempAllowlistBroadcastOptions());
375                     stateMachine.sendMessage(PbapStateMachine.REJECTED);
376                     break;
377                 case MSG_ACQUIRE_WAKE_LOCK:
378                     if (mWakeLock == null) {
379                         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
380                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
381                                 "StartingObexPbapTransaction");
382                         mWakeLock.setReferenceCounted(false);
383                         mWakeLock.acquire();
384                         Log.w(TAG, "Acquire Wake Lock");
385                     }
386                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
387                     mSessionStatusHandler.sendMessageDelayed(
388                             mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK),
389                             RELEASE_WAKE_LOCK_DELAY);
390                     break;
391                 case MSG_RELEASE_WAKE_LOCK:
392                     if (mWakeLock != null) {
393                         mWakeLock.release();
394                         mWakeLock = null;
395                     }
396                     break;
397                 case SHUTDOWN:
398                     closeService();
399                     break;
400                 case LOAD_CONTACTS:
401                     loadAllContacts();
402                     break;
403                 case CONTACTS_LOADED:
404                     mContactsLoaded = true;
405                     break;
406                 case CHECK_SECONDARY_VERSION_COUNTER:
407                     updateSecondaryVersion();
408                     break;
409                 case ROLLOVER_COUNTERS:
410                     BluetoothPbapUtils.rolloverCounters();
411                     break;
412                 case MSG_STATE_MACHINE_DONE:
413                     PbapStateMachine sm = (PbapStateMachine) msg.obj;
414                     BluetoothDevice remoteDevice = sm.getRemoteDevice();
415                     sm.quitNow();
416                     synchronized (mPbapStateMachineMap) {
417                         mPbapStateMachineMap.remove(remoteDevice);
418                     }
419                     break;
420                 case GET_LOCAL_TELEPHONY_DETAILS:
421                     getLocalTelephonyDetails();
422                 default:
423                     break;
424             }
425         }
426     }
427 
428     /**
429      * Get the current connection state of PBAP with the passed in device
430      *
431      * @param device is the device whose connection state to PBAP we are trying to get
432      * @return current connection state, one of {@link BluetoothProfile#STATE_DISCONNECTED},
433      * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, or
434      * {@link BluetoothProfile#STATE_DISCONNECTING}
435      */
436     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
getConnectionState(BluetoothDevice device)437     public int getConnectionState(BluetoothDevice device) {
438         enforceCallingOrSelfPermission(
439                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
440         if (mPbapStateMachineMap == null) {
441             return BluetoothProfile.STATE_DISCONNECTED;
442         }
443 
444         synchronized (mPbapStateMachineMap) {
445             PbapStateMachine sm = mPbapStateMachineMap.get(device);
446             if (sm == null) {
447                 return BluetoothProfile.STATE_DISCONNECTED;
448             }
449             return sm.getConnectionState();
450         }
451     }
452 
getConnectedDevices()453     List<BluetoothDevice> getConnectedDevices() {
454         if (mPbapStateMachineMap == null) {
455             return new ArrayList<>();
456         }
457         synchronized (mPbapStateMachineMap) {
458             return new ArrayList<>(mPbapStateMachineMap.keySet());
459         }
460     }
461 
getDevicesMatchingConnectionStates(int[] states)462     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
463         List<BluetoothDevice> devices = new ArrayList<>();
464         if (mPbapStateMachineMap == null || states == null) {
465             return devices;
466         }
467         synchronized (mPbapStateMachineMap) {
468             for (int state : states) {
469                 for (BluetoothDevice device : mPbapStateMachineMap.keySet()) {
470                     if (state == mPbapStateMachineMap.get(device).getConnectionState()) {
471                         devices.add(device);
472                     }
473                 }
474             }
475         }
476         return devices;
477     }
478 
479     /**
480      * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
481      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
482      *
483      * <p> The device should already be paired.
484      * Connection policy can be one of:
485      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
486      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
487      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
488      *
489      * @param device Paired bluetooth device
490      * @param connectionPolicy is the connection policy to set to for this profile
491      * @return true if connectionPolicy is set, false on error
492      */
493     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)494     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
495         enforceCallingOrSelfPermission(
496                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
497         if (DEBUG) {
498             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
499         }
500 
501         if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.PBAP,
502                   connectionPolicy)) {
503             return false;
504         }
505         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
506             disconnect(device);
507         }
508         return true;
509     }
510 
511     /**
512      * Get the connection policy of the profile.
513      *
514      * <p> The connection policy can be any of:
515      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
516      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
517      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
518      *
519      * @param device Bluetooth device
520      * @return connection policy of the device
521      * @hide
522      */
getConnectionPolicy(BluetoothDevice device)523     public int getConnectionPolicy(BluetoothDevice device) {
524         if (device == null) {
525             throw new IllegalArgumentException("Null device");
526         }
527         return mDatabaseManager
528                 .getProfileConnectionPolicy(device, BluetoothProfile.PBAP);
529     }
530 
531     /**
532      * Disconnects pbap server profile with device
533      * @param device is the remote bluetooth device
534      */
disconnect(BluetoothDevice device)535     public void disconnect(BluetoothDevice device) {
536         synchronized (mPbapStateMachineMap) {
537             PbapStateMachine sm = mPbapStateMachineMap.get(device);
538             if (sm != null) {
539                 sm.sendMessage(PbapStateMachine.DISCONNECT);
540             }
541         }
542     }
543 
getLocalPhoneNum()544     static String getLocalPhoneNum() {
545         return sLocalPhoneNum;
546     }
547 
getLocalPhoneName()548     static String getLocalPhoneName() {
549         return sLocalPhoneName;
550     }
551 
552     @Override
initBinder()553     protected IProfileServiceBinder initBinder() {
554         return new PbapBinder(this);
555     }
556 
557     @Override
start()558     protected boolean start() {
559         if (VERBOSE) {
560             Log.v(TAG, "start()");
561         }
562         mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
563             "DatabaseManager cannot be null when PbapService starts");
564 
565         mContext = this;
566         mContactsLoaded = false;
567         mHandlerThread = new HandlerThread("PbapHandlerThread");
568         mHandlerThread.start();
569         mSessionStatusHandler = new PbapHandler(mHandlerThread.getLooper());
570         IntentFilter filter = new IntentFilter();
571         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
572         filter.addAction(AUTH_RESPONSE_ACTION);
573         filter.addAction(AUTH_CANCELLED_ACTION);
574         BluetoothPbapConfig.init(this);
575         registerReceiver(mPbapReceiver, filter);
576         try {
577             mContactChangeObserver = new BluetoothPbapContentObserver();
578             getContentResolver().registerContentObserver(
579                     DevicePolicyUtils.getEnterprisePhoneUri(this), false,
580                     mContactChangeObserver);
581         } catch (SQLiteException e) {
582             Log.e(TAG, "SQLite exception: " + e);
583         } catch (IllegalStateException e) {
584             Log.e(TAG, "Illegal state exception, content observer is already registered");
585         }
586 
587         setBluetoothPbapService(this);
588 
589         mSessionStatusHandler.sendMessage(
590                 mSessionStatusHandler.obtainMessage(GET_LOCAL_TELEPHONY_DETAILS));
591         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(LOAD_CONTACTS));
592         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
593         return true;
594     }
595 
596     @Override
stop()597     protected boolean stop() {
598         if (VERBOSE) {
599             Log.v(TAG, "stop()");
600         }
601         setBluetoothPbapService(null);
602         if (mSessionStatusHandler != null) {
603             mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
604         }
605         if (mHandlerThread != null) {
606             mHandlerThread.quitSafely();
607         }
608         mContactsLoaded = false;
609         if (mContactChangeObserver == null) {
610             Log.i(TAG, "Avoid unregister when receiver it is not registered");
611             return true;
612         }
613         unregisterReceiver(mPbapReceiver);
614         getContentResolver().unregisterContentObserver(mContactChangeObserver);
615         mContactChangeObserver = null;
616         return true;
617     }
618 
619     /**
620      * Get the current instance of {@link BluetoothPbapService}
621      *
622      * @return current instance of {@link BluetoothPbapService}
623      */
624     @VisibleForTesting
getBluetoothPbapService()625     public static synchronized BluetoothPbapService getBluetoothPbapService() {
626         if (sBluetoothPbapService == null) {
627             Log.w(TAG, "getBluetoothPbapService(): service is null");
628             return null;
629         }
630         if (!sBluetoothPbapService.isAvailable()) {
631             Log.w(TAG, "getBluetoothPbapService(): service is not available");
632             return null;
633         }
634         return sBluetoothPbapService;
635     }
636 
setBluetoothPbapService(BluetoothPbapService instance)637     private static synchronized void setBluetoothPbapService(BluetoothPbapService instance) {
638         if (DEBUG) {
639             Log.d(TAG, "setBluetoothPbapService(): set to: " + instance);
640         }
641         sBluetoothPbapService = instance;
642     }
643 
644     @Override
setCurrentUser(int userId)645     protected void setCurrentUser(int userId) {
646         Log.i(TAG, "setCurrentUser(" + userId + ")");
647         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
648         if (userManager.isUserUnlocked(userId)) {
649             setUserUnlocked(userId);
650         }
651     }
652 
653     @Override
setUserUnlocked(int userId)654     protected void setUserUnlocked(int userId) {
655         Log.i(TAG, "setUserUnlocked(" + userId + ")");
656         sendUpdateRequest();
657     }
658 
659     private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
660         private BluetoothPbapService mService;
661 
662         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)663         private BluetoothPbapService getService(AttributionSource source) {
664             if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
665                     || !Utils.checkServiceAvailable(mService, TAG)
666                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
667                 return null;
668             }
669             return mService;
670         }
671 
PbapBinder(BluetoothPbapService service)672         PbapBinder(BluetoothPbapService service) {
673             if (VERBOSE) {
674                 Log.v(TAG, "PbapBinder()");
675             }
676             mService = service;
677         }
678 
679         @Override
cleanup()680         public void cleanup() {
681             mService = null;
682         }
683 
684         @Override
getConnectedDevices(AttributionSource source)685         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
686             if (DEBUG) {
687                 Log.d(TAG, "getConnectedDevices");
688             }
689             BluetoothPbapService service = getService(source);
690             if (service == null) {
691                 return new ArrayList<>(0);
692             }
693             return service.getConnectedDevices();
694         }
695 
696         @Override
getDevicesMatchingConnectionStates(int[] states, AttributionSource source)697         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states,
698                 AttributionSource source) {
699             if (DEBUG) {
700                 Log.d(TAG, "getDevicesMatchingConnectionStates");
701             }
702             BluetoothPbapService service = getService(source);
703             if (service == null) {
704                 return new ArrayList<>(0);
705             }
706             return service.getDevicesMatchingConnectionStates(states);
707         }
708 
709         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)710         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
711             if (DEBUG) {
712                 Log.d(TAG, "getConnectionState: " + device);
713             }
714             Attributable.setAttributionSource(device, source);
715             BluetoothPbapService service = getService(source);
716             if (service == null) {
717                 return BluetoothAdapter.STATE_DISCONNECTED;
718             }
719             return service.getConnectionState(device);
720         }
721 
722         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source)723         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
724                 AttributionSource source) {
725             if (DEBUG) {
726                 Log.d(TAG, "setConnectionPolicy for device: " + device + ", policy:"
727                         + connectionPolicy);
728             }
729             Attributable.setAttributionSource(device, source);
730             BluetoothPbapService service = getService(source);
731             if (service == null) {
732                 return false;
733             }
734             return service.setConnectionPolicy(device, connectionPolicy);
735         }
736 
737         @Override
disconnect(BluetoothDevice device, AttributionSource source)738         public void disconnect(BluetoothDevice device, AttributionSource source) {
739             if (DEBUG) {
740                 Log.d(TAG, "disconnect");
741             }
742             Attributable.setAttributionSource(device, source);
743             BluetoothPbapService service = getService(source);
744             if (service == null) {
745                 return;
746             }
747             service.disconnect(device);
748         }
749     }
750 
751     @Override
onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket)752     public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
753         if (remoteDevice == null || socket == null) {
754             Log.e(TAG, "onConnect(): Unexpected null. remoteDevice=" + remoteDevice
755                     + " socket=" + socket);
756             return false;
757         }
758 
759         PbapStateMachine sm = PbapStateMachine.make(this, mHandlerThread.getLooper(), remoteDevice,
760                 socket,  this, mSessionStatusHandler, mNextNotificationId);
761         mNextNotificationId++;
762         if (mNextNotificationId == PBAP_NOTIFICATION_ID_END) {
763             mNextNotificationId = PBAP_NOTIFICATION_ID_START;
764         }
765         synchronized (mPbapStateMachineMap) {
766             mPbapStateMachineMap.put(remoteDevice, sm);
767         }
768         sm.sendMessage(PbapStateMachine.REQUEST_PERMISSION);
769         return true;
770     }
771 
772     /**
773      * Get the phonebook access permission for the device; if unknown, ask the user.
774      * Send the result to the state machine.
775      * @param stateMachine PbapStateMachine which sends the request
776      */
777     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
778     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
checkOrGetPhonebookPermission(PbapStateMachine stateMachine)779     public void checkOrGetPhonebookPermission(PbapStateMachine stateMachine) {
780         BluetoothDevice device = stateMachine.getRemoteDevice();
781         int permission = device.getPhonebookAccessPermission();
782         if (DEBUG) {
783             Log.d(TAG, "getPhonebookAccessPermission() = " + permission);
784         }
785 
786         if (permission == BluetoothDevice.ACCESS_ALLOWED) {
787             setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
788             stateMachine.sendMessage(PbapStateMachine.AUTHORIZED);
789         } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
790             stateMachine.sendMessage(PbapStateMachine.REJECTED);
791         } else { // permission == BluetoothDevice.ACCESS_UNKNOWN
792             Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
793             intent.setClassName(BluetoothPbapService.ACCESS_AUTHORITY_PACKAGE,
794                     BluetoothPbapService.ACCESS_AUTHORITY_CLASS);
795             intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
796                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
797             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
798             intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, this.getPackageName());
799             this.sendOrderedBroadcast(intent, BLUETOOTH_CONNECT,
800                     Utils.getTempAllowlistBroadcastOptions(), null/* resultReceiver */,
801                     null/* scheduler */, Activity.RESULT_OK/* initialCode */, null/* initialData */,
802                     null/* initialExtras */);
803             if (VERBOSE) {
804                 Log.v(TAG, "waiting for authorization for connection from: " + device);
805             }
806             /* In case car kit time out and try to use HFP for phonebook
807              * access, while UI still there waiting for user to confirm */
808             Message msg = mSessionStatusHandler.obtainMessage(BluetoothPbapService.USER_TIMEOUT,
809                     stateMachine);
810             mSessionStatusHandler.sendMessageDelayed(msg, USER_CONFIRM_TIMEOUT_VALUE);
811             /* We will continue the process when we receive
812              * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */
813         }
814     }
815 
816     /**
817      * Called when an unrecoverable error occurred in an accept thread.
818      * Close down the server socket, and restart.
819      */
820     @Override
onAcceptFailed()821     public synchronized void onAcceptFailed() {
822         Log.w(TAG, "PBAP server socket accept thread failed. Restarting the server socket");
823 
824         if (mWakeLock != null) {
825             mWakeLock.release();
826             mWakeLock = null;
827         }
828 
829         cleanUpServerSocket();
830 
831         if (mSessionStatusHandler != null) {
832             mSessionStatusHandler.removeCallbacksAndMessages(null);
833         }
834 
835         synchronized (mPbapStateMachineMap) {
836             mPbapStateMachineMap.clear();
837         }
838 
839         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
840     }
841 
loadAllContacts()842     private void loadAllContacts() {
843         if (mThreadLoadContacts == null) {
844             Runnable r = new Runnable() {
845                 @Override
846                 public void run() {
847                     BluetoothPbapUtils.loadAllContacts(mContext,
848                             mSessionStatusHandler);
849                     mThreadLoadContacts = null;
850                 }
851             };
852             mThreadLoadContacts = new Thread(r);
853             mThreadLoadContacts.start();
854         }
855     }
856 
updateSecondaryVersion()857     private void updateSecondaryVersion() {
858         if (mThreadUpdateSecVersionCounter == null) {
859             Runnable r = new Runnable() {
860                 @Override
861                 public void run() {
862                     BluetoothPbapUtils.updateSecondaryVersionCounter(mContext,
863                             mSessionStatusHandler);
864                     mThreadUpdateSecVersionCounter = null;
865                 }
866             };
867             mThreadUpdateSecVersionCounter = new Thread(r);
868             mThreadUpdateSecVersionCounter.start();
869         }
870     }
871 
getLocalTelephonyDetails()872     private void getLocalTelephonyDetails() {
873         TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
874         if (tm != null) {
875             sLocalPhoneNum = tm.getLine1Number();
876             sLocalPhoneName = this.getString(R.string.localPhoneName);
877         }
878         if (VERBOSE)
879             Log.v(TAG, "Local Phone Details- Number:" + sLocalPhoneNum
880                     + ", Name:" + sLocalPhoneName);
881     }
882 }
883