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 21 import androidx.room.Database; 22 import androidx.room.Room; 23 import androidx.room.RoomDatabase; 24 import androidx.room.migration.Migration; 25 import androidx.sqlite.db.SupportSQLiteDatabase; 26 27 import java.util.List; 28 29 /** 30 * MetadataDatabase is a Room database stores Bluetooth persistence data 31 */ 32 @Database(entities = {Metadata.class}, exportSchema = false, version = 102) 33 public abstract class MetadataDatabase extends RoomDatabase { 34 /** 35 * The database file name 36 */ 37 public static final String DATABASE_NAME = "bluetooth_db"; 38 mMetadataDao()39 protected abstract MetadataDao mMetadataDao(); 40 41 /** 42 * Create a {@link MetadataDatabase} database with migrations 43 * 44 * @param context the Context to create database 45 * @return the created {@link MetadataDatabase} 46 */ createDatabase(Context context)47 public static MetadataDatabase createDatabase(Context context) { 48 return Room.databaseBuilder(context, 49 MetadataDatabase.class, DATABASE_NAME) 50 .addMigrations(MIGRATION_100_101) 51 .addMigrations(MIGRATION_101_102) 52 .build(); 53 } 54 55 /** 56 * Create a {@link MetadataDatabase} database without migration, database 57 * would be reset if any load failure happens 58 * 59 * @param context the Context to create database 60 * @return the created {@link MetadataDatabase} 61 */ createDatabaseWithoutMigration(Context context)62 public static MetadataDatabase createDatabaseWithoutMigration(Context context) { 63 return Room.databaseBuilder(context, 64 MetadataDatabase.class, DATABASE_NAME) 65 .fallbackToDestructiveMigration() 66 .build(); 67 } 68 69 /** 70 * Insert a {@link Metadata} to database 71 * 72 * @param metadata the data wish to put into storage 73 */ insert(Metadata... metadata)74 public void insert(Metadata... metadata) { 75 mMetadataDao().insert(metadata); 76 } 77 78 /** 79 * Load all data from database as a {@link List} of {@link Metadata} 80 * 81 * @return a {@link List} of {@link Metadata} 82 */ load()83 public List<Metadata> load() { 84 return mMetadataDao().load(); 85 } 86 87 /** 88 * Delete one of the {@link Metadata} contains in database 89 * 90 * @param address the address of Metadata to delete 91 */ delete(String address)92 public void delete(String address) { 93 mMetadataDao().delete(address); 94 } 95 96 /** 97 * Clear database. 98 */ deleteAll()99 public void deleteAll() { 100 mMetadataDao().deleteAll(); 101 } 102 103 private static final Migration MIGRATION_100_101 = new Migration(100, 101) { 104 @Override 105 public void migrate(SupportSQLiteDatabase database) { 106 database.execSQL("ALTER TABLE metadata ADD COLUMN `pbap_client_priority` INTEGER"); 107 } 108 }; 109 110 private static final Migration MIGRATION_101_102 = new Migration(101, 102) { 111 @Override 112 public void migrate(SupportSQLiteDatabase database) { 113 database.execSQL("CREATE TABLE IF NOT EXISTS `metadata_tmp` (" 114 + "`address` TEXT NOT NULL, `migrated` INTEGER NOT NULL, " 115 + "`a2dpSupportsOptionalCodecs` INTEGER NOT NULL, " 116 + "`a2dpOptionalCodecsEnabled` INTEGER NOT NULL, " 117 + "`a2dp_priority` INTEGER, `a2dp_sink_priority` INTEGER, " 118 + "`hfp_priority` INTEGER, `hfp_client_priority` INTEGER, " 119 + "`hid_host_priority` INTEGER, `pan_priority` INTEGER, " 120 + "`pbap_priority` INTEGER, `pbap_client_priority` INTEGER, " 121 + "`map_priority` INTEGER, `sap_priority` INTEGER, " 122 + "`hearing_aid_priority` INTEGER, `map_client_priority` INTEGER, " 123 + "`manufacturer_name` BLOB, `model_name` BLOB, `software_version` BLOB, " 124 + "`hardware_version` BLOB, `companion_app` BLOB, `main_icon` BLOB, " 125 + "`is_untethered_headset` BLOB, `untethered_left_icon` BLOB, " 126 + "`untethered_right_icon` BLOB, `untethered_case_icon` BLOB, " 127 + "`untethered_left_battery` BLOB, `untethered_right_battery` BLOB, " 128 + "`untethered_case_battery` BLOB, `untethered_left_charging` BLOB, " 129 + "`untethered_right_charging` BLOB, `untethered_case_charging` BLOB, " 130 + "`enhanced_settings_ui_uri` BLOB, PRIMARY KEY(`address`))"); 131 132 database.execSQL("INSERT INTO metadata_tmp (" 133 + "address, migrated, a2dpSupportsOptionalCodecs, a2dpOptionalCodecsEnabled, " 134 + "a2dp_priority, a2dp_sink_priority, hfp_priority, hfp_client_priority, " 135 + "hid_host_priority, pan_priority, pbap_priority, pbap_client_priority, " 136 + "map_priority, sap_priority, hearing_aid_priority, map_client_priority, " 137 + "manufacturer_name, model_name, software_version, hardware_version, " 138 + "companion_app, main_icon, is_untethered_headset, untethered_left_icon, " 139 + "untethered_right_icon, untethered_case_icon, untethered_left_battery, " 140 + "untethered_right_battery, untethered_case_battery, " 141 + "untethered_left_charging, untethered_right_charging, " 142 + "untethered_case_charging, enhanced_settings_ui_uri) " 143 + "SELECT " 144 + "address, migrated, a2dpSupportsOptionalCodecs, a2dpOptionalCodecsEnabled, " 145 + "a2dp_priority, a2dp_sink_priority, hfp_priority, hfp_client_priority, " 146 + "hid_host_priority, pan_priority, pbap_priority, pbap_client_priority, " 147 + "map_priority, sap_priority, hearing_aid_priority, map_client_priority, " 148 + "CAST (manufacturer_name AS BLOB), " 149 + "CAST (model_name AS BLOB), " 150 + "CAST (software_version AS BLOB), " 151 + "CAST (hardware_version AS BLOB), " 152 + "CAST (companion_app AS BLOB), " 153 + "CAST (main_icon AS BLOB), " 154 + "CAST (is_unthethered_headset AS BLOB), " 155 + "CAST (unthethered_left_icon AS BLOB), " 156 + "CAST (unthethered_right_icon AS BLOB), " 157 + "CAST (unthethered_case_icon AS BLOB), " 158 + "CAST (unthethered_left_battery AS BLOB), " 159 + "CAST (unthethered_right_battery AS BLOB), " 160 + "CAST (unthethered_case_battery AS BLOB), " 161 + "CAST (unthethered_left_charging AS BLOB), " 162 + "CAST (unthethered_right_charging AS BLOB), " 163 + "CAST (unthethered_case_charging AS BLOB), " 164 + "CAST (enhanced_settings_ui_uri AS BLOB)" 165 + "FROM metadata"); 166 167 database.execSQL("DROP TABLE `metadata`"); 168 database.execSQL("ALTER TABLE `metadata_tmp` RENAME TO `metadata`"); 169 } 170 }; 171 } 172