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