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