• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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