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