USB aksesuarına genel bakış

USB aksesuarı modu, kullanıcıların Android destekli cihazlar için özel olarak tasarlanmış USB ana makine donanımını bağlamasına olanak tanır. Aksesuarlar, Android Aksesuar Geliştirme Kiti dokümanlarında belirtilen Android aksesuar protokolüne uygun olmalıdır. Bu, USB ana makinesi olarak çalışamayan Android destekli cihazların USB donanımıyla etkileşimde bulunmaya devam etmesine olanak tanır. Android destekli bir cihaz USB aksesuarı modundayken, takılı Android USB aksesuarı ana makine görevi görür, USB veri yoluna güç sağlar ve bağlı cihazları numaralandırır. Android 3.1 (API düzeyi 12), USB aksesuar modunu destekler ve özellik, daha geniş bir cihaz yelpazesinin desteklenmesini sağlamak için Android 2.3.4'e (API düzeyi 10) geri getirilmiştir.

Doğru USB aksesuar API'lerini seçin

USB aksesuarı API'leri Android 3.1'de platforma sunulmuş olsa da, Google API'leri eklenti kitaplığı kullanılarak Android 2.3.4'te de kullanılabilmektedir. Bu API'ler harici bir kitaplık kullanılarak geri bağlandığından, USB aksesuar modunu desteklemek için içe aktarabileceğiniz iki paket vardır. Desteklemek istediğiniz Android destekli cihazlara bağlı olarak, bu cihazlardan birini diğerini kullanmanız gerekebilir:

  • com.android.future.usb: Android 2.3.4'te USB aksesuarı modunu desteklemek için Google API'leri eklenti kitaplığında geri taşınan USB aksesuar API'leri yer alır ve bunlar bu ad alanında yer alır. Android 3.1, eklenti kitaplığıyla yazılan uygulamaları desteklemek için bu ad alanındaki sınıfların içe aktarılmasını ve çağrılmasını da destekler. Bu eklenti kitaplığı, android.hardware.usb aksesuar API'leri etrafında ince bir sarmalayıcıdır ve USB ana makine modunu desteklemez. USB aksesuarı modunu destekleyen en geniş cihaz yelpazesini desteklemek istiyorsanız eklenti kitaplığını kullanın ve bu paketi içe aktarın. Tüm Android 2.3.4 cihazların USB aksesuarı özelliğini desteklemesi gerekli değildir. Bu özelliğin desteklenip desteklenmeyeceğine her cihaz üreticisi karar verir. Bu nedenle, özelliği manifest dosyanızda beyan etmeniz gerekir.
  • android.hardware.usb: Bu ad alanı, Android 3.1'de USB aksesuarı modunu destekleyen sınıfları içerir. Bu paket, çerçeve API'lerinin bir parçası olarak yer aldığı için Android 3.1, eklenti kitaplığı kullanılmadan USB aksesuarı modunu destekler. Yalnızca manifest dosyanızda beyan edebileceğiniz, USB aksesuarı modu için donanım desteği olan Android 3.1 veya daha yeni cihazlarla ilgileniyorsanız bu paketi kullanın.

Google API'leri eklenti kitaplığını yükleyin

Eklentiyi yüklemek istiyorsanız bunu SDK Manager ile Google API'leri Android API 10 paketini yükleyerek yapabilirsiniz. Eklenti kitaplığını yükleme hakkında daha fazla bilgi için Google API'leri Eklentisini Yükleme bölümüne bakın.

API'ye genel bakış

Eklenti kitaplığı, çerçeve API'leri için bir sarmalayıcı olduğundan USB aksesuarı özelliğini destekleyen sınıflar benzerdir. Eklenti kitaplığını kullanıyor olsanız bile android.hardware.usb referans dokümanlarından yararlanabilirsiniz.

Not: Eklenti kitaplığı ile çerçeve API'leri arasında dikkat etmeniz gereken küçük bir kullanım farkı vardır.

Aşağıdaki tabloda, USB aksesuarı API'lerini destekleyen sınıflar açıklanmaktadır:

Sınıf Açıklama
UsbManager Bağlı USB aksesuarlarını numaralandırmanıza ve bunlarla iletişim kurmanıza olanak tanır.
UsbAccessory USB aksesuarını temsil eder ve bu aksesuarın tanımlayıcı bilgilerine erişmek için kullanılacak yöntemler içerir.

Eklenti kitaplığı ve platform API'leri arasındaki kullanım farklılıkları

Google API'leri eklenti kitaplığını ve platform API'lerini kullanmak arasında iki kullanım farkı vardır.

Eklenti kitaplığını kullanıyorsanız UsbManager nesnesini aşağıdaki şekilde edinmeniz gerekir:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Eklenti kitaplığını kullanmıyorsanız UsbManager nesnesini aşağıdaki şekilde edinmeniz gerekir:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

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

Amaç filtresi olan bağlı bir aksesuarı filtrelediğinizde UsbAccessory nesnesi, uygulamanıza iletilen intent'in içinde yer alır. Eklenti kitaplığını kullanıyorsanız UsbAccessory nesnesini aşağıdaki şekilde edinmeniz gerekir:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Eklenti kitaplığını kullanmıyorsanız UsbAccessory nesnesini aşağıdaki şekilde edinmeniz gerekir:

Kotlin

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

Java

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

Android manifest şartları

Aşağıdaki listede, USB aksesuarı API'leriyle çalışmadan önce uygulamanızın manifest dosyasına eklemeniz gerekenler açıklanmaktadır. Manifest ve kaynak dosyası örneklerinde, aşağıdaki öğelerin nasıl bildirileceği gösterilmektedir:

  • Tüm Android destekli cihazların USB aksesuarı API'lerini desteklemesi garanti edilmediğinden, uygulamanızın android.hardware.usb.accessory özelliğini kullandığını belirten bir <uses-feature> öğesi ekleyin.
  • Eklenti kitaplığını kullanıyorsanız kitaplık için com.android.future.usb.accessory belirten <uses-library> öğesini ekleyin.
  • Eklenti kitaplığını kullanıyorsanız uygulamanın minimum SDK değerini API Düzeyi 10, android.hardware.usb paketini kullanıyorsanız 12 olarak ayarlayın.
  • Uygulamanıza takılı bir USB aksesuarı hakkında bildirim gönderilmesini istiyorsanız ana etkinliğinizde android.hardware.usb.action.USB_ACCESSORY_ATTACHED amacı için bir <intent-filter> ve <meta-data> öğe çifti belirtin. <meta-data> öğesi, algılamak istediğiniz aksesuarla ilgili tanımlayıcı bilgileri açıklayan harici bir XML kaynak dosyasını işaret eder.

    XML kaynak dosyasında, filtrelemek istediğiniz aksesuarlar için <usb-accessory> öğelerini tanımlayın. Her <usb-accessory> aşağıdaki özelliklere sahip olabilir:

    • manufacturer
    • model
    • version

    version filtresi önerilmez. Bir aksesuar veya cihaz her zaman (kasıtlı olarak veya istemeden) bir sürüm dizesi belirtmeyebilir. Uygulamanız, filtreleme yapmak üzere bir sürüm özelliği beyan ettiğinde ve aksesuar ya da cihaz bir sürüm dizesi belirtmediğinde Android'in önceki sürümlerinde bir NullPointerException oluşur. Bu sorun Android 12'de düzeltilmiştir.

    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 de gösterilmektedir.

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

Bu durumda, aşağıdaki kaynak dosyası res/xml/accessory_filter.xml içine kaydedilmelidir ve ilgili model, üretici ve sürüme sahip tüm aksesuarların filtrelenmesi gerekir. Aksesuar, Android destekli cihaza şu özellikleri gönderir:

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

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

Aksesuarlarla çalışma

Kullanıcılar Android destekli bir cihaza USB aksesuarları bağladığında Android sistemi, uygulamanızın bağlı aksesuarla ilgilenip ilgilenmediğini belirleyebilir. Böyle bir durumda, isterseniz aksesuarla iletişim kurabilirsiniz. Bunun için uygulamanız:

  1. Aksesuarlara bağlı etkinlikleri filtreleyen bir intent filtresi kullanarak veya bağlı aksesuarları numaralandırıp uygun olanı bularak bağlı aksesuarları keşfedin.
  2. Aksesuarla iletişim kurabilmek için, daha önce kullanıcıdan izin almadıysanız kullanıcıdan izin isteyin.
  3. Uygun arayüz uç noktalarında veri okuyup yazarak aksesuarla iletişim kurun.

Bir aksesuarı keşfedin

Uygulamanız, kullanıcı bir aksesuarı bağladığında bildirim almak için intent filtresi kullanarak veya bağlı aksesuarları sayarak aksesuarları keşfedebilir. Uygulamanızın istediğiniz bir aksesuarı otomatik olarak algılamasını sağlamak istiyorsanız intent filtresi kullanmak faydalı olabilir. Bağlı aksesuarların numaralandırılması, tüm bağlı aksesuarların listesini almak istediğinizde veya uygulamanız belirli bir amaca yönelik filtre uygulamadıysa işinize yarar.

Amaç filtresi kullanma

Uygulamanızın belirli bir USB aksesuarını keşfetmesini sağlamak için android.hardware.usb.action.USB_ACCESSORY_ATTACHED amacını filtrelemek üzere bir intent filtresi belirtebilirsiniz. Bu intent filtresiyle birlikte USB aksesuarının özelliklerini (ör. üretici, model ve sürüm) belirten bir kaynak dosyası belirtmeniz gerekir.

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

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

Aşağıdaki örnekte, ilgilendiğiniz USB aksesuarlarını belirten ilgili kaynak dosyasını nasıl beyan edeceğiniz gösterilmektedir:

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

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

Etkinliğinizde, ekteki aksesuarı temsil eden UsbAccessory bilgisini aşağıdaki şekilde edinebilirsiniz (eklenti kitaplığıyla birlikte):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

veya bunun gibi (platform API'leriyle):

Kotlin

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

Java

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

Aksesuarları numaralandır

Uygulamanızın, çalışırken kendini tanımlayan aksesuarları numaralandırmasını sağlayabilirsiniz.

Bağlı tüm USB aksesuarlarını bir diziye almak için getAccessoryList() yöntemini kullanın:

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

Not: Aynı anda yalnızca bir bağlı aksesuar desteklenir.

Bir aksesuarla iletişim kurmak için izin alma

USB aksesuarı ile iletişim kurmadan önce uygulamanızın kullanıcılarınızdan izin alması gerekir.

Not: Uygulamanız, birbirlerine bağlı aksesuarları keşfetmek için niyet filtresi kullanıyorsa, kullanıcı, uygulamanızın amacı işlemesine izin verdiği takdirde otomatik olarak izin alır. Aksi takdirde, aksesuara bağlanmadan önce uygulamanızda açık bir şekilde izin istemeniz gerekir.

Uygulamanızın halihazırda bağlı olan aksesuarları numaralandırıp ardından bu aksesuarlardan biriyle iletişim kurmak istemesi gibi bazı durumlarda açıkça izin istemek gerekebilir. Bir aksesuarla iletişim kurmaya çalışmadan önce aksesuara erişim izni olup olmadığını kontrol etmeniz gerekir. Aksi takdirde, kullanıcı aksesuara erişim iznini reddettiğinde bir ç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ında, kullanıcıya, aksesuara bağlanmak için izin isteyen bir iletişim kutusu gösterilir. 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 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);
                }
            }
        }
    }
};

Yayın alıcısını kaydetmek için etkinliğinizdeki onCreate() yöntemine bunu 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 aksesuara bağlanmak için izin isteyen iletişim kutusunu görüntülemek üzere requestPermission() yöntemini çağırın:

Kotlin

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

Java

UsbAccessory accessory;
...
usbManager.requestPermission(accessory, 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. Aksesuara bağlanmadan önce bu ek değeri kontrol ederek true değerini kontrol edin.

Bir aksesuarla iletişim kurma

Veri okuma ve tanımlayıcıya veri yazmak için giriş ve çıkış akışları ayarlayabileceğiniz bir dosya tanımlayıcı almak amacıyla UsbManager aracılığıyla aksesuarla iletişim kurabilirsiniz. Akışlar, aksesuarın giriş ve çıkış toplu uç noktalarını temsil eder. Ana kullanıcı arayüzü iş parçacığını kilitlememek için cihaz ile aksesuar arasındaki iletişimi başka bir iş parçacığında ayarlamalısınız. Aşağıdaki örnekte, iletişim kurmak için bir aksesuarın nasıl açılacağı gösterilmektedir:

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

İş parçacığının run() yönteminde FileInputStream veya FileOutputStream nesnelerini kullanarak aksesuarda okuma ve yazma işlemleri yapabilirsiniz. FileInputStream nesnesine sahip bir aksesuardan veri okurken, kullandığınız arabelleğin USB paket verilerini depolayacak kadar büyük olduğundan emin olun. Android aksesuar protokolü, 16.384 bayta kadar paket arabelleklerini destekler. Bu sayede basit olması için arabelleğinizin her zaman bu boyutta olduğunu belirtebilirsiniz.

Not: Daha düşük bir düzeyde, paketler USB tam hızlı aksesuarlar için 64 bayt ve USB yüksek hızlı aksesuarlar için 512 bayttır. Android aksesuar protokolü, kolaylık sağlamak amacıyla her iki hıza ait paketleri tek bir mantıksal pakette gruplandırır.

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.

Aksesuarla iletişimi sonlandırma

Bir aksesuarla iletişiminiz sona erdiğinde veya aksesuar çıkarıldıysa close() numaralı telefonu arayarak açtığınız dosya tanımlayıcısını kapatın. Ayrılmış 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_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
            }
        }
    }
};

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.