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