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