SapService.java revision 71a7019b7ae6809085ba1ae8de74e3d3b1f83513
1package com.android.bluetooth.sap;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.List;
6import java.util.Set;
7
8import android.annotation.TargetApi;
9import android.app.AlarmManager;
10import android.app.Notification;
11import android.app.NotificationManager;
12import android.app.PendingIntent;
13import android.bluetooth.BluetoothAdapter;
14import android.bluetooth.BluetoothDevice;
15import android.bluetooth.BluetoothProfile;
16import android.bluetooth.BluetoothServerSocket;
17import android.bluetooth.BluetoothSocket;
18import android.bluetooth.BluetoothUuid;
19import android.bluetooth.IBluetooth;
20import android.bluetooth.IBluetoothSap;
21import android.bluetooth.BluetoothUuid;
22import android.bluetooth.BluetoothSap;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.os.Build;
28import android.os.Handler;
29import android.os.Message;
30import android.os.ParcelUuid;
31import android.os.PowerManager;
32import android.provider.Settings;
33import android.text.TextUtils;
34import android.util.Log;
35import com.android.bluetooth.R;
36import com.android.bluetooth.Utils;
37import com.android.bluetooth.btservice.AdapterService;
38import com.android.bluetooth.btservice.ProfileService;
39import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder;
40import com.android.bluetooth.sdp.SdpManager;
41
42@TargetApi(Build.VERSION_CODES.ECLAIR)
43public class SapService extends ProfileService {
44
45    private static final String SDP_SAP_SERVICE_NAME = "SIM Access";
46    private static final int SDP_SAP_VERSION = 0x0102;
47    private static final String TAG = "SapService";
48    public static final boolean DEBUG = false;
49    public static final boolean VERBOSE = false;
50
51    /* Message ID's */
52    private static final int START_LISTENER = 1;
53    private static final int USER_TIMEOUT = 2;
54    private static final int SHUTDOWN = 3;
55
56    public static final int MSG_SERVERSESSION_CLOSE = 5000;
57    public static final int MSG_SESSION_ESTABLISHED = 5001;
58    public static final int MSG_SESSION_DISCONNECTED = 5002;
59
60    public static final int MSG_ACQUIRE_WAKE_LOCK = 5005;
61    public static final int MSG_RELEASE_WAKE_LOCK = 5006;
62
63    public static final int MSG_CHANGE_STATE = 5007;
64
65    /* Each time a transaction between the SIM and the BT Client is detected a wakelock is taken.
66     * After an idle period of RELEASE_WAKE_LOCK_DELAY ms the wakelock is released.
67     *
68     * NOTE: While connected the the Nokia 616 car-kit it was noticed that the carkit do
69     *       TRANSFER_APDU_REQ with 20-30 seconds interval, and it sends no requests less than 1 sec
70     *       apart. Additionally the responses from the RIL seems to come within 100 ms, hence a
71     *       one second timeout should be enough.
72     */
73    private static final int RELEASE_WAKE_LOCK_DELAY = 1000;
74
75    /* Intent indicating timeout for user confirmation. */
76    public static final String USER_CONFIRM_TIMEOUT_ACTION =
77            "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT";
78    private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000;
79
80    private PowerManager.WakeLock mWakeLock = null;
81    private BluetoothAdapter mAdapter;
82    private SocketAcceptThread mAcceptThread = null;
83    private BluetoothServerSocket mServerSocket = null;
84    private int mSdpHandle = -1;
85    private BluetoothSocket mConnSocket = null;
86    private BluetoothDevice mRemoteDevice = null;
87    private static String sRemoteDeviceName = null;
88    private volatile boolean mInterrupted;
89    private int mState;
90    private SapServer mSapServer = null;
91    private AlarmManager mAlarmManager = null;
92    private boolean mRemoveTimeoutMsg = false;
93
94    private boolean mIsWaitingAuthorization = false;
95    private boolean mIsRegistered = false;
96
97    // package and class name to which we send intent to check message access access permission
98    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
99    private static final String ACCESS_AUTHORITY_CLASS =
100        "com.android.settings.bluetooth.BluetoothPermissionRequest";
101
102    private static final ParcelUuid[] SAP_UUIDS = {
103        BluetoothUuid.SAP,
104    };
105
106
107    public SapService() {
108        mState = BluetoothSap.STATE_DISCONNECTED;
109    }
110
111    /***
112     * Call this when ever an activity is detected to renew the wakelock
113     *
114     * @param messageHandler reference to the handler to notify
115     *  - typically mSessionStatusHandler, but it cannot be accessed in a static manner.
116     */
117    public static void notifyUpdateWakeLock(Handler messageHandler) {
118        if (messageHandler != null) {
119            Message msg = Message.obtain(messageHandler);
120            msg.what = MSG_ACQUIRE_WAKE_LOCK;
121            msg.sendToTarget();
122        }
123    }
124
125    private void removeSdpRecord() {
126        if (mAdapter != null && mSdpHandle >= 0 &&
127                                SdpManager.getDefaultManager() != null) {
128            if(VERBOSE) Log.d(TAG, "Removing SDP record handle: " + mSdpHandle);
129            boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
130            mSdpHandle = -1;
131        }
132    }
133
134    private void startRfcommSocketListener() {
135        if (VERBOSE) Log.v(TAG, "Sap Service startRfcommSocketListener");
136
137        if (mAcceptThread == null) {
138            mAcceptThread = new SocketAcceptThread();
139            mAcceptThread.setName("SapAcceptThread");
140            mAcceptThread.start();
141        }
142    }
143
144    private final boolean initSocket() {
145        if (VERBOSE) Log.v(TAG, "Sap Service initSocket");
146
147        boolean initSocketOK = false;
148        final int CREATE_RETRY_TIME = 10;
149
150        // It's possible that create will fail in some cases. retry for 10 times
151        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
152            initSocketOK = true;
153            try {
154                // It is mandatory for MSE to support initiation of bonding and encryption.
155                // TODO: Consider reusing the mServerSocket - it is indented to be reused
156                //       for multiple connections.
157                mServerSocket = mAdapter.listenUsingRfcommOn(
158                        BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true);
159                removeSdpRecord();
160                mSdpHandle = SdpManager.getDefaultManager().createSapsRecord(SDP_SAP_SERVICE_NAME,
161                        mServerSocket.getChannel(), SDP_SAP_VERSION);
162            } catch (IOException e) {
163                Log.e(TAG, "Error create RfcommServerSocket ", e);
164                initSocketOK = false;
165            }
166
167            if (!initSocketOK) {
168                // Need to break out of this loop if BT is being turned off.
169                if (mAdapter == null) break;
170                int state = mAdapter.getState();
171                if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
172                    (state != BluetoothAdapter.STATE_ON)) {
173                    Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
174                    break;
175                }
176                try {
177                    if (VERBOSE) Log.v(TAG, "wait 300 ms");
178                    Thread.sleep(300);
179                } catch (InterruptedException e) {
180                    Log.e(TAG, "socketAcceptThread thread was interrupted (3)", e);
181                }
182            } else {
183                break;
184            }
185        }
186
187        if (initSocketOK) {
188            if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
189
190        } else {
191            Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
192        }
193        return initSocketOK;
194    }
195
196    private final synchronized void closeServerSocket() {
197        // exit SocketAcceptThread early
198        if (mServerSocket != null) {
199            try {
200                // this will cause mServerSocket.accept() return early with IOException
201                mServerSocket.close();
202                mServerSocket = null;
203            } catch (IOException ex) {
204                Log.e(TAG, "Close Server Socket error: ", ex);
205            }
206        }
207    }
208    private final synchronized void closeConnectionSocket() {
209        if (mConnSocket != null) {
210            try {
211                mConnSocket.close();
212                mConnSocket = null;
213            } catch (IOException e) {
214                Log.e(TAG, "Close Connection Socket error: ", e);
215            }
216        }
217    }
218
219    private final void closeService() {
220        if (VERBOSE) Log.v(TAG, "SAP Service closeService in");
221
222        // exit initSocket early
223        mInterrupted = true;
224        closeServerSocket();
225
226        if (mAcceptThread != null) {
227            try {
228                mAcceptThread.shutdown();
229                mAcceptThread.join();
230                mAcceptThread = null;
231            } catch (InterruptedException ex) {
232                Log.w(TAG, "mAcceptThread close error", ex);
233            }
234        }
235
236        if (mWakeLock != null) {
237            mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
238            mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
239            mWakeLock.release();
240            mWakeLock = null;
241        }
242
243        closeConnectionSocket();
244
245        if (VERBOSE) Log.v(TAG, "SAP Service closeService out");
246    }
247
248    private final void startSapServerSession() throws IOException {
249        if (VERBOSE) Log.v(TAG, "Sap Service startSapServerSession");
250
251        // acquire the wakeLock before start SAP transaction thread
252        if (mWakeLock == null) {
253            PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
254            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
255                    "StartingSapTransaction");
256            mWakeLock.setReferenceCounted(false);
257            mWakeLock.acquire();
258        }
259
260        /* Start the SAP I/O thread and associate with message handler */
261        mSapServer = new SapServer(mSessionStatusHandler, this, mConnSocket.getInputStream(), mConnSocket.getOutputStream());
262        mSapServer.start();
263        /* Warning: at this point we most likely have already handled the initial connect
264         *          request from the SAP client, hence we need to be prepared to handle the
265         *          response. (the SapHandler should have been started before this point)*/
266
267        mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
268        mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
269                .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
270
271        if (VERBOSE) {
272            Log.v(TAG, "startSapServerSession() success!");
273        }
274    }
275
276    private void stopSapServerSession() {
277
278        /* When we reach this point, the SapServer is closed down, and the client is
279         * supposed to close the RFCOMM connection. */
280        if (VERBOSE) Log.v(TAG, "SAP Service stopSapServerSession");
281
282        mAcceptThread = null;
283        closeConnectionSocket();
284        closeServerSocket();
285
286        setState(BluetoothSap.STATE_DISCONNECTED);
287
288        if (mWakeLock != null) {
289            mWakeLock.release();
290            mWakeLock = null;
291        }
292
293        // Last SAP transaction is finished, we start to listen for incoming
294        // rfcomm connection again
295        if (mAdapter.isEnabled()) {
296            startRfcommSocketListener();
297        }
298    }
299
300    /**
301     * A thread that runs in the background waiting for remote rfcomm
302     * connect.Once a remote socket connected, this thread shall be
303     * shutdown.When the remote disconnect,this thread shall run again waiting
304     * for next request.
305     */
306    private class SocketAcceptThread extends Thread {
307
308        private boolean stopped = false;
309
310        @Override
311        public void run() {
312            BluetoothServerSocket serverSocket;
313            if (mServerSocket == null) {
314                if (!initSocket()) {
315                    return;
316                }
317            }
318
319            while (!stopped) {
320                try {
321                    if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
322                    serverSocket = mServerSocket;
323                    if (serverSocket == null) {
324                        Log.w(TAG, "mServerSocket is null");
325                        break;
326                    }
327                    mConnSocket = mServerSocket.accept();
328                    if (VERBOSE) Log.v(TAG, "Accepted socket connection...");
329                    synchronized (SapService.this) {
330                        if (mConnSocket == null) {
331                            Log.w(TAG, "mConnSocket is null");
332                            break;
333                        }
334                        mRemoteDevice = mConnSocket.getRemoteDevice();
335                    }
336                    if (mRemoteDevice == null) {
337                        Log.i(TAG, "getRemoteDevice() = null");
338                        break;
339                    }
340
341                    sRemoteDeviceName = mRemoteDevice.getName();
342                    // In case getRemoteName failed and return null
343                    if (TextUtils.isEmpty(sRemoteDeviceName)) {
344                        sRemoteDeviceName = getString(R.string.defaultname);
345                    }
346                    int permission = mRemoteDevice.getSimAccessPermission();
347
348                    if (VERBOSE) Log.v(TAG, "getSimAccessPermission() = " + permission);
349
350                    if (permission == BluetoothDevice.ACCESS_ALLOWED) {
351                        try {
352                            if (VERBOSE) Log.v(TAG, "incoming connection accepted from: "
353                                + sRemoteDeviceName + " automatically as trusted device");
354                            startSapServerSession();
355                        } catch (IOException ex) {
356                            Log.e(TAG, "catch exception starting obex server session", ex);
357                        }
358                    } else if (permission != BluetoothDevice.ACCESS_REJECTED){
359                        Intent intent = new
360                                Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
361                        intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
362                        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
363                                        BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
364                        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
365                        intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
366
367                        mIsWaitingAuthorization = true;
368                        setUserTimeoutAlarm();
369                        sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
370
371                        if (VERBOSE) Log.v(TAG, "waiting for authorization for connection from: "
372                                + sRemoteDeviceName);
373
374                    } else {
375                        // Assuming reject is the stored state - continue to accept new connection.
376                        continue;
377                    }
378                    stopped = true; // job done ,close this thread;
379                } catch (IOException ex) {
380                    stopped=true;
381                    if (VERBOSE) Log.v(TAG, "Accept exception: ", ex);
382                }
383            }
384        }
385
386        void shutdown() {
387            stopped = true;
388            interrupt();
389        }
390    }
391
392    private final Handler mSessionStatusHandler = new Handler() {
393        @Override
394        public void handleMessage(Message msg) {
395            if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
396
397            switch (msg.what) {
398                case START_LISTENER:
399                    if (mAdapter.isEnabled()) {
400                        startRfcommSocketListener();
401                    }
402                    break;
403                case USER_TIMEOUT:
404                    if (mIsWaitingAuthorization){
405                        sendCancelUserConfirmationIntent(mRemoteDevice);
406                        cancelUserTimeoutAlarm();
407                        mIsWaitingAuthorization = false;
408                        stopSapServerSession(); // And restart RfcommListener if needed
409                    }
410                    break;
411                case MSG_SERVERSESSION_CLOSE:
412                    stopSapServerSession();
413                    break;
414                case MSG_SESSION_ESTABLISHED:
415                    break;
416                case MSG_SESSION_DISCONNECTED:
417                    // handled elsewhere
418                    break;
419                case MSG_ACQUIRE_WAKE_LOCK:
420                    if (VERBOSE)Log.i(TAG, "Acquire Wake Lock request message");
421                    if (mWakeLock == null) {
422                        PowerManager pm = (PowerManager)getSystemService(
423                                          Context.POWER_SERVICE);
424                        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
425                                    "StartingObexMapTransaction");
426                        mWakeLock.setReferenceCounted(false);
427                    }
428                    if (!mWakeLock.isHeld()) {
429                        mWakeLock.acquire();
430                        if (DEBUG)Log.i(TAG, "  Acquired Wake Lock by message");
431                    }
432                    mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
433                    mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
434                      .obtainMessage(MSG_RELEASE_WAKE_LOCK), RELEASE_WAKE_LOCK_DELAY);
435                    break;
436                case MSG_RELEASE_WAKE_LOCK:
437                    if (VERBOSE)Log.i(TAG, "Release Wake Lock request message");
438                    if (mWakeLock != null) {
439                        mWakeLock.release();
440                        if (DEBUG) Log.i(TAG, "  Released Wake Lock by message");
441                    }
442                    break;
443                case MSG_CHANGE_STATE:
444                    if (DEBUG) Log.d(TAG, "change state message: newState = " + msg.arg1);
445                    setState(msg.arg1);
446                    break;
447                case SHUTDOWN:
448                    /* Ensure to call close from this handler to avoid starting new stuff
449                       because of pending messages */
450                    closeService();
451                    break;
452                default:
453                    break;
454            }
455        }
456    };
457
458    private void setState(int state) {
459        setState(state, BluetoothSap.RESULT_SUCCESS);
460    }
461
462    private synchronized void setState(int state, int result) {
463        if (state != mState) {
464            if (DEBUG) Log.d(TAG, "Sap state " + mState + " -> " + state + ", result = "
465                    + result);
466            int prevState = mState;
467            mState = state;
468            Intent intent = new Intent(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
469            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
470            intent.putExtra(BluetoothProfile.EXTRA_STATE, mState);
471            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
472            sendBroadcast(intent, BLUETOOTH_PERM);
473            AdapterService s = AdapterService.getAdapterService();
474            if (s != null) {
475                s.onProfileConnectionStateChanged(mRemoteDevice, BluetoothProfile.SAP,
476                        mState, prevState);
477            }
478        }
479    }
480
481    public int getState() {
482        return mState;
483    }
484
485    public BluetoothDevice getRemoteDevice() {
486        return mRemoteDevice;
487    }
488
489    public static String getRemoteDeviceName() {
490        return sRemoteDeviceName;
491    }
492
493    public boolean disconnect(BluetoothDevice device) {
494        boolean result = false;
495        synchronized (SapService.this) {
496            if (getRemoteDevice().equals(device)) {
497                switch (mState) {
498                    case BluetoothSap.STATE_CONNECTED:
499                        closeConnectionSocket();
500                        setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
501                        result = true;
502                        break;
503                    default:
504                        break;
505                }
506            }
507        }
508        return result;
509    }
510
511    public List<BluetoothDevice> getConnectedDevices() {
512        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
513        synchronized(this) {
514            if (mState == BluetoothSap.STATE_CONNECTED && mRemoteDevice != null) {
515                devices.add(mRemoteDevice);
516            }
517        }
518        return devices;
519    }
520
521    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
522        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
523        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
524        int connectionState;
525        synchronized (this) {
526            for (BluetoothDevice device : bondedDevices) {
527                ParcelUuid[] featureUuids = device.getUuids();
528                if (!BluetoothUuid.containsAnyUuid(featureUuids, SAP_UUIDS)) {
529                    continue;
530                }
531                connectionState = getConnectionState(device);
532                for(int i = 0; i < states.length; i++) {
533                    if (connectionState == states[i]) {
534                        deviceList.add(device);
535                    }
536                }
537            }
538        }
539        return deviceList;
540    }
541
542    public int getConnectionState(BluetoothDevice device) {
543        synchronized(this) {
544            if (getState() == BluetoothSap.STATE_CONNECTED && getRemoteDevice().equals(device)) {
545                return BluetoothProfile.STATE_CONNECTED;
546            } else {
547                return BluetoothProfile.STATE_DISCONNECTED;
548            }
549        }
550    }
551
552    public boolean setPriority(BluetoothDevice device, int priority) {
553        Settings.Global.putInt(getContentResolver(),
554            Settings.Global.getBluetoothSapPriorityKey(device.getAddress()),
555            priority);
556        if (DEBUG) Log.d(TAG, "Saved priority " + device + " = " + priority);
557        return true;
558    }
559
560    public int getPriority(BluetoothDevice device) {
561        int priority = Settings.Global.getInt(getContentResolver(),
562            Settings.Global.getBluetoothSapPriorityKey(device.getAddress()),
563            BluetoothProfile.PRIORITY_UNDEFINED);
564        return priority;
565    }
566
567    @Override
568    protected IProfileServiceBinder initBinder() {
569        return new SapBinder(this);
570    }
571
572    @Override
573    protected boolean start() {
574        Log.v(TAG, "start()");
575        IntentFilter filter = new IntentFilter();
576        filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
577        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
578        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
579        filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
580
581        try {
582            registerReceiver(mSapReceiver, filter);
583            mIsRegistered = true;
584        } catch (Exception e) {
585            Log.w(TAG,"Unable to register sap receiver",e);
586        }
587        mInterrupted = false;
588        mAdapter = BluetoothAdapter.getDefaultAdapter();
589        // start RFCOMM listener
590        mSessionStatusHandler.sendMessage(mSessionStatusHandler
591                .obtainMessage(START_LISTENER));
592        return true;
593    }
594
595    @Override
596    protected boolean stop() {
597        Log.v(TAG, "stop()");
598        if (!mIsRegistered){
599            Log.i(TAG, "Avoid unregister when receiver it is not registered");
600            return true;
601        }
602        try {
603            mIsRegistered = false;
604            unregisterReceiver(mSapReceiver);
605        } catch (Exception e) {
606            Log.w(TAG,"Unable to unregister sap receiver",e);
607        }
608        setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
609        sendShutdownMessage();
610        return true;
611    }
612
613    public boolean cleanup()  {
614        setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
615        closeService();
616        if (mSessionStatusHandler != null) {
617            mSessionStatusHandler.removeCallbacksAndMessages(null);
618        }
619        return true;
620    }
621
622    private void setUserTimeoutAlarm(){
623        if (DEBUG)Log.d(TAG,"SetUserTimeOutAlarm()");
624        if (mAlarmManager == null){
625            mAlarmManager =(AlarmManager) this.getSystemService (Context.ALARM_SERVICE);
626        }
627        if(mRemoveTimeoutMsg) {
628            cancelUserTimeoutAlarm();
629        }
630        mRemoveTimeoutMsg = true;
631        Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
632        PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
633        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
634                + USER_CONFIRM_TIMEOUT_VALUE,pIntent);
635    }
636
637    private void cancelUserTimeoutAlarm(){
638        if (DEBUG)Log.d(TAG,"cancelUserTimeOutAlarm()");
639        Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
640        PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
641        AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
642        alarmManager.cancel(sender);
643        mRemoveTimeoutMsg = false;
644    }
645
646    private void sendCancelUserConfirmationIntent(BluetoothDevice device) {
647        Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
648        intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
649        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
650        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
651                        BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
652        sendBroadcast(intent, BLUETOOTH_PERM);
653    }
654
655    private void sendShutdownMessage() {
656        /* Any pending messages are no longer valid.
657        To speed up things, simply delete them. */
658        if (mRemoveTimeoutMsg) {
659            Intent timeoutIntent =
660                    new Intent(USER_CONFIRM_TIMEOUT_ACTION);
661            sendBroadcast(timeoutIntent, BLUETOOTH_PERM);
662            mIsWaitingAuthorization = false;
663            cancelUserTimeoutAlarm();
664        }
665        removeSdpRecord();
666        mSessionStatusHandler.removeCallbacksAndMessages(null);
667        // Request release of all resources
668        mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
669    }
670
671    private void sendConnectTimeoutMessage() {
672        if (DEBUG) Log.d(TAG, "sendConnectTimeoutMessage()");
673        if (mSessionStatusHandler != null) {
674            Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT);
675            msg.sendToTarget();
676        } // Can only be null during shutdown
677    }
678
679    private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver();
680
681    private class SapBroadcastReceiver extends BroadcastReceiver {
682        @Override
683        public void onReceive(Context context, Intent intent) {
684
685            if (VERBOSE) Log.v(TAG, "onReceive");
686            String action = intent.getAction();
687            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
688                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
689                                               BluetoothAdapter.ERROR);
690                if (state == BluetoothAdapter.STATE_TURNING_OFF) {
691                    if (DEBUG) Log.d(TAG, "STATE_TURNING_OFF");
692                    sendShutdownMessage();
693                } else if (state == BluetoothAdapter.STATE_ON) {
694                    if (DEBUG) Log.d(TAG, "STATE_ON");
695                    // start RFCOMM listener
696                    mSessionStatusHandler.sendMessage(mSessionStatusHandler
697                                  .obtainMessage(START_LISTENER));
698                }
699            } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
700                Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
701                if (!mIsWaitingAuthorization) {
702                    // this reply is not for us
703                    return;
704                }
705
706                mIsWaitingAuthorization = false;
707
708                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
709                                       BluetoothDevice.CONNECTION_ACCESS_NO) ==
710                    BluetoothDevice.CONNECTION_ACCESS_YES) {
711                    //bluetooth connection accepted by user
712                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
713                        boolean result = mRemoteDevice.setSimAccessPermission(
714                                BluetoothDevice.ACCESS_ALLOWED);
715                        if (VERBOSE) {
716                            Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result);
717                        }                    }
718                    try {
719                        if (mConnSocket != null) {
720                            // start obex server and rfcomm connection
721                            startSapServerSession();
722                        } else {
723                            stopSapServerSession();
724                        }
725                    } catch (IOException ex) {
726                        Log.e(TAG, "Caught the error: ", ex);
727                    }
728                } else {
729                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
730                        boolean result = mRemoteDevice.setSimAccessPermission(
731                                BluetoothDevice.ACCESS_REJECTED);
732                        if (VERBOSE) {
733                            Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result="
734                                    + result);
735                        }
736                    }
737                    // Ensure proper cleanup, and prepare for new connect.
738                    mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
739                }
740            } else if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)){
741                if (DEBUG) Log.d(TAG, "USER_CONFIRM_TIMEOUT ACTION Received.");
742                // send us self a message about the timeout.
743                sendConnectTimeoutMessage();
744            }  else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) &&
745                    mIsWaitingAuthorization) {
746                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
747
748                if (mRemoteDevice == null || device == null) {
749                    Log.i(TAG, "Unexpected error!");
750                    return;
751                }
752
753                if (DEBUG) Log.d(TAG,"ACL disconnected for " + device);
754
755                if (mRemoteDevice.equals(device)) {
756                    if (mRemoveTimeoutMsg) {
757                        // Send any pending timeout now, as ACL got disconnected.
758                        cancelUserTimeoutAlarm();
759                        mSessionStatusHandler.removeMessages(USER_TIMEOUT);
760                        sendCancelUserConfirmationIntent(mRemoteDevice);
761                    }
762                    mIsWaitingAuthorization = false;
763                    setState(BluetoothSap.STATE_DISCONNECTED);
764                    // Ensure proper cleanup, and prepare for new connect.
765                    mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
766                }
767            }
768        }
769    };
770
771    //Binder object: Must be static class or memory leak may occur
772    /**
773     * This class implements the IBluetoothSap interface - or actually it validates the
774     * preconditions for calling the actual functionality in the SapService, and calls it.
775     */
776    private static class SapBinder extends IBluetoothSap.Stub
777        implements IProfileServiceBinder {
778        private SapService mService;
779
780        private SapService getService() {
781            if (!Utils.checkCaller()) {
782                Log.w(TAG,"call not allowed for non-active user");
783                return null;
784            }
785
786            if (mService != null && mService.isAvailable() ) {
787                mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
788                return mService;
789            }
790            return null;
791        }
792
793        SapBinder(SapService service) {
794            Log.v(TAG, "SapBinder()");
795            mService = service;
796        }
797
798        public boolean cleanup()  {
799            mService = null;
800            return true;
801        }
802
803        public int getState() {
804            Log.v(TAG, "getState()");
805            SapService service = getService();
806            if (service == null) return BluetoothSap.STATE_DISCONNECTED;
807            return getService().getState();
808        }
809
810        public BluetoothDevice getClient() {
811            Log.v(TAG, "getClient()");
812            SapService service = getService();
813            if (service == null) return null;
814            Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
815            return service.getRemoteDevice();
816        }
817
818        public boolean isConnected(BluetoothDevice device) {
819            Log.v(TAG, "isConnected()");
820            SapService service = getService();
821            if (service == null) return false;
822            return (service.getState() == BluetoothSap.STATE_CONNECTED
823                    && service.getRemoteDevice().equals(device));
824        }
825
826        public boolean connect(BluetoothDevice device) {
827            Log.v(TAG, "connect()");
828            SapService service = getService();
829            if (service == null) return false;
830            return false;
831        }
832
833        public boolean disconnect(BluetoothDevice device) {
834            Log.v(TAG, "disconnect()");
835            SapService service = getService();
836            if (service == null) return false;
837            return service.disconnect(device);
838        }
839
840        public List<BluetoothDevice> getConnectedDevices() {
841            Log.v(TAG, "getConnectedDevices()");
842            SapService service = getService();
843            if (service == null) return new ArrayList<BluetoothDevice>(0);
844            return service.getConnectedDevices();
845        }
846
847        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
848            Log.v(TAG, "getDevicesMatchingConnectionStates()");
849            SapService service = getService();
850            if (service == null) return new ArrayList<BluetoothDevice>(0);
851            return service.getDevicesMatchingConnectionStates(states);
852        }
853
854        public int getConnectionState(BluetoothDevice device) {
855            Log.v(TAG, "getConnectionState()");
856            SapService service = getService();
857            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
858            return service.getConnectionState(device);
859        }
860
861        public boolean setPriority(BluetoothDevice device, int priority) {
862            SapService service = getService();
863            if (service == null) return false;
864            return service.setPriority(device, priority);
865        }
866
867        public int getPriority(BluetoothDevice device) {
868            SapService service = getService();
869            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
870            return service.getPriority(device);
871        }
872    }
873}
874