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