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