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