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