36 static final int DB_VERSION = 18;
37 private static final String DB_NAME =
"bookmarks.db";
41 System.loadLibrary(
"sqlcipher");
48 public static AppDatabase getInstance(Context context)
56 byte[] key = getOrCreateDbKey(context);
57 migrateUnencryptedIfNeeded(context, key);
59 instance = Room.databaseBuilder(context.getApplicationContext(),
61 .openHelperFactory(
new SupportOpenHelperFactory(key))
62 .addMigrations(MIGRATION_10_11)
63 .addMigrations(MIGRATION_11_12)
64 .addMigrations(MIGRATION_12_13)
65 .addMigrations(MIGRATION_13_14)
66 .addMigrations(MIGRATION_14_15)
67 .addMigrations(MIGRATION_15_16)
68 .addMigrations(MIGRATION_16_17)
69 .addMigrations(MIGRATION_17_18)
77 private static byte[] getOrCreateDbKey(Context context)
81 return KeystoreHelper.getInstance(context).getOrCreateDbKey();
83 catch (KeystoreHelper.KeystoreException e)
85 throw new RuntimeException(
"Cannot obtain database encryption key", e);
90 private static void migrateUnencryptedIfNeeded(Context context,
byte[] key)
92 File dbFile = context.getDatabasePath(DB_NAME);
96 String path = dbFile.getAbsolutePath();
101 SQLiteDatabase.openDatabase(path, key,
null, SQLiteDatabase.OPEN_READONLY,
null,
null)
105 catch (Exception ignored)
109 SQLiteDatabase db =
null;
113 db = SQLiteDatabase.openDatabase(path,
new byte[0],
null, SQLiteDatabase.OPEN_READWRITE,
119 SQLiteDatabase.deleteDatabase(dbFile);
125 String tmpPath = path +
".migrating";
126 File tmpFile =
new File(tmpPath);
127 SQLiteDatabase.deleteDatabase(tmpFile);
133 .openDatabase(tmpPath, key,
null,
134 SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY,
138 catch (Exception ignored)
146 db.execSQL(
"ATTACH DATABASE '" + tmpPath +
"' AS encrypted KEY X'" + toHex(key) +
148 db.rawQuery(
"SELECT sqlcipher_export('encrypted')",
null).moveToFirst();
149 db.execSQL(
"DETACH DATABASE encrypted");
157 SQLiteDatabase.deleteDatabase(dbFile);
158 if (!tmpFile.renameTo(dbFile))
159 throw new RuntimeException(
"Could not replace database file after encryption");
163 SQLiteDatabase.deleteDatabase(tmpFile);
164 throw new RuntimeException(
"Failed to encrypt existing database", e);
168 private static String toHex(
byte[] bytes)
170 StringBuilder sb =
new StringBuilder(bytes.length * 2);
172 sb.append(String.format(java.util.Locale.US,
"%02x", b));
173 return sb.toString();
176 private static final Migration MIGRATION_17_18 =
new Migration(17, 18) {
177 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
180 "ALTER TABLE 'bookmarks' ADD 'redirect_camera' INTEGER NOT NULL DEFAULT false;");
184 private static final Migration MIGRATION_16_17 =
new Migration(16, 17) {
185 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
187 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'alternate_shell' TEXT NOT NULL DEFAULT '';");
188 db.execSQL(
"UPDATE bookmarks SET alternate_shell = remote_program;");
189 db.execSQL(
"UPDATE bookmarks SET remote_program = '';");
193 private static final Migration MIGRATION_15_16 =
new Migration(15, 16) {
194 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
197 "ALTER TABLE 'bookmarks' ADD 'redirect_printer' INTEGER NOT NULL DEFAULT false;");
201 private static final Migration MIGRATION_14_15 =
new Migration(14, 15) {
202 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
204 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'scale_mode' TEXT NOT NULL DEFAULT '100';");
205 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'scale_desktop' INTEGER NOT NULL DEFAULT 100;");
206 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'scale_device' INTEGER NOT NULL DEFAULT 100;");
208 "ALTER TABLE 'bookmarks' ADD 'vmconnect_mode' INTEGER NOT NULL DEFAULT false;");
209 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'vmconnect_guid' TEXT NOT NULL DEFAULT '';");
213 private static final Migration MIGRATION_13_14 =
new Migration(13, 14) {
214 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
216 final String[] columns =
new String[] {
217 "label",
"hostname",
"username",
"password",
218 "domain",
"gateway_hostname",
"gateway_username",
"gateway_password",
219 "gateway_domain",
"remote_program",
"work_dir",
"loadbalanceinfo"
221 for (String column : columns)
223 db.execSQL(
"ALTER TABLE 'bookmarks' ADD '" + column +
224 "_with_default' TEXT NOT NULL DEFAULT '';");
225 db.execSQL(
"UPDATE bookmarks SET " + column +
"_with_default = " + column);
226 db.execSQL(
"ALTER TABLE 'bookmarks' DROP '" + column +
"';");
227 db.execSQL(
"ALTER TABLE 'bookmarks' RENAME COLUMN '" + column +
228 "_with_default' to '" + column +
"';");
231 final String[] debugColumns =
new String[] {
"debug_level" };
232 for (String column : debugColumns)
234 db.execSQL(
"ALTER TABLE 'bookmarks' ADD '" + column +
235 "_with_default' TEXT NOT NULL DEFAULT 'INFO';");
236 db.execSQL(
"UPDATE bookmarks SET " + column +
"_with_default = " + column);
237 db.execSQL(
"ALTER TABLE 'bookmarks' DROP '" + column +
"';");
238 db.execSQL(
"ALTER TABLE 'bookmarks' RENAME COLUMN '" + column +
239 "_with_default' to '" + column +
"';");
241 final Map<String, Integer> intColumns =
new HashMap<>();
242 intColumns.put(
"port", 3389);
243 intColumns.put(
"colors", 32);
244 intColumns.put(
"resolution", -1);
245 intColumns.put(
"width", 0);
246 intColumns.put(
"height", 0);
247 intColumns.put(
"gateway_port", 443);
248 intColumns.put(
"redirect_sound", 0);
249 intColumns.put(
"security", 0);
250 intColumns.put(
"tlsSecLevel", -1);
251 intColumns.put(
"tlsMinLevel", -1);
253 for (Map.Entry<String, Integer> column : intColumns.entrySet())
255 db.execSQL(
"ALTER TABLE 'bookmarks' ADD '" + column.getKey() +
256 "_with_default' INTEGER NOT NULL DEFAULT " +
257 column.getValue().toString() +
";");
258 db.execSQL(
"UPDATE bookmarks SET " + column.getKey() +
259 "_with_default = " + column.getKey());
260 db.execSQL(
"ALTER TABLE 'bookmarks' DROP '" + column.getKey() +
"';");
261 db.execSQL(
"ALTER TABLE 'bookmarks' RENAME COLUMN '" + column.getKey() +
262 "_with_default' to '" + column.getKey() +
"';");
265 final Map<String, Boolean> boolColumns =
new HashMap<>();
266 boolColumns.put(
"perf_remotefx",
true);
267 boolColumns.put(
"perf_gfx",
true);
268 boolColumns.put(
"perf_gfx_h264",
true);
269 boolColumns.put(
"perf_wallpaper",
true);
270 boolColumns.put(
"perf_theming",
true);
271 boolColumns.put(
"perf_full_window_drag",
true);
272 boolColumns.put(
"perf_menu_animations",
true);
273 boolColumns.put(
"perf_font_smoothing",
true);
274 boolColumns.put(
"perf_desktop_composition",
true);
275 boolColumns.put(
"enable_gateway_settings",
false);
276 boolColumns.put(
"redirect_sdcard",
false);
277 boolColumns.put(
"redirect_microphone",
false);
278 boolColumns.put(
"console_mode",
false);
279 boolColumns.put(
"async_channel",
false);
280 boolColumns.put(
"async_update",
false);
282 for (Map.Entry<String, Boolean> column : boolColumns.entrySet())
284 db.execSQL(
"ALTER TABLE 'bookmarks' ADD '" + column.getKey() +
285 "_with_default' INTEGER NOT NULL DEFAULT " +
286 column.getValue().toString() +
";");
287 db.execSQL(
"UPDATE bookmarks SET " + column.getKey() +
288 "_with_default = " + column.getKey());
289 db.execSQL(
"ALTER TABLE 'bookmarks' DROP '" + column.getKey() +
"';");
290 db.execSQL(
"ALTER TABLE 'bookmarks' RENAME COLUMN '" + column.getKey() +
291 "_with_default' to '" + column.getKey() +
"';");
296 private static final Migration MIGRATION_12_13 =
new Migration(12, 13) {
297 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
299 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'loadbalanceinfo' TEXT NOT NULL DEFAULT '';");
303 private static final Migration MIGRATION_11_12 =
new Migration(11, 12) {
304 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
306 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'tlsSecLevel' INTEGER NOT NULL CONSTRAINT "
308 +
"CHECK (tlsSecLevel >= -1 AND tlsSecLevel <= 5) DEFAULT -1;");
309 db.execSQL(
"ALTER TABLE 'bookmarks' ADD 'tlsMinLevel' INTEGER NOT NULL CONSTRAINT "
311 +
"CHECK (tlsMinLevel >= -1) DEFAULT -1;");
312 final String[] list = {
"screen_3g_colors",
313 "screen_3g_resolution",
321 "perf_3g_full_window_drag",
322 "perf_3g_menu_animations",
323 "perf_3g_font_smoothing",
324 "perf_3g_desktop_composition",
325 "enable_3g_settings" };
327 for (String s : list)
329 db.execSQL(
"ALTER TABLE 'bookmarks' DROP COLUMN '" + s +
"';");
336 private static final Migration MIGRATION_10_11 =
new Migration(10, 11) {
337 @Override
public void migrate(@NonNull SupportSQLiteDatabase db)
339 db.execSQL(
"CREATE TABLE IF NOT EXISTS `bookmarks` ("
340 +
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
346 +
"`port` INTEGER NOT NULL,"
347 +
"`colors` INTEGER NOT NULL,"
348 +
"`resolution` INTEGER NOT NULL,"
349 +
"`width` INTEGER NOT NULL,"
350 +
"`height` INTEGER NOT NULL,"
351 +
"`perf_remotefx` INTEGER NOT NULL,"
352 +
"`perf_gfx` INTEGER NOT NULL,"
353 +
"`perf_gfx_h264` INTEGER NOT NULL,"
354 +
"`perf_wallpaper` INTEGER NOT NULL,"
355 +
"`perf_theming` INTEGER NOT NULL,"
356 +
"`perf_full_window_drag` INTEGER NOT NULL,"
357 +
"`perf_menu_animations` INTEGER NOT NULL,"
358 +
"`perf_font_smoothing` INTEGER NOT NULL,"
359 +
"`perf_desktop_composition` INTEGER NOT NULL,"
360 +
"`screen_3g_colors` INTEGER NOT NULL,"
361 +
"`screen_3g_resolution` INTEGER NOT NULL,"
362 +
"`screen_3g_width` INTEGER NOT NULL,"
363 +
"`screen_3g_height` INTEGER NOT NULL,"
364 +
"`perf_3g_remotefx` INTEGER NOT NULL,"
365 +
"`perf_3g_gfx` INTEGER NOT NULL,"
366 +
"`perf_3g_gfx_h264` INTEGER NOT NULL,"
367 +
"`perf_3g_wallpaper` INTEGER NOT NULL,"
368 +
"`perf_3g_theming` INTEGER NOT NULL,"
369 +
"`perf_3g_full_window_drag` INTEGER NOT NULL,"
370 +
"`perf_3g_menu_animations` INTEGER NOT NULL,"
371 +
"`perf_3g_font_smoothing` INTEGER NOT NULL,"
372 +
"`perf_3g_desktop_composition` INTEGER NOT NULL,"
373 +
"`enable_3g_settings` INTEGER NOT NULL,"
374 +
"`enable_gateway_settings` INTEGER NOT NULL,"
375 +
"`gateway_hostname` TEXT,"
376 +
"`gateway_port` INTEGER NOT NULL,"
377 +
"`gateway_username` TEXT,"
378 +
"`gateway_password` TEXT,"
379 +
"`gateway_domain` TEXT,"
380 +
"`redirect_sdcard` INTEGER NOT NULL,"
381 +
"`redirect_sound` INTEGER NOT NULL,"
382 +
"`redirect_microphone` INTEGER NOT NULL,"
383 +
"`security` INTEGER NOT NULL,"
384 +
"`remote_program` TEXT,"
386 +
"`console_mode` INTEGER NOT NULL,"
387 +
"`debug_level` TEXT,"
388 +
"`async_channel` INTEGER NOT NULL,"
389 +
"`async_update` INTEGER NOT NULL"
394 "INSERT INTO bookmarks ("
395 +
" id, label, hostname, username, password, domain, port,"
396 +
" colors, resolution, width, height,"
397 +
" perf_remotefx, perf_gfx, perf_gfx_h264, perf_wallpaper, perf_theming,"
398 +
" perf_full_window_drag, perf_menu_animations, perf_font_smoothing, "
399 +
"perf_desktop_composition,"
400 +
" screen_3g_colors, screen_3g_resolution, screen_3g_width, screen_3g_height,"
401 +
" perf_3g_remotefx, perf_3g_gfx, perf_3g_gfx_h264, perf_3g_wallpaper, "
403 +
" perf_3g_full_window_drag, perf_3g_menu_animations, perf_3g_font_smoothing, "
404 +
"perf_3g_desktop_composition,"
405 +
" enable_3g_settings, enable_gateway_settings,"
406 +
" gateway_hostname, gateway_port, gateway_username, gateway_password, "
408 +
" redirect_sdcard, redirect_sound, redirect_microphone,"
409 +
" security, remote_program, work_dir, console_mode,"
410 +
" debug_level, async_channel, async_update"
412 +
" b._id, IFNULL(b.label, ''), IFNULL(b.hostname, ''), IFNULL(b.username, ''), "
413 +
"b.password, b.domain,"
414 +
" IFNULL(CAST(NULLIF(b.port, '') AS INTEGER), 3389),"
415 +
" IFNULL(s.colors, 32), IFNULL(s.resolution, -1), IFNULL(s.width, 0), "
416 +
"IFNULL(s.height, 0),"
417 +
" IFNULL(p.perf_remotefx, 0), IFNULL(p.perf_gfx, 1), IFNULL(p.perf_gfx_h264, "
418 +
"0), IFNULL(p.perf_wallpaper, 0), IFNULL(p.perf_theming, 0),"
419 +
" IFNULL(p.perf_full_window_drag, 0), IFNULL(p.perf_menu_animations, 0), "
420 +
"IFNULL(p.perf_font_smoothing, 0), IFNULL(p.perf_desktop_composition, 0),"
421 +
" IFNULL(s3.colors, 16), IFNULL(s3.resolution, -1), IFNULL(s3.width, 0), "
422 +
"IFNULL(s3.height, 0),"
423 +
" IFNULL(p3.perf_remotefx, 0), IFNULL(p3.perf_gfx, 0), IFNULL(p3.perf_gfx_h264, "
424 +
"0), IFNULL(p3.perf_wallpaper, 0), IFNULL(p3.perf_theming, 0),"
425 +
" IFNULL(p3.perf_full_window_drag, 0), IFNULL(p3.perf_menu_animations, 0), "
426 +
"IFNULL(p3.perf_font_smoothing, 0), IFNULL(p3.perf_desktop_composition, 0),"
427 +
" IFNULL(b.enable_3g_settings, 0), IFNULL(b.enable_gateway_settings, 0),"
428 +
" b.gateway_hostname, IFNULL(b.gateway_port, 443), b.gateway_username, "
429 +
"b.gateway_password, b.gateway_domain,"
430 +
" IFNULL(b.redirect_sdcard, 0), IFNULL(b.redirect_sound, 0), "
431 +
"IFNULL(b.redirect_microphone, 0),"
433 " IFNULL(b.security, 0), b.remote_program, b.work_dir, IFNULL(b.console_mode, 0),"
434 +
" IFNULL(b.debug_level, 'INFO'), IFNULL(b.async_channel, 0), "
435 +
"IFNULL(b.async_update, 0)"
436 +
" FROM tbl_manual_bookmarks b"
437 +
" LEFT JOIN tbl_screen_settings s ON s._id = b.screen_settings"
438 +
" LEFT JOIN tbl_screen_settings s3 ON s3._id = b.screen_3g"
439 +
" LEFT JOIN tbl_performance_flags p ON p._id = b.performance_flags"
440 +
" LEFT JOIN tbl_performance_flags p3 ON p3._id = b.performance_3g");
442 db.execSQL(
"DROP TABLE IF EXISTS tbl_manual_bookmarks");
443 db.execSQL(
"DROP TABLE IF EXISTS tbl_screen_settings");
444 db.execSQL(
"DROP TABLE IF EXISTS tbl_performance_flags");