Отключите разъем Bluetooth в Android

Я разрабатываю программу, в которой с телефона Android я должен подключиться в качестве клиента к медицинскому датчику Bluetooth. Я использую официальный API Bluetooth и без проблем во время соединения (профиль SPP), но когда я заканчиваю розетку, датчик все еще подключен к моему телефону (хотя я закрыл соединение).

Есть ли способ отключить Bluetooth? Я думаю, что есть намерение под названием ACTION_ACL_CONNECTED, которое делает это. Может кто-нибудь объяснить мне, как это использовать?

Заранее спасибо.

Отредактировано: вот код, если кому-то нужна дополнительная информация, это медицинский датчик Nonin 4100.

Set<BluetoothDevice> pairedDevices = Activa.myBluetoothAdapter.getBondedDevices();
        // If there are paired devices
        if (pairedDevices.size() > 0) {
            // Loop through paired devices
            for (BluetoothDevice device : pairedDevices) {
                // Add the name and address to an array adapter to show in a ListView
                String name = device.getName();
                if (name.contains("Nonin")) {
                    try {
                        found = true;
//                      socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
//                      handler.sendEmptyMessage(5);
//                      Activa.myBluetoothAdapter.cancelDiscovery();
//                      socket.connect();
                        BluetoothDevice hxm = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(device.getAddress());
                        Method m;
                        try {
                            m = hxm.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                            socket = (BluetoothSocket)m.invoke(hxm, Integer.valueOf(1));
                            handler.sendEmptyMessage(5);
                            socket.connect();
                        } catch (Exception e) {
                            handler.sendEmptyMessage(7);
                            e.printStackTrace();
                            break;
                        }
                        handler.sendEmptyMessage(6);
                        InputStream in = socket.getInputStream();
                        OutputStream out = socket.getOutputStream();
                        byte[] retrieve = { 0x44, 0x31};
                        out.write(retrieve);
                        byte [] ack = new byte [1];
                        in.read(ack);
                        if (ack[0] == 0x15) {
                            cancelMeasurement();
                            return;
                        }
                        byte [] data = new byte [3];
                        long timeStart = System.currentTimeMillis();
                        this.timePassed = System.currentTimeMillis() - timeStart;
                        while ((this.timePassed < (this.time))&&(this.finished)) {
                            try {
                                in.read(data);
                                processData(data);
                                Thread.sleep(1000);
                                this.timePassed = System.currentTimeMillis() - timeStart;
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        in.close();
                        out.close();
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
}

6 ответов

Решение

Пожалуйста, не забудьте сначала закрыть потоки ввода / вывода, а затем закрыть сокет.

Закрывая потоки, вы запускаете процесс отключения. После того, как вы закроете сокет, соединение должно быть полностью разорвано.

Если вы закроете сокет до потоков, вы можете пропустить определенные этапы выключения, такие как (правильное) закрытие соединения физического уровня.

Вот метод, который я использую, когда пришло время разорвать соединение.

/**
 * Reset input and output streams and make sure socket is closed. 
 * This method will be used during shutdown() to ensure that the connection is properly closed during a shutdown.  
 * @return
 */
private void resetConnection() {
        if (mBTInputStream != null) {
                try {mBTInputStream.close();} catch (Exception e) {}
                mBTInputStream = null;
        }

        if (mBTOutputStream != null) {
                try {mBTOutputStream.close();} catch (Exception e) {}
                mBTOutputStream = null;
        }

        if (mBTSocket != null) {
                try {mBTSocket.close();} catch (Exception e) {}
                mBTSocket = null;
        }

}

РЕДАКТИРОВАТЬ: Добавление кода для подключения ():

// bluetooth adapter which provides access to bluetooth functionality. 
BluetoothAdapter        mBTAdapter      = null;
// socket represents the open connection.
BluetoothSocket         mBTSocket   = null;
// device represents the peer
BluetoothDevice         mBTDevice       = null; 

// streams
InputStream           mBTInputStream  = null;
OutputStream          mBTOutputStream = null;

static final UUID UUID_RFCOMM_GENERIC = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

/**
 * Try to establish a connection with the peer. 
 * This method runs synchronously and blocks for one or more seconds while it does its thing 
 * SO CALL IT FROM A NON-UI THREAD!
 * @return - returns true if the connection has been established and is ready for use. False otherwise. 
 */
private  boolean connect() {

        // Reset all streams and socket.
        resetConnection();

        // make sure peer is defined as a valid device based on their MAC. If not then do it. 
        if (mBTDevice == null) 
                mBTDevice = mBTAdapter.getRemoteDevice(mPeerMAC);

        // Make an RFCOMM binding. 
        try {mBTSocket = mBTDevice.createRfcommSocketToServiceRecord(UUID_RFCOMM_GENERIC);
        } catch (Exception e1) {
                msg ("connect(): Failed to bind to RFCOMM by UUID. msg=" + e1.getMessage());
                return false;
        }

        msg ("connect(): Trying to connect.");

        try {
                mBTSocket.connect();
        } catch (Exception e) {
                msg ("connect(): Exception thrown during connect: " + e.getMessage());
                return false;
        }

        msg ("connect(): CONNECTED!");

        try {
                mBTOutputStream = mBTSocket.getOutputStream();
                mBTInputStream  = mBTSocket.getInputStream();
        } catch (Exception e) {
                msg ("connect(): Error attaching i/o streams to socket. msg=" + e.getMessage());
                return false;
        }

        return true;
}

Я обнаружил, что если я вызываю socket.close() слишком скоро после недавней связи через OutputStream, то закрытие завершается неудачно, и я не могу восстановить соединение. Я добавил Thread.sleep(1000) непосредственно перед вызовом close (), и это, похоже, решает эту проблему.

ПРИВЕТ,

Я видел точно такую ​​же проблему (HTC Desire). Несмотря на закрытие сокета книгой (как предлагает Брэд), следующий connect () блокируется навсегда - пока не завершится close () другим потоком.

Я обошел проблему, всегда вызывая BluetoothAdapter.disable()/. Enable () перед подключением. Ужасный, недружественный взломать, я знаю...

Я подозреваю, что некоторые из существующих проблем с BT связаны с конкретным производителем, так как некоторые разработчики приложений, кажется, счастливо живут с createRfcommSocketToServiceRecord(), который определенно не работает на моем HTC Desire (Android 2.1, обновление 1).

Я видел признаки (извините, у меня нет ссылок), что стек BT HTC Desire отличается от Nexus One, хотя кажется, что это очень похожие устройства...

BR Per

(дополнение) Вот очень простое упражнение для воспроизведения проблемы (без моего отключения / включения 'лечения'):

package com.care2wear.BtTest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class BtTestActivity extends Activity {
    private static final String TAG="BtTest";

    BluetoothAdapter mBtAdapter = null;
    BluetoothDevice mBtDev = null;
    BluetoothSocket mBtSocket = null;
    InputStream isBt;
    OutputStream osBt;  
    String mAddress = "00:18:E4:1C:A4:66";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        init();

        connect();  // ok
        disconnect(); // ok
        connect(); // this invariably fails - blocked until BT is switched off by someone else, or the peer device turns off/goes out of range
        disconnect();
    }

    private void init() {
        Log.d(TAG, "initializing");
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        mBtDev = mBtAdapter.getRemoteDevice(mAddress);
        Log.d(TAG, "initialized");
    }

    private void connect() {
        try {
            Log.d(TAG, "connecting");
            Method m = mBtDev.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
            mBtSocket = (BluetoothSocket) m.invoke(mBtDev, 1);
            mBtSocket.connect();
            Log.d(TAG, "connected");
        } catch (SecurityException e) {
            Log.e(TAG, "SecEx", e);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "NsmEx", e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "IArgEx", e);
        } catch (IllegalAccessException e) {
            Log.e(TAG, "IAccEx", e);
        } catch (InvocationTargetException e) {
            Log.e(TAG, "ItEx", e);
        } catch (IOException e) {
            Log.e(TAG, "IOEx", e);
        }

    }

    private void disconnect() {
        Log.d(TAG, "closing");

        if (isBt != null) {
            try {
                isBt.close();
            } catch (IOException e) {
                Log.e(TAG, "isBt IOE", e);              
            }
            isBt = null;
        }
        if (osBt != null) {
            try {
                osBt.close();
            } catch (IOException e) {
                Log.e(TAG, "osBt IOE", e);              
            }
            osBt = null;
        }
        if (mBtSocket != null) {
            try {
                mBtSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "socket IOE", e);                
            }
            mBtSocket = null;
        }
        Log.d(TAG, "closed");       
    }
}

Если кто-то может определить, если я делаю это неправильно, не стесняйтесь комментировать:)

(дополнение 2) Я думаю, что я получил его на работу сейчас:

  1. Официальный метод подключения RFCOMM (через SDP) теперь, похоже, действительно работает (HTC Desire, 2.1, обновление 1), НО мне пришлось удалить и заново подключить устройство BT. Пойди разберись..
  2. Переподключение все равно может завершиться неудачей (ошибка обнаружения службы), если я подключусь "слишком быстро" (выйдите из приложения, а затем немедленно перезапустите). Думаю, связь еще не полностью разорвана..
  3. Если я всегда заканчиваю (последнее) действие не только с помощью finish (), но и с помощью Runtime.getRuntime (). Exit(0);, это работает намного лучше. Пойди разберись снова...

Если кто-нибудь сможет это объяснить, я с удовольствием выучу. / Пер

(дополнение 3) Наконец-то появилось обновление Froyo (2.2) для моего Desire, и, насколько я вижу, SPP теперь работает:) /Per

Я разрабатывал приложение, которое подключается к устройству BT. Ваш код отлично работает в моем HTC Wildfire, но с Samsung Galaxy I5700 не работает. Обе ОС 2.1, но.....

Исключением было "InvocationTargetException"

Единственное, что мне пришлось изменить, - это отключить ().

private void disconnect() {
         if(Conectado){ 
            try {
                ***mBtSocket.close();***
                 texto.setText(texto.getText()+"\nDesconectado");
                 Conectado = false;
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                texto.setText(texto.getText()+"\n"+e1.getMessage());
            } 
            catch (Exception e2) {
                // TODO Auto-generated catch block
                texto.setText(texto.getText()+"\n"+e2.getMessage());
            }    
         }

Итак, я использую приложение Bluetooth Chat с сайта Android Development, и они предоставляют stop() метод в классе BluetoothChatService. Поэтому я просто создал его экземпляр в своем основном классе и вызвал функцию stop из моей кнопки отключения.

Вот как я называю это в моем основном классе

// Член объекта для служб чата

private BluetoothManager mChatService = null;
case R.id.disconnect:
        mChatService.stop();
break;

Метод stop() в BluetoothChatService

private AcceptThread mAcceptThread;
private ConnectThread mConnectThread;
public synchronized void stop() 
{
    if (mConnectThread != null)
    {
        mConnectThread.cancel(); mConnectThread = null;
    }
    if (mConnectedThread != null) 
    {
        mConnectedThread.cancel(); mConnectedThread = null;
    }
    if (mAcceptThread != null) 
    {
        mAcceptThread.cancel(); mAcceptThread = null;
    }
}

У меня такая же проблема. Это проблема с модулем Bluetooth CSR BC417, присутствующим во многих устройствах как адаптер последовательного интерфейса с Bluetooth с профилем SPP. С другим модулем Bluetooth Android-устройство работает хорошо, и Bluetooth освобождает соединение после закрытия сокета, но с устройствами с этим ядром CSR нет. Протестировано на SPP Bluetooth для последовательного адаптера на основе CSR BC417 и модуля Bluetooth от Actisys. Оба с устройствами Android 4.0. Я не знаю почему, но проблема совместимости между харварсами, попробуйте поменять последовательный адаптер на другой с другим ядром. Я пробую программно найти решение, даже отключив блютус, но невозможно, проблема возникла на модуле CSR.

Другие вопросы по тегам