• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hardware.usb.UsbDevice;
26 import android.util.Log;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * Provides API to persist USB device settings.
33  */
34 public 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 
UsbSettingsStorage(Context context)48     public UsbSettingsStorage(Context context) {
49         mDbHelper = new UsbSettingsDbHelper(context);
50     }
51 
queryFor(SQLiteDatabase db, UsbDevice device)52     private Cursor queryFor(SQLiteDatabase db, UsbDevice device) {
53         String serial = device.getSerialNumber();
54         String selection;
55         List<String> selectionArgs = new ArrayList<>();
56         if (AoapInterface.isDeviceInAoapMode(device)) {
57             selection = COLUMN_SERIAL + " = ? AND " + COLUMN_AOAP + " = 1";
58             selectionArgs.add(serial);
59         } else if (serial == null) {
60             selection = COLUMN_SERIAL + " IS NULL";
61         } else {
62             selection = COLUMN_SERIAL + " = ?";
63             selectionArgs.add(serial);
64         }
65 
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);
72     }
73 
74     /**
75      * Returns settings for {@serialNumber} or null if it doesn't exist.
76      */
77     @Nullable
getSettings(UsbDevice device)78     public UsbDeviceSettings getSettings(UsbDevice device) {
79         try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
80                 Cursor resultCursor = queryFor(db, device)) {
81             if (resultCursor.getCount() > 1) {
82                 throw new RuntimeException("Querying for device: " + device
83                         + " returned " + resultCursor.getCount() + " results");
84             }
85             if (resultCursor.getCount() == 0) {
86                 Log.w(TAG, "Usb setting missing for device: " + device);
87                 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      */
saveSettings(UsbDeviceSettings settings)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      */
deleteSettings(String serialNumber, int vid, int pid)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      */
getAllSettings()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 
constructSettings(Cursor cursor)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      */
settingsToContentValues(UsbDeviceSettings settings)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 
186     private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
187         private static final int DATABASE_VERSION = 2;
188         private static final String DATABASE_NAME = "usb_devices.db";
189 
190         // we are using device protected storage because we may need to access the db before the
191         // user has authenticated
UsbSettingsDbHelper(Context context)192         UsbSettingsDbHelper(Context context) {
193             super(
194                     context.createDeviceProtectedStorageContext(),
195                     DATABASE_NAME,
196                     null,
197                     DATABASE_VERSION);
198         }
199 
200         @Override
onCreate(SQLiteDatabase db)201         public void onCreate(SQLiteDatabase db) {
202             createTable(db, TABLE_USB_SETTINGS);
203             createSerialIndex(db);
204         }
205 
createTable(SQLiteDatabase db, String tableName)206         private void createTable(SQLiteDatabase db, String tableName) {
207             db.execSQL("CREATE TABLE " + tableName + " ("
208                     + COLUMN_SERIAL + " TEXT,"
209                     + COLUMN_VID + " INTEGER,"
210                     + COLUMN_PID + " INTEGER,"
211                     + COLUMN_NAME + " TEXT, "
212                     + COLUMN_HANDLER + " TEXT,"
213                     + COLUMN_AOAP + " INTEGER,"
214                     + COLUMN_DEFAULT_HANDLER + " INTEGER,"
215                     + "PRIMARY KEY (" + COLUMN_SERIAL + ", " + COLUMN_VID + ", " + COLUMN_PID
216                     + "))");
217         }
218 
createSerialIndex(SQLiteDatabase db)219         private void createSerialIndex(SQLiteDatabase db) {
220             db.execSQL("CREATE INDEX " + TABLE_USB_SETTINGS + "_" + COLUMN_SERIAL + " ON "
221                     + TABLE_USB_SETTINGS + "(" + COLUMN_SERIAL + ")");
222         }
223 
224         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)225         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
226             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             }
243         }
244     }
245 }
246