BluetoothPbapService.java revision 00bac0c867db92336ac808fc8c6f845fa2849023
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.app.Notification;
36import android.app.NotificationChannel;
37import android.app.NotificationManager;
38import android.app.PendingIntent;
39import android.bluetooth.BluetoothAdapter;
40import android.bluetooth.BluetoothDevice;
41import android.bluetooth.BluetoothPbap;
42import android.bluetooth.BluetoothProfile;
43import android.bluetooth.BluetoothSocket;
44import android.bluetooth.IBluetoothPbap;
45import android.content.BroadcastReceiver;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentFilter;
49import android.database.ContentObserver;
50import android.database.sqlite.SQLiteException;
51import android.os.Handler;
52import android.os.Message;
53import android.os.PowerManager;
54import android.telephony.TelephonyManager;
55import android.text.TextUtils;
56import android.util.Log;
57
58import com.android.bluetooth.BluetoothObexTransport;
59import com.android.bluetooth.IObexConnectionHandler;
60import com.android.bluetooth.ObexServerSockets;
61import com.android.bluetooth.R;
62import com.android.bluetooth.Utils;
63import com.android.bluetooth.btservice.ProfileService;
64import com.android.bluetooth.sdp.SdpManager;
65import com.android.bluetooth.util.DevicePolicyUtils;
66
67import java.io.IOException;
68import java.util.ArrayList;
69import java.util.List;
70
71import javax.obex.ServerSession;
72
73public class BluetoothPbapService extends ProfileService implements IObexConnectionHandler {
74    private static final String TAG = "BluetoothPbapService";
75
76    /**
77     * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
78     * restart com.android.bluetooth process. only enable DEBUG log:
79     * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
80     * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
81     */
82
83    public static final boolean DEBUG = true;
84
85    public static final boolean VERBOSE = false;
86
87    /**
88     * Intent indicating incoming obex authentication request which is from
89     * PCE(Carkit)
90     */
91    static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
92
93    /**
94     * Intent indicating obex session key input complete by user which is sent
95     * from BluetoothPbapActivity
96     */
97    static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
98
99    /**
100     * Intent indicating user canceled obex authentication session key input
101     * which is sent from BluetoothPbapActivity
102     */
103    static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
104
105    /**
106     * Intent indicating timeout for user confirmation, which is sent to
107     * BluetoothPbapActivity
108     */
109    static final String USER_CONFIRM_TIMEOUT_ACTION =
110            "com.android.bluetooth.pbap.userconfirmtimeout";
111
112    /**
113     * Intent Extra name indicating session key which is sent from
114     * BluetoothPbapActivity
115     */
116    static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
117
118    static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
119
120    static final int MSG_SERVERSESSION_CLOSE = 5000;
121
122    static final int MSG_SESSION_ESTABLISHED = 5001;
123
124    static final int MSG_OBEX_AUTH_CHALL = 5003;
125
126    static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
127
128    private static final int MSG_RELEASE_WAKE_LOCK = 5005;
129
130    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
131
132    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
133
134    private static final int START_LISTENER = 1;
135
136    private static final int USER_TIMEOUT = 2;
137
138    private static final int AUTH_TIMEOUT = 3;
139
140    private static final int SHUTDOWN = 4;
141
142    static final int LOAD_CONTACTS = 5;
143
144    private static final int CHECK_SECONDARY_VERSION_COUNTER = 6;
145
146    static final int ROLLOVER_COUNTERS = 7;
147
148    private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
149
150    private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
151
152    private static final int NOTIFICATION_ID_AUTH = -1000002;
153
154    private static final String PBAP_NOTIFICATION_CHANNEL = "pbap_notification_channel";
155
156    private PowerManager.WakeLock mWakeLock;
157
158    private BluetoothPbapAuthenticator mAuth;
159
160    private BluetoothPbapObexServer mPbapServer;
161
162    private ServerSession mServerSession;
163
164    private BluetoothSocket mConnSocket;
165
166    private BluetoothDevice mRemoteDevice;
167
168    private static String sLocalPhoneNum;
169
170    private static String sLocalPhoneName;
171
172    private static String sRemoteDeviceName;
173
174    private volatile boolean mInterrupted;
175
176    private int mState;
177
178    private boolean mIsWaitingAuthorization = false;
179
180    private ObexServerSockets mServerSockets;
181
182    private static final int SDP_PBAP_SERVER_VERSION = 0x0102;
183
184    private static final int SDP_PBAP_SUPPORTED_REPOSITORIES = 0x0001;
185
186    private static final int SDP_PBAP_SUPPORTED_FEATURES = 0x021F;
187
188    private int mSdpHandle = -1;
189
190    protected Context mContext;
191
192    private PbapHandler mSessionStatusHandler;
193
194    // package and class name to which we send intent to check phone book access permission
195    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
196    private static final String ACCESS_AUTHORITY_CLASS =
197            "com.android.settings.bluetooth.BluetoothPermissionRequest";
198
199    private Thread mThreadLoadContacts;
200
201    private Thread mThreadUpdateSecVersionCounter;
202
203    private class BluetoothPbapContentObserver extends ContentObserver {
204        BluetoothPbapContentObserver() {
205            super(new Handler());
206        }
207
208        @Override
209        public void onChange(boolean selfChange) {
210            Log.d(TAG, " onChange on contact uri ");
211            if (BluetoothPbapUtils.contactsLoaded) {
212                if (!mSessionStatusHandler.hasMessages(CHECK_SECONDARY_VERSION_COUNTER)) {
213                    mSessionStatusHandler.sendMessage(
214                            mSessionStatusHandler.obtainMessage(CHECK_SECONDARY_VERSION_COUNTER));
215                }
216            }
217        }
218    }
219
220    private BluetoothPbapContentObserver mContactChangeObserver;
221
222    // process the intent from receiver
223    private void parseIntent(final Intent intent) {
224        String action = intent.getAction();
225        if (DEBUG) {
226            Log.d(TAG, "action: " + action);
227        }
228        if (action == null) {
229            return;             // Nothing to do
230        }
231        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
232        if (DEBUG) {
233            Log.d(TAG, "state: " + state);
234        }
235
236        if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
237            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
238
239            if (mRemoteDevice == null) {
240                return;
241            }
242            if (DEBUG) {
243                Log.d(TAG, "ACL disconnected for " + device);
244            }
245            if (mRemoteDevice.equals(device)) {
246                if (mIsWaitingAuthorization) {
247                    mSessionStatusHandler.removeMessages(USER_TIMEOUT);
248                    mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget();
249                }
250                mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE)
251                    .sendToTarget();
252            }
253            return;
254        }
255
256        if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
257            int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
258                    BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
259
260            if ((!mIsWaitingAuthorization) || (requestType
261                    != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
262                // this reply is not for us
263                return;
264            }
265
266            mSessionStatusHandler.removeMessages(USER_TIMEOUT);
267            mIsWaitingAuthorization = false;
268
269            if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
270                    BluetoothDevice.CONNECTION_ACCESS_NO)
271                    == BluetoothDevice.CONNECTION_ACCESS_YES) {
272                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
273                    boolean result = mRemoteDevice.setPhonebookAccessPermission(
274                            BluetoothDevice.ACCESS_ALLOWED);
275                    if (VERBOSE) {
276                        Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED)=" + result);
277                    }
278                }
279                try {
280                    if (mConnSocket != null) {
281                        startObexServerSession();
282                    } else {
283                        mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE).sendToTarget();
284                    }
285                } catch (IOException ex) {
286                    Log.e(TAG, "Caught the error: " + ex.toString());
287                }
288            } else {
289                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
290                    boolean result = mRemoteDevice.setPhonebookAccessPermission(
291                            BluetoothDevice.ACCESS_REJECTED);
292                    if (VERBOSE) {
293                        Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED)=" + result);
294                    }
295                }
296                mSessionStatusHandler.obtainMessage(MSG_SERVERSESSION_CLOSE).sendToTarget();
297            }
298            return;
299        }
300
301        if (action.equals(AUTH_RESPONSE_ACTION)) {
302            String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
303            notifyAuthKeyInput(sessionkey);
304        } else if (action.equals(AUTH_CANCELLED_ACTION)) {
305            notifyAuthCancelled();
306        } else {
307            Log.w(TAG, "Unrecognized intent!");
308            return;
309        }
310
311        mSessionStatusHandler.removeMessages(USER_TIMEOUT);
312    }
313
314    private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() {
315        @Override
316        public void onReceive(Context context, Intent intent) {
317            parseIntent(intent);
318        }
319    };
320
321    private synchronized void closeConnectionSocket() {
322        if (mConnSocket != null) {
323            try {
324                mConnSocket.close();
325                mConnSocket = null;
326            } catch (IOException e) {
327                Log.e(TAG, "Close Connection Socket error: " + e.toString());
328            }
329        }
330    }
331
332    private void closeService() {
333        if (VERBOSE) {
334            Log.v(TAG, "Pbap Service closeService");
335        }
336
337        BluetoothPbapUtils.savePbapParams(this, BluetoothPbapUtils.sPrimaryVersionCounter,
338                BluetoothPbapUtils.sSecondaryVersionCounter, BluetoothPbapUtils.sDbIdentifier.get(),
339                BluetoothPbapUtils.contactsLastUpdated, BluetoothPbapUtils.totalFields,
340                BluetoothPbapUtils.totalSvcFields, BluetoothPbapUtils.totalContacts);
341
342        // exit initSocket early
343        mInterrupted = true;
344        if (mWakeLock != null) {
345            mWakeLock.release();
346            mWakeLock = null;
347        }
348
349        cleanUpServerSocket();
350
351        if (mSessionStatusHandler != null) {
352            mSessionStatusHandler.removeCallbacksAndMessages(null);
353        }
354        mRemoteDevice = null;
355    }
356
357    private void cleanUpServerSocket() {
358        // Step 1: clean up active server session
359        if (mServerSession != null) {
360            mServerSession.close();
361            mServerSession = null;
362        }
363        // Step 2: clean up existing connection socket
364        closeConnectionSocket();
365        // Step 3: clean up SDP record
366        cleanUpSdpRecord();
367        // Step 4: clean up existing server sockets
368        if (mServerSockets != null) {
369            mServerSockets.shutdown(false);
370            mServerSockets = null;
371        }
372    }
373
374    private void cleanUpSdpRecord() {
375        if (mSdpHandle < 0) {
376            Log.w(TAG, "cleanUpSdpRecord, SDP record never created");
377            return;
378        }
379        int sdpHandle = mSdpHandle;
380        mSdpHandle = -1;
381        SdpManager sdpManager = SdpManager.getDefaultManager();
382        if (DEBUG) {
383            Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
384        }
385        if (sdpManager == null) {
386            Log.e(TAG, "sdpManager is null");
387        } else if (!sdpManager.removeSdpRecord(sdpHandle)) {
388            Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
389        }
390    }
391
392    private void startObexServerSession() throws IOException {
393        if (VERBOSE) {
394            Log.v(TAG, "Pbap Service startObexServerSession");
395        }
396
397        // acquire the wakeLock before start Obex transaction thread
398        if (mWakeLock == null) {
399            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
400            mWakeLock =
401                    pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingObexPbapTransaction");
402            mWakeLock.setReferenceCounted(false);
403            mWakeLock.acquire();
404        }
405        TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
406        if (tm != null) {
407            sLocalPhoneNum = tm.getLine1Number();
408            sLocalPhoneName = tm.getLine1AlphaTag();
409            if (TextUtils.isEmpty(sLocalPhoneName)) {
410                sLocalPhoneName = this.getString(R.string.localPhoneName);
411            }
412        }
413
414        mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
415        synchronized (this) {
416            mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
417            mAuth.setChallenged(false);
418            mAuth.setCancelled(false);
419        }
420        BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket);
421        mServerSession = new ServerSession(transport, mPbapServer, mAuth);
422        setState(BluetoothProfile.STATE_CONNECTED);
423
424        mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
425        mSessionStatusHandler.sendMessageDelayed(
426                mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK),
427                RELEASE_WAKE_LOCK_DELAY);
428
429        if (VERBOSE) {
430            Log.v(TAG, "startObexServerSession() success!");
431        }
432    }
433
434    private void stopObexServerSession() {
435        if (VERBOSE) {
436            Log.v(TAG, "Pbap Service stopObexServerSession");
437        }
438        mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
439        mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
440        // Release the wake lock if obex transaction is over
441        if (mWakeLock != null) {
442            mWakeLock.release();
443            mWakeLock = null;
444        }
445
446        if (mServerSession != null) {
447            mServerSession.close();
448            mServerSession = null;
449        }
450        closeConnectionSocket();
451
452        // Last obex transaction is finished, we start to listen for incoming
453        // connection again
454        startSocketListeners();
455        setState(BluetoothProfile.STATE_DISCONNECTED);
456    }
457
458    private void notifyAuthKeyInput(final String key) {
459        synchronized (mAuth) {
460            if (key != null) {
461                mAuth.setSessionKey(key);
462            }
463            mAuth.setChallenged(true);
464            mAuth.notify();
465        }
466    }
467
468    private void notifyAuthCancelled() {
469        synchronized (mAuth) {
470            mAuth.setCancelled(true);
471            mAuth.notify();
472        }
473    }
474
475    private class PbapHandler extends Handler {
476        @Override
477        public void handleMessage(Message msg) {
478            if (VERBOSE) {
479                Log.v(TAG, "Handler(): got msg=" + msg.what);
480            }
481
482            switch (msg.what) {
483                case START_LISTENER:
484                    startSocketListeners();
485                    break;
486                case USER_TIMEOUT:
487                    Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
488                    intent.setPackage(getString(R.string.pairing_ui_package));
489                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
490                    intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
491                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
492                    sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
493                    mIsWaitingAuthorization = false;
494                    stopObexServerSession();
495                    break;
496                case AUTH_TIMEOUT:
497                    Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
498                    sendBroadcast(i);
499                    removePbapNotification(NOTIFICATION_ID_AUTH);
500                    notifyAuthCancelled();
501                    break;
502                case MSG_SERVERSESSION_CLOSE:
503                    stopObexServerSession();
504                    break;
505                case MSG_OBEX_AUTH_CHALL:
506                    createPbapNotification(AUTH_CHALL_ACTION);
507                    mSessionStatusHandler.sendMessageDelayed(
508                            mSessionStatusHandler.obtainMessage(AUTH_TIMEOUT),
509                            USER_CONFIRM_TIMEOUT_VALUE);
510                    break;
511                case MSG_ACQUIRE_WAKE_LOCK:
512                    if (mWakeLock == null) {
513                        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
514                        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
515                                "StartingObexPbapTransaction");
516                        mWakeLock.setReferenceCounted(false);
517                        mWakeLock.acquire();
518                        Log.w(TAG, "Acquire Wake Lock");
519                    }
520                    mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
521                    mSessionStatusHandler.sendMessageDelayed(
522                            mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK),
523                            RELEASE_WAKE_LOCK_DELAY);
524                    break;
525                case MSG_RELEASE_WAKE_LOCK:
526                    if (mWakeLock != null) {
527                        mWakeLock.release();
528                        mWakeLock = null;
529                        Log.w(TAG, "Release Wake Lock");
530                    }
531                    break;
532                case SHUTDOWN:
533                    closeService();
534                    break;
535                case LOAD_CONTACTS:
536                    loadAllContacts();
537                    break;
538                case CHECK_SECONDARY_VERSION_COUNTER:
539                    updateSecondaryVersion();
540                    break;
541                case ROLLOVER_COUNTERS:
542                    BluetoothPbapUtils.rolloverCounters();
543                    break;
544                default:
545                    break;
546            }
547        }
548    }
549
550    private void setState(int state) {
551        setState(state, BluetoothPbap.RESULT_SUCCESS);
552    }
553
554    private synchronized void setState(int state, int result) {
555        if (state != mState) {
556            if (DEBUG) {
557                Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = " + result);
558            }
559            int prevState = mState;
560            mState = state;
561            Intent intent = new Intent(BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
562            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
563            intent.putExtra(BluetoothProfile.EXTRA_STATE, mState);
564            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
565            sendBroadcast(intent, BLUETOOTH_PERM);
566        }
567    }
568
569    int getConnectionState(BluetoothDevice device) {
570        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
571        return mState;
572    }
573
574    List<BluetoothDevice> getConnectedDevices() {
575        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
576        List<BluetoothDevice> devices = new ArrayList<>();
577        if (mRemoteDevice != null) {
578            devices.add(mRemoteDevice);
579        }
580        return devices;
581    }
582
583    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
584        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
585        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
586
587        if (mRemoteDevice != null) {
588            for (int state : states) {
589                if (state == mState) {
590                    devices.add(mRemoteDevice);
591                    break;
592                }
593            }
594        }
595        return devices;
596    }
597
598    private void createPbapNotification(String action) {
599
600        NotificationManager nm =
601                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
602        NotificationChannel notificationChannel = new NotificationChannel(PBAP_NOTIFICATION_CHANNEL,
603                getString(R.string.pbap_notification_group), NotificationManager.IMPORTANCE_HIGH);
604        nm.createNotificationChannel(notificationChannel);
605
606        // Create an intent triggered by clicking on the status icon.
607        Intent clickIntent = new Intent();
608        clickIntent.setClass(this, BluetoothPbapActivity.class);
609        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
610        clickIntent.setAction(action);
611
612        // Create an intent triggered by clicking on the
613        // "Clear All Notifications" button
614        Intent deleteIntent = new Intent();
615        deleteIntent.setClass(this, BluetoothPbapService.class);
616        deleteIntent.setAction(AUTH_CANCELLED_ACTION);
617
618        String name = getRemoteDeviceName();
619
620        if (action.equals(AUTH_CHALL_ACTION)) {
621            Notification notification =
622                    new Notification.Builder(this, PBAP_NOTIFICATION_CHANNEL).setWhen(
623                            System.currentTimeMillis())
624                            .setContentTitle(getString(R.string.auth_notif_title))
625                            .setContentText(getString(R.string.auth_notif_message, name))
626                            .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
627                            .setTicker(getString(R.string.auth_notif_ticker))
628                            .setColor(getResources().getColor(
629                                    com.android.internal.R.color.system_notification_accent_color,
630                                    this.getTheme()))
631                            .setFlag(Notification.FLAG_AUTO_CANCEL, true)
632                            .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true)
633                            .setDefaults(Notification.DEFAULT_SOUND)
634                            .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0))
635                            .setDeleteIntent(PendingIntent.getBroadcast(this, 0, deleteIntent, 0))
636                            .build();
637            nm.notify(NOTIFICATION_ID_AUTH, notification);
638        }
639    }
640
641    private void removePbapNotification(int id) {
642        NotificationManager nm =
643                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
644        nm.cancel(id);
645    }
646
647    public static String getLocalPhoneNum() {
648        return sLocalPhoneNum;
649    }
650
651    public static String getLocalPhoneName() {
652        return sLocalPhoneName;
653    }
654
655    public static String getRemoteDeviceName() {
656        return sRemoteDeviceName;
657    }
658
659    @Override
660    protected IProfileServiceBinder initBinder() {
661        return new PbapBinder(this);
662    }
663
664    @Override
665    protected boolean start() {
666        Log.v(TAG, "start()");
667        mState = BluetoothProfile.STATE_DISCONNECTED;
668        mContext = this;
669        mSessionStatusHandler = new PbapHandler();
670        IntentFilter filter = new IntentFilter();
671        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
672        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
673        filter.addAction(AUTH_RESPONSE_ACTION);
674        filter.addAction(AUTH_CANCELLED_ACTION);
675        mInterrupted = false;
676        BluetoothPbapConfig.init(this);
677        mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
678        registerReceiver(mPbapReceiver, filter);
679        try {
680            mContactChangeObserver = new BluetoothPbapContentObserver();
681            getContentResolver().registerContentObserver(
682                    DevicePolicyUtils.getEnterprisePhoneUri(this), false,
683                    mContactChangeObserver);
684        } catch (SQLiteException e) {
685            Log.e(TAG, "SQLite exception: " + e);
686        } catch (IllegalStateException e) {
687            Log.e(TAG, "Illegal state exception, content observer is already registered");
688        }
689        return true;
690    }
691
692    @Override
693    protected boolean stop() {
694        Log.v(TAG, "stop()");
695        if (mContactChangeObserver == null) {
696            Log.i(TAG, "Avoid unregister when receiver it is not registered");
697            return true;
698        }
699        try {
700            unregisterReceiver(mPbapReceiver);
701            getContentResolver().unregisterContentObserver(mContactChangeObserver);
702            mContactChangeObserver = null;
703        } catch (Exception e) {
704            Log.w(TAG, "Unable to unregister pbap receiver", e);
705        }
706        mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
707        setState(BluetoothProfile.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
708        return true;
709    }
710
711    void disconnect(BluetoothDevice device) {
712        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
713        // TODO: disconnect the specified device.
714        synchronized (this) {
715            if (mState == BluetoothProfile.STATE_CONNECTED) {
716                if (mServerSession != null) {
717                    mServerSession.close();
718                    mServerSession = null;
719                }
720
721                closeConnectionSocket();
722
723                setState(BluetoothProfile.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
724            }
725        }
726    }
727
728    // Has to be a static class or a memory leak can occur.
729    private static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder {
730        private BluetoothPbapService mService;
731
732        private BluetoothPbapService getService() {
733            if (!Utils.checkCaller()) {
734                Log.w(TAG, "not allowed for non-active user");
735                return null;
736            }
737            if (mService != null && mService.isAvailable()) {
738                return mService;
739            }
740            return null;
741        }
742
743        PbapBinder(BluetoothPbapService service) {
744            Log.v(TAG, "PbapBinder()");
745            mService = service;
746        }
747
748        @Override
749        public boolean cleanup() {
750            mService = null;
751            return true;
752        }
753
754        @Override
755        public List<BluetoothDevice> getConnectedDevices() {
756            if (DEBUG) {
757                Log.d(TAG, "getConnectedDevices");
758            }
759            BluetoothPbapService service = getService();
760            if (service == null) {
761                return new ArrayList<>(0);
762            }
763            return service.getConnectedDevices();
764        }
765
766        @Override
767        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
768            if (DEBUG) {
769                Log.d(TAG, "getDevicesMatchingConnectionStates");
770            }
771            BluetoothPbapService service = getService();
772            if (service == null) {
773                return new ArrayList<>(0);
774            }
775            return service.getDevicesMatchingConnectionStates(states);
776        }
777
778        @Override
779        public int getConnectionState(BluetoothDevice device) {
780            if (DEBUG) {
781                Log.d(TAG, "getConnectionState: " + device);
782            }
783            BluetoothPbapService service = getService();
784            if (service == null) {
785                return BluetoothAdapter.STATE_DISCONNECTED;
786            }
787            return service.getConnectionState(device);
788        }
789
790        @Override
791        public void disconnect(BluetoothDevice device) {
792            if (DEBUG) {
793                Log.d(TAG, "disconnect");
794            }
795            BluetoothPbapService service = getService();
796            if (service == null) {
797                return;
798            }
799            service.disconnect(device);
800        }
801    }
802
803    /**
804     * Start server side socket listeners. Caller should make sure that adapter is in a ready state
805     * and SDP record is cleaned up. Otherwise, this method will fail.
806     */
807    private synchronized void startSocketListeners() {
808        if (DEBUG) {
809            Log.d(TAG, "startsocketListener");
810        }
811        if (mServerSession != null) {
812            if (DEBUG) {
813                Log.d(TAG, "mServerSession exists - shutting it down...");
814            }
815            mServerSession.close();
816            mServerSession = null;
817        }
818        closeConnectionSocket();
819        if (mServerSockets != null) {
820            mServerSockets.prepareForNewConnect();
821        } else {
822            mServerSockets = ObexServerSockets.create(this);
823            if (mServerSockets == null) {
824                // TODO: Handle - was not handled before
825                Log.e(TAG, "Failed to start the listeners");
826                return;
827            }
828            if (mSdpHandle >= 0) {
829                Log.e(TAG, "SDP handle was not cleaned up, mSdpHandle=" + mSdpHandle);
830                return;
831            }
832            mSdpHandle = SdpManager.getDefaultManager()
833                    .createPbapPseRecord("OBEX Phonebook Access Server",
834                            mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(),
835                            SDP_PBAP_SERVER_VERSION, SDP_PBAP_SUPPORTED_REPOSITORIES,
836                            SDP_PBAP_SUPPORTED_FEATURES);
837            // fetch Pbap Params to check if significant change has happened to Database
838            BluetoothPbapUtils.fetchPbapParams(mContext);
839
840            if (DEBUG) {
841                Log.d(TAG, "PBAP server with handle:" + mSdpHandle);
842            }
843        }
844    }
845
846    long getDbIdentifier() {
847        return BluetoothPbapUtils.sDbIdentifier.get();
848    }
849
850    @Override
851    public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) {
852        mRemoteDevice = remoteDevice;
853        if (DEBUG) {
854            Log.d(TAG, "onConnect(): mRemoteDevice=" + mRemoteDevice + " socket=" + socket);
855        }
856        if (mRemoteDevice == null || socket == null) {
857            Log.e(TAG, "onConnect(): Unexpected null. mRemoteDevice=" + mRemoteDevice
858                    + " socket=" + socket);
859            return false;
860        }
861        mConnSocket = socket;
862        sRemoteDeviceName = mRemoteDevice.getName();
863        // In case getRemoteName failed and return null
864        if (TextUtils.isEmpty(sRemoteDeviceName)) {
865            sRemoteDeviceName = getString(R.string.defaultname);
866        }
867        int permission = mRemoteDevice.getPhonebookAccessPermission();
868        if (DEBUG) {
869            Log.d(TAG, "getPhonebookAccessPermission() = " + permission);
870        }
871
872        if (permission == BluetoothDevice.ACCESS_ALLOWED) {
873            try {
874                startObexServerSession();
875            } catch (IOException ex) {
876                Log.e(TAG, "Caught exception starting obex server session" + ex.toString());
877            }
878
879            if (!BluetoothPbapUtils.contactsLoaded) {
880                mSessionStatusHandler.sendMessage(
881                        mSessionStatusHandler.obtainMessage(LOAD_CONTACTS));
882            }
883
884        } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
885            if (DEBUG) {
886                Log.d(TAG, "incoming connection rejected from: " + sRemoteDeviceName
887                        + " automatically as already rejected device");
888            }
889            return false;
890        } else { // permission == BluetoothDevice.ACCESS_UNKNOWN
891            // Send an Intent to Settings app to ask user preference.
892            Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
893            intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
894            intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
895                    BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
896            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
897            intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
898            mIsWaitingAuthorization = true;
899            sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
900            if (VERBOSE) {
901                Log.v(TAG, "waiting for authorization for connection from: " + sRemoteDeviceName);
902            }
903            /* In case car kit time out and try to use HFP for phonebook
904             * access, while UI still there waiting for user to confirm */
905            mSessionStatusHandler.sendMessageDelayed(
906                    mSessionStatusHandler.obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
907            /* We will continue the process when we receive
908             * BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app. */
909        }
910        return true;
911    }
912
913    /**
914     * Called when an unrecoverable error occurred in an accept thread.
915     * Close down the server socket, and restart.
916     * TODO: Change to message, to call start in correct context.
917     */
918    @Override
919    public synchronized void onAcceptFailed() {
920        Log.w(TAG, "PBAP server socket accept thread failed. Restarting the server socket");
921
922        if (mWakeLock != null) {
923            mWakeLock.release();
924            mWakeLock = null;
925        }
926
927        cleanUpServerSocket();
928
929        if (mSessionStatusHandler != null) {
930            mSessionStatusHandler.removeCallbacksAndMessages(null);
931        }
932
933        if (!mInterrupted) {
934            startSocketListeners();
935        }
936    }
937
938    private void loadAllContacts() {
939        if (mThreadLoadContacts == null) {
940            Runnable r = new Runnable() {
941                @Override
942                public void run() {
943                    BluetoothPbapUtils.loadAllContacts(mContext,
944                            mSessionStatusHandler);
945                    mThreadLoadContacts = null;
946                }
947            };
948            mThreadLoadContacts = new Thread(r);
949            mThreadLoadContacts.start();
950        }
951    }
952
953    private void updateSecondaryVersion() {
954        if (mThreadUpdateSecVersionCounter == null) {
955            Runnable r = new Runnable() {
956                @Override
957                public void run() {
958                    BluetoothPbapUtils.updateSecondaryVersionCounter(mContext,
959                            mSessionStatusHandler);
960                    mThreadUpdateSecVersionCounter = null;
961                }
962            };
963            mThreadUpdateSecVersionCounter = new Thread(r);
964            mThreadUpdateSecVersionCounter.start();
965        }
966    }
967
968}
969