1/** 2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import common from '@ohos.app.ability.common'; 17import dataStorage from '@ohos.data.preferences'; 18import deviceInfo from '@ohos.deviceInfo'; 19import relationalStore from '@ohos.data.relationalStore'; 20import fs from '@ohos.file.fs'; 21import i18n from '@ohos.i18n'; 22import settings from '@ohos.settings'; 23import systemParameter from '@ohos.systemparameter'; 24import SettingsDataConfig from './SettingsDataConfig'; 25import { Log } from '../Utils/Log'; 26import { GlobalContext } from './GlobalContext'; 27import contextConstant from '@ohos.app.ability.contextConstant'; 28import { TableType } from '../common/Common'; 29 30const DEFAULT_JSON_FILE_NAME : string = 'default_settings.json'; 31const SETTINGSDATA_PREFERENCE : string = 'SettingsDataPreference'; 32const SETTINGSDATA_PREFERENCE_USER : string = 'SettingsDataPreferenceUser'; 33const EL2_DB_PATH: string = '/data/storage/el2/database/entry/rdb/settingsdata.db' 34const EMULATOR_TYPE: string = 'emulator' 35interface TIME_FORMAT_DATA { 36 TIME_FORMAT_24: string ; 37 TIME_FORMAT_12: string ; 38} 39const TIME_FORMAT: TIME_FORMAT_DATA = { 40 TIME_FORMAT_24: '24', 41 TIME_FORMAT_12: '12', 42} 43interface IContent { 44 settings: Array<Map<string,string>> ; 45 user: Array<Map<string,string>> ; 46 userSecure: Array<Map<string,string>> ; 47} 48 49const INITIAL_KEY: string = '_CreatedTime'; 50const VALID_DB_LENGTH: number = 48; 51const SETTINGS_CLONED_STATUS: string = 'settingsClonedStatus'; 52 53class SettingsDBHelper { 54 public static readonly SHARED_TABLE_CREATE_PREFIX: string = 55 `CREATE TABLE IF NOT EXISTS ${SettingsDataConfig.TABLE_NAME}`; 56 // 需要在在表名后拼接当前的userid 57 public static readonly CURRENT_USER_TABLE_CREATE_PREFIX: string = 58 `CREATE TABLE IF NOT EXISTS ${SettingsDataConfig.USER_TABLE_NAME}_`; 59 public static readonly CURRENT_SECURE_TABLE_CREATE_PREFIX: string = 60 `CREATE TABLE IF NOT EXISTS ${SettingsDataConfig.SECURE_TABLE_NAME}_`; 61 public static readonly TABLE_CREATE_SUFFIX = ` (${SettingsDataConfig.FIELD_ID} INTEGER PRIMARY KEY AUTOINCREMENT, ` + 62 `${SettingsDataConfig.FIELD_KEYWORD} TEXT, ` + 63 `${SettingsDataConfig.FIELD_VALUE} TEXT CHECK (LENGTH(VALUE)<=1000))`; 64 65 private rdbStore?: relationalStore.RdbStore; 66 private context: Context; 67 private readonly DEFAULT_USER_ID: number = 100; 68 private area: contextConstant.AreaMode | undefined = undefined; 69 public isFirstStartup: dataStorage.ValueType = true; 70 public maxUserNO: dataStorage.ValueType = 100; 71 private faultOccured: boolean = false; 72 73 private constructor() { 74 this.rdbStore = undefined; 75 this.context = GlobalContext.getContext().getObject('abilityContext') as Context; 76 Log.info('context start'+ JSON.stringify(this.context)); 77 } 78 79 private async emulatorParamInit(): Promise<void> { 80 if (this.getProductModel() !== EMULATOR_TYPE) { 81 Log.info('currently not a emulator'); 82 return; 83 } 84 85 let tableName: string = this.getTableName(TableType.SETTINGS, this.DEFAULT_USER_ID); 86 await this.loadTableSettings('device_provisioned', '1', tableName); 87 tableName = this.getTableName(TableType.USER_SECURE, this.DEFAULT_USER_ID); 88 await this.loadTableSettings('basic_statement_agreed', '1', tableName); 89 await this.loadTableSettings('user_setup_complete', '1', tableName); 90 await this.loadTableSettings('is_ota_finished', '1', tableName); 91 } 92 93 public getProductModel(): string { 94 return deviceInfo.productModel; 95 } 96 97 public getArea() { 98 const dbFile = EL2_DB_PATH; 99 if (this.area === undefined) { 100 try { 101 let stat = fs.statSync(dbFile); 102 if (stat.size > VALID_DB_LENGTH) { 103 this.area = contextConstant.AreaMode.EL2; 104 } else { 105 this.area = contextConstant.AreaMode.EL1; 106 } 107 } catch { 108 this.area = contextConstant.AreaMode.EL1; 109 } 110 } 111 Log.info(`Area ${this.area}`); 112 return this.area; 113 } 114 115 public async initialInsert(tableName: string): Promise<void> { 116 try { 117 Log.info(`insert ${tableName} with key: ${tableName + INITIAL_KEY} `); 118 if (this.rdbStore) { 119 let ret = await this.rdbStore.insert(tableName, 120 { 'KEYWORD': tableName + INITIAL_KEY, 'VALUE': new Date().toString() }); 121 if (ret <= 0) { 122 Log.error(`insert initial key-value failed for ${tableName}`); 123 } 124 } else { 125 Log.error(`insert initial key-value failed for ${tableName}, no rdbStore`); 126 this.faultOccured = true; 127 } 128 } catch (e) { 129 Log.error(`insert initial key-value failed for ${tableName}`); 130 } 131 } 132 133 private async firstStartupConfig() : Promise<void> { 134 Log.info('firstStartupConfig start'); 135 let storage = await dataStorage.getPreferences(this.context as Context, SETTINGSDATA_PREFERENCE); 136 this.isFirstStartup = await storage.get('isFirstStartup', true); 137 storage = await dataStorage.getPreferences(this.context as Context, SETTINGSDATA_PREFERENCE_USER); 138 this.maxUserNO = await storage.get('MAXUSERNO', 100); 139 Log.info(`firstStartupConfig isFirstStartUp = ${this.isFirstStartup} max user no: ${this.maxUserNO}`); 140 // 总是创建以下三张表 if not exists 141 // 创建公共数据表 142 await this.rdbStore?.executeSql(SettingsDBHelper.SHARED_TABLE_CREATE_PREFIX + 143 SettingsDBHelper.TABLE_CREATE_SUFFIX, []); 144 // 创建默认用户数据表 145 await this.rdbStore?.executeSql(SettingsDBHelper.CURRENT_USER_TABLE_CREATE_PREFIX + 146 this.DEFAULT_USER_ID + SettingsDBHelper.TABLE_CREATE_SUFFIX, []); 147 // 创建默认用户 secure 数据表 148 await this.rdbStore?.executeSql(SettingsDBHelper.CURRENT_SECURE_TABLE_CREATE_PREFIX + 149 this.DEFAULT_USER_ID + SettingsDBHelper.TABLE_CREATE_SUFFIX, []); 150 if (this.isFirstStartup) { 151 Log.info('loadDefaultSettingsData begin'); 152 this.loadDefaultSettingsData(); 153 Log.info('loadDefaultSettingsData finish'); 154 await this.initialInsert(SettingsDataConfig.TABLE_NAME); 155 await this.initialInsert(SettingsDataConfig.USER_TABLE_NAME + '_' + this.DEFAULT_USER_ID); 156 await this.initialInsert(SettingsDataConfig.SECURE_TABLE_NAME + '_' + this.DEFAULT_USER_ID); 157 } 158 Log.info('firstStartupConfig end'); 159 return; 160 } 161 162 public async initRdbStore() { 163 Log.info('call initRdbStore start'); 164 let rdbStore = await relationalStore.getRdbStore(this.context as Context, { 165 name: SettingsDataConfig.DB_NAME, 166 securityLevel:1 167 }); 168 if(rdbStore){ 169 this.rdbStore = rdbStore; 170 } 171 await this.firstStartupConfig(); 172 Log.info('call initRdbStore end'); 173 return this.rdbStore; 174 } 175 176 public static getInstance(): SettingsDBHelper { 177 GlobalContext.getContext().getObject('settingsDBHelper') as SettingsDBHelper; 178 if(!GlobalContext.getContext().getObject('settingsDBHelper')){ 179 GlobalContext.getContext().setObject('settingsDBHelper', new SettingsDBHelper()); 180 } 181 return GlobalContext.getContext().getObject('settingsDBHelper') as SettingsDBHelper; 182 } 183 184 public async getRdbStore() { 185 Log.info('call getRdbStore start'); 186 if (!this.rdbStore) { 187 return await (GlobalContext.getContext().getObject('settingsDBHelper') as SettingsDBHelper).initRdbStore(); 188 // return await globalThis.settingsDBHelper.initRdbStore(); 189 } 190 return this.rdbStore 191 } 192 193 public async loadTableData(content: IContent, tableType: TableType, userId: number): Promise<void> { 194 if (!content) { 195 Log.error('content is empty'); 196 return; 197 } 198 switch (tableType) { 199 case TableType.SETTINGS: 200 this.loadDefaultTaleData(content.settings, TableType.SETTINGS, userId); 201 return; 202 case TableType.USER: 203 this.loadDefaultTaleData(content.user, TableType.USER, userId); 204 return; 205 case TableType.SETTINGS: 206 this.loadDefaultTaleData(content.userSecure, TableType.USER_SECURE, userId); 207 return; 208 default: 209 Log.error('invalid type'); 210 } 211 } 212 213 private getTableName(tableType: TableType, userId: number): string { 214 if (tableType === TableType.SETTINGS) { 215 return SettingsDataConfig.TABLE_NAME; 216 } 217 if (tableType === TableType.USER) { 218 return `${SettingsDataConfig.USER_TABLE_NAME}_${userId}`; 219 } 220 return `${SettingsDataConfig.SECURE_TABLE_NAME}_${userId}`; 221 } 222 223 private async loadTableSettings(key: string, value: string, tableName: string): Promise<void> { 224 if (!this.rdbStore) { 225 Log.error('rdbStore is null!'); 226 return 227 } 228 Log.info(`tableName: ${tableName}, key: ${key}, value: ${value}`); 229 try { 230 let ret = await this.rdbStore.insert(tableName, { 'KEYWORD': key, 'VALUE': value }); 231 if (ret >= 0) { 232 Log.info(`insert into DB success; ${ret}`); 233 } else { 234 this.faultOccured = true; 235 Log.error(`insert into DB faild; ${ret}`); 236 } 237 } catch (err) { 238 Log.warn(`insert key ${key} failed`); 239 } 240 } 241 242 public async loadUserSettings(key: string, value: string, userId: number|undefined): Promise<void> { 243 if (!this.rdbStore) { 244 Log.error('rdbStore is null!'); 245 return 246 } 247 Log.info('key=' + key + ' value ' + value + ' userid ' + userId); 248 await this.rdbStore.insert(SettingsDataConfig.USER_TABLE_NAME + '_' + userId, 249 { 'KEYWORD': key, 'VALUE': value }, (err, ret) => { 250 if (err) { 251 Log.error('loadGlobalSettings insert error:' + JSON.stringify(err)); 252 } 253 Log.info('loadGlobalSettings insert ret = ' + ret); 254 }); 255 } 256 257 public async readDefaultFile(): Promise<Object> { 258 let rawStr: string = ''; 259 try { 260 let content: number[] = Array.from(await this.context?.resourceManager.getRawFile(DEFAULT_JSON_FILE_NAME)); 261 rawStr = String.fromCharCode(...Array.from(content)); 262 } catch (err) { 263 Log.error('readDefaultFile readRawFile err' + err); 264 } 265 266 if (rawStr) { 267 Log.info('readDefaultFile success'); 268 return JSON.parse(rawStr); 269 } 270 return rawStr; 271 } 272 273 private async loadDefaultSettingsData(): Promise<void> { 274 if (!this.isFirstStartup) { 275 Log.info('loadDefaultSettingsData exists'); 276 return; 277 } 278 Log.info('loadDefaultSettingsData start'); 279 try { 280 let content = await this.readDefaultFile() as IContent; 281 if (!content) { 282 Log.error('readDefaultFile is failed!'); 283 return; 284 } 285 // 同时加载三张表,主要用于首次加载场景 286 await this.loadTableData(content, TableType.SETTINGS, this.DEFAULT_USER_ID); 287 await this.loadTableData(content, TableType.USER, this.DEFAULT_USER_ID); 288 await this.loadTableData(content, TableType.USER_SECURE, this.DEFAULT_USER_ID); 289 } catch (err) { 290 Log.error('loadDefaultSettingsData catch error! err = ' + err); 291 } 292 293 let tableName: string = this.getTableName(TableType.SETTINGS, this.DEFAULT_USER_ID); 294 // 初始化设备名称 295 let deviceName: string = deviceInfo.marketName; 296 if (deviceName.startsWith('"') && deviceName.endsWith('"')) { 297 deviceName = JSON.parse(deviceName); 298 } 299 await this.loadTableSettings(settings.general.DEVICE_NAME, deviceName, tableName); 300 301 // 初始化亮度值 302 let defaultBrightness = systemParameter.getSync('const.display.brightness.default'); 303 if (defaultBrightness) { 304 await this.loadTableSettings(settings.display.SCREEN_BRIGHTNESS_STATUS, defaultBrightness, tableName); 305 } 306 307 // 初始化克隆标识 308 await this.loadTableSettings(SETTINGS_CLONED_STATUS, '0', tableName); 309 310 // 适配模拟器开机不走OOBE 311 await this.emulatorParamInit(); 312 313 //make sure no faultoccured, then write isFirstStartup false; 314 if (this.faultOccured === false) { 315 let storage = await dataStorage.getPreferences(this.context as Context, SETTINGSDATA_PREFERENCE); 316 await storage.put('isFirstStartUp', false); 317 await storage.flush(); 318 Log.info('settingsdata initial DB success. ') 319 } else { 320 Log.warn('settingsdata initial DB failed! Will retry possible during next startup!!!'); 321 } 322 Log.info('loadDefaultSettingsData end'); 323 } 324 private async loadDefaultTaleData(tableData: Array<Map<string, string>>, tableType: TableType, 325 userID: number): Promise<void> { 326 if (tableData?.length <= 0) { 327 Log.error(`${tableType} table data is empty`); 328 return; 329 } 330 let tableName: string = this.getTableName(tableType, userID); 331 for (let index = 0; index < tableData.length; index++) { 332 await this.loadTableSettings(tableData[index]['name'], tableData[index]['value'], tableName); 333 } 334 } 335} 336 337export default SettingsDBHelper; 338