Подтвердить что ты не робот

Должен ли я оставить bluetooth-отражение взлома в производственном коде?

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

Вот как я сначала открыл соединение:

        UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //SPP long UUID
        BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);

Использование UUID - единственный способ открыть соединения RFCOMM с использованием общедоступного API Android с JellyBean. Работая с SPP-соединениями в BlackBerry и JavaME, где UUID не требуется, я нашел это немного странным. UUID - это сервисное обнаружение, то есть использование SDP для запроса об услугах, присутствующих в устройстве. Мне действительно не нужно инициировать открытие, так как у меня есть мой принтер в паре заранее, и я знаю, что он поддерживает SPP. Однако это именно то, что делает метод BluetoothDevice.createRfcommSocketToServiceRecord и небезопасная версия. Это стек SPP, где мы можем видеть, как SDP является другим протоколом на том же уровне, и таким образом, возможно, следует использовать RFCOMM, не инициируя первое открытие:

        -----------------------------------
        |        My Application           |
        ----------------------------------- 
        |     Serial Port Emulation       |
        |          or other API           |
        -----------------------------------
        |      RFCOMM          |    SDP   |
        -----------------------------------
        |  LMP   |   L2PCAP               |
        -----------------------------------
        |           Baseband              |
        -----------------------------------

Я начал тестировать свое приложение на нескольких старых устройствах HTC без проблем. Позже, тестирование на телефонах Samsung, несколько устройств не смогли открыть соединение. Эти телефоны, как утверждается, не поддерживают профиль SPP, как по спецификации производителя, так и по сторонним спецификациям (EDIT: список сторонних спецификаций SPP поддерживается, а спецификации производителя недостаточно точны). IOException (Service Discovery failed) было сброшено, и я выполнил подход, показанный в этом вопросе:

Обнаружение сбоя службы обнаружения с использованием Bluetooth на Android

Предлагаемое решение заключается в использовании взлома отражения следующим образом:

        Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
        BluetoothSocket socket = socket = (BluetoothSocket) m.invoke(device, 1);

Сбой работал у меня. Удивительно, но этот метод в классе BluetoothDevice является общедоступным, но он удаляется из API с помощью аннотации @hide. Это исходный код JellyBean:

        /**
         * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
         * outgoing connection to this remote device on given channel.
         * <p>The remote device will be authenticated and communication on this
         * socket will be encrypted.
         * <p> Use this socket only if an authenticated socket link is possible.
         * Authentication refers to the authentication of the link key to
         * prevent man-in-the-middle type of attacks.
         * For example, for Bluetooth 2.1 devices, if any of the devices does not
         * have an input and output capability or just has the ability to
         * display a numeric key, a secure socket connection is not possible.
         * In such a case, use {#link createInsecureRfcommSocket}.
         * For more details, refer to the Security Model section 5.2 (vol 3) of
         * Bluetooth Core Specification version 2.1 + EDR.
         * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
         * connection.
         * <p>Valid RFCOMM channels are in range 1 to 30.
         * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
         *
         * @param channel RFCOMM channel to connect to
         * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
         * @throws IOException on error, for example Bluetooth not available, or
         *                     insufficient permissions
         * @hide
         */
        public BluetoothSocket createRfcommSocket(int channel) throws IOException {
            return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
                    null);
        }

Я не понимаю, почему публичный метод удаляется из API таким образом. Но, давая этому приложению, этот метод и официально поддерживаемый с использованием UUID - это тонкие конверты, вызывающие один и тот же конструктор BluetoothSocket с различными параметрами:

        public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
            return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
                    new ParcelUuid(uuid));
        }

Копаясь немного больше в источниках, я понял, что оба открывают соединение RFCOMM, но метод UUID инициирует открытие, а скрытое - нет.

В целом, рефлекторные хаки работают безупречно в каждом устройстве, которое я тестировал, от OS 2.2 до 4.1. EDIT: "неисправные" устройства поддерживают SPP, просто их пользовательская реализация BT-стека запутывается с процессом обнаружения; есть и другие ошибки, такие как отображение диалога сопряжения для уже сопряженных устройств в ICS. Вызов этого скрытого API с помощью отражения позволяет обходным путем ко всем этим ошибкам или различным поведением, представленным разными производителями.

Должен ли я держать хак в производственном коде? Есть ли способ достичь того же с публичным API?

Спасибо заранее.

4b9b3361

Ответ 1

Превосходный вопрос. В основном вы можете использовать отражение, которое вы хотите. Даже я сделал что-то подобное, чтобы рассчитать время запуска приложения, получил метод через отражение и его работу как шарм от FroYo до Jelly Bean. Единственное слово осторожности, которое вам нужно проявить, это,

  • Поскольку это не общедоступный API, Google может изменить его в любое время без предупреждения.
  • Если он изменится, системные приложения или HAL, которые его используют, будут соответственно изменены без каких-либо последствий для приложений.

Где вам нужно быть осторожным?

Скорее всего, аргументы этого метода могут быть изменены в будущем.

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