• 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 (serial == null) {
57             selection = COLUMN_SERIAL + " IS NULL";
58         }
59         else if (AoapInterface.isDeviceInAoapMode(device)) {
60             selection = COLUMN_SERIAL + " = ? AND " + COLUMN_AOAP + " = 1";
61             selectionArgs.add(serial);
62         } else {
63             selection = COLUMN_SERIAL + " = ?";
64             selectionArgs.add(serial);
65         }
66 
67         selection += " AND " + COLUMN_VID + " = ? AND " + COLUMN_PID + " = ?";
68         selectionArgs.add(String.valueOf(device.getVendorId()));
69         selectionArgs.add(String.valueOf(device.getProductId()));
70 
71         return db.query(TABLE_USB_SETTINGS, null, selection,
72                 selectionArgs.toArray(new String[0]), null, null, null);
73     }
74 
75     /**
76      * Returns settings for {@serialNumber} or null if it doesn't exist.
77      */
78     @Nullable
getSettings(UsbDevice device)79     public UsbDeviceSettings getSettings(UsbDevice device) {
80         try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
81                 Cursor resultCursor = queryFor(db, device)) {
82             if (resultCursor.getCount() > 1) {
83                 throw new RuntimeException("Querying for device: " + device
84                         + " returned " + resultCursor.getCount() + " results");
85             }
86             if (resultCursor.getCount() == 0) {
87                 Log.w(TAG, "Usb setting missing for device: " + device);
88                 return null;
89             }
90             List<UsbDeviceSettings> settings = constructSettings(resultCursor);
91             return settings.get(0);
92         }
93     }
94 
95     /**
96      * Saves or updates settings for USB device.
97      */
saveSettings(UsbDeviceSettings settings)98     public void saveSettings(UsbDeviceSettings settings) {
99         try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
100             long result = db.replace(
101                     TABLE_USB_SETTINGS,
102                     null,
103                     settingsToContentValues(settings));
104             if (result == -1) {
105                 Log.e(TAG, "Failed to save settings: " + settings);
106             }
107         }
108     }
109 
110     /**
111      * Delete settings for USB device.
112      */
deleteSettings(String serialNumber, int vid, int pid)113     public void deleteSettings(String serialNumber, int vid, int pid) {
114         try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
115             int result = db.delete(
116                     TABLE_USB_SETTINGS,
117                     COLUMN_SERIAL + " = ? AND " + COLUMN_VID + " = ? AND " + COLUMN_PID
118                     + " = ?",
119                     new String[]{serialNumber, Integer.toString(vid), Integer.toString(pid)});
120             if (result == 0) {
121                 Log.w(TAG, "No settings with serialNumber: " + serialNumber
122                         + " vid: " + vid + " pid: " + pid);
123             }
124             if (result > 1) {
125                 Log.e(TAG, "Deleted multiple rows (" + result + ") for serialNumber: "
126                         + serialNumber + " vid: " + vid + " pid: " + pid);
127             }
128         }
129     }
130 
131     /**
132      * Returns all saved settings.
133      */
getAllSettings()134     public List<UsbDeviceSettings> getAllSettings() {
135         try (SQLiteDatabase db = mDbHelper.getReadableDatabase();
136              Cursor resultCursor = db.query(
137                      TABLE_USB_SETTINGS,
138                      null,
139                      null,
140                      null,
141                      null,
142                      null,
143                      null)) {
144             return constructSettings(resultCursor);
145         }
146     }
147 
constructSettings(Cursor cursor)148     private List<UsbDeviceSettings> constructSettings(Cursor cursor) {
149         if (!cursor.isBeforeFirst()) {
150             throw new RuntimeException("Cursor is not reset to before first element");
151         }
152         int serialNumberColumnId = cursor.getColumnIndex(COLUMN_SERIAL);
153         int vidColumnId = cursor.getColumnIndex(COLUMN_VID);
154         int pidColumnId = cursor.getColumnIndex(COLUMN_PID);
155         int deviceNameColumnId = cursor.getColumnIndex(COLUMN_NAME);
156         int handlerColumnId = cursor.getColumnIndex(COLUMN_HANDLER);
157         int aoapColumnId = cursor.getColumnIndex(COLUMN_AOAP);
158         List<UsbDeviceSettings> results = new ArrayList<>(cursor.getCount());
159         while (cursor.moveToNext()) {
160             results.add(UsbDeviceSettings.constructSettings(
161                                 cursor.getString(serialNumberColumnId),
162                                 cursor.getInt(vidColumnId),
163                                 cursor.getInt(pidColumnId),
164                                 cursor.getString(deviceNameColumnId),
165                                 ComponentName.unflattenFromString(
166                                         cursor.getString(handlerColumnId)),
167                                 cursor.getInt(aoapColumnId) != 0));
168         }
169         return results;
170     }
171 
172     /**
173      * Converts {@code UsbDeviceSettings} to {@code ContentValues}.
174      */
settingsToContentValues(UsbDeviceSettings settings)175     public ContentValues settingsToContentValues(UsbDeviceSettings settings) {
176         ContentValues contentValues = new ContentValues();
177         contentValues.put(COLUMN_SERIAL, settings.getSerialNumber());
178         contentValues.put(COLUMN_VID, settings.getVid());
179         contentValues.put(COLUMN_PID, settings.getPid());
180         contentValues.put(COLUMN_NAME, settings.getDeviceName());
181         contentValues.put(COLUMN_HANDLER, settings.getHandler().flattenToShortString());
182         contentValues.put(COLUMN_AOAP, settings.isAaop() ? 1 : 0);
183         contentValues.put(COLUMN_DEFAULT_HANDLER, settings.isDefaultHandler() ? 1 : 0);
184         return contentValues;
185     }
186 
187     private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
188         private static final int DATABASE_VERSION = 2;
189         private static final String DATABASE_NAME = "usb_devices.db";
190 
191         // we are using device protected storage because we may need to access the db before the
192         // user has authenticated
UsbSettingsDbHelper(Context context)193         UsbSettingsDbHelper(Context context) {
194             super(
195                     context.createDeviceProtectedStorageContext(),
196                     DATABASE_NAME,
197                     null,
198                     DATABASE_VERSION);
199         }
200 
201         @Override
onCreate(SQLiteDatabase db)202         public void onCreate(SQLiteDatabase db) {
203             createTable(db, TABLE_USB_SETTINGS);
204             createSerialIndex(db);
205         }
206 
createTable(SQLiteDatabase db, String tableName)207         private void createTable(SQLiteDatabase db, String tableName) {
208             db.execSQL("CREATE TABLE " + tableName + " ("
209                     + COLUMN_SERIAL + " TEXT,"
210                     + COLUMN_VID + " INTEGER,"
211                     + COLUMN_PID + " INTEGER,"
212                     + COLUMN_NAME + " TEXT, "
213                     + COLUMN_HANDLER + " TEXT,"
214                     + COLUMN_AOAP + " INTEGER,"
215                     + COLUMN_DEFAULT_HANDLER + " INTEGER,"
216                     + "PRIMARY KEY (" + COLUMN_SERIAL + ", " + COLUMN_VID + ", " + COLUMN_PID
217                     + "))");
218         }
219 
createSerialIndex(SQLiteDatabase db)220         private void createSerialIndex(SQLiteDatabase db) {
221             db.execSQL("CREATE INDEX " + TABLE_USB_SETTINGS + "_" + COLUMN_SERIAL + " ON "
222                     + TABLE_USB_SETTINGS + "(" + COLUMN_SERIAL + ")");
223         }
224 
225         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)226         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
227             int version = oldVersion;
228             for (; version != newVersion; version++) {
229                 switch (version) {
230                     case 1:
231                         String tempTableName = "temp_" + TABLE_USB_SETTINGS;
232                         createTable(db, tempTableName);
233                         db.execSQL("INSERT INTO " + tempTableName
234                                 + " SELECT * FROM " + TABLE_USB_SETTINGS);
235                         db.execSQL("DROP TABLE " + TABLE_USB_SETTINGS);
236                         db.execSQL("ALTER TABLE " + tempTableName + " RENAME TO "
237                                 + TABLE_USB_SETTINGS);
238                         createSerialIndex(db);
239                         break;
240                     default:
241                         throw new IllegalArgumentException(
242                                 "Unknown database version " + oldVersion);
243                 }
244             }
245         }
246     }
247 }
248