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