BluetoothPbapService.java revision 91c7bbd92117da7f8fdde343a9a5108a14693387
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.NotificationManager;
37import android.app.PendingIntent;
38import android.app.Service;
39import android.bluetooth.BluetoothAdapter;
40import android.bluetooth.BluetoothDevice;
41import android.bluetooth.BluetoothPbap;
42import android.bluetooth.BluetoothProfile;
43import android.bluetooth.BluetoothServerSocket;
44import android.bluetooth.BluetoothSocket;
45import android.bluetooth.BluetoothUuid;
46import android.bluetooth.IBluetoothPbap;
47import android.content.Context;
48import android.content.Intent;
49import android.os.Handler;
50import android.os.IBinder;
51import android.os.Message;
52import android.os.PowerManager;
53import android.telephony.TelephonyManager;
54import android.text.TextUtils;
55import android.util.Log;
56
57import com.android.bluetooth.BluetoothObexTransport;
58import com.android.bluetooth.Utils;
59
60
61import com.android.bluetooth.R;
62import com.android.bluetooth.btservice.AdapterService;
63
64import java.io.IOException;
65
66import javax.obex.ServerSession;
67
68public class BluetoothPbapService extends Service {
69    private static final String TAG = "BluetoothPbapService";
70
71    /**
72     * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
73     * restart com.android.bluetooth process. only enable DEBUG log:
74     * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
75     * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
76     */
77
78    public static final boolean DEBUG = true;
79
80    public static final boolean VERBOSE = false;
81
82    /**
83     * Intent indicating incoming obex authentication request which is from
84     * PCE(Carkit)
85     */
86    public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
87
88    /**
89     * Intent indicating obex session key input complete by user which is sent
90     * from BluetoothPbapActivity
91     */
92    public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
93
94    /**
95     * Intent indicating user canceled obex authentication session key input
96     * which is sent from BluetoothPbapActivity
97     */
98    public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
99
100    /**
101     * Intent indicating timeout for user confirmation, which is sent to
102     * BluetoothPbapActivity
103     */
104    public static final String USER_CONFIRM_TIMEOUT_ACTION =
105            "com.android.bluetooth.pbap.userconfirmtimeout";
106
107    /**
108     * Intent Extra name indicating session key which is sent from
109     * BluetoothPbapActivity
110     */
111    public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
112
113    public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
114
115    public static final int MSG_SERVERSESSION_CLOSE = 5000;
116
117    public static final int MSG_SESSION_ESTABLISHED = 5001;
118
119    public static final int MSG_SESSION_DISCONNECTED = 5002;
120
121    public static final int MSG_OBEX_AUTH_CHALL = 5003;
122
123    public static final int MSG_ACQUIRE_WAKE_LOCK = 5004;
124
125    public static final int MSG_RELEASE_WAKE_LOCK = 5005;
126
127    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
128
129    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
130
131    private static final int START_LISTENER = 1;
132
133    private static final int USER_TIMEOUT = 2;
134
135    private static final int AUTH_TIMEOUT = 3;
136
137
138    private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
139
140    private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
141
142    // Ensure not conflict with Opp notification ID
143    private static final int NOTIFICATION_ID_ACCESS = -1000001;
144
145    private static final int NOTIFICATION_ID_AUTH = -1000002;
146
147    private PowerManager.WakeLock mWakeLock = null;
148
149    private BluetoothAdapter mAdapter;
150
151    private SocketAcceptThread mAcceptThread = null;
152
153    private BluetoothPbapAuthenticator mAuth = null;
154
155    private BluetoothPbapObexServer mPbapServer;
156
157    private ServerSession mServerSession = null;
158
159    private BluetoothServerSocket mServerSocket = null;
160
161    private BluetoothSocket mConnSocket = null;
162
163    private BluetoothDevice mRemoteDevice = null;
164
165    private static String sLocalPhoneNum = null;
166
167    private static String sLocalPhoneName = null;
168
169    private static String sRemoteDeviceName = null;
170
171    private boolean mHasStarted = false;
172
173    private volatile boolean mInterrupted;
174
175    private int mState;
176
177    private int mStartId = -1;
178
179    private boolean mIsWaitingAuthorization = false;
180
181    // package and class name to which we send intent to check phone book access permission
182    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
183    private static final String ACCESS_AUTHORITY_CLASS =
184        "com.android.settings.bluetooth.BluetoothPermissionRequest";
185
186    public BluetoothPbapService() {
187        mState = BluetoothPbap.STATE_DISCONNECTED;
188    }
189
190    @Override
191    public void onCreate() {
192        super.onCreate();
193        if (VERBOSE) Log.v(TAG, "Pbap Service onCreate");
194
195        mInterrupted = false;
196        mAdapter = BluetoothAdapter.getDefaultAdapter();
197
198        if (!mHasStarted) {
199            mHasStarted = true;
200            if (VERBOSE) Log.v(TAG, "Starting PBAP service");
201            BluetoothPbapConfig.init(this);
202            int state = mAdapter.getState();
203            if (state == BluetoothAdapter.STATE_ON) {
204                mSessionStatusHandler.sendMessage(mSessionStatusHandler
205                        .obtainMessage(START_LISTENER));
206            }
207        }
208    }
209
210    @Override
211    public int onStartCommand(Intent intent, int flags, int startId) {
212        mStartId = startId;
213        if (mAdapter == null) {
214            Log.w(TAG, "Stopping BluetoothPbapService: "
215                    + "device does not have BT or device is not ready");
216            // Release all resources
217            closeService();
218        } else {
219            // No need to handle the null intent case, because we have
220            // all restart work done in onCreate()
221            if (intent != null) {
222                parseIntent(intent);
223            }
224        }
225        return START_NOT_STICKY;
226    }
227
228    // process the intent from receiver
229    private void parseIntent(final Intent intent) {
230        String action = intent.getStringExtra("action");
231        if (action == null) return;             // Nothing to do
232        if (VERBOSE) Log.v(TAG, "action: " + action);
233
234        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
235        if (VERBOSE) Log.v(TAG, "state: " + state);
236
237        boolean removeTimeoutMsg = true;
238        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
239            if (state == BluetoothAdapter.STATE_TURNING_OFF) {
240                // Send any pending timeout now, as this service will be destroyed.
241                if (mSessionStatusHandler.hasMessages(USER_TIMEOUT)) {
242                    Intent timeoutIntent =
243                        new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
244                    timeoutIntent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
245                    timeoutIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
246                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
247                    sendBroadcast(timeoutIntent, BLUETOOTH_ADMIN_PERM);
248                }
249                // Release all resources
250                closeService();
251            } else {
252                removeTimeoutMsg = false;
253            }
254        } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)
255                && mIsWaitingAuthorization) {
256            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
257
258            if (mRemoteDevice == null || device == null) {
259                Log.e(TAG, "Unexpected error!");
260                return;
261            }
262
263            if (DEBUG) Log.d(TAG,"ACL disconnected for "+ device);
264
265            if (mRemoteDevice.equals(device)) {
266                Intent cancelIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
267                cancelIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
268                cancelIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
269                                      BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
270                sendBroadcast(cancelIntent);
271                mIsWaitingAuthorization = false;
272                stopObexServerSession();
273            }
274        } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
275            int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
276                                           BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
277
278            if ((!mIsWaitingAuthorization)
279                    || (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
280                // this reply is not for us
281                return;
282            }
283
284            mIsWaitingAuthorization = false;
285
286            if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
287                                   BluetoothDevice.CONNECTION_ACCESS_NO)
288                    == BluetoothDevice.CONNECTION_ACCESS_YES) {
289                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
290                    boolean result = mRemoteDevice.setPhonebookAccessPermission(
291                            BluetoothDevice.ACCESS_ALLOWED);
292                    if (VERBOSE) {
293                        Log.v(TAG, "setPhonebookAccessPermission(ACCESS_ALLOWED) result=" + result);
294                    }
295                }
296                try {
297                    if (mConnSocket != null) {
298                        startObexServerSession();
299                    } else {
300                        stopObexServerSession();
301                    }
302                } catch (IOException ex) {
303                    Log.e(TAG, "Caught the error: " + ex.toString());
304                }
305            } else {
306                if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
307                    boolean result = mRemoteDevice.setPhonebookAccessPermission(
308                            BluetoothDevice.ACCESS_REJECTED);
309                    if (VERBOSE) {
310                        Log.v(TAG, "setPhonebookAccessPermission(ACCESS_REJECTED) result="
311                                + result);
312                    }
313                }
314                stopObexServerSession();
315            }
316        } else if (action.equals(AUTH_RESPONSE_ACTION)) {
317            String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
318            notifyAuthKeyInput(sessionkey);
319        } else if (action.equals(AUTH_CANCELLED_ACTION)) {
320            notifyAuthCancelled();
321        } else {
322            removeTimeoutMsg = false;
323        }
324
325        if (removeTimeoutMsg) {
326            mSessionStatusHandler.removeMessages(USER_TIMEOUT);
327        }
328    }
329
330    @Override
331    public void onDestroy() {
332        if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
333
334        super.onDestroy();
335        setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
336        closeService();
337        if(mSessionStatusHandler != null) {
338            mSessionStatusHandler.removeCallbacksAndMessages(null);
339        }
340    }
341
342    @Override
343    public IBinder onBind(Intent intent) {
344        if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
345        return mBinder;
346    }
347
348    private void startRfcommSocketListener() {
349        if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
350
351        if (mAcceptThread == null) {
352            mAcceptThread = new SocketAcceptThread();
353            mAcceptThread.setName("BluetoothPbapAcceptThread");
354            mAcceptThread.start();
355        }
356    }
357
358    private final boolean initSocket() {
359        if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
360
361        boolean initSocketOK = false;
362        final int CREATE_RETRY_TIME = 10;
363
364        // It's possible that create will fail in some cases. retry for 10 times
365        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
366            initSocketOK = true;
367            try {
368                // It is mandatory for PSE to support initiation of bonding and
369                // encryption.
370                mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
371                    ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid());
372
373            } catch (IOException e) {
374                Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
375                initSocketOK = false;
376            }
377            if (!initSocketOK) {
378                // Need to break out of this loop if BT is being turned off.
379                if (mAdapter == null) break;
380                int state = mAdapter.getState();
381                if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
382                    (state != BluetoothAdapter.STATE_ON)) {
383                    Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
384                    break;
385                }
386                try {
387                    if (VERBOSE) Log.v(TAG, "wait 300 ms");
388                    Thread.sleep(300);
389                } catch (InterruptedException e) {
390                    Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
391                    break;
392                }
393            } else {
394                break;
395            }
396        }
397
398        if (mInterrupted) {
399            initSocketOK = false;
400            // close server socket to avoid resource leakage
401            closeServerSocket();
402        }
403
404        if (initSocketOK) {
405            if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
406
407        } else {
408            Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
409        }
410        return initSocketOK;
411    }
412
413    private final synchronized void closeServerSocket() {
414        // exit SocketAcceptThread early
415        if (mServerSocket != null) {
416            try {
417                // this will cause mServerSocket.accept() return early with IOException
418                mServerSocket.close();
419                mServerSocket = null;
420            } catch (IOException ex) {
421                Log.e(TAG, "Close Server Socket error: " + ex);
422            }
423        }
424    }
425
426    private final synchronized void closeConnectionSocket() {
427        if (mConnSocket != null) {
428            try {
429                mConnSocket.close();
430                mConnSocket = null;
431            } catch (IOException e) {
432                Log.e(TAG, "Close Connection Socket error: " + e.toString());
433            }
434        }
435    }
436
437    private final void closeService() {
438        if (VERBOSE) Log.v(TAG, "Pbap Service closeService in");
439
440        // exit initSocket early
441        mInterrupted = true;
442        closeServerSocket();
443
444        if (mAcceptThread != null) {
445            try {
446                mAcceptThread.shutdown();
447                mAcceptThread.join();
448                mAcceptThread = null;
449            } catch (InterruptedException ex) {
450                Log.w(TAG, "mAcceptThread close error" + ex);
451            }
452        }
453
454        if (mWakeLock != null) {
455            mWakeLock.release();
456            mWakeLock = null;
457        }
458
459        if (mServerSession != null) {
460            mServerSession.close();
461            mServerSession = null;
462        }
463
464        closeConnectionSocket();
465
466        mHasStarted = false;
467        if (mStartId != -1 && stopSelfResult(mStartId)) {
468            if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
469            mStartId = -1;
470        }
471        if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
472    }
473
474    private final void startObexServerSession() throws IOException {
475        if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
476
477        // acquire the wakeLock before start Obex transaction thread
478        if (mWakeLock == null) {
479            PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
480            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
481                    "StartingObexPbapTransaction");
482            mWakeLock.setReferenceCounted(false);
483            mWakeLock.acquire();
484        }
485        TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
486        if (tm != null) {
487            sLocalPhoneNum = tm.getLine1Number();
488            sLocalPhoneName = tm.getLine1AlphaTag();
489            if (TextUtils.isEmpty(sLocalPhoneName)) {
490                sLocalPhoneName = this.getString(R.string.localPhoneName);
491            }
492        }
493
494        mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
495        synchronized (this) {
496            mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
497            mAuth.setChallenged(false);
498            mAuth.setCancelled(false);
499        }
500        BluetoothObexTransport transport = new BluetoothObexTransport(mConnSocket);
501        mServerSession = new ServerSession(transport, mPbapServer, mAuth);
502        setState(BluetoothPbap.STATE_CONNECTED);
503
504        mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
505        mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
506            .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
507
508        if (VERBOSE) {
509            Log.v(TAG, "startObexServerSession() success!");
510        }
511    }
512
513    private void stopObexServerSession() {
514        if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
515
516        mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
517        mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
518        // Release the wake lock if obex transaction is over
519        if (mWakeLock != null) {
520            mWakeLock.release();
521            mWakeLock = null;
522        }
523
524        if (mServerSession != null) {
525            mServerSession.close();
526            mServerSession = null;
527        }
528
529        mAcceptThread = null;
530
531        closeConnectionSocket();
532
533        // Last obex transaction is finished, we start to listen for incoming
534        // connection again
535        if (mAdapter.isEnabled()) {
536            startRfcommSocketListener();
537        }
538        setState(BluetoothPbap.STATE_DISCONNECTED);
539    }
540
541    private void notifyAuthKeyInput(final String key) {
542        synchronized (mAuth) {
543            if (key != null) {
544                mAuth.setSessionKey(key);
545            }
546            mAuth.setChallenged(true);
547            mAuth.notify();
548        }
549    }
550
551    private void notifyAuthCancelled() {
552        synchronized (mAuth) {
553            mAuth.setCancelled(true);
554            mAuth.notify();
555        }
556    }
557
558    /**
559     * A thread that runs in the background waiting for remote rfcomm
560     * connect.Once a remote socket connected, this thread shall be
561     * shutdown.When the remote disconnect,this thread shall run again waiting
562     * for next request.
563     */
564    private class SocketAcceptThread extends Thread {
565
566        private boolean stopped = false;
567
568        @Override
569        public void run() {
570            BluetoothServerSocket serverSocket;
571            if (mServerSocket == null) {
572                if (!initSocket()) {
573                    return;
574                }
575            }
576
577            while (!stopped) {
578                try {
579                    if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
580                    serverSocket = mServerSocket;
581                    if (serverSocket == null) {
582                        Log.w(TAG, "mServerSocket is null");
583                        break;
584                    }
585                    mConnSocket = serverSocket.accept();
586                    if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
587
588                    synchronized (BluetoothPbapService.this) {
589                        if (mConnSocket == null) {
590                            Log.w(TAG, "mConnSocket is null");
591                            break;
592                        }
593                        mRemoteDevice = mConnSocket.getRemoteDevice();
594                    }
595                    if (mRemoteDevice == null) {
596                        Log.i(TAG, "getRemoteDevice() = null");
597                        break;
598                    }
599                    sRemoteDeviceName = mRemoteDevice.getName();
600                    // In case getRemoteName failed and return null
601                    if (TextUtils.isEmpty(sRemoteDeviceName)) {
602                        sRemoteDeviceName = getString(R.string.defaultname);
603                    }
604                    int permission = mRemoteDevice.getPhonebookAccessPermission();
605                    if (VERBOSE) Log.v(TAG, "getPhonebookAccessPermission() = " + permission);
606
607                    if (permission == BluetoothDevice.ACCESS_ALLOWED) {
608                        try {
609                            if (VERBOSE) {
610                                Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName
611                                        + " automatically as already allowed device");
612                            }
613                            startObexServerSession();
614                        } catch (IOException ex) {
615                            Log.e(TAG, "Caught exception starting obex server session"
616                                    + ex.toString());
617                        }
618                    } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
619                        if (VERBOSE) {
620                            Log.v(TAG, "incoming connection rejected from: " + sRemoteDeviceName
621                                    + " automatically as already rejected device");
622                        }
623                        stopObexServerSession();
624                    } else {  // permission == BluetoothDevice.ACCESS_UNKNOWN
625                        // Send an Intent to Settings app to ask user preference.
626                        Intent intent =
627                                new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
628                        intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
629                        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
630                                        BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
631                        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
632                        intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
633                        intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME,
634                                        BluetoothPbapReceiver.class.getName());
635
636                        mIsWaitingAuthorization = true;
637                        sendOrderedBroadcast(intent, BLUETOOTH_ADMIN_PERM);
638
639                        if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
640                                + sRemoteDeviceName);
641
642                        // In case car kit time out and try to use HFP for
643                        // phonebook
644                        // access, while UI still there waiting for user to
645                        // confirm
646                        mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
647                                .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
648                        // We will continue the process when we receive
649                        // BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY from Settings app.
650                    }
651                    stopped = true; // job done ,close this thread;
652                } catch (IOException ex) {
653                    stopped=true;
654                    /*
655                    if (stopped) {
656                        break;
657                    }
658                    */
659                    if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
660                }
661            }
662        }
663
664        void shutdown() {
665            stopped = true;
666            interrupt();
667        }
668    }
669
670    private final Handler mSessionStatusHandler = new Handler() {
671        @Override
672        public void handleMessage(Message msg) {
673            if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
674
675            switch (msg.what) {
676                case START_LISTENER:
677                    if (mAdapter.isEnabled()) {
678                        startRfcommSocketListener();
679                    } else {
680                        closeService();// release all resources
681                    }
682                    break;
683                case USER_TIMEOUT:
684                    Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
685                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
686                    intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
687                                    BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
688                    sendBroadcast(intent);
689                    mIsWaitingAuthorization = false;
690                    stopObexServerSession();
691                    break;
692                case AUTH_TIMEOUT:
693                    Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
694                    sendBroadcast(i);
695                    removePbapNotification(NOTIFICATION_ID_AUTH);
696                    notifyAuthCancelled();
697                    break;
698                case MSG_SERVERSESSION_CLOSE:
699                    stopObexServerSession();
700                    break;
701                case MSG_SESSION_ESTABLISHED:
702                    break;
703                case MSG_SESSION_DISCONNECTED:
704                    // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
705                    break;
706                case MSG_OBEX_AUTH_CHALL:
707                    createPbapNotification(AUTH_CHALL_ACTION);
708                    mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
709                            .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
710                    break;
711                case MSG_ACQUIRE_WAKE_LOCK:
712                    if (mWakeLock == null) {
713                        PowerManager pm = (PowerManager)getSystemService(
714                                          Context.POWER_SERVICE);
715                        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
716                                    "StartingObexPbapTransaction");
717                        mWakeLock.setReferenceCounted(false);
718                        mWakeLock.acquire();
719                        Log.w(TAG, "Acquire Wake Lock");
720                    }
721                    mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
722                    mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
723                      .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
724                    break;
725                case MSG_RELEASE_WAKE_LOCK:
726                    if (mWakeLock != null) {
727                        mWakeLock.release();
728                        mWakeLock = null;
729                        Log.w(TAG, "Release Wake Lock");
730                    }
731                    break;
732                default:
733                    break;
734            }
735        }
736    };
737
738    private void setState(int state) {
739        setState(state, BluetoothPbap.RESULT_SUCCESS);
740    }
741
742    private synchronized void setState(int state, int result) {
743        if (state != mState) {
744            if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
745                    + result);
746            int prevState = mState;
747            mState = state;
748            Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
749            intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
750            intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
751            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
752            sendBroadcast(intent, BLUETOOTH_PERM);
753            AdapterService s = AdapterService.getAdapterService();
754            if (s != null) {
755                s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP,
756                        mState, prevState);
757            }
758        }
759    }
760
761    private void createPbapNotification(String action) {
762
763        NotificationManager nm = (NotificationManager)
764            getSystemService(Context.NOTIFICATION_SERVICE);
765
766        // Create an intent triggered by clicking on the status icon.
767        Intent clickIntent = new Intent();
768        clickIntent.setClass(this, BluetoothPbapActivity.class);
769        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
770        clickIntent.setAction(action);
771
772        // Create an intent triggered by clicking on the
773        // "Clear All Notifications" button
774        Intent deleteIntent = new Intent();
775        deleteIntent.setClass(this, BluetoothPbapReceiver.class);
776
777        String name = getRemoteDeviceName();
778
779        if (action.equals(AUTH_CHALL_ACTION)) {
780            Notification notification = new Notification.Builder(this)
781                    .setWhen(System.currentTimeMillis())
782                    .setContentTitle(getString(R.string.auth_notif_title))
783                    .setContentText(getString(R.string.auth_notif_message, name))
784                    .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
785                    .setTicker(getString(R.string.auth_notif_ticker))
786                    .setColor(getResources().getColor(
787                            com.android.internal.R.color.system_notification_accent_color,
788                            this.getTheme()))
789                    .setFlag(Notification.FLAG_AUTO_CANCEL, true)
790                    .setFlag(Notification.FLAG_ONLY_ALERT_ONCE, true)
791                    .setDefaults(Notification.DEFAULT_SOUND)
792                    .setContentIntent(PendingIntent.getActivity(this, 0, clickIntent, 0))
793                    .setDeleteIntent(PendingIntent.getBroadcast(this, 0,
794                            deleteIntent.setAction(AUTH_CANCELLED_ACTION), 0))
795                    .build();
796            nm.notify(NOTIFICATION_ID_AUTH, notification);
797        }
798    }
799
800    private void removePbapNotification(int id) {
801        NotificationManager nm = (NotificationManager)
802            getSystemService(Context.NOTIFICATION_SERVICE);
803        nm.cancel(id);
804    }
805
806    public static String getLocalPhoneNum() {
807        return sLocalPhoneNum;
808    }
809
810    public static String getLocalPhoneName() {
811        return sLocalPhoneName;
812    }
813
814    public static String getRemoteDeviceName() {
815        return sRemoteDeviceName;
816    }
817
818    /**
819     * Handlers for incoming service calls
820     */
821    private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
822        public int getState() {
823            if (DEBUG) Log.d(TAG, "getState " + mState);
824
825            if (!Utils.checkCaller()) {
826                Log.w(TAG,"getState(): not allowed for non-active user");
827                return BluetoothPbap.STATE_DISCONNECTED;
828            }
829
830            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
831            return mState;
832        }
833
834        public BluetoothDevice getClient() {
835            if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
836
837            if (!Utils.checkCaller()) {
838                Log.w(TAG,"getClient(): not allowed for non-active user");
839                return null;
840            }
841
842            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
843            if (mState == BluetoothPbap.STATE_DISCONNECTED) {
844                return null;
845            }
846            return mRemoteDevice;
847        }
848
849        public boolean isConnected(BluetoothDevice device) {
850            if (!Utils.checkCaller()) {
851                Log.w(TAG,"isConnected(): not allowed for non-active user");
852                return false;
853            }
854
855            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
856            return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
857        }
858
859        public boolean connect(BluetoothDevice device) {
860            if (!Utils.checkCaller()) {
861                Log.w(TAG,"connect(): not allowed for non-active user");
862                return false;
863            }
864
865            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
866                    "Need BLUETOOTH_ADMIN permission");
867            return false;
868        }
869
870        public void disconnect() {
871            if (DEBUG) Log.d(TAG, "disconnect");
872
873            if (!Utils.checkCaller()) {
874                Log.w(TAG,"disconnect(): not allowed for non-active user");
875                return;
876            }
877
878            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
879                    "Need BLUETOOTH_ADMIN permission");
880            synchronized (BluetoothPbapService.this) {
881                switch (mState) {
882                    case BluetoothPbap.STATE_CONNECTED:
883                        if (mServerSession != null) {
884                            mServerSession.close();
885                            mServerSession = null;
886                        }
887
888                        closeConnectionSocket();
889
890                        setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
891                        break;
892                    default:
893                        break;
894                }
895            }
896        }
897    };
898}
899