USB ana makinesine genel bakış

Android destekli cihazınız USB ana makine modunda olduğunda USB ana makinesi görevi görür, veri yoluna güç verir ve bağlı USB cihazlarını numaralandırır. USB ana makine modu, Android 3.1 ve sonraki sürümlerde desteklenir.

API'ye Genel Bakış

Başlamadan önce, üzerinde çalışmanız gereken sınıfları anlamanız önemlidir. Aşağıdaki tabloda android.hardware.usb paketindeki USB ana makine API'leri açıklanmaktadır.

Tablo 1. USB Ana Makine API'leri

Sınıf Açıklama
UsbManager Bağlı USB cihazlarını numaralandırmanıza ve bu cihazlarla iletişim kurmanıza olanak tanır.
UsbDevice Bağlı bir USB cihazını temsil eder ve tanımlayıcı bilgilere, arayüzlere ve uç noktalara erişmek için yöntemler içerir.
UsbInterface USB cihazın arayüzünü temsil eder ve cihaz için bir dizi işlevi tanımlar. Bir cihazda iletişim kurulabilecek bir veya daha fazla arayüz bulunabilir.
UsbEndpoint Bir arayüz uç noktasını temsil eder ve bu arayüz için bir iletişim kanalıdır. Arayüzde bir veya daha fazla uç nokta bulunur. Arayüz, genellikle cihazla iki yönlü iletişim için giriş ve çıkış uç noktalarına sahiptir.
UsbDeviceConnection Uç noktalarda veri aktaran cihaz bağlantısını temsil eder. Bu sınıf, verileri eşzamanlı veya eşzamansız olarak ileri ve geri göndermenize olanak tanır.
UsbRequest UsbDeviceConnection üzerinden bir cihazla iletişim kurmak için eşzamansız bir isteği temsil eder.
UsbConstants Linux çekirdeğinin linux/usb/ch9.h alanındaki tanımlara karşılık gelen USB sabitlerini tanımlar.

Çoğu durumda, bir USB cihazıyla iletişim kurarken bu sınıfların tümünü kullanmanız gerekir (UsbRequest yalnızca eşzamansız iletişim yapıyorsanız gereklidir). Genel olarak, istenen UsbDevice öğesini almak için bir UsbManager elde edersiniz. Cihaz yanınızda olduğunda, ilgili arayüzün iletişim kurmak için uygun UsbInterface ve UsbEndpoint öğelerini bulmanız gerekir. Doğru uç noktayı edindikten sonra USB cihazıyla iletişim kurmak için bir UsbDeviceConnection açın.

Android manifest şartları

Aşağıdaki listede, USB ana makine API'leriyle çalışmadan önce uygulamanızın manifest dosyasına eklemeniz gerekenler açıklanmaktadır:

  • Tüm Android destekli cihazların USB ana makine API'lerini desteklemesi garanti edilmediğinden, uygulamanızın android.hardware.usb.host özelliğini kullandığını belirten bir <uses-feature> öğesi ekleyin.
  • Uygulamanın minimum SDK'sını API Düzeyi 12 veya sonraki bir sürüme ayarlayın. USB ana makine API'leri önceki API düzeylerinde bulunmaz.
  • Uygulamanızın takılı bir USB cihazıyla ilgili olarak bilgilendirilmesini isterseniz ana etkinliğinizde android.hardware.usb.action.USB_DEVICE_ATTACHED amacı için bir <intent-filter> ve <meta-data> öğe çifti belirtin. <meta-data> öğesi, algılamak istediğiniz cihazla ilgili tanımlayıcı bilgileri açıklayan harici bir XML kaynak dosyasını işaret eder.

    XML kaynak dosyasında, filtrelemek istediğiniz USB cihazları için <usb-device> öğeleri tanımlayın. Aşağıdaki listede <usb-device> özellikleri açıklanmaktadır. Genel olarak, belirli bir cihaza göre filtrelemek istiyorsanız tedarikçi ve ürün kimliğini, toplu depolama cihazları veya dijital kameralar gibi bir grup USB cihazı için filtreleme yapmak istiyorsanız ilgili sınıf, alt sınıf ve protokolü kullanın. Bu özelliklerin hiçbirini veya tümünü belirtebilirsiniz. Her USB cihazıyla eşleşen bir özellik belirtmemeyi tercih edebilirsiniz. Dolayısıyla bunu yalnızca uygulamanız için gerekliyse yapın:

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol (cihaz veya arayüz)

    Kaynak dosyayı res/xml/ dizinine kaydedin. Kaynak dosya adı (.xml uzantısı olmadan), <meta-data> öğesinde belirttiğiniz adla aynı olmalıdır. XML kaynak dosyasının biçimi aşağıdaki örnekte verilmiştir.

Manifest ve kaynak dosyası örnekleri

Aşağıdaki örnekte örnek bir manifest ve ona karşılık gelen kaynak dosyası gösterilmektedir:

<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>

Bu durumda, aşağıdaki kaynak dosyası res/xml/device_filter.xml içine kaydedilmelidir ve belirtilen özelliklere sahip tüm USB cihazlarının filtrelenmesi gerektiğini belirtir:

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

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

Cihazlarla çalışma

Kullanıcılar Android destekli bir cihaza USB cihazları bağladığında Android sistemi, uygulamanızın bağlı cihazla ilgilenip ilgilenmediğini belirleyebilir. Bu durumda, istediğiniz takdirde cihazla iletişim kurabilirsiniz. Bunun için uygulamanız:

  1. Kullanıcı bir USB cihazı bağladığında bildirim almak için intent filtresi kullanarak veya bağlı USB cihazlarını numaralandırarak bağlı USB cihazlarını keşfedin.
  2. Henüz edinmediyseniz USB cihazına bağlanmak için kullanıcıdan izin isteyin.
  3. USB cihazıyla iletişim kurmak için uygun arayüz uç noktalarında veri okuyup yazın.

Cihaz keşfedin

Uygulamanız, kullanıcı bir cihazı bağladığında bildirim almak için intent filtresi kullanarak veya bağlı USB cihazlarını numaralandırarak USB cihazları keşfedebilir. Uygulamanızın istediğiniz cihazı otomatik olarak algılamasını sağlamak istiyorsanız intent filtresi kullanmak faydalı olabilir. Bağlı USB cihazlarını listelemek, tüm bağlı cihazların listesini almak istiyorsanız veya uygulamanız bir amaç için filtreleme yapmadıysa yararlı olur.

Amaç filtresi kullanma

Uygulamanızın belirli bir USB cihazını keşfetmesini sağlamak için android.hardware.usb.action.USB_DEVICE_ATTACHED amacını filtrelemek üzere bir intent filtresi belirtebilirsiniz. Bu intent filtresiyle birlikte, USB cihazının ürün ve tedarikçi kimliği gibi özelliklerini belirten bir kaynak dosyası belirtmeniz gerekir. Kullanıcılar cihaz filtrenizle eşleşen bir cihaz bağladığında sistem onlara uygulamanızı başlatmak isteyip istemediklerini soran bir iletişim kutusu gösterir. Kullanıcılar kabul ederse cihazın bağlantısı kesilene kadar uygulamanızın otomatik olarak cihaza erişim izni olur.

Aşağıdaki örnekte, intent filtresinin nasıl bildirileceği gösterilmektedir:

<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>

Aşağıdaki örnekte, ilgilendiğiniz USB cihazlarını belirten ilgili kaynak dosyasının nasıl tanımlanacağı gösterilmektedir:

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

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

Etkinliğinizde, bağlı cihazı temsil eden UsbDevice bilgisini amaçtan şu şekilde edinebilirsiniz:

Kotlin

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

Java

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

Cihazları numaralandırma

Uygulamanız, çalışırken o anda bağlı olan tüm USB cihazlarını incelemek istiyorsa veri yolu üzerindeki cihazları sıralayabilir. Bağlı tüm USB cihazlarının karma haritasını almak için getDeviceList() yöntemini kullanın. Haritadan bir cihaz elde etmek istiyorsanız karma haritasına USB cihazının adı eklenir.

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");

İsterseniz karma haritasından bir iteratör elde edebilir ve her cihazı tek tek işleyebilirsiniz:

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
}

Bir cihazla iletişim kurma izni alma

Uygulamanız, USB cihazıyla iletişim kurmadan önce kullanıcılarınızdan izin almalıdır.

Not: Uygulamanız, USB cihazları bağlı olduğu sırada keşfetmek için niyet filtresi kullanıyorsa, kullanıcı uygulamanıza bu niyeti yerine getirme izni verdiğinde uygulama otomatik olarak izin alır. Aksi takdirde, cihaza bağlanmadan önce uygulamanızda açık bir şekilde izin istemeniz gerekir.

Uygulamanızın zaten bağlı USB cihazları numaralandırıp ardından bunlardan biriyle iletişim kurmak istediği bazı durumlarda açıkça izin istemek gerekebilir. Bir cihazla iletişim kurmaya çalışmadan önce cihazla ilgili erişim iznini kontrol etmeniz gerekir. Aksi takdirde, kullanıcı cihaza erişim iznini reddettiğinde çalışma zamanı hatası alırsınız.

Açıkça izin almak için önce bir yayın alıcısı oluşturun. Bu alıcı, requestPermission() numaralı telefonu aradığınızda yayınlanan niyeti dinler. requestPermission() çağrısı, kullanıcıya, cihaza bağlanmak için izin isteyen bir iletişim kutusu gösterir. Aşağıdaki örnek kodda yayın alıcısının nasıl oluşturulacağı gösterilmektedir:

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);
                }
            }
        }
    }
};

Yayın alıcısını kaydetmek için bunu etkinliğinizdeki onCreate() yönteminize ekleyin:

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);

Kullanıcılardan cihaza bağlanmak için izin isteyen iletişim kutusunu görüntülemek üzere requestPermission() yöntemini çağırın:

Kotlin

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

Java

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

Kullanıcılar iletişim kutusuna yanıt verdiğinde yayın alıcınız, ekstra EXTRA_PERMISSION_GRANTED içeren niyeti alır. Bu, yanıtı temsil eden bir boole'dir. Cihaza bağlanmadan önce bu ek değeri kontrol ederek true değerini kontrol edin.

Bir cihazla iletişim kurma

USB cihazlarıyla iletişim eşzamanlı veya eşzamansız olabilir. Her iki durumda da, kullanıcı arayüzü iş parçacığını engellememek için tüm veri iletimlerinin gerçekleştirileceği yeni bir iş parçacığı oluşturmanız gerekir. Bir cihazla iletişimi doğru şekilde ayarlamak için iletişim kurmak istediğiniz cihazın uygun UsbInterface ve UsbEndpoint değerlerini edinmeniz ve UsbDeviceConnection ile bu uç nokta üzerinden istek göndermeniz gerekir. Genel olarak kodunuz:

  • Cihazla iletişim kurmak isteyip istemediğinizi öğrenmek için ürün kimliği, tedarikçi firma kimliği veya cihaz sınıfı gibi UsbDevice nesnesinin özelliklerini kontrol edin.
  • Cihazla iletişim kurmak istediğinizden eminseniz söz konusu arayüzün uygun UsbEndpoint ile birlikte iletişim kurmak için kullanmak istediğiniz uygun UsbInterface bilgisini bulun. Arayüzlerde bir veya daha fazla uç nokta bulunabilir. Genellikle iki yönlü iletişim için giriş ve çıkış uç noktası bulunur.
  • Doğru uç noktayı bulduğunuzda bu uç noktada bir UsbDeviceConnection açın.
  • Uç noktada iletmek istediğiniz verileri bulkTransfer() veya controlTransfer() yöntemiyle sağlayın. Ana kullanıcı arayüzü iş parçacığının engellenmesini önlemek için bu adımı başka bir iş parçacığında uygulamalısınız. Android'de iş parçacıklarını kullanma hakkında daha fazla bilgi için İşlemler ve İş Parçacıkları bölümüne bakın.

Aşağıdaki kod snippet'i, eşzamanlı veri aktarımı yapmanın basit bir yoludur. Kodunuzda iletişim kurulacak doğru arayüzü ve uç noktaları doğru şekilde bulmak için daha fazla mantığa sahip olmalı ve ayrıca veri aktarımını ana kullanıcı arayüzü iş parçacığından farklı bir iş parçacığında gerçekleştirmelidir:

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

Verileri eşzamansız olarak göndermek için UsbRequest sınıfını initialize için ve queue eşzamansız istek için kullanın. Ardından, requestWait() ile sonucu bekleyin.

Bir cihazla iletişimi sonlandırma

Bir cihazla iletişiminiz bittiğinde veya cihaz çıkarıldıysa releaseInterface() ve close() çağrılarını yaparak UsbInterface ve UsbDeviceConnection cihazlarını kapatın. Ayrılan etkinlikleri dinlemek için aşağıdaki gibi bir yayın alıcısı oluşturun:

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
            }
        }
    }
};

Manifest'in yerine uygulama içinde yayın alıcısını oluşturmak, uygulamanızın çalışırken yalnızca ayrılmış etkinlikleri işlemesine olanak tanır. Bu şekilde, ayrılmış etkinlikler yalnızca çalışmakta olan uygulamaya gönderilir ve tüm uygulamalara yayınlanmaz.