1 /* 2 * Copyright 2019 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 17 package com.android.bluetooth.btservice.storage; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.database.SQLException; 22 23 import androidx.room.Database; 24 import androidx.room.Room; 25 import androidx.room.RoomDatabase; 26 import androidx.room.migration.Migration; 27 import androidx.sqlite.db.SupportSQLiteDatabase; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.util.List; 32 33 /** 34 * MetadataDatabase is a Room database stores Bluetooth persistence data 35 */ 36 @Database(entities = {Metadata.class}, version = 117) 37 public abstract class MetadataDatabase extends RoomDatabase { 38 /** 39 * The metadata database file name 40 */ 41 public static final String DATABASE_NAME = "bluetooth_db"; 42 43 static int sCurrentConnectionNumber = 0; 44 mMetadataDao()45 protected abstract MetadataDao mMetadataDao(); 46 47 /** 48 * Create a {@link MetadataDatabase} database with migrations 49 * 50 * @param context the Context to create database 51 * @return the created {@link MetadataDatabase} 52 */ createDatabase(Context context)53 public static MetadataDatabase createDatabase(Context context) { 54 return Room.databaseBuilder(context, 55 MetadataDatabase.class, DATABASE_NAME) 56 .addMigrations(MIGRATION_100_101) 57 .addMigrations(MIGRATION_101_102) 58 .addMigrations(MIGRATION_102_103) 59 .addMigrations(MIGRATION_103_104) 60 .addMigrations(MIGRATION_104_105) 61 .addMigrations(MIGRATION_105_106) 62 .addMigrations(MIGRATION_106_107) 63 .addMigrations(MIGRATION_107_108) 64 .addMigrations(MIGRATION_108_109) 65 .addMigrations(MIGRATION_109_110) 66 .addMigrations(MIGRATION_110_111) 67 .addMigrations(MIGRATION_111_112) 68 .addMigrations(MIGRATION_112_113) 69 .addMigrations(MIGRATION_113_114) 70 .addMigrations(MIGRATION_114_115) 71 .addMigrations(MIGRATION_115_116) 72 .addMigrations(MIGRATION_116_117) 73 .allowMainThreadQueries() 74 .build(); 75 } 76 77 /** 78 * Create a {@link MetadataDatabase} database without migration, database 79 * would be reset if any load failure happens 80 * 81 * @param context the Context to create database 82 * @return the created {@link MetadataDatabase} 83 */ createDatabaseWithoutMigration(Context context)84 public static MetadataDatabase createDatabaseWithoutMigration(Context context) { 85 return Room.databaseBuilder(context, 86 MetadataDatabase.class, DATABASE_NAME) 87 .fallbackToDestructiveMigration() 88 .allowMainThreadQueries() 89 .build(); 90 } 91 92 /** 93 * Insert a {@link Metadata} to metadata table 94 * 95 * @param metadata the data wish to put into storage 96 */ insert(Metadata... metadata)97 public void insert(Metadata... metadata) { 98 mMetadataDao().insert(metadata); 99 } 100 101 /** 102 * Load all data from metadata table as a {@link List} of {@link Metadata} 103 * 104 * @return a {@link List} of {@link Metadata} 105 */ load()106 public List<Metadata> load() { 107 return mMetadataDao().load(); 108 } 109 110 /** 111 * Delete one of the {@link Metadata} contained in the metadata table 112 * 113 * @param address the address of Metadata to delete 114 */ delete(String address)115 public void delete(String address) { 116 mMetadataDao().delete(address); 117 } 118 119 /** 120 * Clear metadata table. 121 */ deleteAll()122 public void deleteAll() { 123 mMetadataDao().deleteAll(); 124 } 125 126 @VisibleForTesting 127 static final Migration MIGRATION_100_101 = new Migration(100, 101) { 128 @Override 129 public void migrate(SupportSQLiteDatabase database) { 130 database.execSQL("ALTER TABLE metadata ADD COLUMN `pbap_client_priority` INTEGER"); 131 } 132 }; 133 134 @VisibleForTesting 135 static final Migration MIGRATION_101_102 = new Migration(101, 102) { 136 @Override 137 public void migrate(SupportSQLiteDatabase database) { 138 database.execSQL("CREATE TABLE IF NOT EXISTS `metadata_tmp` (" 139 + "`address` TEXT NOT NULL, `migrated` INTEGER NOT NULL, " 140 + "`a2dpSupportsOptionalCodecs` INTEGER NOT NULL, " 141 + "`a2dpOptionalCodecsEnabled` INTEGER NOT NULL, " 142 + "`a2dp_priority` INTEGER, `a2dp_sink_priority` INTEGER, " 143 + "`hfp_priority` INTEGER, `hfp_client_priority` INTEGER, " 144 + "`hid_host_priority` INTEGER, `pan_priority` INTEGER, " 145 + "`pbap_priority` INTEGER, `pbap_client_priority` INTEGER, " 146 + "`map_priority` INTEGER, `sap_priority` INTEGER, " 147 + "`hearing_aid_priority` INTEGER, `map_client_priority` INTEGER, " 148 + "`manufacturer_name` BLOB, `model_name` BLOB, `software_version` BLOB, " 149 + "`hardware_version` BLOB, `companion_app` BLOB, `main_icon` BLOB, " 150 + "`is_untethered_headset` BLOB, `untethered_left_icon` BLOB, " 151 + "`untethered_right_icon` BLOB, `untethered_case_icon` BLOB, " 152 + "`untethered_left_battery` BLOB, `untethered_right_battery` BLOB, " 153 + "`untethered_case_battery` BLOB, `untethered_left_charging` BLOB, " 154 + "`untethered_right_charging` BLOB, `untethered_case_charging` BLOB, " 155 + "`enhanced_settings_ui_uri` BLOB, PRIMARY KEY(`address`))"); 156 157 database.execSQL("INSERT INTO metadata_tmp (" 158 + "address, migrated, a2dpSupportsOptionalCodecs, a2dpOptionalCodecsEnabled, " 159 + "a2dp_priority, a2dp_sink_priority, hfp_priority, hfp_client_priority, " 160 + "hid_host_priority, pan_priority, pbap_priority, pbap_client_priority, " 161 + "map_priority, sap_priority, hearing_aid_priority, map_client_priority, " 162 + "manufacturer_name, model_name, software_version, hardware_version, " 163 + "companion_app, main_icon, is_untethered_headset, untethered_left_icon, " 164 + "untethered_right_icon, untethered_case_icon, untethered_left_battery, " 165 + "untethered_right_battery, untethered_case_battery, " 166 + "untethered_left_charging, untethered_right_charging, " 167 + "untethered_case_charging, enhanced_settings_ui_uri) " 168 + "SELECT " 169 + "address, migrated, a2dpSupportsOptionalCodecs, a2dpOptionalCodecsEnabled, " 170 + "a2dp_priority, a2dp_sink_priority, hfp_priority, hfp_client_priority, " 171 + "hid_host_priority, pan_priority, pbap_priority, pbap_client_priority, " 172 + "map_priority, sap_priority, hearing_aid_priority, map_client_priority, " 173 + "CAST (manufacturer_name AS BLOB), " 174 + "CAST (model_name AS BLOB), " 175 + "CAST (software_version AS BLOB), " 176 + "CAST (hardware_version AS BLOB), " 177 + "CAST (companion_app AS BLOB), " 178 + "CAST (main_icon AS BLOB), " 179 + "CAST (is_unthethered_headset AS BLOB), " 180 + "CAST (unthethered_left_icon AS BLOB), " 181 + "CAST (unthethered_right_icon AS BLOB), " 182 + "CAST (unthethered_case_icon AS BLOB), " 183 + "CAST (unthethered_left_battery AS BLOB), " 184 + "CAST (unthethered_right_battery AS BLOB), " 185 + "CAST (unthethered_case_battery AS BLOB), " 186 + "CAST (unthethered_left_charging AS BLOB), " 187 + "CAST (unthethered_right_charging AS BLOB), " 188 + "CAST (unthethered_case_charging AS BLOB), " 189 + "CAST (enhanced_settings_ui_uri AS BLOB)" 190 + "FROM metadata"); 191 192 database.execSQL("DROP TABLE `metadata`"); 193 database.execSQL("ALTER TABLE `metadata_tmp` RENAME TO `metadata`"); 194 } 195 }; 196 197 @VisibleForTesting 198 static final Migration MIGRATION_102_103 = new Migration(102, 103) { 199 @Override 200 public void migrate(SupportSQLiteDatabase database) { 201 try { 202 database.execSQL("CREATE TABLE IF NOT EXISTS `metadata_tmp` (" 203 + "`address` TEXT NOT NULL, `migrated` INTEGER NOT NULL, " 204 + "`a2dpSupportsOptionalCodecs` INTEGER NOT NULL, " 205 + "`a2dpOptionalCodecsEnabled` INTEGER NOT NULL, " 206 + "`a2dp_connection_policy` INTEGER, " 207 + "`a2dp_sink_connection_policy` INTEGER, `hfp_connection_policy` INTEGER, " 208 + "`hfp_client_connection_policy` INTEGER, " 209 + "`hid_host_connection_policy` INTEGER, `pan_connection_policy` INTEGER, " 210 + "`pbap_connection_policy` INTEGER, " 211 + "`pbap_client_connection_policy` INTEGER, " 212 + "`map_connection_policy` INTEGER, `sap_connection_policy` INTEGER, " 213 + "`hearing_aid_connection_policy` INTEGER, " 214 + "`map_client_connection_policy` INTEGER, `manufacturer_name` BLOB, " 215 + "`model_name` BLOB, `software_version` BLOB, `hardware_version` BLOB, " 216 + "`companion_app` BLOB, `main_icon` BLOB, `is_untethered_headset` BLOB, " 217 + "`untethered_left_icon` BLOB, `untethered_right_icon` BLOB, " 218 + "`untethered_case_icon` BLOB, `untethered_left_battery` BLOB, " 219 + "`untethered_right_battery` BLOB, `untethered_case_battery` BLOB, " 220 + "`untethered_left_charging` BLOB, `untethered_right_charging` BLOB, " 221 + "`untethered_case_charging` BLOB, `enhanced_settings_ui_uri` BLOB, " 222 + "PRIMARY KEY(`address`))"); 223 224 database.execSQL("INSERT INTO metadata_tmp (" 225 + "address, migrated, a2dpSupportsOptionalCodecs, " 226 + "a2dpOptionalCodecsEnabled, a2dp_connection_policy, " 227 + "a2dp_sink_connection_policy, hfp_connection_policy," 228 + "hfp_client_connection_policy, hid_host_connection_policy," 229 + "pan_connection_policy, pbap_connection_policy," 230 + "pbap_client_connection_policy, map_connection_policy, " 231 + "sap_connection_policy, hearing_aid_connection_policy, " 232 + "map_client_connection_policy, manufacturer_name, model_name, " 233 + "software_version, hardware_version, companion_app, main_icon, " 234 + "is_untethered_headset, untethered_left_icon, untethered_right_icon, " 235 + "untethered_case_icon, untethered_left_battery, " 236 + "untethered_right_battery, untethered_case_battery, " 237 + "untethered_left_charging, untethered_right_charging, " 238 + "untethered_case_charging, enhanced_settings_ui_uri) " 239 + "SELECT " 240 + "address, migrated, a2dpSupportsOptionalCodecs, " 241 + "a2dpOptionalCodecsEnabled, a2dp_priority, a2dp_sink_priority, " 242 + "hfp_priority, hfp_client_priority, hid_host_priority, pan_priority, " 243 + "pbap_priority, pbap_client_priority, map_priority, sap_priority, " 244 + "hearing_aid_priority, map_client_priority, " 245 + "CAST (manufacturer_name AS BLOB), " 246 + "CAST (model_name AS BLOB), " 247 + "CAST (software_version AS BLOB), " 248 + "CAST (hardware_version AS BLOB), " 249 + "CAST (companion_app AS BLOB), " 250 + "CAST (main_icon AS BLOB), " 251 + "CAST (is_untethered_headset AS BLOB), " 252 + "CAST (untethered_left_icon AS BLOB), " 253 + "CAST (untethered_right_icon AS BLOB), " 254 + "CAST (untethered_case_icon AS BLOB), " 255 + "CAST (untethered_left_battery AS BLOB), " 256 + "CAST (untethered_right_battery AS BLOB), " 257 + "CAST (untethered_case_battery AS BLOB), " 258 + "CAST (untethered_left_charging AS BLOB), " 259 + "CAST (untethered_right_charging AS BLOB), " 260 + "CAST (untethered_case_charging AS BLOB), " 261 + "CAST (enhanced_settings_ui_uri AS BLOB)" 262 + "FROM metadata"); 263 264 database.execSQL("UPDATE metadata_tmp SET a2dp_connection_policy = 100 " 265 + "WHERE a2dp_connection_policy = 1000"); 266 database.execSQL("UPDATE metadata_tmp SET a2dp_sink_connection_policy = 100 " 267 + "WHERE a2dp_sink_connection_policy = 1000"); 268 database.execSQL("UPDATE metadata_tmp SET hfp_connection_policy = 100 " 269 + "WHERE hfp_connection_policy = 1000"); 270 database.execSQL("UPDATE metadata_tmp SET hfp_client_connection_policy = 100 " 271 + "WHERE hfp_client_connection_policy = 1000"); 272 database.execSQL("UPDATE metadata_tmp SET hid_host_connection_policy = 100 " 273 + "WHERE hid_host_connection_policy = 1000"); 274 database.execSQL("UPDATE metadata_tmp SET pan_connection_policy = 100 " 275 + "WHERE pan_connection_policy = 1000"); 276 database.execSQL("UPDATE metadata_tmp SET pbap_connection_policy = 100 " 277 + "WHERE pbap_connection_policy = 1000"); 278 database.execSQL("UPDATE metadata_tmp SET pbap_client_connection_policy = 100 " 279 + "WHERE pbap_client_connection_policy = 1000"); 280 database.execSQL("UPDATE metadata_tmp SET map_connection_policy = 100 " 281 + "WHERE map_connection_policy = 1000"); 282 database.execSQL("UPDATE metadata_tmp SET sap_connection_policy = 100 " 283 + "WHERE sap_connection_policy = 1000"); 284 database.execSQL("UPDATE metadata_tmp SET hearing_aid_connection_policy = 100 " 285 + "WHERE hearing_aid_connection_policy = 1000"); 286 database.execSQL("UPDATE metadata_tmp SET map_client_connection_policy = 100 " 287 + "WHERE map_client_connection_policy = 1000"); 288 289 database.execSQL("DROP TABLE `metadata`"); 290 database.execSQL("ALTER TABLE `metadata_tmp` RENAME TO `metadata`"); 291 } catch (SQLException ex) { 292 // Check if user has new schema, but is just missing the version update 293 Cursor cursor = database.query("SELECT * FROM metadata"); 294 if (cursor == null || cursor.getColumnIndex("a2dp_connection_policy") == -1) { 295 throw ex; 296 } 297 } 298 } 299 }; 300 301 @VisibleForTesting 302 static final Migration MIGRATION_103_104 = new Migration(103, 104) { 303 @Override 304 public void migrate(SupportSQLiteDatabase database) { 305 try { 306 database.execSQL("ALTER TABLE metadata ADD COLUMN `last_active_time` " 307 + "INTEGER NOT NULL DEFAULT -1"); 308 database.execSQL("ALTER TABLE metadata ADD COLUMN `is_active_a2dp_device` " 309 + "INTEGER NOT NULL DEFAULT 0"); 310 } catch (SQLException ex) { 311 // Check if user has new schema, but is just missing the version update 312 Cursor cursor = database.query("SELECT * FROM metadata"); 313 if (cursor == null || cursor.getColumnIndex("last_active_time") == -1) { 314 throw ex; 315 } 316 } 317 } 318 }; 319 320 @VisibleForTesting 321 static final Migration MIGRATION_104_105 = new Migration(104, 105) { 322 @Override 323 public void migrate(SupportSQLiteDatabase database) { 324 try { 325 database.execSQL("ALTER TABLE metadata ADD COLUMN `device_type` BLOB"); 326 database.execSQL("ALTER TABLE metadata ADD COLUMN `main_battery` BLOB"); 327 database.execSQL("ALTER TABLE metadata ADD COLUMN `main_charging` BLOB"); 328 database.execSQL("ALTER TABLE metadata ADD COLUMN " 329 + "`main_low_battery_threshold` BLOB"); 330 database.execSQL("ALTER TABLE metadata ADD COLUMN " 331 + "`untethered_left_low_battery_threshold` BLOB"); 332 database.execSQL("ALTER TABLE metadata ADD COLUMN " 333 + "`untethered_right_low_battery_threshold` BLOB"); 334 database.execSQL("ALTER TABLE metadata ADD COLUMN " 335 + "`untethered_case_low_battery_threshold` BLOB"); 336 } catch (SQLException ex) { 337 // Check if user has new schema, but is just missing the version update 338 Cursor cursor = database.query("SELECT * FROM metadata"); 339 if (cursor == null || cursor.getColumnIndex("device_type") == -1) { 340 throw ex; 341 } 342 } 343 } 344 }; 345 346 @VisibleForTesting 347 static final Migration MIGRATION_105_106 = new Migration(105, 106) { 348 @Override 349 public void migrate(SupportSQLiteDatabase database) { 350 try { 351 database.execSQL("ALTER TABLE metadata ADD COLUMN `le_audio_connection_policy` " 352 + "INTEGER DEFAULT 100"); 353 } catch (SQLException ex) { 354 // Check if user has new schema, but is just missing the version update 355 Cursor cursor = database.query("SELECT * FROM metadata"); 356 if (cursor == null || cursor.getColumnIndex("le_audio_connection_policy") == -1) { 357 throw ex; 358 } 359 } 360 } 361 }; 362 363 364 @VisibleForTesting 365 static final Migration MIGRATION_106_107 = new Migration(106, 107) { 366 @Override 367 public void migrate(SupportSQLiteDatabase database) { 368 try { 369 database.execSQL("ALTER TABLE metadata ADD COLUMN " 370 + "`volume_control_connection_policy` INTEGER DEFAULT 100"); 371 } catch (SQLException ex) { 372 // Check if user has new schema, but is just missing the version update 373 Cursor cursor = database.query("SELECT * FROM metadata"); 374 if (cursor == null 375 || cursor.getColumnIndex("volume_control_connection_policy") == -1) { 376 throw ex; 377 } 378 } 379 } 380 }; 381 382 @VisibleForTesting 383 static final Migration MIGRATION_107_108 = new Migration(107, 108) { 384 @Override 385 public void migrate(SupportSQLiteDatabase database) { 386 try { 387 database.execSQL( 388 "ALTER TABLE metadata ADD COLUMN `csip_set_coordinator_connection_policy` " 389 + "INTEGER DEFAULT 100"); 390 } catch (SQLException ex) { 391 // Check if user has new schema, but is just missing the version update 392 Cursor cursor = database.query("SELECT * FROM metadata"); 393 if (cursor == null 394 || cursor.getColumnIndex("csip_set_coordinator_connection_policy") == -1) { 395 throw ex; 396 } 397 } 398 } 399 }; 400 401 @VisibleForTesting 402 static final Migration MIGRATION_108_109 = new Migration(108, 109) { 403 @Override 404 public void migrate(SupportSQLiteDatabase database) { 405 try { 406 database.execSQL( 407 "ALTER TABLE metadata ADD COLUMN `le_call_control_connection_policy` " 408 + "INTEGER DEFAULT 100"); 409 } catch (SQLException ex) { 410 // Check if user has new schema, but is just missing the version update 411 Cursor cursor = database.query("SELECT * FROM metadata"); 412 if (cursor == null || cursor.getColumnIndex("le_call_control_connection_policy") == -1) { 413 throw ex; 414 } 415 } 416 } 417 }; 418 419 @VisibleForTesting 420 static final Migration MIGRATION_109_110 = new Migration(109, 110) { 421 @Override 422 public void migrate(SupportSQLiteDatabase database) { 423 try { 424 database.execSQL( 425 "ALTER TABLE metadata ADD COLUMN `hap_client_connection_policy` " 426 + "INTEGER DEFAULT 100"); 427 } catch (SQLException ex) { 428 // Check if user has new schema, but is just missing the version update 429 Cursor cursor = database.query("SELECT * FROM metadata"); 430 if (cursor == null || cursor.getColumnIndex("hap_client_connection_policy") == -1) { 431 throw ex; 432 } 433 } 434 } 435 }; 436 437 @VisibleForTesting 438 static final Migration MIGRATION_110_111 = new Migration(110, 111) { 439 @Override 440 public void migrate(SupportSQLiteDatabase database) { 441 try { 442 database.execSQL( 443 "ALTER TABLE metadata ADD COLUMN `bass_client_connection_policy` " 444 + "INTEGER DEFAULT 100"); 445 } catch (SQLException ex) { 446 // Check if user has new schema, but is just missing the version update 447 Cursor cursor = database.query("SELECT * FROM metadata"); 448 if (cursor == null 449 || cursor.getColumnIndex("bass_client_connection_policy") == -1) { 450 throw ex; 451 } 452 } 453 } 454 }; 455 456 @VisibleForTesting 457 static final Migration MIGRATION_111_112 = new Migration(111, 112) { 458 @Override 459 public void migrate(SupportSQLiteDatabase database) { 460 try { 461 database.execSQL( 462 "ALTER TABLE metadata ADD COLUMN `battery_connection_policy` " 463 + "INTEGER DEFAULT 100"); 464 } catch (SQLException ex) { 465 // Check if user has new schema, but is just missing the version update 466 Cursor cursor = database.query("SELECT * FROM metadata"); 467 if (cursor == null || cursor.getColumnIndex("battery_connection_policy") == -1) { 468 throw ex; 469 } 470 } 471 } 472 }; 473 474 @VisibleForTesting 475 static final Migration MIGRATION_112_113 = new Migration(112, 113) { 476 @Override 477 public void migrate(SupportSQLiteDatabase database) { 478 try { 479 database.execSQL("ALTER TABLE metadata ADD COLUMN `spatial_audio` BLOB"); 480 database.execSQL("ALTER TABLE metadata ADD COLUMN `fastpair_customized` BLOB"); 481 } catch (SQLException ex) { 482 // Check if user has new schema, but is just missing the version update 483 Cursor cursor = database.query("SELECT * FROM metadata"); 484 if (cursor == null || cursor.getColumnIndex("spatial_audio") == -1) { 485 throw ex; 486 } 487 } 488 } 489 }; 490 491 @VisibleForTesting 492 static final Migration MIGRATION_113_114 = new Migration(113, 114) { 493 @Override 494 public void migrate(SupportSQLiteDatabase database) { 495 try { 496 database.execSQL("ALTER TABLE metadata ADD COLUMN `le_audio` BLOB"); 497 } catch (SQLException ex) { 498 // Check if user has new schema, but is just missing the version update 499 Cursor cursor = database.query("SELECT * FROM metadata"); 500 if (cursor == null || cursor.getColumnIndex("le_audio") == -1) { 501 throw ex; 502 } 503 } 504 } 505 }; 506 507 @VisibleForTesting 508 static final Migration MIGRATION_114_115 = new Migration(114, 115) { 509 @Override 510 public void migrate(SupportSQLiteDatabase database) { 511 try { 512 database.execSQL( 513 "ALTER TABLE metadata ADD COLUMN `call_establish_audio_policy` " 514 + "INTEGER DEFAULT 0"); 515 database.execSQL( 516 "ALTER TABLE metadata ADD COLUMN `connecting_time_audio_policy` " 517 + "INTEGER DEFAULT 0"); 518 database.execSQL( 519 "ALTER TABLE metadata ADD COLUMN `in_band_ringtone_audio_policy` " 520 + "INTEGER DEFAULT 0"); 521 } catch (SQLException ex) { 522 // Check if user has new schema, but is just missing the version update 523 Cursor cursor = database.query("SELECT * FROM metadata"); 524 if (cursor == null 525 || cursor.getColumnIndex("call_establish_audio_policy") == -1) { 526 throw ex; 527 } 528 } 529 } 530 }; 531 532 @VisibleForTesting 533 static final Migration MIGRATION_115_116 = new Migration(115, 116) { 534 @Override 535 public void migrate(SupportSQLiteDatabase database) { 536 try { 537 database.execSQL("ALTER TABLE metadata ADD COLUMN `preferred_output_only_profile` " 538 + "INTEGER NOT NULL DEFAULT 0"); 539 database.execSQL("ALTER TABLE metadata ADD COLUMN `preferred_duplex_profile` " 540 + "INTEGER NOT NULL DEFAULT 0"); 541 } catch (SQLException ex) { 542 // Check if user has new schema, but is just missing the version update 543 Cursor cursor = database.query("SELECT * FROM metadata"); 544 if (cursor == null 545 || cursor.getColumnIndex("preferred_output_only_profile") == -1 546 || cursor.getColumnIndex("preferred_duplex_profile") == -1) { 547 throw ex; 548 } 549 } 550 } 551 }; 552 553 @VisibleForTesting 554 static final Migration MIGRATION_116_117 = new Migration(116, 117) { 555 @Override 556 public void migrate(SupportSQLiteDatabase database) { 557 try { 558 database.execSQL("ALTER TABLE metadata ADD COLUMN `gmcs_cccd` BLOB"); 559 database.execSQL("ALTER TABLE metadata ADD COLUMN `gtbs_cccd` BLOB"); 560 } catch (SQLException ex) { 561 // Check if user has new schema, but is just missing the version update 562 Cursor cursor = database.query("SELECT * FROM metadata"); 563 if (cursor == null || cursor.getColumnIndex("gmcs_cccd") == -1) { 564 throw ex; 565 } 566 } 567 } 568 }; 569 } 570