summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Crossan2017-03-27 18:06:21 -0500
committerKevin Crossan2017-04-03 18:04:21 -0500
commit4f208d8be0fd9cbf1d9defb0587507f11a092b98 (patch)
tree867002002ce4ec93b974b7dacd8eee3d7850dacb /car-usb-handler
parent045c3284c2fdb4cf7c61121af35027d4771664fc (diff)
downloadplatform-packages-services-car-4f208d8be0fd9cbf1d9defb0587507f11a092b98.tar.gz
platform-packages-services-car-4f208d8be0fd9cbf1d9defb0587507f11a092b98.tar.xz
platform-packages-services-car-4f208d8be0fd9cbf1d9defb0587507f11a092b98.zip
Support handling USB devices without serials.
If the serial number is null, then match based on the product ID and vendor ID. The serial number is optional depending on the USB device class. Note: if the device supports AOAP, it is required to have a unique serial number. This is because when a device is in AOAP mode it is signaled by advertising a specific vendor and product ID; the serial is the only data that can re-identify the USB device. Test: manual - plugged in Lilliput touchscreen and verified logging with no crash. Test: manual - plugged in MD before new database schema and launched projection. After updating code+schema, plugging in MD still launched projection. Test: manual - plugged in new MD and was able to start projection. Change-Id: If2010feea075f88b2bb6f3cda7e9b2e1c5c4a15c Fixes: 36599687
Diffstat (limited to 'car-usb-handler')
-rw-r--r--car-usb-handler/res/values/strings.xml1
-rw-r--r--car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java13
-rw-r--r--car-usb-handler/src/android/car/usb/handler/UsbHostController.java43
-rw-r--r--car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java75
4 files changed, 96 insertions, 36 deletions
diff --git a/car-usb-handler/res/values/strings.xml b/car-usb-handler/res/values/strings.xml
index 22426489..be1e7a23 100644
--- a/car-usb-handler/res/values/strings.xml
+++ b/car-usb-handler/res/values/strings.xml
@@ -26,4 +26,5 @@
26 <string name="usb_pref_delete_yes">Yes</string> 26 <string name="usb_pref_delete_yes">Yes</string>
27 <string name="usb_pref_delete_cancel">Cancel</string> 27 <string name="usb_pref_delete_cancel">Cancel</string>
28 <string name="usb_resolving_handlers">Getting supported handlers</string> 28 <string name="usb_resolving_handlers">Getting supported handlers</string>
29 <string name="usb_unknown_device">Unknown USB device</string>
29</resources> 30</resources>
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java b/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
index 50844140..288f5982 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbDeviceSettings.java
@@ -17,7 +17,6 @@ package android.car.usb.handler;
17 17
18import android.content.ComponentName; 18import android.content.ComponentName;
19import android.hardware.usb.UsbDevice; 19import android.hardware.usb.UsbDevice;
20import com.android.internal.util.Preconditions;
21 20
22/** 21/**
23 * Settings for USB device. 22 * Settings for USB device.
@@ -34,8 +33,6 @@ public final class UsbDeviceSettings {
34 private boolean mDefaultHandler; 33 private boolean mDefaultHandler;
35 34
36 UsbDeviceSettings(String serialNumber, int vid, int pid) { 35 UsbDeviceSettings(String serialNumber, int vid, int pid) {
37 Preconditions.checkNotNull(serialNumber);
38
39 mSerialNumber = serialNumber; 36 mSerialNumber = serialNumber;
40 mVid = vid; 37 mVid = vid;
41 mPid = pid; 38 mPid = pid;
@@ -96,7 +93,15 @@ public final class UsbDeviceSettings {
96 * Checks if setting matches {@code UsbDevice}. 93 * Checks if setting matches {@code UsbDevice}.
97 */ 94 */
98 public boolean matchesDevice(UsbDevice device) { 95 public boolean matchesDevice(UsbDevice device) {
99 return getSerialNumber().equals(device.getSerialNumber()); 96 String deviceSerial = device.getSerialNumber();
97 if (AoapInterface.isDeviceInAoapMode(device)) {
98 return mAoap && deviceSerial.equals(mSerialNumber);
99 } else if (deviceSerial == null) {
100 return mVid == device.getVendorId() && mPid == device.getProductId();
101 } else {
102 return mVid == device.getVendorId() && mPid == device.getProductId()
103 && deviceSerial.equals(mSerialNumber);
104 }
100 } 105 }
101 106
102 /** 107 /**
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
index b705928f..5c61a980 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
@@ -68,10 +68,10 @@ public final class UsbHostController
68 public void onReceive(Context context, Intent intent) { 68 public void onReceive(Context context, Intent intent) {
69 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { 69 if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
70 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 70 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
71 unsetActiveDeviceIfSerialMatch(device); 71 unsetActiveDeviceIfMatch(device);
72 } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { 72 } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
73 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE); 73 UsbDevice device = intent.<UsbDevice>getParcelableExtra(UsbManager.EXTRA_DEVICE);
74 setActiveDeviceIfSerialMatch(device); 74 setActiveDeviceIfMatch(device);
75 } 75 }
76 } 76 }
77 }; 77 };
@@ -79,9 +79,6 @@ public final class UsbHostController
79 @GuardedBy("this") 79 @GuardedBy("this")
80 private UsbDevice mActiveDevice; 80 private UsbDevice mActiveDevice;
81 81
82 @GuardedBy("this")
83 private String mProcessingDeviceSerial;
84
85 public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) { 82 public UsbHostController(Context context, UsbHostControllerCallbacks callbacks) {
86 mContext = context; 83 mContext = context;
87 mCallback = callbacks; 84 mCallback = callbacks;
@@ -96,17 +93,17 @@ public final class UsbHostController
96 93
97 } 94 }
98 95
99 private synchronized void setActiveDeviceIfSerialMatch(UsbDevice device) { 96 private synchronized void setActiveDeviceIfMatch(UsbDevice device) {
100 if (device != null && device.getSerialNumber() != null 97 if (mActiveDevice != null && device != null
101 && device.getSerialNumber().equals(mProcessingDeviceSerial)) { 98 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
102 mActiveDevice = device; 99 mActiveDevice = device;
103 } 100 }
104 } 101 }
105 102
106 private synchronized void unsetActiveDeviceIfSerialMatch(UsbDevice device) { 103 private synchronized void unsetActiveDeviceIfMatch(UsbDevice device) {
107 mHandler.requestDeviceRemoved(); 104 mHandler.requestDeviceRemoved();
108 if (mActiveDevice != null && mActiveDevice.getSerialNumber() != null 105 if (mActiveDevice != null && device != null
109 && mActiveDevice.getSerialNumber().equals(device.getSerialNumber())) { 106 && UsbUtil.isDevicesMatching(device, mActiveDevice)) {
110 mActiveDevice = null; 107 mActiveDevice = null;
111 } 108 }
112 } 109 }
@@ -114,7 +111,6 @@ public final class UsbHostController
114 private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) { 111 private synchronized boolean startDeviceProcessingIfNull(UsbDevice device) {
115 if (mActiveDevice == null) { 112 if (mActiveDevice == null) {
116 mActiveDevice = device; 113 mActiveDevice = device;
117 mProcessingDeviceSerial = device.getSerialNumber();
118 return true; 114 return true;
119 } 115 }
120 return false; 116 return false;
@@ -122,7 +118,6 @@ public final class UsbHostController
122 118
123 private synchronized void stopDeviceProcessing() { 119 private synchronized void stopDeviceProcessing() {
124 mActiveDevice = null; 120 mActiveDevice = null;
125 mProcessingDeviceSerial = null;
126 } 121 }
127 122
128 private synchronized UsbDevice getActiveDevice() { 123 private synchronized UsbDevice getActiveDevice() {
@@ -131,8 +126,22 @@ public final class UsbHostController
131 126
132 private boolean deviceMatchedActiveDevice(UsbDevice device) { 127 private boolean deviceMatchedActiveDevice(UsbDevice device) {
133 UsbDevice activeDevice = getActiveDevice(); 128 UsbDevice activeDevice = getActiveDevice();
134 return activeDevice != null && activeDevice.getSerialNumber() != null 129 return activeDevice != null && UsbUtil.isDevicesMatching(activeDevice, device);
135 && activeDevice.getSerialNumber().equals(device.getSerialNumber()); 130 }
131
132 private String generateTitle() {
133 String manufacturer = mActiveDevice.getManufacturerName();
134 String product = mActiveDevice.getProductName();
135 if (manufacturer == null && product == null) {
136 return mContext.getString(R.string.usb_unknown_device);
137 }
138 if (manufacturer != null && product != null) {
139 return manufacturer + " " + product;
140 }
141 if (manufacturer != null) {
142 return manufacturer;
143 }
144 return product;
136 } 145 }
137 146
138 /** 147 /**
@@ -147,7 +156,7 @@ public final class UsbHostController
147 mCallback.optionsUpdated(mEmptyList); 156 mCallback.optionsUpdated(mEmptyList);
148 mCallback.processingStateChanged(true); 157 mCallback.processingStateChanged(true);
149 158
150 UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device.getSerialNumber()); 159 UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
151 if (settings != null && mUsbResolver.dispatch( 160 if (settings != null && mUsbResolver.dispatch(
152 mActiveDevice, settings.getHandler(), settings.getAoap())) { 161 mActiveDevice, settings.getHandler(), settings.getAoap())) {
153 if (LOCAL_LOGV) { 162 if (LOCAL_LOGV) {
@@ -156,7 +165,7 @@ public final class UsbHostController
156 } 165 }
157 return; 166 return;
158 } 167 }
159 mCallback.titleChanged(device.getManufacturerName() + " " + device.getProductName()); 168 mCallback.titleChanged(generateTitle());
160 mUsbResolver.resolve(device); 169 mUsbResolver.resolve(device);
161 } 170 }
162 171
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
index 157c92f9..1b251f8e 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
@@ -22,6 +22,7 @@ import android.content.Context;
22import android.database.Cursor; 22import android.database.Cursor;
23import android.database.sqlite.SQLiteDatabase; 23import android.database.sqlite.SQLiteDatabase;
24import android.database.sqlite.SQLiteOpenHelper; 24import android.database.sqlite.SQLiteOpenHelper;
25import android.hardware.usb.UsbDevice;
25import android.util.Log; 26import android.util.Log;
26import java.util.ArrayList; 27import java.util.ArrayList;
27import java.util.List; 28import java.util.List;
@@ -47,26 +48,43 @@ public final class UsbSettingsStorage {
47 mDbHelper = new UsbSettingsDbHelper(context); 48 mDbHelper = new UsbSettingsDbHelper(context);
48 } 49 }
49 50
51 private Cursor queryFor(SQLiteDatabase db, UsbDevice device) {
52 String serial = device.getSerialNumber();
53 String selection;
54 String[] selectionArgs;
55 if (AoapInterface.isDeviceInAoapMode(device)) {
56 selection = COLUMN_SERIAL + " = ? AND " + COLUMN_AOAP + " = 1";
57 selectionArgs = new String[] {serial};
58 } else if (serial == null) {
59 selection = COLUMN_SERIAL + " IS NULL AND "
60 + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
61 selectionArgs = new String[] {
62 Integer.toString(device.getVendorId()),
63 Integer.toString(device.getProductId())};
64 } else {
65 selection =
66 COLUMN_SERIAL + " = ? AND " + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
67 selectionArgs = new String[] {
68 device.getSerialNumber(),
69 Integer.toString(device.getVendorId()),
70 Integer.toString(device.getProductId())};
71 }
72 return db.query(TABLE_USB_SETTINGS, null, selection, selectionArgs, null, null, null);
73 }
74
50 /** 75 /**
51 * Returns settings for {@serialNumber} or null if it doesn't exist. 76 * Returns settings for {@serialNumber} or null if it doesn't exist.
52 */ 77 */
53 @Nullable 78 @Nullable
54 public UsbDeviceSettings getSettings(String serialNumber) { 79 public UsbDeviceSettings getSettings(UsbDevice device) {
55 try (SQLiteDatabase db = mDbHelper.getReadableDatabase(); 80 try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
56 Cursor resultCursor = db.query( 81 Cursor resultCursor = queryFor(db, device)) {
57 TABLE_USB_SETTINGS,
58 null,
59 COLUMN_SERIAL + " = ?",
60 new String[]{serialNumber},
61 null,
62 null,
63 null)) {
64 if (resultCursor.getCount() > 1) { 82 if (resultCursor.getCount() > 1) {
65 throw new RuntimeException("Querying for serial number: " + serialNumber 83 throw new RuntimeException("Querying for device: " + device
66 + " returned " + resultCursor.getCount() + " results"); 84 + " returned " + resultCursor.getCount() + " results");
67 } 85 }
68 if (resultCursor.getCount() == 0) { 86 if (resultCursor.getCount() == 0) {
69 Log.w(TAG, "Usb setting missing for device serial: " + serialNumber); 87 Log.w(TAG, "Usb setting missing for device: " + device);
70 return null; 88 return null;
71 } 89 }
72 List<UsbDeviceSettings> settings = constructSettings(resultCursor); 90 List<UsbDeviceSettings> settings = constructSettings(resultCursor);
@@ -168,7 +186,7 @@ public final class UsbSettingsStorage {
168 186
169 187
170 private static class UsbSettingsDbHelper extends SQLiteOpenHelper { 188 private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
171 private static final int DATABASE_VERSION = 1; 189 private static final int DATABASE_VERSION = 2;
172 private static final String DATABASE_NAME = "usb_devices.db"; 190 private static final String DATABASE_NAME = "usb_devices.db";
173 191
174 UsbSettingsDbHelper(Context context) { 192 UsbSettingsDbHelper(Context context) {
@@ -177,20 +195,47 @@ public final class UsbSettingsStorage {
177 195
178 @Override 196 @Override
179 public void onCreate(SQLiteDatabase db) { 197 public void onCreate(SQLiteDatabase db) {
180 db.execSQL("CREATE TABLE " + TABLE_USB_SETTINGS + " (" 198 createTable(db, TABLE_USB_SETTINGS);
199 createSerialIndex(db);
200 }
201
202 private void createTable(SQLiteDatabase db, String tableName) {
203 db.execSQL("CREATE TABLE " + tableName + " ("
181 + COLUMN_SERIAL + " TEXT," 204 + COLUMN_SERIAL + " TEXT,"
182 + COLUMN_VID + " INTEGER," 205 + COLUMN_VID + " INTEGER,"
183 + COLUMN_PID + " INTEGER," 206 + COLUMN_PID + " INTEGER,"
184 + COLUMN_NAME + " TEXT, " 207 + COLUMN_NAME + " TEXT, "
185 + COLUMN_HANDLER + " TEXT," 208 + COLUMN_HANDLER + " TEXT,"
186 + COLUMN_AOAP + " INTEGER," 209 + COLUMN_AOAP + " INTEGER,"
187 + COLUMN_DEFAULT_HANDLER + " INTEGER," + "PRIMARY KEY (" + COLUMN_SERIAL 210 + COLUMN_DEFAULT_HANDLER + " INTEGER,"
211 + "PRIMARY KEY (" + COLUMN_SERIAL + ", " + COLUMN_VID + ", " + COLUMN_PID
188 + "))"); 212 + "))");
189 } 213 }
190 214
215 private void createSerialIndex(SQLiteDatabase db) {
216 db.execSQL("CREATE INDEX " + TABLE_USB_SETTINGS + "_" + COLUMN_SERIAL + " ON "
217 + TABLE_USB_SETTINGS + "(" + COLUMN_SERIAL + ")");
218 }
219
191 @Override 220 @Override
192 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 221 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
193 // Do nothing at this point. Not required for v1 database. 222 for (; oldVersion != newVersion; oldVersion++) {
223 switch (oldVersion) {
224 case 1:
225 String tempTableName = "temp_" + TABLE_USB_SETTINGS;
226 createTable(db, tempTableName);
227 db.execSQL("INSERT INTO " + tempTableName
228 + " SELECT * FROM " + TABLE_USB_SETTINGS);
229 db.execSQL("DROP TABLE " + TABLE_USB_SETTINGS);
230 db.execSQL("ALTER TABLE " + tempTableName + " RENAME TO "
231 + TABLE_USB_SETTINGS);
232 createSerialIndex(db);
233 break;
234 default:
235 throw new IllegalArgumentException(
236 "Unknown database version " + oldVersion);
237 }
238 }
194 } 239 }
195 } 240 }
196} 241}