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