• 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 isWaitingAuthorization = 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_CONNECTION_ACCESS_REPLY)) {
260             int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
261                                            BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
262 
263             if ((!isWaitingAuthorization) ||
264                 (requestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS)) {
265                 // this reply is not for us
266                 return;
267             }
268 
269             isWaitingAuthorization = false;
270 
271             if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
272                                    BluetoothDevice.CONNECTION_ACCESS_NO) ==
273                 BluetoothDevice.CONNECTION_ACCESS_YES) {
274 
275                 if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
276                     boolean result = mRemoteDevice.setTrust(true);
277                     if (VERBOSE) Log.v(TAG, "setTrust() result=" + result);
278                 }
279                 try {
280                     if (mConnSocket != null) {
281                         startObexServerSession();
282                     } else {
283                         stopObexServerSession();
284                     }
285                 } catch (IOException ex) {
286                     Log.e(TAG, "Caught the error: " + ex.toString());
287                 }
288             } else {
289                 stopObexServerSession();
290             }
291         } else if (action.equals(AUTH_RESPONSE_ACTION)) {
292             String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
293             notifyAuthKeyInput(sessionkey);
294         } else if (action.equals(AUTH_CANCELLED_ACTION)) {
295             notifyAuthCancelled();
296         } else {
297             removeTimeoutMsg = false;
298         }
299 
300         if (removeTimeoutMsg) {
301             mSessionStatusHandler.removeMessages(USER_TIMEOUT);
302         }
303     }
304 
305     @Override
onDestroy()306     public void onDestroy() {
307         if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
308 
309         super.onDestroy();
310         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
311         closeService();
312         if(mSessionStatusHandler != null) {
313             mSessionStatusHandler.removeCallbacksAndMessages(null);
314         }
315     }
316 
317     @Override
onBind(Intent intent)318     public IBinder onBind(Intent intent) {
319         if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
320         return mBinder;
321     }
322 
startRfcommSocketListener()323     private void startRfcommSocketListener() {
324         if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
325 
326         if (mAcceptThread == null) {
327             mAcceptThread = new SocketAcceptThread();
328             mAcceptThread.setName("BluetoothPbapAcceptThread");
329             mAcceptThread.start();
330         }
331     }
332 
initSocket()333     private final boolean initSocket() {
334         if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
335 
336         boolean initSocketOK = false;
337         final int CREATE_RETRY_TIME = 10;
338 
339         // It's possible that create will fail in some cases. retry for 10 times
340         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
341             initSocketOK = true;
342             try {
343                 // It is mandatory for PSE to support initiation of bonding and
344                 // encryption.
345                 mServerSocket = mAdapter.listenUsingEncryptedRfcommWithServiceRecord
346                     ("OBEX Phonebook Access Server", BluetoothUuid.PBAP_PSE.getUuid());
347 
348             } catch (IOException e) {
349                 Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
350                 initSocketOK = false;
351             }
352             if (!initSocketOK) {
353                 // Need to break out of this loop if BT is being turned off.
354                 if (mAdapter == null) break;
355                 int state = mAdapter.getState();
356                 if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
357                     (state != BluetoothAdapter.STATE_ON)) {
358                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
359                     break;
360                 }
361                 try {
362                     if (VERBOSE) Log.v(TAG, "wait 300 ms");
363                     Thread.sleep(300);
364                 } catch (InterruptedException e) {
365                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
366                     break;
367                 }
368             } else {
369                 break;
370             }
371         }
372 
373         if (mInterrupted) {
374             initSocketOK = false;
375             // close server socket to avoid resource leakage
376             closeServerSocket();
377         }
378 
379         if (initSocketOK) {
380             if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
381 
382         } else {
383             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
384         }
385         return initSocketOK;
386     }
387 
closeServerSocket()388     private final synchronized void closeServerSocket() {
389         // exit SocketAcceptThread early
390         if (mServerSocket != null) {
391             try {
392                 // this will cause mServerSocket.accept() return early with IOException
393                 mServerSocket.close();
394                 mServerSocket = null;
395             } catch (IOException ex) {
396                 Log.e(TAG, "Close Server Socket error: " + ex);
397             }
398         }
399     }
400 
closeConnectionSocket()401     private final synchronized void closeConnectionSocket() {
402         if (mConnSocket != null) {
403             try {
404                 mConnSocket.close();
405                 mConnSocket = null;
406             } catch (IOException e) {
407                 Log.e(TAG, "Close Connection Socket error: " + e.toString());
408             }
409         }
410     }
411 
closeService()412     private final void closeService() {
413         if (VERBOSE) Log.v(TAG, "Pbap Service closeService in");
414 
415         // exit initSocket early
416         mInterrupted = true;
417         closeServerSocket();
418 
419         if (mAcceptThread != null) {
420             try {
421                 mAcceptThread.shutdown();
422                 mAcceptThread.join();
423                 mAcceptThread = null;
424             } catch (InterruptedException ex) {
425                 Log.w(TAG, "mAcceptThread close error" + ex);
426             }
427         }
428 
429         if (mWakeLock != null) {
430             mWakeLock.release();
431             mWakeLock = null;
432         }
433 
434         if (mServerSession != null) {
435             mServerSession.close();
436             mServerSession = null;
437         }
438 
439         closeConnectionSocket();
440 
441         mHasStarted = false;
442         if (mStartId != -1 && stopSelfResult(mStartId)) {
443             if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
444             mStartId = -1;
445         }
446         if (VERBOSE) Log.v(TAG, "Pbap Service closeService out");
447     }
448 
startObexServerSession()449     private final void startObexServerSession() throws IOException {
450         if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
451 
452         // acquire the wakeLock before start Obex transaction thread
453         if (mWakeLock == null) {
454             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
455             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
456                     "StartingObexPbapTransaction");
457             mWakeLock.setReferenceCounted(false);
458             mWakeLock.acquire();
459         }
460         TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
461         if (tm != null) {
462             sLocalPhoneNum = tm.getLine1Number();
463             sLocalPhoneName = tm.getLine1AlphaTag();
464             if (TextUtils.isEmpty(sLocalPhoneName)) {
465                 sLocalPhoneName = this.getString(R.string.localPhoneName);
466             }
467         }
468 
469         mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
470         synchronized (this) {
471             mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
472             mAuth.setChallenged(false);
473             mAuth.setCancelled(false);
474         }
475         BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket);
476         mServerSession = new ServerSession(transport, mPbapServer, mAuth);
477         setState(BluetoothPbap.STATE_CONNECTED);
478 
479         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
480         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
481             .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
482 
483         if (VERBOSE) {
484             Log.v(TAG, "startObexServerSession() success!");
485         }
486     }
487 
stopObexServerSession()488     private void stopObexServerSession() {
489         if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
490 
491         mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
492         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
493         // Release the wake lock if obex transaction is over
494         if (mWakeLock != null) {
495             mWakeLock.release();
496             mWakeLock = null;
497         }
498 
499         if (mServerSession != null) {
500             mServerSession.close();
501             mServerSession = null;
502         }
503 
504         mAcceptThread = null;
505 
506         closeConnectionSocket();
507 
508         // Last obex transaction is finished, we start to listen for incoming
509         // connection again
510         if (mAdapter.isEnabled()) {
511             startRfcommSocketListener();
512         }
513         setState(BluetoothPbap.STATE_DISCONNECTED);
514     }
515 
notifyAuthKeyInput(final String key)516     private void notifyAuthKeyInput(final String key) {
517         synchronized (mAuth) {
518             if (key != null) {
519                 mAuth.setSessionKey(key);
520             }
521             mAuth.setChallenged(true);
522             mAuth.notify();
523         }
524     }
525 
notifyAuthCancelled()526     private void notifyAuthCancelled() {
527         synchronized (mAuth) {
528             mAuth.setCancelled(true);
529             mAuth.notify();
530         }
531     }
532 
533     /**
534      * A thread that runs in the background waiting for remote rfcomm
535      * connect.Once a remote socket connected, this thread shall be
536      * shutdown.When the remote disconnect,this thread shall run again waiting
537      * for next request.
538      */
539     private class SocketAcceptThread extends Thread {
540 
541         private boolean stopped = false;
542 
543         @Override
run()544         public void run() {
545             BluetoothServerSocket serverSocket;
546             if (mServerSocket == null) {
547                 if (!initSocket()) {
548                     return;
549                 }
550             }
551 
552             while (!stopped) {
553                 try {
554                     if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
555                     serverSocket = mServerSocket;
556                     if (serverSocket == null) {
557                         Log.w(TAG, "mServerSocket is null");
558                         break;
559                     }
560                     mConnSocket = serverSocket.accept();
561                     if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
562 
563                     synchronized (BluetoothPbapService.this) {
564                         if (mConnSocket == null) {
565                             Log.w(TAG, "mConnSocket is null");
566                             break;
567                         }
568                         mRemoteDevice = mConnSocket.getRemoteDevice();
569                     }
570                     if (mRemoteDevice == null) {
571                         Log.i(TAG, "getRemoteDevice() = null");
572                         break;
573                     }
574                     sRemoteDeviceName = mRemoteDevice.getName();
575                     // In case getRemoteName failed and return null
576                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
577                         sRemoteDeviceName = getString(R.string.defaultname);
578                     }
579                     boolean trust = mRemoteDevice.getTrustState();
580                     if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
581 
582                     if (trust) {
583                         try {
584                             if (VERBOSE) Log.v(TAG, "incoming connection accepted from: "
585                                 + sRemoteDeviceName + " automatically as trusted device");
586                             startObexServerSession();
587                         } catch (IOException ex) {
588                             Log.e(TAG, "catch exception starting obex server session"
589                                     + ex.toString());
590                         }
591                     } else {
592                         Intent intent = new
593                             Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
594                         intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
595                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
596                                         BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
597                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
598                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
599                         intent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME,
600                                         BluetoothPbapReceiver.class.getName());
601 
602                         isWaitingAuthorization = true;
603                         sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
604 
605                         if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
606                                 + sRemoteDeviceName);
607 
608                         // In case car kit time out and try to use HFP for
609                         // phonebook
610                         // access, while UI still there waiting for user to
611                         // confirm
612                         mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
613                                 .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
614                     }
615                     stopped = true; // job done ,close this thread;
616                 } catch (IOException ex) {
617                     stopped=true;
618                     /*
619                     if (stopped) {
620                         break;
621                     }
622                     */
623                     if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
624                 }
625             }
626         }
627 
shutdown()628         void shutdown() {
629             stopped = true;
630             interrupt();
631         }
632     }
633 
634     private final Handler mSessionStatusHandler = new Handler() {
635         @Override
636         public void handleMessage(Message msg) {
637             if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
638 
639             switch (msg.what) {
640                 case START_LISTENER:
641                     if (mAdapter.isEnabled()) {
642                         startRfcommSocketListener();
643                     } else {
644                         closeService();// release all resources
645                     }
646                     break;
647                 case USER_TIMEOUT:
648                     Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
649                     intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
650                     intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
651                                     BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
652                     sendBroadcast(intent);
653                     isWaitingAuthorization = false;
654                     stopObexServerSession();
655                     break;
656                 case AUTH_TIMEOUT:
657                     Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
658                     sendBroadcast(i);
659                     removePbapNotification(NOTIFICATION_ID_AUTH);
660                     notifyAuthCancelled();
661                     break;
662                 case MSG_SERVERSESSION_CLOSE:
663                     stopObexServerSession();
664                     break;
665                 case MSG_SESSION_ESTABLISHED:
666                     break;
667                 case MSG_SESSION_DISCONNECTED:
668                     // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
669                     break;
670                 case MSG_OBEX_AUTH_CHALL:
671                     createPbapNotification(AUTH_CHALL_ACTION);
672                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
673                             .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
674                     break;
675                 case MSG_ACQUIRE_WAKE_LOCK:
676                     if (mWakeLock == null) {
677                         PowerManager pm = (PowerManager)getSystemService(
678                                           Context.POWER_SERVICE);
679                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
680                                     "StartingObexPbapTransaction");
681                         mWakeLock.setReferenceCounted(false);
682                         mWakeLock.acquire();
683                         Log.w(TAG, "Acquire Wake Lock");
684                     }
685                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
686                     mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
687                       .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
688                     break;
689                 case MSG_RELEASE_WAKE_LOCK:
690                     if (mWakeLock != null) {
691                         mWakeLock.release();
692                         mWakeLock = null;
693                         Log.w(TAG, "Release Wake Lock");
694                     }
695                     break;
696                 default:
697                     break;
698             }
699         }
700     };
701 
setState(int state)702     private void setState(int state) {
703         setState(state, BluetoothPbap.RESULT_SUCCESS);
704     }
705 
setState(int state, int result)706     private synchronized void setState(int state, int result) {
707         if (state != mState) {
708             if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
709                     + result);
710             int prevState = mState;
711             mState = state;
712             Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
713             intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, prevState);
714             intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
715             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
716             sendBroadcast(intent, BLUETOOTH_PERM);
717             AdapterService s = AdapterService.getAdapterService();
718             if (s != null) {
719                 s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.PBAP,
720                         mState, prevState);
721             }
722         }
723     }
724 
createPbapNotification(String action)725     private void createPbapNotification(String action) {
726 
727         NotificationManager nm = (NotificationManager)
728             getSystemService(Context.NOTIFICATION_SERVICE);
729 
730         // Create an intent triggered by clicking on the status icon.
731         Intent clickIntent = new Intent();
732         clickIntent.setClass(this, BluetoothPbapActivity.class);
733         clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
734         clickIntent.setAction(action);
735 
736         // Create an intent triggered by clicking on the
737         // "Clear All Notifications" button
738         Intent deleteIntent = new Intent();
739         deleteIntent.setClass(this, BluetoothPbapReceiver.class);
740 
741         Notification notification = null;
742         String name = getRemoteDeviceName();
743 
744         if (action.equals(AUTH_CHALL_ACTION)) {
745             deleteIntent.setAction(AUTH_CANCELLED_ACTION);
746             notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
747                 getString(R.string.auth_notif_ticker), System.currentTimeMillis());
748             notification.setLatestEventInfo(this, getString(R.string.auth_notif_title),
749                     getString(R.string.auth_notif_message, name), PendingIntent
750                             .getActivity(this, 0, clickIntent, 0));
751 
752             notification.flags |= Notification.FLAG_AUTO_CANCEL;
753             notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
754             notification.defaults = Notification.DEFAULT_SOUND;
755             notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0);
756             nm.notify(NOTIFICATION_ID_AUTH, notification);
757         }
758     }
759 
removePbapNotification(int id)760     private void removePbapNotification(int id) {
761         NotificationManager nm = (NotificationManager)
762             getSystemService(Context.NOTIFICATION_SERVICE);
763         nm.cancel(id);
764     }
765 
getLocalPhoneNum()766     public static String getLocalPhoneNum() {
767         return sLocalPhoneNum;
768     }
769 
getLocalPhoneName()770     public static String getLocalPhoneName() {
771         return sLocalPhoneName;
772     }
773 
getRemoteDeviceName()774     public static String getRemoteDeviceName() {
775         return sRemoteDeviceName;
776     }
777 
778     /**
779      * Handlers for incoming service calls
780      */
781     private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
782         public int getState() {
783             if (DEBUG) Log.d(TAG, "getState " + mState);
784 
785             if (!Utils.checkCaller()) {
786                 Log.w(TAG,"getState(): not allowed for non-active user");
787                 return BluetoothPbap.STATE_DISCONNECTED;
788             }
789 
790             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
791             return mState;
792         }
793 
794         public BluetoothDevice getClient() {
795             if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
796 
797             if (!Utils.checkCaller()) {
798                 Log.w(TAG,"getClient(): not allowed for non-active user");
799                 return null;
800             }
801 
802             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
803             if (mState == BluetoothPbap.STATE_DISCONNECTED) {
804                 return null;
805             }
806             return mRemoteDevice;
807         }
808 
809         public boolean isConnected(BluetoothDevice device) {
810             if (!Utils.checkCaller()) {
811                 Log.w(TAG,"isConnected(): not allowed for non-active user");
812                 return false;
813             }
814 
815             enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
816             return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
817         }
818 
819         public boolean connect(BluetoothDevice device) {
820             if (!Utils.checkCaller()) {
821                 Log.w(TAG,"connect(): not allowed for non-active user");
822                 return false;
823             }
824 
825             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
826                     "Need BLUETOOTH_ADMIN permission");
827             return false;
828         }
829 
830         public void disconnect() {
831             if (DEBUG) Log.d(TAG, "disconnect");
832 
833             if (!Utils.checkCaller()) {
834                 Log.w(TAG,"disconnect(): not allowed for non-active user");
835                 return;
836             }
837 
838             enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
839                     "Need BLUETOOTH_ADMIN permission");
840             synchronized (BluetoothPbapService.this) {
841                 switch (mState) {
842                     case BluetoothPbap.STATE_CONNECTED:
843                         if (mServerSession != null) {
844                             mServerSession.close();
845                             mServerSession = null;
846                         }
847 
848                         closeConnectionSocket();
849 
850                         setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
851                         break;
852                     default:
853                         break;
854                 }
855             }
856         }
857     };
858 }
859