Android usb manager permission

Android usb manager permission

Handling USB connections in Android

Share this post

The conventional way to use an Android tablet or smartphone is as a USB peripheral that connects to a USB host (e.g. PC) and synchronizes its data and media files. However an Android device can also behave as a USB host since the API level 12 (Android 3.1). With this feature, you can for example connect and use a USB mouse, keyboard or memory stick with an Android phone. In order to physically attach the peripheral device, you need a special cable adapter called OTG (On The Go). The most common form factor includes a female Type-A USB connector at one end, where the peripheral device is connected to, and a male micro USB connector (at the other end) that plugs into the android device.

Android is quite flexible on what can be done with a connected USB device, but when writing an App we need to make sure that we configure it properly and that we abide by the security requirements implemented by the Android team.

In this post we will explain the different approaches for fulfilling the said security requirements, either hardcoded at the manifest file or defined dynamically inside the application code.

The first step is to ask for permission to access the USB port, which is done in our manifest file:

Now we have two options, to configure the USB connectivity statically in the manifest file or to do it dynamically in the code of our App.

USB configuration in the Manifest file (declarative)

This first mechanism is less flexible but it is a good choice if we know beforehand the type of devices that our App will connect to. The USB functionality is defined at the Activity level, and we need to register for USB intents:

In this example MainActivity registers to be informed when a USB device is connected ( USB_DEVICE_ATTACHED ) or disconneted ( USB_DEVICE_DETACHED ). Note that this is done at the Activity level. Other activities or services in our project can also register to these events.

If we want to restrict the type of devices that will trigger our Activity, we can include a device filter XML file indicated in the meta-data element. This file is stored in /res/xml folder and lists combinations of vendor id and/or product id as shown in our example file:

If we register an Activity like this, the event USB_DEVICE_ATTACHED will arrive as an intent, but the triggering method will depend on the state of the Activity:

  • If the Activity has not been created yet, it will be created and the intent arrive through the onCreate() method:
  • If the activity has already been created/instantiated, the event will arrive through the ‘onNewIntent()’ method:

To receive a USB_DEVICE_DETACHED event we need to register first a BroadcastReceiver, as explained in the following section.

USB configuration in the application code (imperative)

To achieve the equivalent functionality, now programmatically from our code, we need to create a BroadcastReceiver and then register it to receive the USB events.

In this case, the events will be received via the onReceive() method of BroadcastReceiver .

To stop receiving events, just unregister the receiver with:
unregisterReceiver(bReceiver);

The main advantage of this method is that we can control when we want to receive USB events. On the other hand, if our Activity or service is not started previously, plugging a USB device will not trigger it.

Security and user permission to connect to a USB device

In order to avoid security threats, Android mandates that the user must give explicit permission to an application willing to use a USB peripheral device. This user permission is granted at application level, that is, once obtained for an Activity, the rest of the Activities and services packaged in the application will also be granted the permission.

When a USB device is connected, Android looks for apps/Activities registered for the USB_DEVICE_ATTACHED event, either in the Manifest or pogrammatically. If XML filters have been defined, these are also applied.

Android then presents a dialog to the user with the list of all registered applications. The user is requested to select one of the Apps and decide if he wants to grant permission only once or always (i.e. default). Only the selected App will receive the USB_DEVICE_ATTACHED intent.

If the user selects default, Android will remember this option and from that moment on will trigger automatically the chosen application whenever that USB device (vendor id + product id) is connected in the future.

There is no way to avoid this user permission dialog behaviour. It must be executed at least once (if the user selects always/default).

Since our App does not know if the user has already granted permission, we need to check the user permission flag always, every time that we want to start a USB connection:

Читайте также:  Hp eprint with android

If we require user permission, we requestPermission() for the device with a PendingIntent. Android will pop up the user permission dialog and then will contact back our application with the PendingIntent. We need a BroadcastReceiver to receive this notification:

As happened in the previous user permission dialog, the user can grant access only once or forever («Use by default for this USB device”).

Once we have the user permission, we can start the communication with the peripheral USB device. For instance, in one of our projects we are using the USBSerial library created by Felipe Herranz to connect an Android device to Arduino.

Images

The images used in this post are solely owned by their respective rights holders:

We hope that you found this post useful. Do subscribe to our mailing list if you want to keep updated about new articles in our blog.

Источник

Работа с устройствами USB в Android

В недавней статье на Geektimes в комментариях возник вопрос о поддержке в ОС Android периферии, подключенной к шине USB. Действительно, большинство вендорского ПО, к примеру, для работы с принтерами и МФУ, поддерживает только подключение по сети. Однако это не означает, что в самой ОС Android нет такой возможности — это означает лишь то, что большинство устройств не имеют полноценного USB хоста, и далеко не все имеют поддержку OTG. По сети же могут работать абсолютно все без исключения.

Большинство устройств на Android при наличии порта OTG поддерживают на уровне системы (ядра Linux или стандартных компонентов Android) следующие классы устройств:

  • Устройства ввода — клавиатуры, мыши, джойстики (HID)
  • Накопители (Mass Storage)

Несколько реже:

  • Сотовые модемы
  • Сетевые адаптеры
  • Вебкамеры

Хабы поддерживаются при наличии полноценных хост-портов, но не поддерживаются на портах OTG.

Подробнее список устройств, поддерживаемых на уровне ядра Linux, можно получить в sysfs:

$ ls /sys/bus/usb/drivers

Если же модуль в принципе доступен в исходниках ядра Linux, но не включен в Android — не стоит рассчитывать на то, что его получится собрать и расставить на все целевые системы.

Однако, начиная с Android 3.1 (API 12), в системе содержатся средства, достаточные для поддержки на уровне приложения любой USB периферии. Данные средства описаны в разделе USB Host руководства по Android API. Здесь же я хочу привести примеры реальной работы с некоторыми видами устройств.

Права доступа

Как и для прочих действий, Android требует, чтобы приложение получило разрешение на доступ к USB периферии. Существует 2 способа получить такое разрешение:

  • Задекларировать список устройств в AndroidManifest
  • Явно показать пользователю диалог “разрешить”

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

Итак, нам необходимо добавить в манифест следующее:

А в res/xml/device_filter.xml вписать следующее:

Отмечу, что хотя общепринято указывать VID:PID в 16-ричной системе счисления, здесь они должны быть указаны в десятичной. В документации заявляется, что возможно указание только класса, без VID и PID, но у меня это не стало работать.

Принтеры

На примере принтера я покажу, как непосредственно использовать API android.hardware.usb. На уровне передачи данных все принтеры поддерживают стандартый класс USB устройств:

Класс предельно простой. В рамках этого класса устройство должно поддерживать:

  • Обязательный bulk out endpoind для отправки данных на принтер
  • Опциональный bulk in endpoind для получения статуса принтера
  • 3 управляющих запроса

Код, приведенный ниже, предоставляет функциональность, аналогичную устройству /dev/usb/lp в Linux. Далее нам нужен фильтр, преобразующий исходный документ в пакет данных, понятный конкретной модели принтера. Но это тема иной статьи. Как один из вариантов — можно собрать ghostscript с помощью NDK.

Для работы с устройством нам в первую очередь нужно:

1. Найти устройство. В примере для простоты я ищу первый попавшийся:

2. Получить endpoint’ы:

3. Непосредсвенно открыть устройство:

4. После этого мы можем читать и писать в устройство:

5. По завершении работы — закрыть устройство:

Преобразователи USB-Serial

В отличие от притеров, преобразователи USB-Serial гораздо менее стандартизированы. Существует несколько распространенных чипов, для которых существенно отличается установка параметров последовательного порта — битрейта, чётности и проч. К счастью, есть библиотека github.com/mik3y/usb-serial-for-android, поддерживающая практически все существующие чипы. Библиотека полностью скрывает USB API, сводя все необходимые действия к минимуму вызовов с минимумом параметров.

1. Найти и открыть устройство:

2. Установить параметры последовательного порта:

3. Читать и писать в порт:

4. По завершении работы — закрыть порт:

Резюме

Надеюсь, что мне удалось показать, что работа с USB периферией достаточно проста и логична. Безусловно, реализация протоколов некоторых конкретных устройств не блещет простотой — но это проявится в любой системе в одинаковой степени.

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

Источник

Android usb manager permission

Когда устройство на базе Android работает в режиме USB host, оно становится главным устройством на шине USB, подает питание на шину, и производит энумерацию для подключенных к шине устройств USB. Режим хоста USB поддерживается в Android 3.1 и более новых версиях. Непонятные термины касательно шины USB см. в USB in a NutShell [2] (на русском языке), а непонятные термины и сокращения, касающиеся Androd, в Словарике [5].

[Обзор API]

Чтобы разобраться в режиме USB host для Android, важно понимать, с какими классами устройств USB Вам предстоит работать. В следующей таблице описаны классы USB host API в пакете android.hardware.usb.

Читайте также:  Песочница успокаивает нервы для андроид

Таблица 1. USB Host API.

Класс Описание
UsbManager Позволяет Вам производить энумерацию подключенных устройств USB и организовать обмен с ними.
UsbDevice Представляет подключенное устройство USB, и содержит методы для доступа к идентификационной информации, интерфейсам и конечным точкам.
UsbInterface Представляет интерфейс устройства USB определяющий набор функционала устройства. Устройство может иметь один или большее количество интерфейсов, с которыми можно взаимодействовать.
UsbEndpoint Представляет конечную точку интерфейса (interface endpoint), которая дает канал связи для этого интерфейса. Интерфейс может иметь 1 или большее количество конечных точек, и обычно имеет отдельно конечную точку для ввода и отдельно конечную точку для вывода, чтобы можно было вести обмен данными в двух направлениях.
UsbDeviceConnection Представляет соединение с устройством, которое переносит данные между Android и конечными точками. Этот класс позволяет Вам отправить блок данных туда и обратно, синхронно или асинхронно.
UsbRequest Представляет асинхронный запрос для обмена с устройством через UsbDeviceConnection .
UsbConstants Определяет константы USB, соответствующие определениям в linux/usb/ch9.h ядра Linux.

В большинстве ситуаций Вам нужно использовать все эти классы (UsbRequest требуется только если Вы осуществляете асинхронную связь), когда происходит взаимодействие с устройством USB device. Обычно Вы задействуете UsbManager для получения доступа к нужному UsbDevice. Когда Вы получили устройство USB, то для обмена данными нужно найти подходящий интерфейс UsbInterface и конечную точку UsbEndpoint на этом интерфейсе. Как только Вы получили правильную конечную точку, откройте соединение UsbDeviceConnection для обмена данными с устройством USB.

[Требования к Android Manifest]

Следующий список описывает, что Вам нужно добавить в манифест приложения перед тем, как начать работать с USB host API:

• Поскольку не все устройства на базе Android гарантированно поддерживают USB host API, включите элемент , который декларирует, что Ваше приложение использует фичу android.hardware.usb.host .
• Установите минимальный уровень SDK приложение на API Level 12 или более высокий, потому что USB host API не представлены в более старых уровнях API.
• Если Вы хотите, чтобы Ваше приложение получало оповещение о подключенных к шине USB устройствах, укажите пару элементов и для intent android.hardware.usb.action.USB_DEVICE_ATTACHED в главной активности приложения. Элемент указывает на внешний XML-файл ресурса, в котором указана идентификационная информация об устройстве, подключение которого нужно детектировать.

В XML-файле ресурсов продекларируйте элементы для устройств USB, которые хотите отфильтровать. Следующий список описывает атрибуты элемента . Обычно указываются идентификаторы вендора (vendor ID, VID) и продукта (product ID, PID), если хотите фильтровать специфическое устройство, и используется class, subclass и protocol, если хотите фильтровать группы (классы) устройств USB, такие как устройства хранения (mass storage devices, MSD) или цифровые камеры. Вы можете не указывать ни одного из этих атрибутов, в этом случае фильтрация подойдет под любое устройство USB, не только то, что Вам нужно. Вот имена атрибутов для устройств USB:

vendor-id
product-id
class
subclass
protocol (device или interface)

Сохраните XML-файл ресурсов в директории res/xml/ . Имя файла ресурса (без расширения .xml) должно быть то же самое, как указано в элементе . Формат файла ресурса XML показан в примере ниже (пример манифеста и соответствующего ему файла ресурсов):

В этом случае следующий файл ресурсов будет сохранен в файл res/xml/device_filter.xml (здесь указано, что должно фильтроваться любое устройство USB с указанными параметрами):

Примечание: указанные здесь в текстовом виде значения VID (vendor-id) и PID (product-id) должны быть не в шестнадцатеричном виде, а в десятичном. Таким образом, если например у Вас VID=0x16C0, PID=0x05DF, то вы должны преобразовать эти шестнадцатеричные значения в десятичные, и их в текстовом виде указать в XML:

VID 0x16C0 -> 5824
PID 0x05DF -> 1503

Если необходимо, чтобы под фильтр попадали все без исключения подключаемые устройства USB, то просто удалите из элемента usb-device его атрибуты:

[Как работать с устройствами USB, подключенными к Android]

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

1. Определение подключенных устройств USB, используя intent фильтр для оповещения о ситуациях, когда пользователь подключает устройство USB, или путем энумерации устройств USB, которые уже подключены.
2. Запросить у пользователя разрешение на подключение к устройству USB, если оно еще не получено.
3. Далее можно осуществлять взаимодействие с устройством USB путем чтения и записи данных через соответствующие конечные точки интерфейса (interface endpoints).

[Определение наличия подключенного устройства USB]

Ваше приложение может распознать устройства USB либо с помощью использования intent-фильтра, который будет оповещать подключение пользователем устройств, либо путем энумерации устройств USB, которые уже подключены. Использование фильтра intent полезно, если Вы хотите иметь в приложении возможность автоматического детектирования (подключения) нужного устройства. Энумерация подключенных устройств USB полезна, если Вы хотите получить список всех подключенных устройств, или если Ваше приложение не делает фильтр для intent.

Использование intent filter

Чтобы приложение могло обнаружить какое-то отдельное устройство USB, Вы можете указать intent filter, чтобы фильтровать android.hardware.usb.action.USB_DEVICE_ATTACHED intent. Вместе с этим intent filter Вам нужно указать файл ресурса, который задает свойства устройства USB, такие как VID и/или PID. Когда пользователь подключает устройство, которое подходит под условие фильтра устройства, система представляет диалог, запрашивающий у пользователя, хочет ли он запустить Вашу программу. Если пользователь подтвердит запуск, то Ваше приложение автоматически будет иметь право на доступ (permission access) к устройству, пока устройство не будет отключено.

В следующем примере показано, как декларировать intent filter:

Читайте также:  Расширение для андроид для заработка денег

Следующий пример показывает, как декларировать соответствующий файл ресурса, который указывает интересующие Вас устройства USB:

В activity приложения Вы можете получить объект UsbDevice, который представляет подключенное устройство USB из intent примерно так:

Энумерация устройств

Если Ваше приложение нуждается в инспекции всех устройств USB, уже подключенных во время работы приложения, то оно может произвести энумерацию устройств на шине USB. Используйте метод getDeviceList() для получения карты всех устройств USB (hash map), которые сейчас подключены. В списке hash map есть имена устройств USB, которые Вы можете использовать для получения доступа к устройству.

Если необходимо, Вы также можете просто получить итератор из hash map и обработать каждое устройство друг за другом:

[Получение права на взаимодействие с устройством USB]

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

Примечание: если Ваше приложение использует intent filter, чтобы распознать подключенные устройства USB, то оно автоматически принимает разрешение, если пользователь разрешает разрешает Вашему приложению обработать intent. Если нет, то Вы должны в приложении явным образом запросить разрешение перед подключением к устройству.

Явный запрос на разрешение может быть необходим в некоторых ситуациях, таких как если Ваше приложение делает энумерацию уже подключенных устройств USB, и затем хочет обмениваться данными с ними. Вы должны проверить право на доступ к устройству, перед тем как сделать попытку взаимодействовать с ним. Если этого не сделать, то Вы получите runtime error (ошибка во время выполнения), когда пользователь не дал разрешения на доступ к устройству.

Чтобы явно получить разрешение, создайте сначала broadcast receiver (широковещательный приемник). Этот receiver прослушивает intent, которое получает broadcast, когда Вы вызываете requestPermission(). Вызов requestPermission() отображает диалог для пользователя с запросом разрешения на подключение к устройству USB. В следующем примере кода показано, как создать broadcast receiver:

Для того, чтобы зарегистрировать broadcast receiver, добавьте в метод onCreate() Вашей activity следующий код:

Чтобы отобразить диалог, запрашивающий у пользователей разрешение на подключение к устройству, вызовите метод requestPermission():

Когда пользователь ответит на этот диалог, Ваш broadcast receiver получит intent, который содержит расширение EXTRA_PERMISSION_GRANTED, представляющее суть ответа в двоичном виде. Проверьте его на значение true перед тем, как подключиться к устройству.

[Обмен с устройством USB]

Обмен с устройством USB может быть либо синхронным, либо асинхронным. В любом случае Вы должны создать новый поток, который будет обрабатывать все перемещения данных, чтобы не блокировать на ожидании поток интерфейса (UI thread). Чтобы правильно установить обмен с устройством, Вам нужно получить соответствующие объекты UsbInterface и UsbEndpoint устройства, с которым Вы хотите наладить обмен, и отправлять запросы в эти конечные точки с помощью UsbDeviceConnection . В общем случае Ваш код должен делать следующее:

• Нужно проверить атрибуты объекта UsbDevice , такие как product ID (идентификатор продукта, PID), vendor ID (идентификатор производителя, VID), или класс устройства — чтобы определить, хотите ли Вы соединиться именно с этим устройством USB.

• Когда принято положительное решение на обмен с этим устройством USB, найдите подходящий UsbInterface (), который Вы хотите использовать для обмена с подходящей UsbEndpoint интерфейса (конечные точки и их привязка к интерфейсу также задана в дескрипторах USB).

Прим. переводчика: интерфейс — это некая логическая сущность, описывающая метод взаимодействия с устройством USB. И интерфейсу привязаны конечные точки — другая логическая сущность, олицетворяющая какой-то буфер данных в устройстве USB. Наличие конкретного интерфейса (он обычно в устройстве USB один, но интерфейсом может быть и несколько), его тип, привязка к интерфейсу конечных точек, тип конечных точек — все это зависит от дескрипторов устройства USB (подробнее см. [2]). Дескрипторы устройств USB автоматически считываются системой Android при энумерации и подключении устройств USB.

Интерфейсу может принадлежать одна или большее количество конечных точек (endpoint). Обычно для осуществления двунаправленного обмена имеются 2 конечные точки — одна конечная точка работает на ввод от устройства (IN endpoint), а другая на вывод в устройство (OUT endpoint).

• Когда Вы нашли корректную конечную точку, откройте UsbDeviceConnection к этой конечной точке.

• Предоставьте данные, которые Вы хотите передать в конечную точку методами bulkTransfer() или controlTransfer() . Вы должны провести эту операцию передачи данных в другом потоке, чтобы не блокировать главный поток интерфейса пользователя (main UI thread). Для дополнительной информации по использованию потоков в Android см. [3].

Следующий кусок кода показывает тривиальный способ осуществить синхронную передачу данных. Ваш реальный рабочий код должен содержать больше логики, чтобы корректно найти нужный интерфейс и конечные точки для обмена, и также должен передавать данные в отдельном потоке (чтобы не блокировать main UI thread, и чтобы приложение визуально не тормозило [4]):

Чтобы отправить данные асинхронно (без ожидания), используйте класс UsbRequest для инициализации очереди на асинхронном запросе, и затем ждите результата с помощью вызова requestWait() .

Для дополнительной информации см. пример AdbTest, который показывает как делать асинхронные bulk передачи, и пример MissleLauncher, который показывает как асинхронно слушать interrupt endpoint.

[Прекращение обмена с устройством USB]

Когда Вы завершили обмен с устройством USB, или устройство USB было отключено, закройте UsbInterface и UsbDeviceConnection вызовом releaseInterface() и close() . Чтобы прослушивать события отключения, создайте broadcast receiver примерно так:

Создание broadcast receiver в приложении, не в манифесте, позволит Вашему приложению во время выполнения обрабатывать только события отключения. С таким методом события отключения будут отправлены только приложению, которое сейчас работает, и не всем приложениям.

Источник

Оцените статью