FileDocCategorySizeDatePackage
BluetoothEventLoop.javaAPI DocAndroid 1.5 API17152Wed May 06 22:41:56 BST 2009android.server

BluetoothEventLoop

public class BluetoothEventLoop extends Object
TODO: Move this to java/services/com/android/server/BluetoothEventLoop.java and make the contructor package private again.
hide

Fields Summary
private static final String
TAG
private static final boolean
DBG
private int
mNativeData
private Thread
mThread
private boolean
mStarted
private boolean
mInterrupted
private final HashMap
mPasskeyAgentRequestData
private final HashMap
mGetRemoteServiceChannelCallbacks
private final BluetoothDeviceService
mBluetoothService
private final android.content.Context
mContext
private static final int
EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY
private static final int
EVENT_RESTART_BLUETOOTH
private static final long
INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY
private static final long
MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY
private static final String
BLUETOOTH_ADMIN_PERM
private static final String
BLUETOOTH_PERM
private final android.os.Handler
mHandler
Constructors Summary
BluetoothEventLoop(android.content.Context context, BluetoothDeviceService bluetoothService)


      classInitNative(); 
        mBluetoothService = bluetoothService;
        mContext = context;
        mPasskeyAgentRequestData = new HashMap();
        mGetRemoteServiceChannelCallbacks = new HashMap();
        initializeNativeDataNative();
    
Methods Summary
private static native voidclassInitNative()

private native voidcleanupNativeDataNative()

protected voidfinalize()

        try {
            cleanupNativeDataNative();
        } finally {
            super.finalize();
        }
    
java.util.HashMapgetPasskeyAgentRequestData()

        return mPasskeyAgentRequestData;
    
java.util.HashMapgetRemoteServiceChannelCallbacks()

        return mGetRemoteServiceChannelCallbacks;
    
private native voidinitializeNativeDataNative()

public synchronized booleanisEventLoopRunning()

        return mThread != null && mStarted;
    
private static voidlog(java.lang.String msg)

        Log.d(TAG, msg);
    
private booleanonAuthAgentAuthorize(java.lang.String address, java.lang.String service, java.lang.String uuid)

        boolean authorized = false;
        if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) {
            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
            authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
            if (authorized) {
                Log.i(TAG, "Allowing incoming A2DP connection from " + address);
            } else {
                Log.i(TAG, "Rejecting incoming A2DP connection from " + address);
            }
        } else {
            Log.i(TAG, "Rejecting incoming " + service + " connection from " + address);
        }
        return authorized;
    
private voidonAuthAgentCancel(java.lang.String address, java.lang.String service, java.lang.String uuid)

        // We immediately response to DBUS Authorize() so this should not
        // usually happen
        log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")");
    
private voidonBondingCreated(java.lang.String address)

        mBluetoothService.getBondState().setBondState(address.toUpperCase(),
                                                      BluetoothDevice.BOND_BONDED);
    
private voidonBondingRemoved(java.lang.String address)

        mBluetoothService.getBondState().setBondState(address.toUpperCase(),
                BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
    
private voidonCreateBondingResult(java.lang.String address, int result)

        address = address.toUpperCase();
        if (result == BluetoothError.SUCCESS) {
            mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
            if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
                mBluetoothService.getBondState().clearPinAttempts(address);
            }
        } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
                mBluetoothService.getBondState().getAttempt(address) == 1) {
            mBluetoothService.getBondState().addAutoPairingFailure(address);
            pairingAttempt(address, result);
        } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
                mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
            pairingAttempt(address, result);
        } else {
            mBluetoothService.getBondState().setBondState(address,
                                                          BluetoothDevice.BOND_NOT_BONDED, result);
            if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
                mBluetoothService.getBondState().clearPinAttempts(address);
            }
        }
    
private voidonDiscoveryCompleted()

        mBluetoothService.setIsDiscovering(false);
        Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonDiscoveryStarted()

        mBluetoothService.setIsDiscovering(true);
        Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonGetRemoteServiceChannelResult(java.lang.String address, int channel)

        IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address);
        if (callback != null) {
            mGetRemoteServiceChannelCallbacks.remove(address);
            try {
                callback.onGetRemoteServiceChannelResult(address, channel);
            } catch (RemoteException e) {}
        }
    
voidonModeChanged(java.lang.String bluezMode)

        int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode);
        if (mode >= 0) {
            Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
            intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
        }
    
private voidonNameChanged(java.lang.String name)

        Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
        intent.putExtra(BluetoothIntent.NAME, name);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonPasskeyAgentCancel(java.lang.String address)

        address = address.toUpperCase();
        mBluetoothService.cancelPin(address);
        Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
        mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
                                                      BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
    
private voidonPasskeyAgentRequest(java.lang.String address, int nativeData)

        address = address.toUpperCase();
        mPasskeyAgentRequestData.put(address, new Integer(nativeData));

        if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
            // shutdown path
            mBluetoothService.cancelPin(address);
            return;
        }

        if (mBluetoothService.getBondState().getBondState(address) ==
                BluetoothDevice.BOND_BONDING) {
            // we initiated the bonding
            int btClass = mBluetoothService.getRemoteClass(address);

            // try 0000 once if the device looks dumb
            switch (BluetoothClass.Device.getDevice(btClass)) {
            case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
            case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
            case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
            case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
            case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
            case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
                if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) &&
                    !mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) {
                    mBluetoothService.getBondState().attempt(address);
                    mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
                    return;
                }
           }
        }
        Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    
private voidonRemoteClassUpdated(java.lang.String address, int deviceClass)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        intent.putExtra(BluetoothIntent.CLASS, deviceClass);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteDeviceConnected(java.lang.String address)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteDeviceDisappeared(java.lang.String address)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteDeviceDisconnectRequested(java.lang.String address)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteDeviceDisconnected(java.lang.String address)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteDeviceFound(java.lang.String address, int deviceClass, short rssi)

        Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        intent.putExtra(BluetoothIntent.CLASS, deviceClass);
        intent.putExtra(BluetoothIntent.RSSI, rssi);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteNameChanged(java.lang.String address, java.lang.String name)

        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        intent.putExtra(BluetoothIntent.NAME, name);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteNameFailed(java.lang.String address)

        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRemoteNameUpdated(java.lang.String address, java.lang.String name)

        Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
        intent.putExtra(BluetoothIntent.ADDRESS, address);
        intent.putExtra(BluetoothIntent.NAME, name);
        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
    
private voidonRestartRequired()

        if (mBluetoothService.isEnabled()) {
            Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***");
            mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
        }
    
private voidpairingAttempt(java.lang.String address, int result)

        // This happens when our initial guess of "0000" as the pass key
        // fails. Try to create the bond again and display the pin dialog
        // to the user. Use back-off while posting the delayed
        // message. The initial value is
        // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
        // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
        // reached, display an error to the user.
        int attempt = mBluetoothService.getBondState().getAttempt(address);
        if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
                    MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
            mBluetoothService.getBondState().clearPinAttempts(address);
            mBluetoothService.getBondState().setBondState(address,
                    BluetoothDevice.BOND_NOT_BONDED, result);
            return;
        }

        Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
        message.obj = address;
        boolean postResult =  mHandler.sendMessageDelayed(message,
                                        attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
        if (!postResult) {
            mBluetoothService.getBondState().clearPinAttempts(address);
            mBluetoothService.getBondState().setBondState(address,
                    BluetoothDevice.BOND_NOT_BONDED, result);
            return;
        }
        mBluetoothService.getBondState().attempt(address);
    
private native booleansetUpEventLoopNative()

synchronized voidstart()


        if (mThread != null) {
            // Already running.
            return;
        }
        mThread = new Thread("Bluetooth Event Loop") {
                @Override
                public void run() {
                    try {
                        if (setUpEventLoopNative()) {
                            mStarted = true;
                            while (!mInterrupted) {
                                waitForAndDispatchEvent(0);
                                sleep(500);
                            }
                        }
                        // tear down even in the error case to clean
                        // up anything we started to setup
                        tearDownEventLoopNative();
                    } catch (InterruptedException e) { }
                    if (DBG) log("Event Loop thread finished");
                    mThread = null;
                }
            };
        if (DBG) log("Starting Event Loop thread");
        mInterrupted = false;
        mThread.start();
    
public synchronized voidstop()

        if (mThread != null) {
            mInterrupted = true;
            try {
                mThread.join();
                mThread = null;
            } catch (InterruptedException e) {
                Log.i(TAG, "Interrupted waiting for Event Loop thread to join");
            }
        }
    
private native voidtearDownEventLoopNative()

private synchronized booleanwaitForAndDispatchEvent(int timeout_ms)

        return waitForAndDispatchEventNative(timeout_ms);
    
private native booleanwaitForAndDispatchEventNative(int timeout_ms)