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:
- 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.
- Henüz edinmediyseniz USB cihazına bağlanmak için kullanıcıdan izin isteyin.
- 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ı olan USB cihazlarını 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 uygunUsbInterface
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()
veyacontrolTransfer()
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.