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