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