1 /**
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  * <p>http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * <p>Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11  * express or implied. See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 package android.car.usb.handler;
15 
16 import android.hardware.usb.UsbConstants;
17 import android.hardware.usb.UsbDevice;
18 import android.hardware.usb.UsbDeviceConnection;
19 import android.util.Log;
20 import java.io.IOException;
21 
22 final class AoapInterface {
23     /**
24      * Use Google Vendor ID when in accessory mode
25      */
26     public static final int USB_ACCESSORY_VENDOR_ID = 0x18D1;
27 
28     /**
29      * Product ID to use when in accessory mode
30      */
31     public static final int USB_ACCESSORY_PRODUCT_ID = 0x2D00;
32 
33     /**
34      * Product ID to use when in accessory mode and adb is enabled
35      */
36     public static final int USB_ACCESSORY_ADB_PRODUCT_ID = 0x2D01;
37 
38     /**
39      * Indexes for strings sent by the host via ACCESSORY_SEND_STRING
40      */
41     public static final int ACCESSORY_STRING_MANUFACTURER = 0;
42     public static final int ACCESSORY_STRING_MODEL = 1;
43     public static final int ACCESSORY_STRING_DESCRIPTION = 2;
44     public static final int ACCESSORY_STRING_VERSION = 3;
45     public static final int ACCESSORY_STRING_URI = 4;
46     public static final int ACCESSORY_STRING_SERIAL = 5;
47 
48     /**
49      * Control request for retrieving device's protocol version
50      *
51      *  requestType:    USB_DIR_IN | USB_TYPE_VENDOR
52      *  request:        ACCESSORY_GET_PROTOCOL
53      *  value:          0
54      *  index:          0
55      *  data            version number (16 bits little endian)
56      *                     1 for original accessory support
57      *                     2 adds HID and device to host audio support
58      */
59     public static final int ACCESSORY_GET_PROTOCOL = 51;
60 
61     /**
62      * Control request for host to send a string to the device
63      *
64      *  requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
65      *  request:        ACCESSORY_SEND_STRING
66      *  value:          0
67      *  index:          string ID
68      *  data            zero terminated UTF8 string
69      *
70      *  The device can later retrieve these strings via the
71      *  ACCESSORY_GET_STRING_* ioctls
72      */
73     public static final int ACCESSORY_SEND_STRING = 52;
74 
75     /**
76      * Control request for starting device in accessory mode.
77      * The host sends this after setting all its strings to the device.
78      *
79      *  requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
80      *  request:        ACCESSORY_START
81      *  value:          0
82      *  index:          0
83      *  data            none
84      */
85     public static final int ACCESSORY_START = 53;
86 
87     /**
88      * Max payload size for AOAP. Limited by driver.
89      */
90     public static final int MAX_PAYLOAD_SIZE = 16384;
91 
92     /**
93      * Accessory write timeout.
94      */
95     public static final int AOAP_TIMEOUT_MS = 2000;
96 
97     private static final String TAG = AoapInterface.class.getSimpleName();
98 
getProtocol(UsbDeviceConnection conn)99     public static int getProtocol(UsbDeviceConnection conn) {
100         byte[] buffer = new byte[2];
101         int len = conn.controlTransfer(
102                 UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR,
103                 ACCESSORY_GET_PROTOCOL, 0, 0, buffer, 2, AOAP_TIMEOUT_MS);
104         if (len != 2) {
105             return -1;
106         }
107         return (buffer[1] << 8) | buffer[0];
108     }
109 
isSupported(UsbDeviceConnection conn)110     public static boolean isSupported(UsbDeviceConnection conn) {
111         return getProtocol(conn) >= 1;
112     }
113 
sendString(UsbDeviceConnection conn, int index, String string)114     public static void sendString(UsbDeviceConnection conn, int index, String string)
115             throws IOException {
116         byte[] buffer = (string + "\0").getBytes();
117         int len = conn.controlTransfer(
118                 UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
119                 ACCESSORY_SEND_STRING, 0, index,
120                 buffer, buffer.length, AOAP_TIMEOUT_MS);
121         if (len != buffer.length) {
122             throw new IOException("Failed to send string " + index + ": \"" + string + "\"");
123         } else {
124             Log.i(TAG, "Sent string " + index + ": \"" + string + "\"");
125         }
126     }
127 
sendAoapStart(UsbDeviceConnection conn)128     public static void sendAoapStart(UsbDeviceConnection conn) throws IOException {
129         int len = conn.controlTransfer(
130                 UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR,
131                 ACCESSORY_START, 0, 0, null, 0, AOAP_TIMEOUT_MS);
132         if (len < 0) {
133             throw new IOException("Control transfer for accessory start failed: " + len);
134         }
135     }
136 
isDeviceInAoapMode(UsbDevice device)137     public static boolean isDeviceInAoapMode(UsbDevice device) {
138         if (device == null) {
139             return false;
140         }
141         final int vid = device.getVendorId();
142         final int pid = device.getProductId();
143         return vid == USB_ACCESSORY_VENDOR_ID
144                 && (pid == USB_ACCESSORY_PRODUCT_ID || pid == USB_ACCESSORY_ADB_PRODUCT_ID);
145     }
146 }
147