Home | History | Annotate | Download | only in usb
      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