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