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