1 /* 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 */ 16 package android.car.usb.handler; 17 18 import android.annotation.Nullable; 19 import android.content.ComponentName; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.database.sqlite.SQLiteDatabase; 24 import android.database.sqlite.SQLiteOpenHelper; 25 import android.util.Log; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * Provides API to persist USB device settings. 31 */ 32 public final class UsbSettingsStorage { 33 private static final String TAG = UsbSettingsStorage.class.getSimpleName(); 34 35 private static final String TABLE_USB_SETTINGS = "usb_devices"; 36 private static final String COLUMN_SERIAL = "serial"; 37 private static final String COLUMN_VID = "vid"; 38 private static final String COLUMN_PID = "pid"; 39 private static final String COLUMN_NAME = "name"; 40 private static final String COLUMN_HANDLER = "handler"; 41 private static final String COLUMN_AOAP = "aoap"; 42 private static final String COLUMN_DEFAULT_HANDLER = "default_handler"; 43 44 private final UsbSettingsDbHelper mDbHelper; 45 UsbSettingsStorage(Context context)46 public UsbSettingsStorage(Context context) { 47 mDbHelper = new UsbSettingsDbHelper(context); 48 } 49 50 /** 51 * Returns settings for {@serialNumber} or null if it doesn't exist. 52 */ 53 @Nullable getSettings(String serialNumber)54 public UsbDeviceSettings getSettings(String serialNumber) { 55 try (SQLiteDatabase db = mDbHelper.getReadableDatabase(); 56 Cursor resultCursor = db.query( 57 TABLE_USB_SETTINGS, 58 null, 59 COLUMN_SERIAL + " = ?", 60 new String[]{serialNumber}, 61 null, 62 null, 63 null)) { 64 if (resultCursor.getCount() > 1) { 65 throw new RuntimeException("Querying for serial number: " + serialNumber 66 + " returned " + resultCursor.getCount() + " results"); 67 } 68 if (resultCursor.getCount() == 0) { 69 Log.w(TAG, "Usb setting missing for device serial: " + serialNumber); 70 return null; 71 } 72 List<UsbDeviceSettings> settings = constructSettings(resultCursor); 73 return settings.get(0); 74 } 75 } 76 77 /** 78 * Saves or updates settings for USB device. 79 */ saveSettings(UsbDeviceSettings settings)80 public void saveSettings(UsbDeviceSettings settings) { 81 try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) { 82 long result = db.replace( 83 TABLE_USB_SETTINGS, 84 null, 85 settingsToContentValues(settings)); 86 if (result == -1) { 87 Log.e(TAG, "Failed to save settings: " + settings); 88 } 89 } 90 } 91 92 /** 93 * Delete settings for USB device. 94 */ deleteSettings(String serialNumber, int vid, int pid)95 public void deleteSettings(String serialNumber, int vid, int pid) { 96 try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) { 97 int result = db.delete( 98 TABLE_USB_SETTINGS, 99 COLUMN_SERIAL + " = ? AND " + COLUMN_VID + " = ? AND " + COLUMN_PID 100 + " = ?", 101 new String[]{serialNumber, Integer.toString(vid), Integer.toString(pid)}); 102 if (result == 0) { 103 Log.w(TAG, "No settings with serialNumber: " + serialNumber 104 + " vid: " + vid + " pid: " + pid); 105 } 106 if (result > 1) { 107 Log.e(TAG, "Deleted multiple rows (" + result + ") for serialNumber: " 108 + serialNumber + " vid: " + vid + " pid: " + pid); 109 } 110 } 111 } 112 113 /** 114 * Returns all saved settings. 115 */ getAllSettings()116 public List<UsbDeviceSettings> getAllSettings() { 117 try (SQLiteDatabase db = mDbHelper.getReadableDatabase(); 118 Cursor resultCursor = db.query( 119 TABLE_USB_SETTINGS, 120 null, 121 null, 122 null, 123 null, 124 null, 125 null)) { 126 return constructSettings(resultCursor); 127 } 128 } 129 constructSettings(Cursor cursor)130 private List<UsbDeviceSettings> constructSettings(Cursor cursor) { 131 if (!cursor.isBeforeFirst()) { 132 throw new RuntimeException("Cursor is not reset to before first element"); 133 } 134 int serialNumberColumnId = cursor.getColumnIndex(COLUMN_SERIAL); 135 int vidColumnId = cursor.getColumnIndex(COLUMN_VID); 136 int pidColumnId = cursor.getColumnIndex(COLUMN_PID); 137 int deviceNameColumnId = cursor.getColumnIndex(COLUMN_NAME); 138 int handlerColumnId = cursor.getColumnIndex(COLUMN_HANDLER); 139 int aoapColumnId = cursor.getColumnIndex(COLUMN_AOAP); 140 List<UsbDeviceSettings> results = new ArrayList<>(cursor.getCount()); 141 while (cursor.moveToNext()) { 142 results.add(UsbDeviceSettings.constructSettings( 143 cursor.getString(serialNumberColumnId), 144 cursor.getInt(vidColumnId), 145 cursor.getInt(pidColumnId), 146 cursor.getString(deviceNameColumnId), 147 ComponentName.unflattenFromString( 148 cursor.getString(handlerColumnId)), 149 cursor.getInt(aoapColumnId) != 0)); 150 } 151 return results; 152 } 153 154 /** 155 * Converts {@code UsbDeviceSettings} to {@code ContentValues}. 156 */ settingsToContentValues(UsbDeviceSettings settings)157 public ContentValues settingsToContentValues(UsbDeviceSettings settings) { 158 ContentValues contentValues = new ContentValues(); 159 contentValues.put(COLUMN_SERIAL, settings.getSerialNumber()); 160 contentValues.put(COLUMN_VID, settings.getVid()); 161 contentValues.put(COLUMN_PID, settings.getPid()); 162 contentValues.put(COLUMN_NAME, settings.getDeviceName()); 163 contentValues.put(COLUMN_HANDLER, settings.getHandler().flattenToShortString()); 164 contentValues.put(COLUMN_AOAP, settings.getAoap() ? 1 : 0); 165 contentValues.put(COLUMN_DEFAULT_HANDLER, settings.isDefaultHandler() ? 1 : 0); 166 return contentValues; 167 } 168 169 170 private static class UsbSettingsDbHelper extends SQLiteOpenHelper { 171 private static final int DATABASE_VERSION = 1; 172 private static final String DATABASE_NAME = "usb_devices.db"; 173 UsbSettingsDbHelper(Context context)174 UsbSettingsDbHelper(Context context) { 175 super(context, DATABASE_NAME, null, DATABASE_VERSION); 176 } 177 178 @Override onCreate(SQLiteDatabase db)179 public void onCreate(SQLiteDatabase db) { 180 db.execSQL("CREATE TABLE " + TABLE_USB_SETTINGS + " (" 181 + COLUMN_SERIAL + " TEXT," 182 + COLUMN_VID + " INTEGER," 183 + COLUMN_PID + " INTEGER," 184 + COLUMN_NAME + " TEXT, " 185 + COLUMN_HANDLER + " TEXT," 186 + COLUMN_AOAP + " INTEGER," 187 + COLUMN_DEFAULT_HANDLER + " INTEGER," + "PRIMARY KEY (" + COLUMN_SERIAL 188 + "))"); 189 } 190 191 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)192 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 193 // Do nothing at this point. Not required for v1 database. 194 } 195 } 196 } 197