BluetoothPbapService.java revision c5328cf44d025286c0d44008748c4d8493268920
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
33package com.android.bluetooth.pbap;
34
35import android.bluetooth.BluetoothAdapter;
36import android.bluetooth.BluetoothDevice;
37import android.bluetooth.BluetoothProfile;
38import android.bluetooth.BluetoothSocket;
39import android.bluetooth.IBluetoothPbap;
40import android.content.BroadcastReceiver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.database.ContentObserver;
45import android.database.sqlite.SQLiteException;
46import android.os.Handler;
47import android.os.HandlerThread;
48import android.os.Looper;
49import android.os.Message;
50import android.os.PowerManager;
51import android.os.UserManager;
52import android.support.annotation.VisibleForTesting;
53import android.telephony.TelephonyManager;
54import android.text.TextUtils;
55import android.util.Log;
56
57import com.android.bluetooth.IObexConnectionHandler;
58import com.android.bluetooth.ObexServerSockets;
59import com.android.bluetooth.R;
60import com.android.bluetooth.Utils;
61import com.android.bluetooth.btservice.ProfileService;
62import com.android.bluetooth.sdp.SdpManager;
63import com.android.bluetooth.util.DevicePolicyUtils;
64
65import java.util.ArrayList;
66import java.util.HashMap;
67import java.util.List;
68
69public 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 {
172        BluetoothPbapContentObserver() {
173            super(new Handler());
174        }
175
176        @Override
177        public void onChange(boolean selfChange) {
178            Log.d(TAG, " onChange on contact uri ");
179            sendUpdateRequest();
180        }
181    }
182
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
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
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
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
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
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 {
337        private PbapHandler(Looper looper) {
338            super(looper);
339        }
340
341        @Override
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
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
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
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
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
470    static String getLocalPhoneNum() {
471        return sLocalPhoneNum;
472    }
473
474    static String getLocalPhoneName() {
475        return sLocalPhoneName;
476    }
477
478    @Override
479    protected IProfileServiceBinder initBinder() {
480        return new PbapBinder(this);
481    }
482
483    @Override
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
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
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
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
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
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
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
602        PbapBinder(BluetoothPbapService service) {
603            if (VERBOSE) {
604                Log.v(TAG, "PbapBinder()");
605            }
606            mService = service;
607        }
608
609        @Override
610        public void cleanup() {
611            mService = null;
612        }
613
614        @Override
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
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
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
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
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)
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
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
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
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