1# E类加密数据库的使用 (ArkTS) 2<!--Kit: ArkData--> 3<!--Subsystem: DistributedDataManager--> 4<!--Owner: @baijidong--> 5<!--Designer: @widecode; @htt1997; @dboy190--> 6<!--Tester: @yippo; @logic42--> 7<!--Adviser: @ge-yafang--> 8 9 10## 场景介绍 11 12从安全角度考虑,为满足部分敏感数据的安全特性,提供了E类加密数据库的方案以提高锁屏下数据的安全性。存有敏感信息的应用在申请ohos.permission.PROTECT_SCREEN_LOCK_DATA权限后会在[EL5](../reference/apis-ability-kit/js-apis-app-ability-contextConstant.md#areamode)路径下创建一个E类数据库。在锁屏的情况下(未调用Access接口获取保留文件密钥)会触发文件密钥的销毁,此时E类数据库不可读写。当锁屏解锁后,密钥会恢复,E类数据库恢复正常读写操作。这样的设计可以有效防止用户数据的泄露。 13 14在锁屏的情况下,应用程序仍然可以继续写入数据。由于此时E类数据库不可读写,这可能会导致数据丢失。为了解决这个问题,当前提供了一种方案:在锁屏的情况下,将数据存储在[EL2](../reference/apis-ability-kit/js-apis-app-ability-contextConstant.md#areamode)路径下的C类数据库中。当解锁后,再将数据迁移到E类数据库中。这样可以确保数据在锁屏期间的安全性和完整性。 15 16键值型数据库和关系型数据库均支持E类加密数据库。 17 18## 实现机制 19 20通过封装Mover类、Store类、SecretKeyObserver类和ECStoreManager类实现应用数据库密钥加锁和解锁状态下E类数据库和C类数据库的切换和操作。 21 22Mover类:提供数据库数据迁移接口,在锁屏解锁后,若C类数据库中有数据,使用该接口将数据迁移到E类数据库。 23 24Store类:提供了获取数据库,在数据库中插入数据、删除数据、更新数据和获取当前数据数量的接口。 25 26SecretKeyObserver类:提供了获取当前密钥状态的接口,在密钥销毁后,关闭E类数据库。 27 28ECStoreManager类:用于管理应用的E类数据库和C类数据库。 29 30## 配置权限 31 32使用EL5路径下的数据库,需要配置ohos.permission.PROTECT_SCREEN_LOCK_DATA权限。 33 34```ts 35// module.json5 36"requestPermissions": [ 37 { 38 "name": "ohos.permission.PROTECT_SCREEN_LOCK_DATA" 39 } 40 ] 41``` 42 43## 键值型数据库E类加密 44 45本章节提供键值型数据库的E类加密数据库使用方式,提供[Mover](#mover)类、[Store](#store)类、[SecretKeyObserver](#secretkeyobserver)类和[ECStoreManager](#ecstoremanager)类的具体实现,并在[EntryAbility](#entryability)和[index按键事件](#index按键事件)中展示这几个类的使用方式。 46 47### Mover 48 49提供数据库数据迁移接口,在锁屏解锁后,若C类数据库中存在数据,使用该接口将数据迁移到E类数据库。 50 51```ts 52// Mover.ts 53import { distributedKVStore } from '@kit.ArkData'; 54 55export class Mover { 56 async move(eStore: distributedKVStore.SingleKVStore, cStore: distributedKVStore.SingleKVStore): Promise<void> { 57 if (eStore != null && cStore != null) { 58 let entries: distributedKVStore.Entry[] = await cStore.getEntries('key_test_string'); 59 await eStore.putBatch(entries); 60 console.info(`ECDB_Encry move success`); 61 } 62 } 63} 64``` 65 66### Store 67 68提供了获取数据库,在数据库中插入数据、删除数据、更新数据和获取当前数据数量的接口。 69 70```ts 71// Store.ts 72import { distributedKVStore } from '@kit.ArkData'; 73import { BusinessError } from '@kit.BasicServicesKit'; 74 75let kvManager: distributedKVStore.KVManager; 76 77export class StoreInfo { 78 kvManagerConfig: distributedKVStore.KVManagerConfig; 79 storeId: string; 80 option: distributedKVStore.Options; 81} 82 83export class Store { 84 async getECStore(storeInfo: StoreInfo): Promise<distributedKVStore.SingleKVStore> { 85 try { 86 kvManager = distributedKVStore.createKVManager(storeInfo.kvManagerConfig); 87 console.info("Succeeded in creating KVManager"); 88 } catch (e) { 89 let error = e as BusinessError; 90 console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`); 91 } 92 if (kvManager !== undefined) { 93 let kvStore: distributedKVStore.SingleKVStore | null; 94 try { 95 kvStore = await kvManager.getKVStore<distributedKVStore.SingleKVStore>(storeInfo.storeId, storeInfo.option); 96 if (kvStore != undefined) { 97 console.info(`ECDB_Encry succeeded in getting store : ${storeInfo.storeId}`); 98 return kvStore; 99 } 100 } catch (e) { 101 let error = e as BusinessError; 102 console.error(`An unexpected error occurred.code is ${error.code},message is ${error.message}`); 103 } 104 } 105 } 106 107 putOnedata(kvStore: distributedKVStore.SingleKVStore): void { 108 if (kvStore != undefined) { 109 const KEY_TEST_STRING_ELEMENT = 'key_test_string' + String(Date.now()); 110 const VALUE_TEST_STRING_ELEMENT = 'value_test_string' + String(Date.now()); 111 try { 112 kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { 113 if (err !== undefined) { 114 console.error(`Failed to put data. Code:${err.code},message:${err.message}`); 115 return; 116 } 117 console.info(`ECDB_Encry Succeeded in putting data.${KEY_TEST_STRING_ELEMENT}`); 118 }); 119 } catch (e) { 120 let error = e as BusinessError; 121 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 122 } 123 } 124 } 125 126 getDataNum(kvStore: distributedKVStore.SingleKVStore): void { 127 if (kvStore != undefined) { 128 let resultSet: distributedKVStore.KVStoreResultSet; 129 kvStore.getResultSet("key_test_string").then((result: distributedKVStore.KVStoreResultSet) => { 130 console.info(`ECDB_Encry Succeeded in getting result set num ${result.getCount()}`); 131 resultSet = result; 132 if (kvStore != null) { 133 kvStore.closeResultSet(resultSet).then(() => { 134 console.info('Succeeded in closing result set'); 135 }).catch((err: BusinessError) => { 136 console.error(`Failed to close resultset.code is ${err.code},message is ${err.message}`); 137 }); 138 } 139 }).catch((err: BusinessError) => { 140 console.error(`Failed to get resultset.code is ${err.code},message is ${err.message}`); 141 }); 142 } 143 } 144 145 deleteOnedata(kvStore: distributedKVStore.SingleKVStore): void { 146 if (kvStore != undefined) { 147 kvStore.getEntries('key_test_string', (err: BusinessError, entries: distributedKVStore.Entry[]) => { 148 if (err != undefined) { 149 console.error(`Failed to get Entries.code is ${err.code},message is ${err.message}`); 150 return; 151 } 152 if (kvStore != null && entries.length != 0) { 153 kvStore.delete(entries[0].key, (err: BusinessError) => { 154 if (err != undefined) { 155 console.error(`Failed to delete.code is ${err.code},message is ${err.message}`); 156 return; 157 } 158 console.info('ECDB_Encry Succeeded in deleting'); 159 }); 160 } 161 }); 162 } 163 } 164 165 updateOnedata(kvStore: distributedKVStore.SingleKVStore): void { 166 if (kvStore != undefined) { 167 kvStore.getEntries('key_test_string', async (err: BusinessError, entries: distributedKVStore.Entry[]) => { 168 if (err != undefined) { 169 console.error(`Failed to get Entries.code is ${err.code},message is ${err.message}`); 170 return; 171 } 172 if (kvStore != null && entries.length != 0) { 173 console.info(`ECDB_Encry old data:${entries[0].key},value :${entries[0].value.value.toString()}`) 174 await kvStore.put(entries[0].key, "new value_test_string" + String(Date.now()) + 'new').then(() => { 175 }).catch((err: BusinessError) => { 176 console.error(`Failed to put.code is ${err.code},message is ${err.message}`); 177 }); 178 } 179 console.info(`ECDB_Encry update success`) 180 }); 181 } 182 } 183} 184``` 185 186### SecretKeyObserver 187 188该类提供了获取当前密钥状态的接口,在密钥销毁后,关闭E类数据库。 189 190```ts 191// SecretKeyObserver.ts 192import { ECStoreManager } from './ECStoreManager'; 193 194export enum SecretStatus { 195 Lock, 196 UnLock 197} 198 199export class SecretKeyObserver { 200 onLock(): void { 201 this.lockStatus = SecretStatus.Lock; 202 this.storeManager.closeEStore(); 203 } 204 205 onUnLock(): void { 206 this.lockStatus = SecretStatus.UnLock; 207 } 208 209 getCurrentStatus(): number { 210 return this.lockStatus; 211 } 212 213 initialize(storeManager: ECStoreManager): void { 214 this.storeManager = storeManager; 215 } 216 217 updateLockStatus(code: number) { 218 if (code === SecretStatus.Lock) { 219 this.onLock(); 220 } else { 221 this.lockStatus = code; 222 } 223 } 224 225 // 初始获取锁屏状态 226 private lockStatus: number = SecretStatus.UnLock; 227 private storeManager: ECStoreManager; 228} 229 230export let lockObserve = new SecretKeyObserver(); 231``` 232 233### ECStoreManager 234 235ECStoreManager类用于管理应用的E类数据库和C类数据库。支持配置数据库信息、配置迁移函数的信息,可根据密钥状态为应用提供相应的数据库句柄,并提供了关闭E类数据库、数据迁移完成后销毁C类数据库等接口。 236 237```ts 238// ECStoreManager.ts 239import { distributedKVStore } from '@kit.ArkData'; 240import { Mover } from './Mover'; 241import { BusinessError } from '@kit.BasicServicesKit'; 242import { StoreInfo, Store } from './Store'; 243import { SecretStatus } from './SecretKeyObserver'; 244 245let store = new Store(); 246 247export class ECStoreManager { 248 config(cInfo: StoreInfo, other: StoreInfo): void { 249 this.cInfo = cInfo; 250 this.eInfo = other; 251 } 252 253 configDataMover(mover: Mover): void { 254 this.mover = mover; 255 } 256 257 async getCurrentStore(screenStatus: number): Promise<distributedKVStore.SingleKVStore> { 258 console.info(`ECDB_Encry GetCurrentStore start screenStatus: ${screenStatus}`); 259 if (screenStatus == SecretStatus.UnLock) { 260 try { 261 this.eStore = await store.getECStore(this.eInfo); 262 } catch (e) { 263 let error = e as BusinessError; 264 console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`); 265 } 266 // 解锁状态 获取e类库 267 if (this.needMove) { 268 if (this.eStore != undefined && this.cStore != undefined) { 269 await this.mover.move(this.eStore, this.cStore); 270 } 271 this.deleteCStore(); 272 console.info(`ECDB_Encry Data migration is complete. Destroy cstore`); 273 this.needMove = false; 274 } 275 return this.eStore; 276 } else { 277 // 加锁状态 获取c类库 278 this.needMove = true; 279 try { 280 this.cStore = await store.getECStore(this.cInfo); 281 } catch (e) { 282 let error = e as BusinessError; 283 console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`); 284 } 285 return this.cStore; 286 } 287 } 288 289 closeEStore(): void { 290 try { 291 let kvManager = distributedKVStore.createKVManager(this.eInfo.kvManagerConfig); 292 console.info("Succeeded in creating KVManager"); 293 if (kvManager != undefined) { 294 kvManager.closeKVStore(this.eInfo.kvManagerConfig.bundleName, this.eInfo.storeId); 295 this.eStore = null; 296 console.info(`ECDB_Encry close EStore success`) 297 } 298 } catch (e) { 299 let error = e as BusinessError; 300 console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`); 301 } 302 } 303 304 deleteCStore(): void { 305 try { 306 let kvManager = distributedKVStore.createKVManager(this.cInfo.kvManagerConfig); 307 console.info("Succeeded in creating KVManager"); 308 if (kvManager != undefined) { 309 kvManager.deleteKVStore(this.cInfo.kvManagerConfig.bundleName, this.cInfo.storeId); 310 this.cStore = null; 311 console.info("ECDB_Encry delete cStore success"); 312 } 313 } catch (e) { 314 let error = e as BusinessError; 315 console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`); 316 } 317 } 318 319 private eStore: distributedKVStore.SingleKVStore = null; 320 private cStore: distributedKVStore.SingleKVStore = null; 321 private cInfo: StoreInfo | null = null; 322 private eInfo: StoreInfo | null = null; 323 private needMove: boolean = false; 324 private mover: Mover | null = null; 325} 326``` 327 328### EntryAbility 329 330模拟应用启动期间,注册对COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED公共事件的监听,并配置相应的数据库信息、密钥状态信息等。 331 332```ts 333// EntryAbility.ets 334import { AbilityConstant, application, contextConstant, UIAbility, Want } from '@kit.AbilityKit'; 335import { hilog } from '@kit.PerformanceAnalysisKit'; 336import { window } from '@kit.ArkUI'; 337import { distributedKVStore } from '@kit.ArkData'; 338import { ECStoreManager } from './ECStoreManager'; 339import { StoreInfo } from './Store'; 340import { Mover } from './Mover'; 341import { SecretKeyObserver } from './SecretKeyObserver'; 342import { commonEventManager } from '@kit.BasicServicesKit'; 343import { BusinessError } from '@kit.BasicServicesKit'; 344 345 346export let storeManager = new ECStoreManager(); 347 348export let e_secretKeyObserver = new SecretKeyObserver(); 349 350let mover = new Mover(); 351 352let subscriber: commonEventManager.CommonEventSubscriber; 353 354export function createCB(err: BusinessError, commonEventSubscriber: commonEventManager.CommonEventSubscriber) { 355 if (!err) { 356 console.info('ECDB_Encry createSubscriber'); 357 subscriber = commonEventSubscriber; 358 try { 359 commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => { 360 if (err) { 361 console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`); 362 } else { 363 console.info(`ECDB_Encry SubscribeCB ${data.code}`); 364 e_secretKeyObserver.updateLockStatus(data.code); 365 } 366 }); 367 } catch (error) { 368 const err: BusinessError = error as BusinessError; 369 console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`); 370 } 371 } else { 372 console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`); 373 } 374} 375 376let cInfo: StoreInfo | null = null; 377let eInfo: StoreInfo | null = null; 378 379export default class EntryAbility extends UIAbility { 380 async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> { 381 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 382 let cContext = this.context; 383 cInfo = { 384 "kvManagerConfig": { 385 context: cContext, 386 bundleName: 'com.example.ecstoredemo', 387 }, 388 "storeId": "cstore", 389 "option": { 390 createIfMissing: true, 391 encrypt: false, 392 backup: false, 393 autoSync: false, 394 // kvStoreType不填时,默认创建多设备协同数据库 395 kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, 396 // 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION 397 securityLevel: distributedKVStore.SecurityLevel.S3 398 } 399 } 400 let eContext = await application.createModuleContext(this.context,"entry"); 401 eContext.area = contextConstant.AreaMode.EL5; 402 eInfo = { 403 "kvManagerConfig": { 404 context: eContext, 405 bundleName: 'com.example.ecstoredemo', 406 }, 407 "storeId": "estore", 408 "option": { 409 createIfMissing: true, 410 encrypt: false, 411 backup: false, 412 autoSync: false, 413 // kvStoreType不填时,默认创建多设备协同数据库 414 kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, 415 // 多设备协同数据库:kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION 416 securityLevel: distributedKVStore.SecurityLevel.S3 417 } 418 } 419 console.info(`ECDB_Encry store area : estore:${eContext.area},cstore${cContext.area}`); 420 // 监听COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED事件 code == 1解锁状态,code==0加锁状态 421 try { 422 commonEventManager.createSubscriber({ 423 events: ['COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED'] 424 }, createCB); 425 console.info(`ECDB_Encry success subscribe`); 426 } catch (error) { 427 const err: BusinessError = error as BusinessError; 428 console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`); 429 } 430 storeManager.config(cInfo, eInfo); 431 storeManager.configDataMover(mover); 432 e_secretKeyObserver.initialize(storeManager); 433 } 434 435 onDestroy(): void { 436 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 437 } 438 439 onWindowStageCreate(windowStage: window.WindowStage): void { 440 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 441 442 windowStage.loadContent('pages/Index', (err) => { 443 if (err.code) { 444 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 445 return; 446 } 447 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 448 }); 449 } 450 451 onWindowStageDestroy(): void { 452 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 453 } 454 455 onForeground(): void { 456 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 457 } 458 459 onBackground(): void { 460 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 461 } 462} 463``` 464 465### Index按键事件 466 467使用Button按钮,通过点击按钮来模拟应用操作数据库,如插入数据、删除数据、更新数据和获取数据数量的操作等,展示数据库基本的增删改查能力。 468 469```ts 470// Index.ets 471import { storeManager, e_secretKeyObserver } from "../entryability/EntryAbility"; 472import { distributedKVStore } from '@kit.ArkData'; 473import { Store } from '../entryability/Store'; 474 475let storeOption = new Store(); 476 477let lockStatus: number = 1; 478 479@Entry 480@Component 481struct Index { 482 @State message: string = 'Hello World'; 483 484 build() { 485 Row() { 486 Column() { 487 Button('加锁/解锁').onClick((event: ClickEvent) => { 488 if (lockStatus) { 489 e_secretKeyObserver.onLock(); 490 lockStatus = 0; 491 } else { 492 e_secretKeyObserver.onUnLock(); 493 lockStatus = 1; 494 } 495 lockStatus ? this.message = "解锁" : this.message = "加锁"; 496 }).margin("5"); 497 Button('store type').onClick(async (event: ClickEvent) => { 498 e_secretKeyObserver.getCurrentStatus() ? this.message = "estore" : this.message = "cstore"; 499 }).margin("5"); 500 501 Button("put").onClick(async (event: ClickEvent) => { 502 let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 503 storeOption.putOnedata(store); 504 }).margin(5) 505 506 Button("Get").onClick(async (event: ClickEvent) => { 507 let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 508 storeOption.getDataNum(store); 509 }).margin(5) 510 511 Button("delete").onClick(async (event: ClickEvent) => { 512 let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 513 storeOption.deleteOnedata(store); 514 }).margin(5) 515 516 Button("update").onClick(async (event: ClickEvent) => { 517 let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 518 storeOption.updateOnedata(store); 519 }).margin(5) 520 521 Text(this.message) 522 .fontSize(50) 523 .fontWeight(FontWeight.Bold) 524 } 525 .width('100%') 526 } 527 .height('100%') 528 } 529} 530``` 531 532## 关系型数据库E类加密 533 534本章节提供关系型数据库的E类加密数据库使用方式,提供[Mover](#mover-1)类,[Store](#store-1)类,[SecretKeyObserver](#secretkeyobserver-1)类和[ECStoreManager](#ecstoremanager-1)类的具体实现,并在[EntryAbility](#entryability-1)和[index按键事件](#index按键事件-1)中展示这几个类的使用方式。 535 536### Mover 537 538提供数据库数据迁移接口,在锁屏解锁后,若C类数据库中有数据,使用该接口将数据迁移到E类数据库。 539 540```ts 541// Mover.ts 542import { relationalStore } from '@kit.ArkData'; 543 544export class Mover { 545 async move(eStore: relationalStore.RdbStore, cStore: relationalStore.RdbStore) { 546 if (eStore != null && cStore != null) { 547 let predicates = new relationalStore.RdbPredicates('employee'); 548 let resultSet = await cStore.query(predicates); 549 while (resultSet.goToNextRow()) { 550 let bucket = resultSet.getRow(); 551 await eStore.insert('employee', bucket); 552 } 553 } 554 } 555} 556``` 557 558### Store 559 560提供了获取数据库,在数据库中插入数据、删除数据、更新数据和获取当前数据数量的接口。其中StoreInfo类用于存储获取数据库相关信息。 561 562```ts 563// Store.ts 564import { relationalStore } from '@kit.ArkData'; 565import { BusinessError } from '@kit.BasicServicesKit'; 566import { Context } from '@kit.AbilityKit'; 567 568export class StoreInfo { 569 context: Context; 570 config: relationalStore.StoreConfig; 571 storeId: string; 572} 573 574let id = 1; 575const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; 576 577 578export class Store { 579 async getECStore(storeInfo: StoreInfo): Promise<relationalStore.RdbStore> { 580 let rdbStore: relationalStore.RdbStore | null; 581 try { 582 rdbStore = await relationalStore.getRdbStore(storeInfo.context, storeInfo.config); 583 if (rdbStore.version == 0) { 584 await rdbStore.executeSql(SQL_CREATE_TABLE); 585 console.info(`ECDB_Encry succeeded in getting Store :${storeInfo.storeId}`); 586 rdbStore.version = 1; 587 } 588 } catch (e) { 589 let error = e as BusinessError; 590 console.error(`An unexpected error occurred.code is ${error.code},message is ${error.message}`); 591 } 592 return rdbStore; 593 } 594 595 async putOnedata(rdbStore: relationalStore.RdbStore) { 596 if (rdbStore != undefined) { 597 const valueBucket: relationalStore.ValuesBucket = { 598 ID: id++, 599 NAME: 'Lisa', 600 AGE: 18, 601 SALARY: 100.5, 602 CODES: new Uint8Array([1, 2, 3, 4, 5]), 603 }; 604 try { 605 await rdbStore.insert("EMPLOYEE", valueBucket); 606 console.info(`ECDB_Encry insert success`); 607 } catch (e) { 608 let error = e as BusinessError; 609 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 610 } 611 } 612 } 613 614 async getDataNum(rdbStore: relationalStore.RdbStore) { 615 if (rdbStore != undefined) { 616 try { 617 let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); 618 let resultSet = await rdbStore.query(predicates); 619 let count = resultSet.rowCount; 620 console.info(`ECDB_Encry getdatanum success count : ${count}`); 621 } catch (e) { 622 let error = e as BusinessError; 623 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 624 } 625 } 626 } 627 628 async deleteAlldata(rdbStore: relationalStore.RdbStore) { 629 if (rdbStore != undefined) { 630 try { 631 let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); 632 predicates.equalTo('AGE', 18); 633 await rdbStore.delete(predicates); 634 console.info(`ECDB_Encry delete Success`); 635 } catch (e) { 636 let error = e as BusinessError; 637 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 638 } 639 } 640 } 641 642 async updateOnedata(rdbStore: relationalStore.RdbStore) { 643 if (rdbStore != undefined) { 644 try { 645 let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); 646 predicates.equalTo('NAME', 'Lisa'); 647 const valueBucket: relationalStore.ValuesBucket = { 648 NAME: 'Anna', 649 SALARY: 100.5, 650 CODES: new Uint8Array([1, 2, 3, 4, 5]), 651 }; 652 await rdbStore.update(valueBucket, predicates); 653 console.info(`ECDB_Encry update success`); 654 } catch (e) { 655 let error = e as BusinessError; 656 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 657 } 658 } 659 } 660} 661``` 662 663### SecretKeyObserver 664 665该类提供了获取当前密钥状态的接口,在密钥销毁后,关闭E类数据库。 666 667```ts 668// SecretKeyObserver.ts 669import { ECStoreManager } from './ECStoreManager'; 670 671export enum SecretStatus { 672 Lock, 673 UnLock 674} 675 676export class SecretKeyObserver { 677 onLock(): void { 678 this.lockStatus = SecretStatus.Lock; 679 this.storeManager.closeEStore(); 680 } 681 682 onUnLock(): void { 683 this.lockStatus = SecretStatus.UnLock; 684 } 685 686 getCurrentStatus(): number { 687 return this.lockStatus; 688 } 689 690 initialize(storeManager: ECStoreManager): void { 691 this.storeManager = storeManager; 692 } 693 694 updateLockStatus(code: number) { 695 if (this.lockStatus === SecretStatus.Lock) { 696 this.onLock(); 697 } else { 698 this.lockStatus = code; 699 } 700 } 701 702 private lockStatus: number = SecretStatus.UnLock; 703 private storeManager: ECStoreManager; 704} 705 706export let lockObserve = new SecretKeyObserver(); 707``` 708 709### ECStoreManager 710 711ECStoreManager类用于管理应用的E类数据库和C类数据库。支持配置数据库信息、配置迁移函数的信息,可根据密钥状态为应用提供相应的数据库句柄,并提供了关闭E类数据库、数据迁移完成后销毁C类数据库等接口。 712 713```ts 714// ECStoreManager.ts 715import { relationalStore } from '@kit.ArkData'; 716import { Mover } from './Mover'; 717import { BusinessError } from '@kit.BasicServicesKit'; 718import { StoreInfo, Store } from './Store'; 719import { SecretStatus } from './SecretKeyObserver'; 720 721let store = new Store(); 722 723export class ECStoreManager { 724 config(cInfo: StoreInfo, other: StoreInfo): void { 725 this.cInfo = cInfo; 726 this.eInfo = other; 727 } 728 729 configDataMover(mover: Mover): void { 730 this.mover = mover; 731 } 732 733 async getCurrentStore(screenStatus: number): Promise<relationalStore.RdbStore> { 734 if (screenStatus === SecretStatus.UnLock) { 735 try { 736 this.eStore = await store.getECStore(this.eInfo); 737 } catch (e) { 738 let error = e as BusinessError; 739 console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`); 740 } 741 // 解锁状态 获取e类库 742 if (this.needMove) { 743 if (this.eStore != undefined && this.cStore != undefined) { 744 await this.mover.move(this.eStore, this.cStore); 745 console.info(`ECDB_Encry cstore data move to estore success`); 746 } 747 this.deleteCStore(); 748 this.needMove = false; 749 } 750 return this.eStore; 751 } else { 752 // 加锁状态 获取c类库 753 this.needMove = true; 754 try { 755 this.cStore = await store.getECStore(this.cInfo); 756 } catch (e) { 757 let error = e as BusinessError; 758 console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`); 759 } 760 return this.cStore; 761 } 762 } 763 764 closeEStore(): void { 765 this.eStore = undefined; 766 } 767 768 async deleteCStore() { 769 try { 770 await relationalStore.deleteRdbStore(this.cInfo.context, this.cInfo.storeId) 771 } catch (e) { 772 let error = e as BusinessError; 773 console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`); 774 } 775 } 776 777 private eStore: relationalStore.RdbStore = null; 778 private cStore: relationalStore.RdbStore = null; 779 private cInfo: StoreInfo | null = null; 780 private eInfo: StoreInfo | null = null; 781 private needMove: boolean = false; 782 private mover: Mover | null = null; 783} 784``` 785 786### EntryAbility 787 788模拟在应用启动期间,注册对COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED公共事件的监听,并配置相应的数据库信息、密钥状态信息等。 789 790```ts 791// EntryAbility.ets 792import { AbilityConstant, contextConstant, UIAbility, Want, application } from '@kit.AbilityKit'; 793import { hilog } from '@kit.PerformanceAnalysisKit'; 794import { window } from '@kit.ArkUI'; 795import { relationalStore } from '@kit.ArkData'; 796import { ECStoreManager } from './ECStoreManager'; 797import { StoreInfo } from './Store'; 798import { Mover } from './Mover'; 799import { SecretKeyObserver } from './SecretKeyObserver'; 800import { commonEventManager } from '@kit.BasicServicesKit'; 801import { BusinessError } from '@kit.BasicServicesKit'; 802 803 804export let storeManager = new ECStoreManager(); 805 806export let e_secretKeyObserver = new SecretKeyObserver(); 807 808let mover = new Mover(); 809 810let subscriber: commonEventManager.CommonEventSubscriber; 811 812export function createCB(err: BusinessError, commonEventSubscriber: commonEventManager.CommonEventSubscriber) { 813 if (!err) { 814 console.info('ECDB_Encry createSubscriber'); 815 subscriber = commonEventSubscriber; 816 try { 817 commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => { 818 if (err) { 819 console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`); 820 } else { 821 console.info(`ECDB_Encry SubscribeCB ${data.code}`); 822 e_secretKeyObserver.updateLockStatus(data.code); 823 } 824 }); 825 } catch (error) { 826 const err: BusinessError = error as BusinessError; 827 console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`); 828 } 829 } else { 830 console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`); 831 } 832} 833 834let cInfo: StoreInfo | null = null; 835let eInfo: StoreInfo | null = null; 836 837export default class EntryAbility extends UIAbility { 838 async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> { 839 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 840 let cContext = this.context; 841 cInfo = { 842 context: cContext, 843 config: { 844 name: 'cstore.db', 845 securityLevel: relationalStore.SecurityLevel.S3, 846 }, 847 storeId: "cstore.db" 848 }; 849 let eContext = await application.createModuleContext(this.context, "entry"); 850 eContext.area = contextConstant.AreaMode.EL5; 851 eInfo = { 852 context: eContext, 853 config: { 854 name: 'estore.db', 855 securityLevel: relationalStore.SecurityLevel.S3, 856 }, 857 storeId: "estore.db", 858 }; 859 // 监听COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED事件 code == 1解锁状态,code==0加锁状态 860 console.info(`ECDB_Encry store area : estore:${eContext.area},cstore${cContext.area}`) 861 try { 862 commonEventManager.createSubscriber({ 863 events: ['COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED'] 864 }, createCB); 865 console.info(`ECDB_Encry success subscribe`); 866 } catch (error) { 867 const err: BusinessError = error as BusinessError; 868 console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`); 869 } 870 storeManager.config(cInfo, eInfo); 871 storeManager.configDataMover(mover); 872 e_secretKeyObserver.initialize(storeManager); 873 } 874 875 onDestroy(): void { 876 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 877 } 878 879 onWindowStageCreate(windowStage: window.WindowStage): void { 880 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 881 882 windowStage.loadContent('pages/Index', (err) => { 883 if (err.code) { 884 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 885 return; 886 } 887 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 888 }); 889 } 890 891 onWindowStageDestroy(): void { 892 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 893 } 894 895 onForeground(): void { 896 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 897 } 898 899 onBackground(): void { 900 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 901 } 902} 903``` 904 905### Index按键事件 906 907使用Button按钮,通过点击按钮来模拟应用操作数据库,如插入数据、删除数据、更新数据和获取数据数量的操作等,展示数据库基本的增删改查能力。 908 909```ts 910// Index.ets 911import { storeManager, e_secretKeyObserver } from "../entryability/EntryAbility"; 912import { relationalStore } from '@kit.ArkData'; 913import { Store } from '../entryability/Store'; 914 915let storeOption = new Store(); 916 917let lockStatus: number = 1; 918 919@Entry 920@Component 921struct Index { 922 @State message: string = 'Hello World'; 923 924 build() { 925 Row() { 926 Column() { 927 Button('加锁/解锁').onClick((event: ClickEvent) => { 928 if (lockStatus) { 929 e_secretKeyObserver.onLock(); 930 lockStatus = 0; 931 } else { 932 e_secretKeyObserver.onUnLock(); 933 lockStatus = 1; 934 } 935 lockStatus ? this.message = "解锁" : this.message = "加锁"; 936 }).margin("5"); 937 Button('store type').onClick(async (event: ClickEvent) => { 938 e_secretKeyObserver.getCurrentStatus() ? this.message = "estore" : this.message = "cstore"; 939 console.info(`ECDB_Encry current store : ${this.message}`); 940 }).margin("5"); 941 942 Button("put").onClick(async (event: ClickEvent) => { 943 let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 944 storeOption.putOnedata(store); 945 }).margin(5) 946 947 Button("Get").onClick(async (event: ClickEvent) => { 948 let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 949 storeOption.getDataNum(store); 950 }).margin(5) 951 952 Button("delete").onClick(async (event: ClickEvent) => { 953 let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 954 storeOption.deleteAlldata(store); 955 }).margin(5) 956 957 Button("update").onClick(async (event: ClickEvent) => { 958 let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus()); 959 storeOption.updateOnedata(store); 960 }).margin(5) 961 962 Text(this.message) 963 .fontSize(50) 964 .fontWeight(FontWeight.Bold) 965 } 966 .width('100%') 967 } 968 .height('100%') 969 } 970} 971``` 972