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