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