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