Соединение Bluetooth RFCOMM не может быть установлено последовательно на Android 4.2

У меня есть приложение, которое разговаривает с пользовательским устройством через RFCOMM через Bluetooth. Код связи основан на примере проекта BluetoothTalk. Он работал без каких-либо проблем раньше на Galaxy S3, Galaxy S2, Galaxy Note и Nexus 7.

Недавно Nexus 7 был обновлен до Android 4.2, и с тех пор проблема происходит следующим образом:

  • Когда вы используете приложение для настройки соединения в первый раз, что означает, что устройство только что включено, а приложение только что запущено, никаких проблем, вы можете получить данные в обычном режиме.

  • Затем, если вы остановите связь и попытаетесь перезапустить, сообщение завершится с ошибкой "java.io.IOException: bt socket closed, read return: -1". С этого момента, независимо от того, сколько раз вы пытаетесь восстановить соединение, он просто всегда терпит неудачу.

  • Единственный способ заставить его работать снова - перезагрузить пользовательское устройство и приложение, а затем попытаться подключиться, общение станет нормальным. Но затем, как только вы остановите и перезапустите сообщение, он продолжает терпеть неудачу.

Я позаимствовал Nexus 4 с Android 4.2, и проблема остается.

Это действительно раздражает, потому что основная ценность нашего устройства зависит от приложения RFCOMM Bluetooth. Я дважды проверял документацию на BT в Android 4.2 и не видел никаких существенных изменений. Я довольно уверен в том, что код на моей стороне, потому что он работает для любого Android-устройства, которое не работает 4.2

Любые намеки или предложения были бы весьма полезны. Устройство необходимо демонтировать в самом начале декабря, и мы действительно хотим решить эту проблему как можно скорее.

EDIT: теперь, когда был выпущен 4.2.1, и проблема все еще не решена. Можем ли мы хотя бы получить подтверждение относительно того, будет ли оно работать и скоро будет исправлено?

Ответ 1

Я наконец понял решение.

Оказывается, это не ошибка в новом Bluetooth-драйвере. Это угловой случай, который не рассматривается в примере с образцом Bluetooth, в частности, проект BluetoothChat, предоставляемый Android SDK.

В служебном коде Bluetooth внутри проекта BluetoothChat, когда соединение проваливается или теряется, он всегда запускает службу для перезапуска режима прослушивания. Это не проблема, когда телефон используется как сервер, однако в некоторых сценариях, когда телефон используется как клиент, как только он переходит в режим прослушивания, вы не можете подключиться к другим серверам. Потому что в функции "connect" она не отменяет (In) SecureAcceptThread. Таким образом, вы слушаете другие соединения как сервер, пытаясь подключиться к другим серверам в качестве клиента. Это противоречиво.

Способом решения этой проблемы является то, что если вы уверены, что ваш телефон не будет использоваться в качестве сервера, который будет прослушивать входящее соединение, просто удалите этот код, чтобы перезапустить режим прослушивания после того, как соединение было неудачно или остановлено.

Ответ 2

Это не поможет вам, но обратите внимание, что Google представил совершенно новый стек Bluetooth с 4.2.

Это должно быть хорошо - по моему опыту как пользователя и разработчика Android с Bluez (старый комбо) никогда не работал надежно, поэтому я был очень рад услышать, что они взяли на себя полную переписку.

Я думаю, все, что я могу сказать, это то, что кажется, что вы столкнулись с ошибкой или причудой в новом стеке. К сожалению, есть проблемы с новым стеком.

Что касается вашей демонстрации, обратите внимание, что Google публикует изображения прошивки для всех своих устройств Nexus (https://developers.google.com/android/nexus/images) и довольно легко мигает ими на ваших устройствах.

Поэтому я предлагаю вам записать отчет об ошибке, а затем запустил устройства на 4.1.2.

Ответ 3

У меня была аналогичная проблема, и я потратил некоторое время на отладку этой проблемы. Я запускаю Android 4.3 и нашел единственную базовую модификацию, которая требовалась для кода образца BluetoothChat:

MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

Я пытаюсь подключиться к модулю bluetooth HC-05, и я считаю, что он использует профиль SPP, как указано в этом UUID. Вы можете запросить UUID устройства с помощью этой строки кода:

UUID uuid = device.getUuids()[0].getUuid();

Проверьте UUID и посмотрите, совпадает ли он с профилем, который имеет смысл, в зависимости от типа устройства, к которому вы подключаетесь.

MY_UUID_SECURE = uuid;

UUID может указывать другой тип профиля устройства, в зависимости от того, к чему вы подключаетесь. При правильном UUID я все еще мог запускать код AcceptThread и слушать как BluetoothServerSocket без каких-либо проблем. Надеюсь, это поможет, однако, я относительно новичок в разработке Android и Bluetooth, поэтому, если мои предположения ошибочны, пожалуйста, исправьте меня.

Ответ 4

Обнаружены аналогичные проблемы. Исправлены некоторые из них путем создания с 4.2 SDK с 17 в качестве цели сборки.

Ответ 5

Встречается с той же проблемой с моим Nexus 4.

В моем случае, я думаю, проблема заключается в протоколе обнаружения службы SDP. Устройство и сервер должны использовать один и тот же UUID для определенной службы.

Я использую SPP (протокол последовательного порта) в своем приложении. Поэтому я изменяю UUID с того, что находится в примере кода BluetoothChat как "fa87c0d0-afac-11de-8a39-0800200c9a66" на SPP "00001101-0000-1000-8000-00805F9B34FB". Теперь это нормально.

И я отключу код прослушивания для acceptThread в BluetoothChatService.start(). Теперь более кратки. Надеюсь, это поможет.

Ответ 6

Это случилось со мной и в моих тестах. Я пример кода BluetoothChat вы должны посмотреть на метод connectionLost. Я не помню, есть ли какая-либо переменная, которая поддерживает количество потерянных соединений, но вы можете добавить это сами. в методе connectionLost, проверьте, меньше ли количество потерянных соединений, чем предопределенное число (в моем случае 3). Если это правда, отправьте сообщение в пользовательский интерфейс с помощью mHandler (тост) и снова вызовите connect (device). Если это неверно (вы потеряли соединение более 3 раз), вызовите метод stop().

Также не забудьте открыть сокет в ConnectThread следующим образом:

    public ConnectThread(BluetoothDevice device, boolean isSecure) {
        mmDevice = device;
        BluetoothSocket tmp = null;
        mSocketType = isSecure ? "Secure" : "Insecure";
        // Get a BluetoothSocket for a connection with the given BluetoothDevice
        if (isSecure) {
            // reflection is better to use
            Method m = null;
            try {
                Log.d(TAG, "create reflection");
                m = device.getClass().getMethod("createRfcommSocket",new Class[] { int.class });
                } catch (NoSuchMethodException e1) {
                e1.printStackTrace();
            }
            try {
                tmp = (BluetoothSocket) m.invoke(device, 1);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            mmSocketFallBack = tmp;
        } else {
            Log.d(TAG, "create insecure");
            try {
                tmp = device
                        .createInsecureRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        mmSocket = mmSocketFallBack;
    }

Ваше соединениеLost должно выглядеть примерно так:

public void connectionLost() {
    init = false;
    Log.d(TAG, "connectionLost -> " + mConnectionLostCount);
    mConnectionLostCount++;
  if (mConnectionLostCount < 3) {
    // Send a reconnect message back to the Activity
        Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(WebAppInterface.TOAST, "Connection lost. Reconnecting...");
        msg.setData(bundle);
        mHandler.sendMessage(msg);
        connect(mSavedDevice,true);     
    } else {
    mConnectionLostCount = 0;
    Message msg = mHandler.obtainMessage(cBluetooth.MESSAGE_TOAST);
    Bundle bundle = new Bundle();
    bundle.putString(WebAppInterface.TOAST,"Device connection was lost!");
    msg.setData(bundle);
    mHandler.sendMessage(msg);
    cBluetooth.this.stop();
    }
}

Надеюсь, вы сможете приспособить его к своему делу. Вы также можете проверить эти ссылки, они мне очень помогли:

Ответ 7

У меня такой же опыт в 4.2.2.

Но я обнаружил, что стек bluetooth начинает плохо себя вести только после того, как мое приложение падает или убивается без должной очистки ресурсов (сокет и/или потоки). До этого, когда я закрываю приложение правильно, он отлично работает.

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

Итак, кажется, что новый стек Android bluetooth имеет некоторую ошибку в неявном механизме очистки. Когда приложение падает, Android должен очистить открытые ресурсы bluetooth, чего не происходит.

Мой код управления bluetooth находится в подклассе Application. Невозможно - или я не вижу одного - как подключить приложение к уничтожению и выполнить очистку.

Любое предложение оценено

EDIT:

Я сделал довольно грязное обходное решение.

Я создал отдельное приложение, содержащее один удаленный сервис. Я переместил весь этот код связи (открытие сокетов и потоков, чтение, запись, закрытие) в эту удаленную службу. Затем, если основное приложение выйдет из строя или будет убито, служба все еще работает. Когда я снова запустил приложение, соединение все еще продолжается. Байты, считываемые из входного потока bluetooth, передаются в основное приложение через стандартную службу обмена сообщениями. Для моего случая это нормально, поскольку я переношу очень маленькие объемы данных.

Тем не менее, когда приложение, содержащее службу, убивается, происходит тот же самый беспорядок.

Ответ 8

Я столкнулся с той же проблемой. Это работает для меня:

try {
    Thread.sleep(1000); 
}
catch(Exception e3)
{
    Toast.makeText(getApplicationContext(), "wa ni sud sa thread sleep!", Toast.LENGTH_SHORT).show();
}
btSocket.close();