1# Using an EL5 Database
2
3
4## When to Use
5
6An [EL5](../reference/apis-ability-kit/js-apis-app-ability-contextConstant.md#contextconstantareamode) database is created in the **el5/** directory to store the application's sensitive information. When the screen is locked and certain conditions are met, the key used to encrypt the sensitive information will be destroyed and the database cannot be operated. After the screen is unlocked, the key is restored and the read and write operations on the database are restored. This mechanism can effectively protect the user data. For details about how to manage the encryption directories, see [Obtaining and Modifying Encrypted Levels](../application-models/application-context-stage.md#obtaining-and-modifying-encryption-levels).
7
8However, the application may write data when the screen is locked. Data loss will be caused if the EL5 database cannot be operated when data is written. A solution is provided to solve this problem. When the screen is locked, incremental data is stored in an [EL2](../reference/apis-ability-kit/js-apis-app-ability-contextConstant.md#contextconstantareamode) database. The data temporarily stored in the EL2 database will be moved to the EL5 database when the EL5 database is unlocked. This ensures data security and consistency when the screen is locked.
9
10Both the KV store and RDB store can be used as an EL5 database.
11
12## Working Principles
13
14The following classes are encapsulated to implement the data operations and transfer between the EL2 and EL5 databases:
15
16- **Mover** class: provides APIs for moving data from an EL2 database to an EL5 database after the screen is unlocked.
17
18- **Store** class: provides APIs for accessing and operating the currently operable database.
19
20- **SecretKeyObserver** class: provides APIs for obtaining the key status. After the key is destroyed, the EL5 database will be closed.
21
22- **ECStoreManager** class: provides APIs for managing the EL2 and EL5 databases.
23
24## Requesting Permissions
25
26To access the database in the **el5/** directory, the application must have the ohos.permission.PROTECT_SCREEN_LOCK_DATA permission. Add this permission in the **module.json5** file.
27
28```ts
29// module.json5
30"requestPermissions": [
31      {
32        "name": "ohos.permission.PROTECT_SCREEN_LOCK_DATA"
33      }
34    ]
35```
36
37## Using an EL5 KV Store
38
39The following describes how to use the [Mover](#mover), [Store](#store), [SecretKeyObserver](#secretkeyobserver), and [ECStoreManager](#ecstoremanager) classes to implement the use of a KV store in the **el5/** directory. In the following example, [EntryAbility](#entryability) and [index key event](#index-key-event) are used to present how to use these classes.
40
41### Mover
42
43Use **Mover** to move data from an EL2 database to an EL5 database after the screen is unlocked.
44
45```ts
46// Mover.ts
47import { distributedKVStore } from '@kit.ArkData';
48
49export class Mover {
50  async move(eStore: distributedKVStore.SingleKVStore, cStore: distributedKVStore.SingleKVStore): Promise<void> {
51    if (eStore != null && cStore != null) {
52      let entries: distributedKVStore.Entry[] = await cStore.getEntries('key_test_string');
53      await eStore.putBatch(entries);
54      console.info(`ECDB_Encry move success`);
55    }
56  }
57}
58```
59
60### Store
61
62Use the APIs provided by the **Store** class to obtain a database instance, add, delete, and update data, and obtain the data count in the database.
63
64```ts
65// Store.ts
66import { distributedKVStore } from '@kit.ArkData';
67import { BusinessError } from '@kit.BasicServicesKit';
68
69let kvManager: distributedKVStore.KVManager;
70
71export class StoreInfo {
72  kvManagerConfig: distributedKVStore.KVManagerConfig;
73  storeId: string;
74  option: distributedKVStore.Options;
75}
76
77export class Store {
78  async getECStore(storeInfo: StoreInfo): Promise<distributedKVStore.SingleKVStore> {
79    try {
80      kvManager = distributedKVStore.createKVManager(storeInfo.kvManagerConfig);
81      console.info("Succeeded in creating KVManager");
82    } catch (e) {
83      let error = e as BusinessError;
84      console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`);
85    }
86    if (kvManager !== undefined) {
87      kvManager = kvManager as distributedKVStore.KVManager;
88      let kvStore: distributedKVStore.SingleKVStore | null;
89      try {
90        kvStore = await kvManager.getKVStore<distributedKVStore.SingleKVStore>(storeInfo.storeId, storeInfo.option);
91        if (kvStore != undefined) {
92          console.info(`ECDB_Encry succeeded in getting store : ${storeInfo.storeId}`);
93          return kvStore;
94        }
95      } catch (e) {
96        let error = e as BusinessError;
97        console.error(`An unexpected error occurred.code is ${error.code},message is ${error.message}`);
98      }
99    }
100  }
101
102  putOnedata(kvStore: distributedKVStore.SingleKVStore): void {
103    if (kvStore != undefined) {
104      const KEY_TEST_STRING_ELEMENT = 'key_test_string' + String(Date.now());
105      const VALUE_TEST_STRING_ELEMENT = 'value_test_string' + String(Date.now());
106      try {
107        kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
108          if (err !== undefined) {
109            console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
110            return;
111          }
112          console.info(`ECDB_Encry Succeeded in putting data.${KEY_TEST_STRING_ELEMENT}`);
113        });
114      } catch (e) {
115        let error = e as BusinessError;
116        console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
117      }
118    }
119  }
120
121  getDataNum(kvStore: distributedKVStore.SingleKVStore): void {
122    if (kvStore != undefined) {
123      let resultSet: distributedKVStore.KVStoreResultSet;
124      kvStore.getResultSet("key_test_string").then((result: distributedKVStore.KVStoreResultSet) => {
125        console.info(`ECDB_Encry Succeeded in getting result set num ${result.getCount()}`);
126        resultSet = result;
127        if (kvStore != null) {
128          kvStore.closeResultSet(resultSet).then(() => {
129            console.info('Succeeded in closing result set');
130          }).catch((err: BusinessError) => {
131            console.error(`Failed to close resultset.code is ${err.code},message is ${err.message}`);
132          });
133        }
134      }).catch((err: BusinessError) => {
135        console.error(`Failed to get resultset.code is ${err.code},message is ${err.message}`);
136      });
137    }
138  }
139
140  deleteOnedata(kvStore: distributedKVStore.SingleKVStore): void {
141    if (kvStore != undefined) {
142      kvStore.getEntries('key_test_string', (err: BusinessError, entries: distributedKVStore.Entry[]) => {
143        if (err != undefined) {
144          console.error(`Failed to get Entries.code is ${err.code},message is ${err.message}`);
145          return;
146        }
147        if (kvStore != null && entries.length != 0) {
148          kvStore.delete(entries[0].key, (err: BusinessError) => {
149            if (err != undefined) {
150              console.error(`Failed to delete.code is ${err.code},message is ${err.message}`);
151              return;
152            }
153            console.info('ECDB_Encry Succeeded in deleting');
154          });
155        }
156      });
157    }
158  }
159
160  updataOnedata(kvStore: distributedKVStore.SingleKVStore): void {
161    if (kvStore != undefined) {
162      kvStore.getEntries('key_test_string', async (err: BusinessError, entries: distributedKVStore.Entry[]) => {
163        if (err != undefined) {
164          console.error(`Failed to get Entries.code is ${err.code},message is ${err.message}`);
165          return;
166        }
167        if (kvStore != null && entries.length != 0) {
168          console.info(`ECDB_Encry old data:${entries[0].key},value :${entries[0].value.value.toString()}`)
169          await kvStore.put(entries[0].key, "new value_test_string" + String(Date.now()) + 'new').then(() => {
170          }).catch((err: BusinessError) => {
171            console.error(`Failed to put.code is ${err.code},message is ${err.message}`);
172          });
173        }
174        console.info(`ECDB_Encry updata success`)
175      });
176    }
177  }
178}
179```
180
181### SecretKeyObserver
182
183Use the APIs provided by the **SecretKeyObserver** class to obtain the key status. After the key is destroyed, the EL5 database will be closed.
184
185```ts
186// SecretKeyObserver.ts
187import { ECStoreManager } from './ECStoreManager';
188
189export enum SecretStatus {
190  Lock,
191  UnLock
192}
193
194export class SecretKeyObserver {
195  onLock(): void {
196    this.lockStatuas = SecretStatus.Lock;
197    this.storeManager.closeEStore();
198  }
199
200  onUnLock(): void {
201    this.lockStatuas = SecretStatus.UnLock;
202  }
203
204  getCurrentStatus(): number {
205    return this.lockStatuas;
206  }
207
208  initialize(storeManager: ECStoreManager): void {
209    this.storeManager = storeManager;
210  }
211
212  updatalockStatus(code: number) {
213    if (code === SecretStatus.Lock) {
214      this.onLock();
215    } else {
216      this.lockStatuas = code;
217    }
218  }
219
220  // Obtain the screen lock status.
221  private lockStatuas: number = SecretStatus.UnLock;
222  private storeManager: ECStoreManager;
223}
224
225export let lockObserve = new SecretKeyObserver();
226```
227
228### ECStoreManager
229
230Use the APIs provided by the **ECStoreManager** class to manage the EL2 and EL5 databases. Specifically, you can use the APIs to configure a database, set the function used to move data, provide the database handle for the application based on the key status, close an EL5 database, and destroy an El2 database after the data is moved.
231
232```ts
233// ECStoreManager.ts
234import { distributedKVStore } from '@kit.ArkData';
235import { Mover } from './Mover';
236import { BusinessError } from '@kit.BasicServicesKit';
237import { StoreInfo, Store } from './Store';
238import { SecretStatus } from './SecretKeyObserver';
239
240let store = new Store();
241
242export class ECStoreManager {
243  config(cInfo: StoreInfo, other: StoreInfo): void {
244    this.cInfo = cInfo;
245    this.eInfo = other;
246  }
247
248  configDataMover(mover: Mover): void {
249    this.mover = mover;
250  }
251
252  async getCurrentStore(screanStatus: number): Promise<distributedKVStore.SingleKVStore> {
253    console.info(`ECDB_Encry GetCurrentStore start screanStatus: ${screanStatus}`);
254    if (screanStatus === SecretStatus.UnLock) {
255      try {
256        this.eStore = await store.getECStore(this.eInfo);
257      } catch (e) {
258        let error = e as BusinessError;
259        console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`);
260      }
261      // Obtain an EL5 database when the screen is unlocked.
262      if (this.needMove) {
263        if (this.eStore != undefined && this.cStore != undefined) {
264          await this.mover.move(this.eStore, this.cStore);
265        }
266        this.deleteCStore();
267        console.info(`ECDB_Encry Data migration is complete. Destroy cstore`);
268        this.needMove = false;
269      }
270      return this.eStore;
271    } else {
272      // Obtain an EL2 database when the screen is locked.
273      this.needMove = true;
274      try {
275        this.cStore = await store.getECStore(this.cInfo);
276      } catch (e) {
277        let error = e as BusinessError;
278        console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`);
279      }
280      return this.cStore;
281    }
282  }
283
284  closeEStore(): void {
285    try {
286      let kvManager = distributedKVStore.createKVManager(this.eInfo.kvManagerConfig);
287      console.info("Succeeded in creating KVManager");
288      if (kvManager != undefined) {
289        kvManager.closeKVStore(this.eInfo.kvManagerConfig.bundleName, this.eInfo.storeId);
290        this.eStore = null;
291        console.info(`ECDB_Encry close EStore success`)
292      }
293    } catch (e) {
294      let error = e as BusinessError;
295      console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`);
296    }
297  }
298
299  deleteCStore(): void {
300    try {
301      let kvManager = distributedKVStore.createKVManager(this.cInfo.kvManagerConfig);
302      console.info("Succeeded in creating KVManager");
303      if (kvManager != undefined) {
304        kvManager.deleteKVStore(this.cInfo.kvManagerConfig.bundleName, this.cInfo.storeId);
305        this.cStore = null;
306        console.info("ECDB_Encry delete cStore success");
307      }
308    } catch (e) {
309      let error = e as BusinessError;
310      console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`);
311    }
312  }
313
314  private eStore: distributedKVStore.SingleKVStore = null;
315  private cStore: distributedKVStore.SingleKVStore = null;
316  private cInfo: StoreInfo | null = null;
317  private eInfo: StoreInfo | null = null;
318  private needMove: boolean = false;
319  private mover: Mover | null = null;
320}
321```
322
323### EntryAbility
324
325Register a listener for the COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED event when the simulated application starts, and configure the database information and key status information.
326
327```ts
328// EntryAbility.ets
329import { AbilityConstant, contextConstant, UIAbility, Want } from '@kit.AbilityKit';
330import { hilog } from '@kit.PerformanceAnalysisKit';
331import { window } from '@kit.ArkUI';
332import { distributedKVStore } from '@kit.ArkData';
333import { ECStoreManager } from './ECStoreManager';
334import { StoreInfo } from './Store';
335import { Mover } from './Mover';
336import { SecretKeyObserver } from './SecretKeyObserver';
337import { commonEventManager } from '@kit.BasicServicesKit';
338import { BusinessError } from '@kit.BasicServicesKit';
339
340
341export let storeManager = new ECStoreManager();
342
343export let e_secretKeyObserver = new SecretKeyObserver();
344
345let mover = new Mover();
346
347let subscriber: commonEventManager.CommonEventSubscriber;
348
349export function createCB(err: BusinessError, commonEventSubscriber: commonEventManager.CommonEventSubscriber) {
350  if (!err) {
351    console.info('ECDB_Encry createSubscriber');
352    subscriber = commonEventSubscriber;
353    try {
354      commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => {
355        if (err) {
356          console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`);
357        } else {
358          console.info(`ECDB_Encry SubscribeCB ${data.code}`);
359          e_secretKeyObserver.updatalockStatus(data.code);
360        }
361      });
362    } catch (error) {
363      const err: BusinessError = error as BusinessError;
364      console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`);
365    }
366  } else {
367    console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`);
368  }
369}
370
371let cInfo: StoreInfo | null = null;
372let eInfo: StoreInfo | null = null;
373
374export default class EntryAbility extends UIAbility {
375  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
376    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
377    let cContext = this.context;
378    cInfo = {
379      "kvManagerConfig": {
380        context: cContext,
381        bundleName: 'com.example.ecstoredemo',
382      },
383      "storeId": "cstore",
384      "option": {
385        createIfMissing: true,
386        encrypt: false,
387        backup: false,
388        autoSync: false,
389        // If kvStoreType is left empty, a device KV store is created by default.
390        kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
391        // kvStoreType is distributedKVStore.KVStoreType.DEVICE_COLLABORATION for a device KV store.
392        securityLevel: distributedKVStore.SecurityLevel.S3
393      }
394    }
395    let eContext = this.context.createModuleContext("entry");
396    eContext.area = contextConstant.AreaMode.EL5;
397    eInfo = {
398      "kvManagerConfig": {
399        context: eContext,
400        bundleName: 'com.example.ecstoredemo',
401      },
402      "storeId": "estore",
403      "option": {
404        createIfMissing: true,
405        encrypt: false,
406        backup: false,
407        autoSync: false,
408        // If kvStoreType is left empty, a device KV store is created by default.
409        kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
410        // kvStoreType is distributedKVStore.KVStoreType.DEVICE_COLLABORATION for a device KV store.
411        securityLevel: distributedKVStore.SecurityLevel.S3
412      }
413    }
414    console.info(`ECDB_Encry store area : estore:${eContext.area},cstore${cContext.area}`);
415    // Listen for the COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED event. code == 1 indicates the screen is unlocked, and code==0 indicates the screen is locked.
416    try {
417      commonEventManager.createSubscriber({
418        events: ['COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED']
419      }, createCB);
420      console.info(`ECDB_Encry success subscribe`);
421    } catch (error) {
422      const err: BusinessError = error as BusinessError;
423      console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`);
424    }
425    storeManager.config(cInfo, eInfo);
426    storeManager.configDataMover(mover);
427    e_secretKeyObserver.initialize(storeManager);
428  }
429
430  onDestroy(): void {
431    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
432  }
433
434  onWindowStageCreate(windowStage: window.WindowStage): void {
435    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
436
437    windowStage.loadContent('pages/Index', (err) => {
438      if (err.code) {
439        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
440        return;
441      }
442      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
443    });
444  }
445
446  onWindowStageDestroy(): void {
447    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
448  }
449
450  onForeground(): void {
451    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
452  }
453
454  onBackground(): void {
455    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
456  }
457}
458```
459
460### Index Key Event
461
462Use **Button** to simulate application operations on the database, such as inserting, deleting, updating, and obtaining the data count, by clicking the button.
463
464```ts
465// Index.ets
466import { storeManager, e_secretKeyObserver } from "../entryability/EntryAbility";
467import { distributedKVStore } from '@kit.ArkData';
468import { Store } from '../entryability/Store';
469
470let storeOption = new Store();
471
472let lockStatus: number = 1;
473
474@Entry
475@Component
476struct Index {
477  @State message: string = 'Hello World';
478
479  build() {
480    Row() {
481      Column() {
482        Button ('Lock/Unlock').onClick ((event: ClickEvent) => {
483          if (lockStatus) {
484            e_secretKeyObserver.onLock();
485            lockStatus = 0;
486          } else {
487            e_secretKeyObserver.onUnLock();
488            lockStatus = 1;
489          }
490          lockStatus? this.message = "Unlocked": this.message = "Locked";
491        }).margin("5");
492        Button('store type').onClick(async (event: ClickEvent) => {
493          e_secretKeyObserver.getCurrentStatus() ? this.message = "estroe" : this.message = "cstore";
494        }).margin("5");
495
496        Button("put").onClick(async (event: ClickEvent) => {
497          let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
498          storeOption.putOnedata(store);
499        }).margin(5)
500
501        Button("Get").onClick(async (event: ClickEvent) => {
502          let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
503          storeOption.getDataNum(store);
504        }).margin(5)
505
506        Button("delete").onClick(async (event: ClickEvent) => {
507          let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
508          storeOption.deleteOnedata(store);
509        }).margin(5)
510
511        Button("updata").onClick(async (event: ClickEvent) => {
512          let store: distributedKVStore.SingleKVStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
513          storeOption.updataOnedata(store);
514        }).margin(5)
515
516        Text(this.message)
517          .fontSize(50)
518          .fontWeight(FontWeight.Bold)
519      }
520      .width('100%')
521    }
522    .height('100%')
523  }
524}
525```
526
527## Using an EL5 RDB Store
528
529The following describes how to use the [Mover](#mover-1), [Store](#store-1), [SecretKeyObserver](#secretkeyobserver-1), and [ECStoreManager](#ecstoremanager-1) classes to implement the use of an RDB store in the **el5/** directory. In the following example, [EntryAbility](#entryability-1) and [index key event](#index-key-event-1) are used to present how to use these classes.
530
531### Mover
532
533Use **Mover** to move data from an EL2 database to an EL5 database after the screen is unlocked.
534
535```ts
536// Mover.ts
537import { relationalStore } from '@kit.ArkData';
538
539export class Mover {
540  async move(eStore: relationalStore.RdbStore, cStore: relationalStore.RdbStore) {
541    if (eStore != null && cStore != null) {
542      let predicates = new relationalStore.RdbPredicates('employee');
543      let resultSet = await cStore.query(predicates);
544      while (resultSet.goToNextRow()) {
545        let bucket = resultSet.getRow();
546        await eStore.insert('employee', bucket);
547      }
548    }
549  }
550}
551```
552
553### Store
554
555Use the APIs provided by the **Store** class to obtain a database instance, add, delete, and update data, and obtain the data count in the database. The **StoreInfo** class is used to store and obtain database information.
556
557```ts
558// Store.ts
559import { relationalStore } from '@kit.ArkData';
560import { BusinessError } from '@kit.BasicServicesKit';
561import { Context } from '@kit.AbilityKit';
562
563export class StoreInfo {
564  context: Context;
565  config: relationalStore.StoreConfig;
566  storeId: string;
567}
568
569let id = 1;
570const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)';
571
572
573export class Store {
574  async getECStore(storeInfo: StoreInfo): Promise<relationalStore.RdbStore> {
575    let rdbStore: relationalStore.RdbStore | null;
576    try {
577      rdbStore = await relationalStore.getRdbStore(storeInfo.context, storeInfo.config);
578      if (rdbStore.version == 0) {
579        await rdbStore.executeSql(SQL_CREATE_TABLE);
580        console.info(`ECDB_Encry succeeded in getting Store : ${storeInfo.storeId}`);
581        rdbStore.version = 1;
582      }
583    } catch (e) {
584      let error = e as BusinessError;
585      console.error(`An unexpected error occurred.code is ${error.code},message is ${error.message}`);
586    }
587    return rdbStore;
588  }
589
590  async putOnedata(rdbStore: relationalStore.RdbStore) {
591    if (rdbStore != undefined) {
592      const valueBucket: relationalStore.ValuesBucket = {
593        ID: id++,
594        NAME: 'Lisa',
595        AGE: 18,
596        SALARY: 100.5,
597        CODES: new Uint8Array([1, 2, 3, 4, 5]),
598      };
599      try {
600        await rdbStore.insert("EMPLOYEE", valueBucket);
601        console.info(`ECDB_Encry insert success`);
602      } catch (e) {
603        let error = e as BusinessError;
604        console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
605      }
606    }
607  }
608
609  async getDataNum(rdbStore: relationalStore.RdbStore) {
610    if (rdbStore != undefined) {
611      try {
612        let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
613        let resultSet = await rdbStore.query(predicates);
614        let count = resultSet.rowCount;
615        console.info(`ECDB_Encry getdatanum success count : ${count}`);
616      } catch (e) {
617        let error = e as BusinessError;
618        console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
619      }
620    }
621  }
622
623  async deleteAlldata(rdbStore: relationalStore.RdbStore) {
624    if (rdbStore != undefined) {
625      try {
626        let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
627        predicates.equalTo('AGE', 18);
628        await rdbStore.delete(predicates);
629        console.info(`ECDB_Encry delete Success`);
630      } catch (e) {
631        let error = e as BusinessError;
632        console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
633      }
634    }
635  }
636
637  async updataOnedata(rdbStore: relationalStore.RdbStore) {
638    if (rdbStore != undefined) {
639      try {
640        let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
641        predicates.equalTo('NAME', 'Lisa');
642        const valueBucket: relationalStore.ValuesBucket = {
643          NAME: 'Anna',
644          SALARY: 100.5,
645          CODES: new Uint8Array([1, 2, 3, 4, 5]),
646        };
647        await rdbStore.update(valueBucket, predicates);
648        console.info(`ECDB_Encry update success`);
649      } catch (e) {
650        let error = e as BusinessError;
651        console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
652      }
653    }
654  }
655}
656```
657
658### SecretKeyObserver
659
660Use the APIs provided by the **SecretKeyObserver** class to obtain the key status. After the key is destroyed, the EL5 database will be closed.
661
662```ts
663// SecretKeyObserver.ts
664import { ECStoreManager } from './ECStoreManager';
665
666export enum SecretStatus {
667  Lock,
668  UnLock
669}
670
671export class SecretKeyObserver {
672  onLock(): void {
673    this.lockStatuas = SecretStatus.Lock;
674    this.storeManager.closeEStore();
675  }
676
677  onUnLock(): void {
678    this.lockStatuas = SecretStatus.UnLock;
679  }
680
681  getCurrentStatus(): number {
682    return this.lockStatuas;
683  }
684
685  initialize(storeManager: ECStoreManager): void {
686    this.storeManager = storeManager;
687  }
688
689  updatalockStatus(code: number) {
690    if (this.lockStatuas === SecretStatus.Lock) {
691      this.onLock();
692    } else {
693      this.lockStatuas = code;
694    }
695  }
696
697  private lockStatuas: number = SecretStatus.UnLock;
698  private storeManager: ECStoreManager;
699}
700
701export let lockObserve = new SecretKeyObserver();
702```
703
704### ECStoreManager
705
706Use the APIs provided by the **ECStoreManager** class to manage the EL2 and EL5 databases. Specifically, you can use the APIs to configure a database, set the function used to move data, provide the database handle for the application based on the key status, close an EL5 database, and destroy an El2 database after the data is moved.
707
708```ts
709// ECStoreManager.ts
710import { relationalStore } from '@kit.ArkData';
711import { Mover } from './Mover';
712import { BusinessError } from '@kit.BasicServicesKit';
713import { StoreInfo, Store } from './Store';
714import { SecretStatus } from './SecretKeyObserver';
715
716let store = new Store();
717
718export class ECStoreManager {
719  config(cInfo: StoreInfo, other: StoreInfo): void {
720    this.cInfo = cInfo;
721    this.eInfo = other;
722  }
723
724  configDataMover(mover: Mover): void {
725    this.mover = mover;
726  }
727
728  async getCurrentStore(screanStatus: number): Promise<relationalStore.RdbStore> {
729    if (screanStatus === SecretStatus.UnLock) {
730      try {
731        this.eStore = await store.getECStore(this.eInfo);
732      } catch (e) {
733        let error = e as BusinessError;
734        console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`);
735      }
736      // Obtain an EL5 database when the screen is unlocked.
737      if (this.needMove) {
738        if (this.eStore != undefined && this.cStore != undefined) {
739          await this.mover.move(this.eStore, this.cStore);
740          console.info(`ECDB_Encry cstore data move to estore success`);
741        }
742        this.deleteCStore();
743        this.needMove = false;
744      }
745      return this.eStore;
746    } else {
747      // Obtain an EL2 database when the screen is locked.
748      this.needMove = true;
749      try {
750        this.cStore = await store.getECStore(this.cInfo);
751      } catch (e) {
752        let error = e as BusinessError;
753        console.error(`Failed to GetECStore.code is ${error.code},message is ${error.message}`);
754      }
755      return this.cStore;
756    }
757  }
758
759  closeEStore(): void {
760    this.eStore = undefined;
761  }
762
763  async deleteCStore() {
764    try {
765      await relationalStore.deleteRdbStore(this.cInfo.context, this.cInfo.storeId)
766    } catch (e) {
767      let error = e as BusinessError;
768      console.error(`Failed to create KVManager.code is ${error.code},message is ${error.message}`);
769    }
770  }
771
772  private eStore: relationalStore.RdbStore = null;
773  private cStore: relationalStore.RdbStore = null;
774  private cInfo: StoreInfo | null = null;
775  private eInfo: StoreInfo | null = null;
776  private needMove: boolean = false;
777  private mover: Mover | null = null;
778}
779```
780
781### EntryAbility
782
783Register a listener for the COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED event when the simulated application starts, and configure the database information and key status information.
784
785```ts
786// EntryAbility.ets
787import { AbilityConstant, contextConstant, UIAbility, Want } from '@kit.AbilityKit';
788import { hilog } from '@kit.PerformanceAnalysisKit';
789import { window } from '@kit.ArkUI';
790import { relationalStore } from '@kit.ArkData';
791import { ECStoreManager } from './ECStoreManager';
792import { StoreInfo } from './Store';
793import { Mover } from './Mover';
794import { SecretKeyObserver } from './SecretKeyObserver';
795import { commonEventManager } from '@kit.BasicServicesKit';
796import { BusinessError } from '@kit.BasicServicesKit';
797
798
799export let storeManager = new ECStoreManager();
800
801export let e_secretKeyObserver = new SecretKeyObserver();
802
803let mover = new Mover();
804
805let subscriber: commonEventManager.CommonEventSubscriber;
806
807export function createCB(err: BusinessError, commonEventSubscriber: commonEventManager.CommonEventSubscriber) {
808  if (!err) {
809    console.info('ECDB_Encry createSubscriber');
810    subscriber = commonEventSubscriber;
811    try {
812      commonEventManager.subscribe(subscriber, (err: BusinessError, data: commonEventManager.CommonEventData) => {
813        if (err) {
814          console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`);
815        } else {
816          console.info(`ECDB_Encry SubscribeCB ${data.code}`);
817          e_secretKeyObserver.updatalockStatus(data.code);
818        }
819      });
820    } catch (error) {
821      const err: BusinessError = error as BusinessError;
822      console.error(`subscribe failed, code is ${err.code}, message is ${err.message}`);
823    }
824  } else {
825    console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`);
826  }
827}
828
829let cInfo: StoreInfo | null = null;
830let eInfo: StoreInfo | null = null;
831
832export default class EntryAbility extends UIAbility {
833  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
834    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
835    let cContext = this.context;
836    cInfo = {
837      context: cContext,
838      config: {
839        name: 'cstore.db',
840        securityLevel: relationalStore.SecurityLevel.S3,
841      },
842      storeId: "cstore.db"
843    }
844    let eContext = this.context.createModuleContext("entry");
845    eContext.area = contextConstant.AreaMode.EL5;
846    eInfo = {
847      context: eContext,
848      config: {
849        name: 'estore.db',
850        securityLevel: relationalStore.SecurityLevel.S3,
851      },
852      storeId: "estore.db",
853    }
854    // Listen for the COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED event. code == 1 indicates the screen is unlocked, and code==0 indicates the screen is locked.
855    console.info(`ECDB_Encry store area : estore:${eContext.area},cstore${cContext.area}`)
856    try {
857      commonEventManager.createSubscriber({
858        events: ['COMMON_EVENT_SCREEN_LOCK_FILE_ACCESS_STATE_CHANGED']
859      }, createCB);
860      console.info(`ECDB_Encry success subscribe`);
861    } catch (error) {
862      const err: BusinessError = error as BusinessError;
863      console.error(`createSubscriber failed, code is ${err.code}, message is ${err.message}`);
864    }
865    storeManager.config(cInfo, eInfo);
866    storeManager.configDataMover(mover);
867    e_secretKeyObserver.initialize(storeManager);
868  }
869
870  onDestroy(): void {
871    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
872  }
873
874  onWindowStageCreate(windowStage: window.WindowStage): void {
875    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
876
877    windowStage.loadContent('pages/Index', (err) => {
878      if (err.code) {
879        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
880        return;
881      }
882      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
883    });
884  }
885
886  onWindowStageDestroy(): void {
887    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
888  }
889
890  onForeground(): void {
891    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
892  }
893
894  onBackground(): void {
895    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
896  }
897}
898```
899
900### Index Key Event
901
902Use **Button** to simulate application operations on the database, such as inserting, deleting, updating, and obtaining the data count, by clicking the button.
903
904```ts
905// Index.ets
906import { storeManager, e_secretKeyObserver } from "../entryability/EntryAbility";
907import { relationalStore } from '@kit.ArkData';
908import { Store } from '../entryability/Store';
909
910let storeOption = new Store();
911
912let lockStatus: number = 1;
913
914@Entry
915@Component
916struct Index {
917  @State message: string = 'Hello World';
918
919  build() {
920    Row() {
921      Column() {
922        Button ('Lock/Unlock').onClick ((event: ClickEvent) => {
923          if (lockStatus) {
924            e_secretKeyObserver.onLock();
925            lockStatus = 0;
926          } else {
927            e_secretKeyObserver.onUnLock();
928            lockStatus = 1;
929          }
930          lockStatus? this.message = "Unlocked": this.message = "Locked";
931        }).margin("5");
932        Button('store type').onClick(async (event: ClickEvent) => {
933          e_secretKeyObserver.getCurrentStatus() ? this.message = "estroe" : this.message = "cstore";
934          console.info(`ECDB_Encry current store : ${this.message}`);
935        }).margin("5");
936
937        Button("put").onClick(async (event: ClickEvent) => {
938          let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
939          storeOption.putOnedata(store);
940        }).margin(5)
941
942        Button("Get").onClick(async (event: ClickEvent) => {
943          let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
944          storeOption.getDataNum(store);
945        }).margin(5)
946
947        Button("delete").onClick(async (event: ClickEvent) => {
948          let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
949          storeOption.deleteAlldata(store);
950        }).margin(5)
951
952        Button("updata").onClick(async (event: ClickEvent) => {
953          let store: relationalStore.RdbStore = await storeManager.getCurrentStore(e_secretKeyObserver.getCurrentStatus());
954          storeOption.updataOnedata(store);
955        }).margin(5)
956
957        Text(this.message)
958          .fontSize(50)
959          .fontWeight(FontWeight.Bold)
960      }
961      .width('100%')
962    }
963    .height('100%')
964  }
965}
966```
967