Présentation des hôtes USB

Lorsque votre appareil Android est en mode hôte USB, il agit en tant qu'hôte USB, alimente le bus, et énumère les périphériques USB connectés. Le mode hôte USB est compatible avec Android 3.1 ou version ultérieure.

Présentation de l'API

Avant de commencer, il est important de comprendre les cours avec lesquels vous devez travailler. La Le tableau suivant décrit les API hôtes USB du package android.hardware.usb.

Tableau 1. API hôtes USB

Classe Description
UsbManager Permet d'énumérer les appareils USB connectés et de communiquer avec eux.
UsbDevice Représente un périphérique USB connecté et contient des méthodes pour accéder à son identifiant d'informations, d'interfaces et de points de terminaison.
UsbInterface Représente l'interface d'un périphérique USB, qui définit un ensemble de fonctionnalités pour appareil. Un appareil peut avoir une ou plusieurs interfaces sur lesquelles communiquer.
UsbEndpoint Représente un point de terminaison d'interface, qui est un canal de communication pour cette interface. Une interface peut avoir un ou plusieurs points de terminaison, et a généralement des points de terminaison d'entrée et de sortie pour communication bidirectionnelle avec l’appareil.
UsbDeviceConnection Représente une connexion à l'appareil, qui transfère des données sur les points de terminaison. Ce cours vous permet d'échanger des données de manière synchrone ou asynchrone.
UsbRequest Représente une requête asynchrone pour communiquer avec un appareil via un UsbDeviceConnection.
UsbConstants Définit les constantes USB correspondant aux définitions dans linux/usb/ch9.h du système Linux noyau.

Dans la plupart des cas, vous devez utiliser toutes ces classes (UsbRequest n'est requis que si vous effectuez une communication asynchrone). lors de la communication avec un périphérique USB. En général, vous obtenez un UsbManager pour récupérer le UsbDevice souhaité. Une fois que vous avez l'appareil, vous devez trouver le UsbInterface et le UsbEndpoint appropriés. d’interface sur laquelle communiquer. Une fois que vous avez obtenu le bon point de terminaison, ouvrez un UsbDeviceConnection pour communiquer avec le périphérique USB.

Exigences concernant les fichiers manifestes Android

La liste suivante décrit ce que vous devez ajouter au fichier manifeste de votre application avant travailler avec les API hôtes USB:

  • Étant donné que les appareils Android n'acceptent pas tous les API hôtes USB, incluent un élément <uses-feature> qui déclare que votre application utilise la fonctionnalité android.hardware.usb.host.
  • Définissez le SDK minimal de l'application sur le niveau d'API 12 ou supérieur. Les API hôtes USB ne sont pas présentes aux niveaux d'API précédents.
  • Si vous souhaitez que votre application soit informée lorsqu'un périphérique USB est connecté, spécifiez un Paire d'éléments <intent-filter> et <meta-data> pour Intent android.hardware.usb.action.USB_DEVICE_ATTACHED dans votre activité principale. La L'élément <meta-data> pointe vers un fichier de ressources XML externe qui déclare des informations d'identification sur l'appareil que vous souhaitez détecter.

    Dans le fichier de ressources XML, déclarez les éléments <usb-device> pour l'USB appareils que vous souhaitez filtrer. La liste suivante décrit les attributs de <usb-device> En général, utilisez le fournisseur et l'ID produit si vous souhaitez filtrer pour un appareil spécifique, et utilisez la classe, la sous-classe et le protocole si vous souhaitez filtrer les données en fonction d'un groupe. de périphériques USB, tels que les périphériques de stockage de masse ou les appareils photo numériques. Vous pouvez spécifier "aucun" ou tous ces attributs. Si aucun attribut ne correspond à chaque périphérique USB, si votre application l'exige:

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol (appareil ou interface)

    Enregistrez le fichier de ressources dans le répertoire res/xml/. Nom du fichier de ressources (sans l'extension .xml) doit être identique à celui spécifié dans le fichier Élément <meta-data>. Le fichier de ressources XML est au format exemple ci-dessous.

Exemples de fichiers manifestes et de ressources

Voici un exemple de fichier manifeste et le fichier de ressources correspondant:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk android:minSdkVersion="12" />
    ...
    <application>
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>

            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
        </activity>
    </application>
</manifest>

Dans ce cas, le fichier de ressources suivant doit être enregistré dans res/xml/device_filter.xml, et indique que tout périphérique USB doté du paramètre attributs doivent être filtrés:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>

Travailler avec des appareils

Lorsque les utilisateurs connectent un périphérique USB à un appareil Android, le système Android peut déterminer si votre application est intéressée par l'appareil connecté. Si c'est le cas, vous pouvez configurer la communication avec l’appareil si vous le souhaitez. Pour ce faire, votre application doit:

  1. Découvrez les appareils USB connectés à l'aide d'un filtre d'intent pour être averti lorsque l'utilisateur connecte un périphérique USB ou en énumérant les périphériques USB qui sont déjà connectés.
  2. Si ce n'est pas déjà fait, demandez à l'utilisateur l'autorisation de se connecter au périphérique USB.
  3. Communiquer avec le périphérique USB en lisant et en écrivant des données sur l'interface appropriée les points de terminaison.

Découvrir un appareil

Votre application peut détecter les périphériques USB en utilisant un filtre d'intent pour être averti lorsque l'utilisateur connecte un périphérique ou en énumérant les périphériques USB qui sont déjà connectés. À l'aide d'un Le filtre d'intent est utile si vous voulez que votre application détecte automatiquement l'appareil souhaité. L'énumération des périphériques USB connectés est utile si vous souhaitez obtenir une liste de tous les appareils connectés ou si votre application n'a filtré aucun intent.

Utiliser un filtre d'intent

Pour que votre application détecte un périphérique USB particulier, vous pouvez spécifier un filtre d'intent pour filtre pour l'intent android.hardware.usb.action.USB_DEVICE_ATTACHED. ainsi que ce filtre d'intent, vous devez spécifier un fichier de ressources spécifiant les propriétés de la clé USB (par exemple, l'ID du produit et l'ID du fournisseur). Lorsque les utilisateurs connectent un appareil qui correspond à votre appareil le filtre affiche une boîte de dialogue qui leur demande s'ils souhaitent lancer votre application. Si les utilisateurs acceptent, votre application est automatiquement autorisée à accéder à l'appareil jusqu'à ce que le l'appareil est déconnecté.

L'exemple suivant montre comment déclarer le filtre d'intent:

<activity ...>
...
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
    </intent-filter>

    <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
        android:resource="@xml/device_filter" />
</activity>

L'exemple suivant montre comment déclarer le fichier de ressources correspondant qui spécifie le Appareils USB qui vous intéressent:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-device vendor-id="1234" product-id="5678" />
</resources>

Dans votre activité, vous pouvez obtenir le UsbDevice qui représente l'appareil connecté à partir de l'intent, comme ceci:

Kotlin

val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)

Java

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

Énumérer les appareils

Si votre application souhaite inspecter tous les périphériques USB actuellement connectés pendant son exécution, l'application peut énumérer les appareils présents dans le bus. Utilisez la méthode getDeviceList() pour obtenir une carte de hachage de tous les les périphériques USB qui sont connectés. La table de hachage est associée au nom du périphérique USB si vous souhaitez obtenir un appareil sur la carte.

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
...
val deviceList = manager.getDeviceList()
val device = deviceList.get("deviceName")

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");

Si vous le souhaitez, vous pouvez également obtenir un itérateur à partir de la table de hachage et traiter chaque appareil par un:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
..
val deviceList: HashMap<String, UsbDevice> = manager.deviceList
deviceList.values.forEach { device ->
    // your code
}

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
    UsbDevice device = deviceIterator.next();
    // your code
}

Obtenir l'autorisation de communiquer avec un appareil

Pour pouvoir communiquer avec le périphérique USB, votre application doit avoir l'autorisation de votre utilisateurs.

Remarque:Si votre application utilise un d'intent pour détecter les périphériques USB lorsqu'ils sont connectés, il reçoit automatiquement si l'utilisateur autorise votre application à gérer l'intent. Sinon, vous devez demander explicitement dans votre application avant de vous connecter à l'appareil.

Il peut être nécessaire de demander explicitement l'autorisation dans certaines situations, par exemple lorsque votre énumère les périphériques USB qui sont déjà connectés et qui souhaitent communiquer avec 1. Vous devez vérifier si vous êtes autorisé à accéder à un appareil avant d'essayer de communiquer avec lui. Si non, vous recevrez une erreur d'exécution si l'utilisateur a refusé l'autorisation d'accéder à l'appareil.

Pour obtenir explicitement l'autorisation, commencez par créer un broadcast receiver. Ce récepteur écoute L'intent qui est diffusé lorsque vous appelez requestPermission(). L'appel de requestPermission() affiche une boîte de dialogue pour utilisateur demandant l'autorisation de se connecter à l'appareil. L'exemple de code suivant montre comment créez le broadcast receiver:

Kotlin

private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"

private val usbReceiver = object : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (ACTION_USB_PERMISSION == intent.action) {
            synchronized(this) {
                val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    device?.apply {
                        // call method to set up device communication
                    }
                } else {
                    Log.d(TAG, "permission denied for device $device")
                }
            }
        }
    }
}

Java

private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {

    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(device != null){
                      // call method to set up device communication
                   }
                }
                else {
                    Log.d(TAG, "permission denied for device " + device);
                }
            }
        }
    }
};

Pour enregistrer le broadcast receiver, ajoutez ceci à la méthode onCreate() de votre activité:

Kotlin

private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
...
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
...
permissionIntent = PendingIntent.getBroadcast(this, 0,
                  Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)

Java

UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
...
permissionIntent = PendingIntent.getBroadcast(this, 0,
              new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(usbReceiver, filter);

Pour afficher la boîte de dialogue qui demande aux utilisateurs l'autorisation de se connecter à l'appareil, appelez la méthode requestPermission():

Kotlin

lateinit var device: UsbDevice
...
usbManager.requestPermission(device, permissionIntent)

Java

UsbDevice device;
...
usbManager.requestPermission(device, permissionIntent);

Lorsque les utilisateurs répondent à la boîte de dialogue, votre broadcast receiver reçoit l'intent qui contient le EXTRA_PERMISSION_GRANTED supplémentaire, qui est une valeur booléenne représentant la réponse. Vérifiez dans cet extra la valeur "true" avant de vous connecter appareil.

Communiquer avec un appareil

La communication avec un périphérique USB peut être synchrone ou asynchrone. Dans les deux cas, vous doit créer un nouveau thread sur lequel effectuer toutes les transmissions de données, afin de ne pas bloquer le thread UI. Pour configurer correctement la communication avec un appareil, vous devez obtenir les UsbInterface et UsbEndpoint appareil avec lequel vous souhaitez communiquer et envoyer des requêtes sur ce point de terminaison avec un UsbDeviceConnection. En règle générale, votre code doit:

  • Vérifiez les attributs d'un objet UsbDevice, tels que l'ID produit, l'ID du fournisseur ou la classe d'appareil pour déterminer si vous souhaitez ou non communiquer appareil.
  • Lorsque vous êtes certain de vouloir communiquer avec l'appareil, recherchez les UsbInterface que vous souhaitez utiliser pour communiquer avec le les UsbEndpoint appropriées de cette interface. Les interfaces peuvent avoir un ou plus, et possède généralement un point de terminaison d'entrée et de sortie pour une liaison bidirectionnelle de la communication.
  • Une fois que vous avez trouvé le point de terminaison approprié, ouvrez un UsbDeviceConnection sur ce point de terminaison.
  • Fournissez les données que vous souhaitez transmettre sur le point de terminaison à l'aide de la méthode bulkTransfer() ou controlTransfer(). Vous devez effectuez cette étape dans un autre thread pour éviter de bloquer le thread UI principal. Pour plus sur l'utilisation des threads dans Android, consultez la section Processus et Threads :

L'extrait de code suivant constitue un moyen simple d'effectuer un transfert de données synchrone. Votre code devrait avoir plus de logique pour trouver correctement l'interface et les points de terminaison appropriés pour communiquer Vous devez également transférer les données dans un autre thread que le thread UI principal:

Kotlin

private lateinit var bytes: ByteArray
private val TIMEOUT = 0
private val forceClaim = true

...

device?.getInterface(0)?.also { intf ->
    intf.getEndpoint(0)?.also { endpoint ->
        usbManager.openDevice(device)?.apply {
            claimInterface(intf, forceClaim)
            bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) //do in another thread
        }
    }
}

Java

private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;

...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = usbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread

Pour envoyer des données de manière asynchrone, utilisez la classe UsbRequest pour initialize et queue une requête asynchrone, puis attendez le résultat. avec requestWait().

Interruption de la communication avec un appareil

Lorsque vous avez terminé de communiquer avec un appareil ou si celui-ci a été déconnecté, fermez UsbInterface et UsbDeviceConnection en appeler releaseInterface() et close() Pour écouter les événements détachés, créez un broadcast receiver comme ci-dessous:

Kotlin

var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {
            val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
            device?.apply {
                // call your method that cleans up and closes communication with the device
            }
        }
    }
}

Java

BroadcastReceiver usbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

      if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            if (device != null) {
                // call your method that cleans up and closes communication with the device
            }
        }
    }
};

La création du broadcast receiver dans l'application, et non du fichier manifeste, permet à une application pour ne gérer que les événements détachés pendant son exécution. De cette façon, les événements détachés envoyé uniquement à l'application en cours d'exécution et non diffusé à toutes les applications.