blob: b5781616687788fdef479ffa4d7c5e2638efd4e9 [file] [log] [blame]
Kevin Crossand4285492016-11-28 18:40:43 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.car.usb.handler;
17
18import android.annotation.Nullable;
19import android.content.ComponentName;
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.Cursor;
23import android.database.sqlite.SQLiteDatabase;
24import android.database.sqlite.SQLiteOpenHelper;
Kevin Crossan4f208d82017-03-27 16:06:21 -070025import android.hardware.usb.UsbDevice;
Kevin Crossand4285492016-11-28 18:40:43 -080026import android.util.Log;
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080027
Kevin Crossand4285492016-11-28 18:40:43 -080028import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * Provides API to persist USB device settings.
33 */
34public final class UsbSettingsStorage {
35 private static final String TAG = UsbSettingsStorage.class.getSimpleName();
36
37 private static final String TABLE_USB_SETTINGS = "usb_devices";
38 private static final String COLUMN_SERIAL = "serial";
39 private static final String COLUMN_VID = "vid";
40 private static final String COLUMN_PID = "pid";
41 private static final String COLUMN_NAME = "name";
42 private static final String COLUMN_HANDLER = "handler";
43 private static final String COLUMN_AOAP = "aoap";
44 private static final String COLUMN_DEFAULT_HANDLER = "default_handler";
45
46 private final UsbSettingsDbHelper mDbHelper;
47
48 public UsbSettingsStorage(Context context) {
49 mDbHelper = new UsbSettingsDbHelper(context);
50 }
51
Kevin Crossan4f208d82017-03-27 16:06:21 -070052 private Cursor queryFor(SQLiteDatabase db, UsbDevice device) {
53 String serial = device.getSerialNumber();
54 String selection;
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080055 List<String> selectionArgs = new ArrayList<>();
Kevin Crossan4f208d82017-03-27 16:06:21 -070056 if (AoapInterface.isDeviceInAoapMode(device)) {
57 selection = COLUMN_SERIAL + " = ? AND " + COLUMN_AOAP + " = 1";
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080058 selectionArgs.add(serial);
Kevin Crossan4f208d82017-03-27 16:06:21 -070059 } else if (serial == null) {
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080060 selection = COLUMN_SERIAL + " IS NULL";
Kevin Crossan4f208d82017-03-27 16:06:21 -070061 } else {
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080062 selection = COLUMN_SERIAL + " = ?";
63 selectionArgs.add(serial);
Kevin Crossan4f208d82017-03-27 16:06:21 -070064 }
Pavel Maltsev2e33ff52018-11-16 13:47:53 -080065
66 selection += " AND " + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
67 selectionArgs.add(String.valueOf(device.getVendorId()));
68 selectionArgs.add(String.valueOf(device.getProductId()));
69
70 return db.query(TABLE_USB_SETTINGS, null, selection,
71 selectionArgs.toArray(new String[0]), null, null, null);
Kevin Crossan4f208d82017-03-27 16:06:21 -070072 }
73
Kevin Crossand4285492016-11-28 18:40:43 -080074 /**
75 * Returns settings for {@serialNumber} or null if it doesn't exist.
76 */
77 @Nullable
Kevin Crossan4f208d82017-03-27 16:06:21 -070078 public UsbDeviceSettings getSettings(UsbDevice device) {
Kevin Crossand4285492016-11-28 18:40:43 -080079 try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
Kevin Crossan4f208d82017-03-27 16:06:21 -070080 Cursor resultCursor = queryFor(db, device)) {
Kevin Crossand4285492016-11-28 18:40:43 -080081 if (resultCursor.getCount() > 1) {
Kevin Crossan4f208d82017-03-27 16:06:21 -070082 throw new RuntimeException("Querying for device: " + device
Kevin Crossand4285492016-11-28 18:40:43 -080083 + " returned " + resultCursor.getCount() + " results");
84 }
85 if (resultCursor.getCount() == 0) {
Kevin Crossan4f208d82017-03-27 16:06:21 -070086 Log.w(TAG, "Usb setting missing for device: " + device);
Kevin Crossand4285492016-11-28 18:40:43 -080087 return null;
88 }
89 List<UsbDeviceSettings> settings = constructSettings(resultCursor);
90 return settings.get(0);
91 }
92 }
93
94 /**
95 * Saves or updates settings for USB device.
96 */
97 public void saveSettings(UsbDeviceSettings settings) {
98 try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
99 long result = db.replace(
100 TABLE_USB_SETTINGS,
101 null,
102 settingsToContentValues(settings));
103 if (result == -1) {
104 Log.e(TAG, "Failed to save settings: " + settings);
105 }
106 }
107 }
108
109 /**
110 * Delete settings for USB device.
111 */
112 public void deleteSettings(String serialNumber, int vid, int pid) {
113 try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
114 int result = db.delete(
115 TABLE_USB_SETTINGS,
116 COLUMN_SERIAL + " = ? AND " + COLUMN_VID + " = ? AND " + COLUMN_PID
117 + " = ?",
118 new String[]{serialNumber, Integer.toString(vid), Integer.toString(pid)});
119 if (result == 0) {
120 Log.w(TAG, "No settings with serialNumber: " + serialNumber
121 + " vid: " + vid + " pid: " + pid);
122 }
123 if (result > 1) {
124 Log.e(TAG, "Deleted multiple rows (" + result + ") for serialNumber: "
125 + serialNumber + " vid: " + vid + " pid: " + pid);
126 }
127 }
128 }
129
130 /**
131 * Returns all saved settings.
132 */
133 public List<UsbDeviceSettings> getAllSettings() {
134 try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
135 Cursor resultCursor = db.query(
136 TABLE_USB_SETTINGS,
137 null,
138 null,
139 null,
140 null,
141 null,
142 null)) {
143 return constructSettings(resultCursor);
144 }
145 }
146
147 private List<UsbDeviceSettings> constructSettings(Cursor cursor) {
148 if (!cursor.isBeforeFirst()) {
149 throw new RuntimeException("Cursor is not reset to before first element");
150 }
151 int serialNumberColumnId = cursor.getColumnIndex(COLUMN_SERIAL);
152 int vidColumnId = cursor.getColumnIndex(COLUMN_VID);
153 int pidColumnId = cursor.getColumnIndex(COLUMN_PID);
154 int deviceNameColumnId = cursor.getColumnIndex(COLUMN_NAME);
155 int handlerColumnId = cursor.getColumnIndex(COLUMN_HANDLER);
156 int aoapColumnId = cursor.getColumnIndex(COLUMN_AOAP);
157 List<UsbDeviceSettings> results = new ArrayList<>(cursor.getCount());
158 while (cursor.moveToNext()) {
159 results.add(UsbDeviceSettings.constructSettings(
160 cursor.getString(serialNumberColumnId),
161 cursor.getInt(vidColumnId),
162 cursor.getInt(pidColumnId),
163 cursor.getString(deviceNameColumnId),
164 ComponentName.unflattenFromString(
165 cursor.getString(handlerColumnId)),
166 cursor.getInt(aoapColumnId) != 0));
167 }
168 return results;
169 }
170
171 /**
172 * Converts {@code UsbDeviceSettings} to {@code ContentValues}.
173 */
174 public ContentValues settingsToContentValues(UsbDeviceSettings settings) {
175 ContentValues contentValues = new ContentValues();
176 contentValues.put(COLUMN_SERIAL, settings.getSerialNumber());
177 contentValues.put(COLUMN_VID, settings.getVid());
178 contentValues.put(COLUMN_PID, settings.getPid());
179 contentValues.put(COLUMN_NAME, settings.getDeviceName());
180 contentValues.put(COLUMN_HANDLER, settings.getHandler().flattenToShortString());
181 contentValues.put(COLUMN_AOAP, settings.getAoap() ? 1 : 0);
182 contentValues.put(COLUMN_DEFAULT_HANDLER, settings.isDefaultHandler() ? 1 : 0);
183 return contentValues;
184 }
185
Kevin Crossand4285492016-11-28 18:40:43 -0800186 private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
Kevin Crossan4f208d82017-03-27 16:06:21 -0700187 private static final int DATABASE_VERSION = 2;
Kevin Crossand4285492016-11-28 18:40:43 -0800188 private static final String DATABASE_NAME = "usb_devices.db";
189
tadvana63f42f42019-03-26 17:25:21 -0700190 // we are using device protected storage because we may need to access the db before the
191 // user has authenticated
Kevin Crossand4285492016-11-28 18:40:43 -0800192 UsbSettingsDbHelper(Context context) {
tadvana63f42f42019-03-26 17:25:21 -0700193 super(
194 context.createDeviceProtectedStorageContext(),
195 DATABASE_NAME,
196 null,
197 DATABASE_VERSION);
Kevin Crossand4285492016-11-28 18:40:43 -0800198 }
199
200 @Override
201 public void onCreate(SQLiteDatabase db) {
Kevin Crossan4f208d82017-03-27 16:06:21 -0700202 createTable(db, TABLE_USB_SETTINGS);
203 createSerialIndex(db);
204 }
205
206 private void createTable(SQLiteDatabase db, String tableName) {
207 db.execSQL("CREATE TABLE " + tableName + " ("
Kevin Crossand4285492016-11-28 18:40:43 -0800208 + COLUMN_SERIAL + " TEXT,"
209 + COLUMN_VID + " INTEGER,"
210 + COLUMN_PID + " INTEGER,"
211 + COLUMN_NAME + " TEXT, "
212 + COLUMN_HANDLER + " TEXT,"
213 + COLUMN_AOAP + " INTEGER,"
Kevin Crossan4f208d82017-03-27 16:06:21 -0700214 + COLUMN_DEFAULT_HANDLER + " INTEGER,"
215 + "PRIMARY KEY (" + COLUMN_SERIAL + ", " + COLUMN_VID + ", " + COLUMN_PID
Kevin Crossand4285492016-11-28 18:40:43 -0800216 + "))");
217 }
218
Kevin Crossan4f208d82017-03-27 16:06:21 -0700219 private void createSerialIndex(SQLiteDatabase db) {
220 db.execSQL("CREATE INDEX " + TABLE_USB_SETTINGS + "_" + COLUMN_SERIAL + " ON "
221 + TABLE_USB_SETTINGS + "(" + COLUMN_SERIAL + ")");
222 }
223
Kevin Crossand4285492016-11-28 18:40:43 -0800224 @Override
225 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Kevin Crossan4f208d82017-03-27 16:06:21 -0700226 for (; oldVersion != newVersion; oldVersion++) {
227 switch (oldVersion) {
228 case 1:
229 String tempTableName = "temp_" + TABLE_USB_SETTINGS;
230 createTable(db, tempTableName);
231 db.execSQL("INSERT INTO " + tempTableName
232 + " SELECT * FROM " + TABLE_USB_SETTINGS);
233 db.execSQL("DROP TABLE " + TABLE_USB_SETTINGS);
234 db.execSQL("ALTER TABLE " + tempTableName + " RENAME TO "
235 + TABLE_USB_SETTINGS);
236 createSerialIndex(db);
237 break;
238 default:
239 throw new IllegalArgumentException(
240 "Unknown database version " + oldVersion);
241 }
242 }
Kevin Crossand4285492016-11-28 18:40:43 -0800243 }
244 }
245}