SapService.java revision 987a8cdbdc36548f76ad63fc5ab33f8b3ce2f69a
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
96    // package and class name to which we send intent to check message access access permission
97    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
98    private static final String ACCESS_AUTHORITY_CLASS =
99        "com.android.settings.bluetooth.BluetoothPermissionRequest";
100
101    private static final ParcelUuid[] SAP_UUIDS = {
102        BluetoothUuid.SAP,
103    };
104
105
106    public SapService() {
107        mState = BluetoothSap.STATE_DISCONNECTED;
108    }
109
110    /***
111     * Call this when ever an activity is detected to renew the wakelock
112     *
113     * @param messageHandler reference to the handler to notify
114     *  - typically mSessionStatusHandler, but it cannot be accessed in a static manner.
115     */
116    public static void notifyUpdateWakeLock(Handler messageHandler) {
117        if (messageHandler != null) {
118            Message msg = Message.obtain(messageHandler);
119            msg.what = MSG_ACQUIRE_WAKE_LOCK;
120            msg.sendToTarget();
121        }
122    }
123
124
125
126    private void startRfcommSocketListener() {
127        if (VERBOSE) Log.v(TAG, "Sap Service startRfcommSocketListener");
128
129        if (mAcceptThread == null) {
130            mAcceptThread = new SocketAcceptThread();
131            mAcceptThread.setName("SapAcceptThread");
132            mAcceptThread.start();
133        }
134    }
135
136    private final boolean initSocket() {
137        if (VERBOSE) Log.v(TAG, "Sap Service initSocket");
138
139        boolean initSocketOK = false;
140        final int CREATE_RETRY_TIME = 10;
141
142        // It's possible that create will fail in some cases. retry for 10 times
143        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
144            initSocketOK = true;
145            try {
146                // It is mandatory for MSE to support initiation of bonding and encryption.
147                // TODO: Consider reusing the mServerSocket - it is indented to be reused
148                //       for multiple connections.
149                mServerSocket = mAdapter.listenUsingRfcommOn(
150                        BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true);
151                if (mSdpHandle >= 0) {
152                    SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
153                    if (VERBOSE) Log.d(TAG, "Removing SDP record");
154                }
155                mSdpHandle = SdpManager.getDefaultManager().createSapsRecord(SDP_SAP_SERVICE_NAME,
156                        mServerSocket.getChannel(), SDP_SAP_VERSION);
157            } catch (IOException e) {
158                Log.e(TAG, "Error create RfcommServerSocket ", e);
159                initSocketOK = false;
160            }
161
162            if (!initSocketOK) {
163                // Need to break out of this loop if BT is being turned off.
164                if (mAdapter == null) break;
165                int state = mAdapter.getState();
166                if ((state != BluetoothAdapter.STATE_TURNING_ON) &&
167                    (state != BluetoothAdapter.STATE_ON)) {
168                    Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
169                    break;
170                }
171                try {
172                    if (VERBOSE) Log.v(TAG, "wait 300 ms");
173                    Thread.sleep(300);
174                } catch (InterruptedException e) {
175                    Log.e(TAG, "socketAcceptThread thread was interrupted (3)", e);
176                }
177            } else {
178                break;
179            }
180        }
181        if (mInterrupted) {
182            initSocketOK = false;
183            // close server socket to avoid resource leakage
184            closeServerSocket();
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        } catch (Exception e) {
584            Log.w(TAG,"Unable to register sap receiver",e);
585        }
586        mInterrupted = false;
587        mAdapter = BluetoothAdapter.getDefaultAdapter();
588        // start RFCOMM listener
589        mSessionStatusHandler.sendMessage(mSessionStatusHandler
590                .obtainMessage(START_LISTENER));
591        return true;
592    }
593
594    @Override
595    protected boolean stop() {
596        Log.v(TAG, "stop()");
597        try {
598            unregisterReceiver(mSapReceiver);
599        } catch (Exception e) {
600            Log.w(TAG,"Unable to unregister sap receiver",e);
601        }
602        setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
603        sendShutdownMessage();
604        return true;
605    }
606
607    public boolean cleanup()  {
608        setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
609        closeService();
610        if (mSessionStatusHandler != null) {
611            mSessionStatusHandler.removeCallbacksAndMessages(null);
612        }
613        return true;
614    }
615
616    private void setUserTimeoutAlarm(){
617        if (DEBUG)Log.d(TAG,"SetUserTimeOutAlarm()");
618        if (mAlarmManager == null){
619            mAlarmManager =(AlarmManager) this.getSystemService (Context.ALARM_SERVICE);
620        }
621        if(mRemoveTimeoutMsg) {
622            cancelUserTimeoutAlarm();
623        }
624        mRemoveTimeoutMsg = true;
625        Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
626        PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
627        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
628                + USER_CONFIRM_TIMEOUT_VALUE,pIntent);
629    }
630
631    private void cancelUserTimeoutAlarm(){
632        if (DEBUG)Log.d(TAG,"cancelUserTimeOutAlarm()");
633        Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
634        PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
635        AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
636        alarmManager.cancel(sender);
637        mRemoveTimeoutMsg = false;
638    }
639
640    private void sendCancelUserConfirmationIntent(BluetoothDevice device) {
641        Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
642        intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
643        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
644        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
645                        BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
646        sendBroadcast(intent, BLUETOOTH_PERM);
647    }
648
649    private void sendShutdownMessage() {
650        /* Any pending messages are no longer valid.
651        To speed up things, simply delete them. */
652        if (mRemoveTimeoutMsg) {
653            Intent timeoutIntent =
654                    new Intent(USER_CONFIRM_TIMEOUT_ACTION);
655            sendBroadcast(timeoutIntent, BLUETOOTH_PERM);
656            mIsWaitingAuthorization = false;
657            cancelUserTimeoutAlarm();
658        }
659        mSessionStatusHandler.removeCallbacksAndMessages(null);
660        // Request release of all resources
661        mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
662    }
663
664    private void sendConnectTimeoutMessage() {
665        if (DEBUG) Log.d(TAG, "sendConnectTimeoutMessage()");
666        if (mSessionStatusHandler != null) {
667            Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT);
668            msg.sendToTarget();
669        } // Can only be null during shutdown
670    }
671
672    private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver();
673
674    private class SapBroadcastReceiver extends BroadcastReceiver {
675        @Override
676        public void onReceive(Context context, Intent intent) {
677
678            if (VERBOSE) Log.v(TAG, "onReceive");
679            String action = intent.getAction();
680            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
681                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
682                                               BluetoothAdapter.ERROR);
683                if (state == BluetoothAdapter.STATE_TURNING_OFF) {
684                    if (DEBUG) Log.d(TAG, "STATE_TURNING_OFF");
685                    sendShutdownMessage();
686                } else if (state == BluetoothAdapter.STATE_ON) {
687                    if (DEBUG) Log.d(TAG, "STATE_ON");
688                    // start RFCOMM listener
689                    mSessionStatusHandler.sendMessage(mSessionStatusHandler
690                                  .obtainMessage(START_LISTENER));
691                }
692            } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
693                Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
694                if (!mIsWaitingAuthorization) {
695                    // this reply is not for us
696                    return;
697                }
698
699                mIsWaitingAuthorization = false;
700
701                if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
702                                       BluetoothDevice.CONNECTION_ACCESS_NO) ==
703                    BluetoothDevice.CONNECTION_ACCESS_YES) {
704                    //bluetooth connection accepted by user
705                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
706                        boolean result = mRemoteDevice.setSimAccessPermission(
707                                BluetoothDevice.ACCESS_ALLOWED);
708                        if (VERBOSE) {
709                            Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result);
710                        }                    }
711                    try {
712                        if (mConnSocket != null) {
713                            // start obex server and rfcomm connection
714                            startSapServerSession();
715                        } else {
716                            stopSapServerSession();
717                        }
718                    } catch (IOException ex) {
719                        Log.e(TAG, "Caught the error: ", ex);
720                    }
721                } else {
722                    if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
723                        boolean result = mRemoteDevice.setSimAccessPermission(
724                                BluetoothDevice.ACCESS_REJECTED);
725                        if (VERBOSE) {
726                            Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result="
727                                    + result);
728                        }
729                    }
730                    // Ensure proper cleanup, and prepare for new connect.
731                    mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
732                }
733            } else if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)){
734                if (DEBUG) Log.d(TAG, "USER_CONFIRM_TIMEOUT ACTION Received.");
735                // send us self a message about the timeout.
736                sendConnectTimeoutMessage();
737            }  else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) &&
738                    mIsWaitingAuthorization) {
739                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
740
741                if (mRemoteDevice == null || device == null) {
742                    Log.i(TAG, "Unexpected error!");
743                    return;
744                }
745
746                if (DEBUG) Log.d(TAG,"ACL disconnected for " + device);
747
748                if (mRemoteDevice.equals(device) && mRemoveTimeoutMsg) {
749                    // Send any pending timeout now, as ACL got disconnected.
750                    cancelUserTimeoutAlarm();
751                    mSessionStatusHandler.removeMessages(USER_TIMEOUT);
752                    sendCancelUserConfirmationIntent(mRemoteDevice);
753                    mIsWaitingAuthorization = false;
754                    mRemoveTimeoutMsg = false;
755                    // Ensure proper cleanup, and prepare for new connect.
756                    mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
757                }
758            }
759        }
760    };
761
762    //Binder object: Must be static class or memory leak may occur
763    /**
764     * This class implements the IBluetoothSap interface - or actually it validates the
765     * preconditions for calling the actual functionality in the SapService, and calls it.
766     */
767    private static class SapBinder extends IBluetoothSap.Stub
768        implements IProfileServiceBinder {
769        private SapService mService;
770
771        private SapService getService() {
772            if (!Utils.checkCaller()) {
773                Log.w(TAG,"call not allowed for non-active user");
774                return null;
775            }
776
777            if (mService != null && mService.isAvailable() ) {
778                mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
779                return mService;
780            }
781            return null;
782        }
783
784        SapBinder(SapService service) {
785            Log.v(TAG, "SapBinder()");
786            mService = service;
787        }
788
789        public boolean cleanup()  {
790            mService = null;
791            return true;
792        }
793
794        public int getState() {
795            Log.v(TAG, "getState()");
796            SapService service = getService();
797            if (service == null) return BluetoothSap.STATE_DISCONNECTED;
798            return getService().getState();
799        }
800
801        public BluetoothDevice getClient() {
802            Log.v(TAG, "getClient()");
803            SapService service = getService();
804            if (service == null) return null;
805            Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
806            return service.getRemoteDevice();
807        }
808
809        public boolean isConnected(BluetoothDevice device) {
810            Log.v(TAG, "isConnected()");
811            SapService service = getService();
812            if (service == null) return false;
813            return (service.getState() == BluetoothSap.STATE_CONNECTED
814                    && service.getRemoteDevice().equals(device));
815        }
816
817        public boolean connect(BluetoothDevice device) {
818            Log.v(TAG, "connect()");
819            SapService service = getService();
820            if (service == null) return false;
821            return false;
822        }
823
824        public boolean disconnect(BluetoothDevice device) {
825            Log.v(TAG, "disconnect()");
826            SapService service = getService();
827            if (service == null) return false;
828            return service.disconnect(device);
829        }
830
831        public List<BluetoothDevice> getConnectedDevices() {
832            Log.v(TAG, "getConnectedDevices()");
833            SapService service = getService();
834            if (service == null) return new ArrayList<BluetoothDevice>(0);
835            return service.getConnectedDevices();
836        }
837
838        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
839            Log.v(TAG, "getDevicesMatchingConnectionStates()");
840            SapService service = getService();
841            if (service == null) return new ArrayList<BluetoothDevice>(0);
842            return service.getDevicesMatchingConnectionStates(states);
843        }
844
845        public int getConnectionState(BluetoothDevice device) {
846            Log.v(TAG, "getConnectionState()");
847            SapService service = getService();
848            if (service == null) return BluetoothProfile.STATE_DISCONNECTED;
849            return service.getConnectionState(device);
850        }
851
852        public boolean setPriority(BluetoothDevice device, int priority) {
853            SapService service = getService();
854            if (service == null) return false;
855            return service.setPriority(device, priority);
856        }
857
858        public int getPriority(BluetoothDevice device) {
859            SapService service = getService();
860            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
861            return service.getPriority(device);
862        }
863    }
864}
865