Présentation des accessoires USB

Le mode accessoire USB permet aux utilisateurs de se connecter Matériel hôte USB spécialement conçu pour les appareils Android Les accessoires doivent adhérer au protocole d'accessoire Android décrit dans la documentation sur le kit de développement d'accessoires Android. Cela permet aux appareils Android qui ne peuvent pas servir d'hôte USB de continuer à interagir avec l'USB matériel. Lorsqu'un appareil Android est en mode accessoire USB, la clé USB Android connectée l'accessoire agit en tant qu'hôte, alimente le bus USB et énumère les appareils connectés. Android 3.1 (niveau d'API 12) prend en charge le mode accessoire USB et est également rétroporté vers Android 2.3.4 (niveau d'API 10) pour une compatibilité avec un plus grand nombre d'appareils.

Choisir les API d'accessoires USB appropriées

Bien que les API d'accessoires USB aient été introduites sur la plate-forme sous Android 3.1, elles le sont également disponible sur Android 2.3.4 via la bibliothèque de modules complémentaires des API Google. Ces API étant rétroporté à l'aide d'une bibliothèque externe, vous pouvez importer deux packages pour prendre en charge mode accessoire. Selon les appareils Android que vous souhaitez prendre en charge, vous devrez peut-être utilisez l'un plutôt que l'autre:

  • com.android.future.usb: pour prendre en charge le mode accessoire USB sous Android 2.3.4, le paramètre Module complémentaire des API Google qui inclut les API d'accessoires USB rétroportées, qui sont contenues dans ce espace de noms. Android 3.1 permet également d'importer et d'appeler les classes de cet espace de noms pour prendre en charge les applications écrites avec la bibliothèque de modules complémentaires. Cette bibliothèque de modules complémentaires est un wrapper léger autour des API d'accessoires android.hardware.usb et n'est pas compatible avec le mode hôte USB. Si compatible avec le plus grand nombre d'appareils compatibles avec le mode accessoire USB, utilisez le module complémentaire bibliothèque et importer ce package. Il est important de noter que les appareils équipés d'Android 2.3.4 ne sont pas tous requis pour prendre en charge la fonctionnalité d'accessoire USB. Chaque fabricant d'appareil décide accepter ou non cette fonctionnalité, c'est pourquoi vous devez la déclarer dans votre fichier manifeste ; .
  • android.hardware.usb: cet espace de noms contient les classes compatibles USB le mode accessoire sur Android 3.1. Ce package est inclus dans les API du framework : Android 3.1 prend en charge le mode accessoire USB sans bibliothèque de modules complémentaires. Utiliser ce package Si vous ne vous intéressez qu'aux appareils équipés d'Android 3.1 (ou version ultérieure) compatibles USB que vous pouvez déclarer dans votre fichier manifeste.

Installer la bibliothèque de modules complémentaires des API Google

Si vous souhaitez installer le module complémentaire, vous pouvez le faire en installant les API Google pour Android API 10 avec SDK Manager. Reportez-vous à la section Installation des API Google Module complémentaire pour en savoir plus sur l'installation de la bibliothèque de modules complémentaires.

Présentation de l'API

La bibliothèque de modules complémentaires étant un wrapper pour les API du framework, les classes compatibles avec le Les fonctionnalités de l'accessoire USB sont similaires. Vous pouvez utiliser la documentation de référence sur android.hardware.usb même si vous utilisez la bibliothèque de modules complémentaires.

Remarque:Cependant, une utilisation mineure entre la bibliothèque de modules complémentaires et les API de framework.

Le tableau suivant décrit les classes compatibles avec les API d'accessoires USB:

Classe Description
UsbManager Permet d'énumérer les accessoires USB connectés et de communiquer avec eux.
UsbAccessory Représente un accessoire USB et contient des méthodes pour accéder à son identifiant des informations.

Différences d'utilisation entre la bibliothèque de modules complémentaires et les API de la plate-forme

Il existe deux différences d'utilisation entre la bibliothèque de modules complémentaires des API Google et la plate-forme. API.

Si vous utilisez la bibliothèque de modules complémentaires, vous devez obtenir l'objet UsbManager de la manière suivante:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Si vous n'utilisez pas la bibliothèque du module complémentaire, vous devez obtenir l'objet UsbManager de la manière suivante:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

Lorsque vous filtrez un accessoire connecté avec un filtre d'intent, l'objet UsbAccessory est contenu dans l'intent transmis à votre application. Si vous utilisez la bibliothèque de modules complémentaires, vous devez obtenir l'objet UsbAccessory de la manière suivante:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Si vous n'utilisez pas la bibliothèque du module complémentaire, vous devez obtenir l'objet UsbAccessory de la manière suivante:

Kotlin

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

Java

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

Exigences concernant les fichiers manifestes Android

La liste suivante décrit ce que vous devez ajouter au fichier manifeste de votre application avant avec les API d'accessoires USB. Le fichier manifeste et le fichier de ressources exemples montrent comment déclarer ces articles:

  • La compatibilité avec les API d'accessoires USB n'est pas garantie pour tous les appareils Android. incluent un élément <uses-feature> qui déclare que votre application utilise la fonctionnalité android.hardware.usb.accessory.
  • Si vous utilisez bibliothèque de modules complémentaires ajoutez l'élément <uses-library> en spécifiant com.android.future.usb.accessory pour la bibliothèque.
  • Définissez le SDK minimal de l'application sur le niveau d'API 10 si vous utilisez la bibliothèque de modules complémentaires. ou 12 si vous utilisez le package android.hardware.usb.
  • Si vous souhaitez que votre application soit informée lorsqu'un accessoire USB est connecté, spécifiez un Paire d'éléments <intent-filter> et <meta-data> pour Intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED dans votre activité principale. L'élément <meta-data> pointe vers un fichier de ressources XML externe qui déclare les informations permettant d'identifier l'accessoire que vous souhaitez détecter.

    Dans le fichier de ressources XML, déclarez les éléments <usb-accessory> pour accessoires que vous souhaitez filtrer. Chaque <usb-accessory> peut avoir les attributs suivants:

    • manufacturer
    • model
    • version

    Le filtrage sur version n'est pas recommandé. Un accessoire ou que l'appareil ne spécifie pas toujours une chaîne de version (intentionnellement ou non). Lorsque votre application déclare un attribut de version à utiliser comme filtre et que l'accessoire ou l'appareil ne spécifie pas de chaîne de version, une erreur NullPointerException est alors renvoyée sur versions antérieures d'Android. Ce problème est résolu dans Android 12.

    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 format du fichier de ressources XML est également indiqué dans l'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.accessory" />
    
    <uses-sdk android:minSdkVersion="<version>" />
    ...
    <application>
      <uses-library android:name="com.android.future.usb.accessory" />
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
            </intent-filter>

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

Dans ce cas, le fichier de ressources suivant doit être enregistré dans res/xml/accessory_filter.xml, et indique que tout accessoire doté de la le modèle, le fabricant et la version correspondants doivent être filtrés. L'accessoire envoie ces attribue l'appareil Android:

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

<resources>
    <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>

Utiliser des accessoires

Lorsque les utilisateurs connectent des accessoires USB à un appareil Android, le système Android peut déterminer si votre application est intéressée par l'accessoire connecté. Si c'est le cas, vous pouvez définir communiquer avec l'accessoire si vous le souhaitez. Pour ce faire, votre application doit:

  1. Découvrez les accessoires connectés à l'aide d'un filtre d'intent qui filtre les accessoires ou en énumérant les accessoires connectés et en trouvant celui qui convient.
  2. Demander à l'utilisateur l'autorisation de communiquer avec l'accessoire, si ce n'est pas déjà fait obtenues.
  3. Communiquer avec l'accessoire en lisant et en écrivant des données sur l'interface appropriée les points de terminaison.

Découvrir un accessoire

Votre application peut détecter des accessoires en utilisant un filtre d'intent pour être averti lorsque l’utilisateur connecte un accessoire ou en énumérant les accessoires 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'accessoire souhaité. L'énumération des accessoires connectés est utile si vous souhaitez obtenir la liste accessoires connectés ou si votre application n'a filtré aucun intent.

Utiliser un filtre d'intent

Pour que votre application détecte un accessoire USB spécifique, vous pouvez spécifier un filtre d'intent pour filtrer l'intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED. Au-dessus Avec ce filtre d'intent, vous devez spécifier un fichier de ressources spécifiant les propriétés de la clé USB accessoire, tels que le fabricant, le modèle et la version.

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

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

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

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

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

<resources>
    <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>

Dans votre activité, vous pouvez obtenir le UsbAccessory qui représente l'accessoire associé de l'intent comme ceci (avec la bibliothèque de modules complémentaires):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

ou comme ceci (avec les API de la plate-forme):

Kotlin

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

Java

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

Énumérer les accessoires

Vous pouvez demander à votre application d'énumérer les accessoires qui se sont identifiés l'application est en cours d'exécution.

Utiliser la méthode getAccessoryList() pour obtenir un tableau de tous les accessoires USB connectés:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val accessoryList: Array<out UsbAccessory> = manager.accessoryList

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAccessoryList();

Remarque : Un seul accessoire connecté est pris en charge dans à la fois.

Obtenir l'autorisation de communiquer avec un accessoire

Avant de pouvoir communiquer avec l'accessoire USB, votre application doit avoir l'autorisation de votre utilisateurs.

Remarque:Si votre application utilise un d'intention pour détecter les accessoires 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'accessoire.

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

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'accessoire. 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 accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)

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

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) {
                UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

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

Pour enregistrer le broadcast receiver, placez-le dans 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), 0)
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), 0);
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'accessoire, appelez la méthode Méthode requestPermission():

Kotlin

lateinit var accessory: UsbAccessory
...
usbManager.requestPermission(accessory, permissionIntent)

Java

UsbAccessory accessory;
...
usbManager.requestPermission(accessory, 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 accessoire.

Communiquer avec un accessoire

Vous pouvez communiquer avec l'accessoire en utilisant le UsbManager pour obtenir un descripteur de fichier dans lequel vous pouvez configurer des flux d'entrée et de sortie pour lire et écrire des données du descripteur. Les flux représentent les points de terminaison groupés d'entrée et de sortie de l'accessoire. Vous devez définir la communication entre l'appareil et l'accessoire dans un autre thread, afin de ne pas verrouiller thread UI principal. L'exemple suivant montre comment ouvrir un accessoire pour communiquer avec:

Kotlin

private lateinit var accessory: UsbAccessory
private var fileDescriptor: ParcelFileDescriptor? = null
private var inputStream: FileInputStream? = null
private var outputStream: FileOutputStream? = null
...

private fun openAccessory() {
    Log.d(TAG, "openAccessory: $mAccessory")
    fileDescriptor = usbManager.openAccessory(accessory)
    fileDescriptor?.fileDescriptor?.also { fd ->
        inputStream = FileInputStream(fd)
        outputStream = FileOutputStream(fd)
        val thread = Thread(null, this, "AccessoryThread")
        thread.start()
    }
}

Java

UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;
...

private void openAccessory() {
    Log.d(TAG, "openAccessory: " + accessory);
    fileDescriptor = usbManager.openAccessory(accessory);
    if (fileDescriptor != null) {
        FileDescriptor fd = fileDescriptor.getFileDescriptor();
        inputStream = new FileInputStream(fd);
        outputStream = new FileOutputStream(fd);
        Thread thread = new Thread(null, this, "AccessoryThread");
        thread.start();
    }
}

Dans la méthode run() du thread, vous pouvez lire et écrire sur l'accessoire en utilisant les objets FileInputStream ou FileOutputStream. Pendant la lecture d'un accessoire avec un objet FileInputStream, assurez-vous que le tampon que que vous utilisez est suffisamment volumineux pour stocker les données des paquets USB. Le protocole d'accessoire Android est compatible avec de paquets pouvant comporter jusqu'à 16 384 octets. Vous pouvez donc choisir de toujours déclarer votre tampon de cette par souci de simplicité.

Remarque:À un niveau inférieur, la taille des paquets est de 64 octets pour les des accessoires pleine vitesse et 512 octets pour les accessoires USB haut débit. L'accessoire Android regroupe les paquets pour les deux vitesses en un seul paquet logique pour plus de simplicité.

Pour en savoir plus sur l'utilisation des threads sous Android, consultez la section Processus et Threads :

Mettre fin à la communication avec un accessoire

Lorsque vous avez terminé de communiquer avec un accessoire ou si celui-ci a été déconnecté, fermez l'appareil descripteur de fichier que vous avez ouvert en appelant 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_ACCESSORY_DETACHED == intent.action) {
            val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
            accessory?.apply {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
}

Java

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

        if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
            UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
            if (accessory != null) {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
};

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.