提交 fe2f3136 编写于 作者: L l00613276

Merge branch 'master' of https://gitee.com/HelloCrease/docs

......@@ -16,7 +16,11 @@
- [Database Backup and Restoration](data-backup-and-restore.md)
- [Database Encryption](data-encryption.md)
- [Access Control by Device and Data Level](access-control-by-device-and-data-level.md)
- Cross-Application Data Sharing (for System Applications Only)
- [Cross-Application Data Sharing Overview](share-device-data-across-apps-overview.md)
- Cross-Application Data Sharing
- [Data Sharing Overview](data-share-overview.md)
- [Unified Data Definition](unified-data-definition.md)
- One-to-Many Data Sharing (Only for System Applications)
- [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
- [Sharing Data in Silent Access](share-data-by-silent-access.md)
- [Silent Access via the DatamgrService](share-data-by-silent-access.md)
- Many-to-Many Data Sharing
- [Sharing Data Using Unified Data Channels](unified-data-channels.md)
\ No newline at end of file
......@@ -3,7 +3,7 @@
## Function
Data management provides data storage, management, and synchronization capabilities. For example, you can store the Contacts application data in database for secure management and shared access, and synchronize the contacts information with a smart watch.
Data management provides data storage, management, and synchronization capabilities. For example, you can store the Contacts application data in database for secure management and shared access, and synchronize the Contacts information with a smart watch.
- Data storage: provides data persistence capabilities, which can be classified into user preferences, key-value (KV) stores, and relational database (RDB) stores by data characteristics.
......@@ -16,9 +16,9 @@ The database stores created by an application are saved to the application sandb
## Working Principles
The data management module includes user preferences (**Preferences**), KV data management (**KV-Store**), RDB data management (**RelationalStore**), distributed data object (**DataObject**), and cross-application data management (**DataShare**). The interface layer provides standard JavaScript APIs for application development. The Frameworks&System service layer implements storage and synchronization of component data, and provides dependencies for SQLite and other subsystems.
The data management module includes preferences, KV data management (KV-Store), relational data management (RelatoinalStore), distributed data object (DataObject), cross-application data management (DataShare), and unified data management framework (UDMF). The interface layer provides standard JavaScript APIs for application development. The Frameworks&System service layer implements storage and synchronization of component data, and provides dependencies for SQLite and other subsystems.
**Figure 1** Data management architecture
**Figure 1** Data management architecture
![dataManagement](figures/dataManagement.jpg)
......@@ -33,4 +33,7 @@ The data management module includes user preferences (**Preferences**), KV data
- **DataShare**: provides the data provider-consumer mode to implement addition, deletion, modification, and query of cross-application data on a device, and notification subscription. **DataShare** is not bound to any database and can interact with RDB and KV stores. You can also encapsulate your own databases for C/C++ applications.<br> In addition to the provider-consumer mode, **DataShare** provides silent access, which allows direct access to the provider's data via the DatamgrService proxy instead of starting the provider. Currently, only the RDB stores support silent access.
- **UDMF**: defines the data language and standards for cross-application and cross-device data interaction, improving data interaction efficiency. The UDMF provides secure and standard data transmission channels and supports different levels of data access permissions and lifecycle management policies. It helps implement efficient data sharing across applications and devices.
- **DatamgrService**: implements synchronization and cross-application sharing for other components, including cross-device synchronization of **RelationalStore** and **KV-Store**, silent access to provider data of **DataShare**, and temporary storage of **DataObject** synchronization object data.
......@@ -18,7 +18,7 @@ A relational database (RDB) store is used to store data in complex relational mo
**RelationalStore** provides APIs for applications to perform data operations. With SQLite as the underlying persistent storage engine, **RelationalStore** provides SQLite database features, including transactions, indexes, views, triggers, foreign keys, parameterized queries, prepared SQL statements, and more.
**Figure 1** Working mechanism
![relationStore_local](figures/relationStore_local.jpg)
......@@ -37,15 +37,15 @@ A relational database (RDB) store is used to store data in complex relational mo
The following table lists the APIs used for RDB data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
| API| Description|
| API| Description|
| -------- | -------- |
| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback&lt;RdbStore&gt;): void | Obtains a **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.|
| executeSql(sql: string, bindArgs: Array&lt;ValueType&gt;, callback: AsyncCallback&lt;void&gt;):void | Executes an SQL statement that contains specified arguments but returns no value.|
| insert(table: string, values: ValuesBucket, callback: AsyncCallback&lt;number&gt;):void | Inserts a row of data into a table.|
| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.|
| delete(predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.|
| query(predicates: RdbPredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;ResultSet&gt;):void | Queries data in the RDB store based on specified conditions.|
| deleteRdbStore(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes an RDB store.|
| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback&lt;RdbStore&gt;): void | Obtains a **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.|
| executeSql(sql: string, bindArgs: Array&lt;ValueType&gt;, callback: AsyncCallback&lt;void&gt;):void | Executes an SQL statement that contains specified arguments but returns no value.|
| insert(table: string, values: ValuesBucket, callback: AsyncCallback&lt;number&gt;):void | Inserts a row of data into a table.|
| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.|
| delete(predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.|
| query(predicates: RdbPredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;ResultSet&gt;):void | Queries data in the RDB store based on specified conditions.|
| deleteRdbStore(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes an RDB store.|
## How to Develop
......@@ -53,7 +53,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
1. Obtain an **RdbStore** instance.<br> Example:
Stage model:
```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import UIAbility from '@ohos.app.ability.UIAbility';
......@@ -65,7 +65,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
securityLevel: relationalStore.SecurityLevel.S1 // Database security level.
};
// The current RDB store version is 3, and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES).
// The RDB store version is 3, and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES).
const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // SQL statement for creating a data table.
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
......@@ -106,7 +106,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
FA model:
```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import featureAbility from '@ohos.ability.featureAbility';
......@@ -160,10 +160,12 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
>
> - The RDB store created by an application varies with the context. Multiple RDB stores are created for the same database name with different application contexts. For example, each UIAbility has its own context.
>
> - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. If you want to move the files of an RDB store to another place for view, you must also move the temporary files with finename extensions **-wal** or **-shm** in the same directory. Once an application is uninstalled, the database files and temporary files generated by the application on the device are also removed.
> - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. When the RDB store is used, temporary files ended with **-wal** and **-shm** may be generated in the same directory as the database file. If you want to move the database files to other places, you must also move these temporary files. After the application is uninstalled, the database files and temporary files generated on the device are also removed.
2. Use **insert()** to insert data to the RDB store. Example:
2. Use **insert()** to insert data to the RDB store.
Example:
```js
const valueBucket = {
'NAME': 'Lisa',
......@@ -177,13 +179,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
return;
}
console.info(`Succeeded in inserting data. rowId:${rowId}`);
})
})
```
> **NOTE**
>
>
> **RelationalStore** does not provide explicit flush operations for data persistence. Data inserted by **insert()** is stored in files persistently.
3. Modify or delete data based on the specified **Predicates** instance.
Use **update()** to modify data and **delete()** to delete data.
......@@ -254,13 +256,15 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
5. Delete the RDB store.
Use **deleteRdbStore()** to delete the RDB store and related database files.
Use **deleteRdbStore()** to delete the RDB store and related database files.
Example:
> **NOTE**
>
> After the deletion, you are advised to set the database object to null.
Stage model:
```js
import UIAbility from '@ohos.app.ability.UIAbility';
......@@ -271,6 +275,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
store = null;
console.info('Succeeded in deleting RdbStore.');
});
}
......@@ -279,7 +284,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
FA model:
```js
import featureAbility from '@ohos.ability.featureAbility';
......@@ -291,6 +296,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return;
}
store = null;
console.info('Succeeded in deleting RdbStore.');
});
```
# Cross-Application Data Sharing
## Introduction
OpenHarmony provides APIs for an application to manage its own data and share data with other applications.
Data needs to be shared in a wealth of scenarios. For example, the Contacts, short message service (SMS), and Gallery data always needs to be shared with other applications. However, certain data, such as accounts and passwords, cannot be shared. Some data, such as SMS messages, can be queried but not modified by other applications. Therefore, a secure and efficient cross-application data sharing mechanism for different data sharing scenarios and data privacy protection is very important.
Currently, OpenHarmony supports one-to-many and many-to-many cross-application data sharing, based on the number of the data provider applications involved.
## Basic Concepts
Before you start, understand the following concepts:
- **Data provider**: an application that provides data and implements related services. It is also called the producer or server.
- **Data consumer**: an application that accesses the data or services provided by the data provider. It is also called the client.
- **ValuesBucket**: a set of data to be inserted. It can be one or more data records in key-value (KV) pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array.
- **ResultSet**: a set of query results. It provides flexible modes for users to obtain various data.
- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database.
## Unified Data Definition
When data needs to be shared among multiple applications, a large amount of data needs to be converted for data interaction because the data definition and format vary with applications. To reduce application/service data interaction costs, OpenHarmony uses the unified data definition as the unified data language to build cross-application data interaction standards.
The unified data definition defines common data types. Applications can use the APIs provided by the Unified Data Management Framework (UDMF) to create and use these data types. For details, see [Unified Data Definition](unified-data-definition.md).
## One-to-Many Cross-Application Data Sharing
You can use **DataShare** to implement one-to-many data sharing across applications. Two implementation modes are provided, depending on whether the data provider is started in the cross-application data sharing.
### Implementation
The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs because the **DataShare** access mode does not vary with the data provision mode. This greatly reduces the learning time and development difficulty.
**DataShare** implements cross-application data sharing in either of the following ways:
- [Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
You need to implement an ExtensionAbility with callbacks in the HAP. When the data consumer calls an API, the ExtensionAbility of the data provider will be automatically started to invoke the registered callback.
You can use **DataShareExtensionAbility** when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases.
- [Using Silent Access via the DatamgrService](share-data-by-silent-access.md)
You need to configure database access rules in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider.
You can use this mode when the cross-application data access involves only database operations (data addition, deletion, modification, and query) or data hosted to the DatamgrService.
If your application is signed with a system signature, you can use both methods. When data is created for the first time, use **DataShareExtensionAbility**. When data is accessed and modified later, use the **DatamgrService** to share data. That is, the data provider is started only when the data is accessed for the first time.
### Restrictions
- **DataShare** is subject to the limitations on the database used by the data provider. For example, the supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use.
- The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by IPC.
- Currently, **dataShare** supports development based on the stage model only.
## Many-to-Many Cross-Application Data Sharing
In one-to-many cross-application data sharing, there is only one data provider. In many-to-many cross-application data sharing, you need to consider data definition, data exchange, and permission management. The UDMF provides a new data sharing and interaction mode to implement many-to-many cross-application data sharing.
### Implementation
[Sharing Data via Unified Data Channels](unified-data-channels.md)
Applications can call the APIs provided by the UDMF to write data that complies with the unified data definition to different data sharing channels of the UDMF. The data in these channels can be read by other applications. The data written into the UDMF is managed based on the permissions of the application, permissions of the data channels, and the permission management logic of the UDMF. Lifecycle management is also performed on the data written into the channels in the same way. In this way, the data scattered in each application is aggregated via different channels of the UDMF, improving the development efficiency and data experience of users.
......@@ -16,8 +16,7 @@ There are two roles in **DataShare**:
- Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper).
**Figure 1** Data sharing mechanism
**Figure 1** Data sharing mechanism
![dataShare](figures/dataShare.jpg)
- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications.
......@@ -32,7 +31,7 @@ There are two roles in **DataShare**:
## How to Develop
### Data Provider Application Development (Only for System Applications)
### Data Provider Application (Only for System Applications)
[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required.
......@@ -146,7 +145,7 @@ override the service implementation as required. For example, if the data provid
"icon": "$media:icon",
"description": "$string:description_datashareextability",
"type": "dataShare",
"uri": "datashare://com.samples.datasharetest.DataShare",
"uri": "datashareproxy://com.samples.datasharetest.DataShare",
"exported": true,
"metadata": [{"name": "ohos.extension.dataShare", "resource": "$profile:data_share_config"}]
}
......@@ -155,11 +154,11 @@ override the service implementation as required. For example, if the data provid
**Table 2** Fields in the data_share_config.json file
| Field| Description | Mandatory|
| ------------ | ------------------------------------------------------------ | --- |
| tableConfig | Label configuration.| Yes|
| uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:<br> 1. *****: indicates all databases and tables.<br> 2. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}**: specifies a database.<br> 3. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}/{*tableName*}**: specifies a table.<br>If URIs of different formats are configured, only the URI with higher priority takes effect. | Yes|
| crossUserMode | Whether data is shared by multiple users. The value **1** means to share data between multiple users, and the value **2** means the opposite. | Yes|
| Field | Description | Mandatory |
| ------------- | ---------------------------------------- | ---- |
| tableConfig | Label configuration. | Yes |
| uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:<br> - *****: indicates all databases and tables.<br> - **datashareproxy://{bundleName}/{moduleName}/{storeName}**: specifies a database.<br>- **datashareproxy://{bundleName}/{moduleName}/{storeName}/{tableName}**: specifies a table. | Yes |
| crossUserMode | Whether data is shared by multiple users.<br>The value **1** means to share data between multiple users, and the value **2** means the opposite. | Yes |
**data_share_config.json Example**
......@@ -170,18 +169,18 @@ override the service implementation as required. For example, if the data provid
"crossUserMode": 1
},
{
"uri": "datashare:///com.acts.datasharetest/entry/DB00",
"uri": "datashareproxy://com.acts.datasharetest/entry/DB00",
"crossUserMode": 1
},
{
"uri": "datashare:///com.acts.datasharetest/entry/DB00/TBL00",
"uri": "datashareproxy://com.acts.datasharetest/entry/DB00/TBL00",
"crossUserMode": 2
}
]
```
### Data Consumer Application Development
### Data Consumer Application
1. Import the dependencies.
......@@ -195,7 +194,7 @@ override the service implementation as required. For example, if the data provid
```js
// Different from the URI defined in the module.json5 file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/).
let dseUri = ('datashare:///com.samples.datasharetest.DataShare');
let dseUri = ('datashareproxy://com.samples.datasharetest.DataShare');
```
3. Create a **DataShareHelper** instance.
......@@ -239,3 +238,4 @@ override the service implementation as required. For example, if the data provid
console.info(`dsHelper delete result:${data}`);
});
```
# Data Sharing Through Silent Access
# Silent Access via the DatamgrService
## When to Use
In a typical cross-application data access scenario, an application may be started multiple times.
In a typical cross-application data access scenario, the data provider may be started multiple times.
To reduce the number of application startup times and improve the access speed, OpenHarmony provides the silent access feature, which allows direct access to the database without starting the data provider.
To reduce the number of startup times of the data provider and improve the access speed, OpenHarmony provides the silent access feature, which allows access to the database without starting the data provider.
Silent access supports only basic database access. If service processing is required, implement service processing in the data consumer.
In silent data access, the DatamgrService accesses and modifies data without starting the data provider.
If the service processing is complex, use **DataShareExtensionAbility** to start the data provider.
The DatamgrService supports basic database access or data hosting only. If service processing is required, the service processing needs to be encapsulated into APIs for the data consumer to call.
If the service processing is too complex to be processed by the data consumer, use **DataShareExtensionAbility** to start the data provider.
## Working Principles
The DatamgrService can serve as a proxy to access the following types of data:
- Persistent data
Persistent data belongs to the database of the data provider. It is stored in the sandbox of the data provider and can be shared in declaration mode by the data provider. Persistent data is configured as data tables for access.
- Process data
The process data managed by the **DatamgrService** is stored in the DatamgrService sandbox in JSON or byte format. This type of data is automatically deleted 10 days after no subscription.
| Type | Storage Location | Data Format | Validity Period | Application Scenario |
| ----- | --------- | ----------- | ------------ | --------------------------------- |
| Persistent data| Sandbox of the data provider | Tables in the database | Permanent storage | RDB data applications, such as schedules and conferences. |
| Process data | DatamgrService sandbox| JSON or byte| Automatically deleted 10 days after no subscription| Applications featuring simple and time-sensitive data, such as step count, weather, and heart rate.|
**Figure 1** Silent access
![silent_dataShare](figures/silent_dataShare.jpg)
......@@ -21,26 +42,271 @@ If the service processing is complex, use **DataShareExtensionAbility** to start
- In silent access, **DatamgrService** obtains the access rules configured by the data provider through directory mapping, performs preprocessing based on rules, and accesses the database.
- To use silent access, the URIs must be in the following format:
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true
datashareproxy://{bundleName}/{dataPath}
The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data.
**dataPath** identifies the data. It can be customized and must be unique in the same data provider application.
"Proxy=true" means to access data without starting the data provider. If **Proxy** is not set to **true**, the data provider is started.
## Constraints
The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data.
- Currently, only the RDB stores support silent data access.
- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing.
- The proxy is not allowed to create a database for persistent data. To create a database, you must start the data provider.
- If the data provider is an application with a normal signature, the data read/write permission must be system_basic or higher.
## Constraints
## Available APIs
- Currently, only RDB stores support silent access.
The following table lists the APIs for silent data access. Most of the APIs are executed asynchronously in callback or promise mode. In the following table, callback-based APIs are used as an example. For more information about the APIs, see [Data Sharing](../reference/apis/js-apis-data-dataShare.md).
- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing.
### Common API
| API | Description |
| ---------------------------------------- | -------------------- |
| createDataShareHelper(context: Context, uri: string, options: DataShareHelperOptions, callback: AsyncCallback&lt;DataShareHelper&gt;): void | Creates a **DataShareHelper** instance.|
### APIs for Persistent Data
| API | Description |
| ---------------------------------------- | -------------------- |
| insert(uri: string, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Inserts a row of data into a table. |
| delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback&lt;number&gt;): void | Deletes one or more data records from the database. |
| query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;DataShareResultSet&gt;): void | Queries data in the database. |
| update(uri: string, predicates: dataSharePredicates.DataSharePredicates, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Updates data in the database. |
| addTemplate(uri: string, subscriberId: string, template: Template): void | Adds a data template with the specified subscriber. |
| on(type: 'rdbDataChange', uris: Array&lt;string&gt;, templateId: TemplateId, callback: AsyncCallback&lt;RdbDataChangeNode&gt;): Array&lt;OperationResult | Subscribes to the changes of the data corresponding to the specified URI and template.|
### APIs for Process Data
| API | Description |
| ---------------------------------------- | ------------------ |
| publish(data: Array&lt;PublishedItem&gt;, bundleName: string, version: number, callback: AsyncCallback&lt;Array&lt;OperationResult&gt;&gt;): void | Publish data to the **DatamgrService**.|
| on(type: 'publishedDataChange', uris: Array&lt;string&gt;, subscriberId: string, callback: AsyncCallback&lt;PublishedDataChangeNode&gt;): Array&lt;OperationResult&gt; | Subscribes to changes of the published data. |
## Implementation of the Persistence Data
The following describes how to share an RDB store.
### Data Provider Application
1. In the **module.json5** file, set the ID, read/write permissions, and basic information of the table to be shared under **proxyDatas**.
**Table 1** Fields of proxyDatas in module.json5
| Field | Description | Mandatory |
| ----------------------- | ---------------------------------------- | ---- |
| uri | URI of the data, which is the unique identifier for cross-application data access. | Yes |
| requiredReadPermission | Permission required for reading data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md). | No |
| requiredWritePermission | Permission required for modifying data from the data proxy. If this parameter is not set, other applications are not allowed to modify the data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md). | No |
| metadata | Data source information, including the **name** and **resource** fields.<br> The **name** field identifies the configuration, which has a fixed value of **dataProperties**.<br> The value of **resource** is **$profile:{fileName}**, indicating that the name of the configuration file is **{fileName}.json**.| Yes |
**module.json5 example**
```json
"proxyDatas":[
{
"uri": "datashareproxy://com.acts.ohos.data.datasharetest/test",
"requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
"requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"metadata": {
"name": "dataProperties",
"resource": "$profile:my_config"
}
}
]
```
**Table 2** Fields in my_config.json
| Field | Description | Mandatory |
| ----- | ---------------------------------------- | ---- |
| path | Data source path, in the **Database_name/Table_name** format. Currently, only RDB stores are supported. | Yes |
| type | Database type. Currently, only **rdb** is supported. | Yes |
| scope | Scope of the database.<br>- **module** indicates that the database is located in this module.<br>- **application** indicates that the database is located in this application.| No |
**my_config.json example**
```json
{
"path": "DB00/TBL00",
"type": "rdb",
"scope": "application"
}
```
### Data Consumer Application
1. Import dependencies.
```js
import dataShare from '@ohos.data.dataShare';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
2. Define the URI string for communicating with the data provider.
```js
let dseUri = ('datashareproxy://com.acts.ohos.data.datasharetest/test');
```
3. Create a **DataShareHelper** instance.
```js
let dsHelper;
let abilityContext;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, "", {
isProxy: true
}, (err, data) => {
dsHelper = data;
});
}
}
```
4. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
```js
// Construct a piece of data.
let valuesBucket = {
'name': 'ZhangSan', 'age': 21, 'isStudent': false, 'Binary': new Uint8Array([1, 2, 3])
};
let updateBucket = {
'name': 'LiSi', 'age': 18, 'isStudent': true, 'Binary': new Uint8Array([1, 2, 3])
};
let predicates = new dataSharePredicates.DataSharePredicates();
let valArray = ['*'];
// Insert a piece of data.
dsHelper.insert(dseUri, valuesBucket, (err, data) => {
console.info(`dsHelper insert result:${data}`);
});
// Update data.
dsHelper.update(dseUri, predicates, updateBucket, (err, data) => {
console.info(`dsHelper update result:${data}`);
});
// Query data.
dsHelper.query(dseUri, predicates, valArray, (err, data) => {
console.info(`dsHelper query result:${data}`);
});
// Delete data.
dsHelper.delete(dseUri, predicates, (err, data) => {
console.info(`dsHelper delete result:${data}`);
});
```
5. Subscribe to the specified data.
```js
function onCallback(err, node: dataShare.RdbDataChangeNode) {
console.info("uri " + JSON.stringify(node.uri));
console.info("templateId " + JSON.stringify(node.templateId));
console.info("data length " + node.data.length);
for (let i = 0; i < node.data.length; i++) {
console.info("data " + node.data[i]);
}
}
let template = {
predicates: {
"p1": "select * from TBL00",
"p2": "select name from TBL00",
},
scheduler: ""
}
dsProxyHelper.addTemplate(dseUri, "111", template);
let templateId: dataShare.TemplateId = {
subscriberId: "111",
bundleNameOfOwner: "com.acts.ohos.data.datasharetestclient"
}
// When the DatamgrService modifies data, onCallback is invoked to return the data queried based on the rules in the template.
let result: Array<dataShare.OperationResult> = dsProxyHelper.on("rdbDataChange", [dseUri], templateId, onCallback);
```
## Implementation of the Process Data
The following describes how to host process data.
### (Optional) Data Provider Application
In the **module.json5** file of the data provider, set the process data ID, read/write permissions, and basic information under **proxyDatas**.
> **NOTE**
>
> - This step is optional.
> - If **proxyDatas** is not configured, the hosted data cannot be accessed by other applications.
> - If **proxyDatas** is not configured, you do not need to use the full data path. For example, you can use **weather** instead of **datashareproxy://com.acts.ohos.data.datasharetest/weather** when publishing, subscribing to, and querying data.
**Table 3** Fields of proxyDatas in module.json5
| Field | Description | Mandatory |
| ----------------------- | ----------------------------- | ---- |
| uri | URI of the data, which is the unique identifier for cross-application data access. | Yes |
| requiredReadPermission | Permission required for reading data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md).| No |
| requiredWritePermission | Permission required for modifying data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md).| No |
**module.json5 example**
```json
"proxyDatas": [
{
"uri": "datashareproxy://com.acts.ohos.data.datasharetest/weather",
"requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
"requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING"
}
]
```
### Data Consumer Application
1. Import dependencies.
```js
import dataShare from '@ohos.data.dataShare';
```
2. Create a **DataShareHelper** instance.
```js
let dsHelper;
let abilityContext;
- A proxy cannot be used to create a database. If a database needs to be created, the data provider must be started.
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, "", {isProxy : true}, (err, data) => {
dsHelper = data;
});
}
}
```
3. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
## How to Develop
```js
// Construct two pieces of data. The first data is not configured with proxyDatas and cannot be accessed by other applications.
let data : Array<dataShare.PublishedItem> = [
{key:"city", subscriberId:"11", data:"xian"},
{key:"datashareproxy://com.acts.ohos.data.datasharetest/weather", subscriberId:"11", data:JSON.stringify("Qing")}];
// Publish data.
let result: Array<dataShare.OperationResult> = await dsProxyHelper.publish(data, "com.acts.ohos.data.datasharetestclient");
```
The URI must be in the following format:
4. Subscribe to the specified data.
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true
```js
function onPublishCallback(err, node:dataShare.PublishedDataChangeNode) {
console.info("onPublishCallback");
}
let uris:Array<string> = ["city", "datashareproxy://com.acts.ohos.data.datasharetest/weather"];
let result: Array<dataShare.OperationResult> = dsProxyHelper.on("publishedDataChange", uris, "11", onPublishCallback);
```
For details about the development procedure and implementation, see [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md).
......@@ -7,7 +7,7 @@ The distributed application data synchronization allows the data of an applicati
For example, when data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. You can use this feature in the distributed Gallery, Notepad, Contacts, and File Manager.
For details about how to subscribe to database change notifications between different applications, see [Sharing Application Data with Other Applications](share-device-data-across-apps-overview.md).
For details about how to subscribe to database change notifications between different applications, see [Cross-Application Data Sharing](data-share-overview.md).
The data storage modes vary depending on the lifecycle of data to be synchronized:
......@@ -24,7 +24,7 @@ In a distributed scenario, cross-device collaboration demands consistent data be
The data consistency can be classified into the following types:
- Strong consistency: When data is inserted, deleted, or modified on a device, other devices in the same network can obtain the updates eventually, but may not immediately.
- Strong consistency: When data is inserted, deleted, or modified on a device, other devices in the same network will obtain the latest data immediately. Once data is modified, the devices can read the updated data eventually, but may not read the updated data immediately.
- Weak consistency: When data is added, deleted, or modified on a device, other devices in the same network may or may not obtain the updates. The data on these devices may be inconsistent after a certain period of time.
......
# Sharing Data via Unified Data Channels
## When to Use
In many-to-many data sharing across applications, a data channel needs to be provided to access data of different applications and share the data with other applications.
The Unified Data Management Framework (UDMF) provides unified data channels and standard data access interfaces for different service scenarios of many-to-many cross-application data sharing.
## Definition and Implementation of Unified Data Channels
The unified data channel provides cross-application data access for various service scenarios. It can temporarily store the unified data objects to be shared by an application, and manage the access permissions and lifecycle of the data according to certain policies.
The unified data channel is implemented by the system ability provided by the UDMF. When an application (data provider) needs to share data, it calls the **insert()** method provided by the UDMF to write the data to the UDMF data channel, and calls UDMF **update()** or **delete()** to update or delete the data. After passing the permission verification, the target application (data consumer) calls the UDMF **read()** to access the data. After the data is read, the UDMF performs lifecycle management of the data.
The unified data object (**UnifiedData**) is uniquely identified by a URI in the UDMF data channel. The URI is in the **udmf://*intention*/*bundleName*/*groupId*** format, where:
+ **udmf**: protocol used to provide the data channel.
+ *intention*: an enum of the data channel types supported by the UDMF.
+ *bundleName*: bundle name of the data source application.
+ *groupId*: group ID used for batch data management.
Currently, the UDMF provides the public data channel for cross-application data sharing.
**Public data channel**: allows applications to write and read data. The corresponding **intention** is **DATA_HUB**.
## Available APIs
The following table lists the UDMF APIs. All of them are executed asynchronously in callback or promise mode. In the following table, callback-based APIs are used as an example. For more information about the APIs, see [UDMF](../reference/apis/js-apis-data-udmf.md).
| API | Description |
|-----------------------------------------------------------------------------------------|---------------------------------------------|
| insertData(options: Options, data: UnifiedData, callback: AsyncCallback\<string>): void | Inserts data to the UDMF public data channel. A unique data identifier is returned.|
| updateData(options: Options, data: UnifiedData, callback: AsyncCallback\<void>): void | Updates the data in the UDMF public data channel. |
| queryData(options: Options, callback: AsyncCallback\<Array\<UnifiedData>>): void | Queries data in the UDMF public data channel. |
| deleteData(options: Options, callback: AsyncCallback\<Array\<UnifiedData>>): void | Deletes data from the UDMF public data channel. The deleted data set is returned.|
## How to Develop
The following example describes how to implement many-to-many data sharing. The data provider writes data to the UMDF public data channel, and updates and deletes the data. The data consumer obtains the data shared by the data provider.
### Data Provider
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Create a **UnifiedData** object and insert it into the UDMF public data channel.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'hello world!';
let unifiedData = new UDMF.UnifiedData(plainText);
// Specify the type of the data channel to which the data is to be inserted.
let options = {
intention: UDMF.Intention.DATA_HUB
}
try {
UDMF.insertData(options, unifiedData, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in inserting data. key = ${data}`);
} else {
console.error(`Failed to insert data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Insert data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
3. Update the **UnifiedData** object inserted.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'How are you!';
let unifiedData = new UDMF.UnifiedData(plainText);
// Specify the URI of the UnifiedData object to update.
let options = {
key: 'udmf://DataHub/com.ohos.test/0123456789'
};
try {
UDMF.updateData(options, unifiedData, (err) => {
if (err === undefined) {
console.info('Succeeded in updating data.');
} else {
console.error(`Failed to update data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Update data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
4. Delete the **UnifiedData** object from the UDMF public data channel.
```ts
// Specify the type of the data channel whose data is to be deleted.
let options = {
intention: UDMF.Intention.DATA_HUB
};
try {
UDMF.deleteData(options, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in deleting data. size = ${data.length}`);
for (let i = 0; i < data.length; i++) {
let records = data[i].getRecords();
for (let j = 0; j < records.length; j++) {
if (records[j].getType() === UDMF.UnifiedDataType.PLAIN_TEXT) {
let text = <UDMF.PlainText>(records[j]);
console.info(`${i + 1}.${text.textContent}`);
}
}
}
} else {
console.error(`Failed to delete data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Delete data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
### Data Consumer
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Query the **UnifiedData** object in the UDMF public data channel.
```ts
// Specify the type of the data channel whose data is to be queried.
let options = {
intention: UDMF.Intention.DATA_HUB
};
try {
UDMF.queryData(options, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in querying data. size = ${data.length}`);
for (let i = 0; i < data.length; i++) {
let records = data[i].getRecords();
for (let j = 0; j < records.length; j++) {
if (records[j].getType() === UDMF.UnifiedDataType.PLAIN_TEXT) {
let text = <UDMF.PlainText>(records[j]);
console.info(`${i + 1}.${text.textContent}`);
}
}
}
} else {
console.error(`Failed to query data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Query data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
# Unified Data Definition
## When to Use
To streamline cross-application data interaction of OpenHarmony and minimize the application/service data interaction costs, the Unified Data Management Framework (UDMF) provides standard data definitions to define common data types. Applications can use the APIs provided by the UDMF to create and use these data types.
## Unified Data Types
The UDMF provides the following unified data types:
**Basic data types**<br>Basic data types include File and Text, which can be used for cross-application and cross-platform data interaction. Figure 1 and Figure 2 illustrate the basic data types.
**Figure 1** UDMF File
![UDMF_FILE](figures/udmf_type_File.png)
Figure 2 UDMF Text
![UDMF_TEXT](figures/udmf_type_Text.png)
**System Defined Types (SDTs)**<br>The SDTs are specific to the platform or operating system, such as Form (UI card information), AppItem (app description information), and PixelMap (thumbnail). This type of data can be used for cross-application data interaction in a system or platform. Figure 3 illustrates the SDT data.
**Figure 3** UDMF SDT data
![UDMF_SDT](figures/udmf_type_SDT.png)
**App Defined Type (ADT)**<br>The SDT data is application-specific. This type of data can be used for across-platform data interaction for an application. As shown in Figure 4, the MyFile file format can be defined for use in an application ecosystem.
**Figure 4** UDMF ADT data
![UDMF_ADT](figures/udmf_type_ADT.png)
## Restrictions
- The size of each data record in the UDMF cannot exceed 2 MB.
- The UDMF supports data group management. The size of each group cannot exceed 4 MB.
## Available APIs
The UDMF provides the unified data object **UnifiedData** to encapsulate a group of data records **UnifiedRecord**. **UnifiedRecord** is an abstract definition of data content supported by the UDMF, for example, a text record or an image record. The data content type in a data record corresponds to **UnifiedDataType**.
The following table describes common UDMF APIs. For more information, see [UDMF](../reference/apis/js-apis-data-udmf.md).
| Class | API | Description |
|---------------|-------------------|-----------------------------------------------------------------------------------------------|
| UnifiedRecord | getType(): string | Obtains the data type of this data record.|
| UnifiedData | constructor(record: UnifiedRecord) | A constructor used to create a **UnifiedData** object with a data record. |
| UnifiedData | addRecord(record: UnifiedRecord): void | Adds a data record to this **UnifiedRecord** object. |
| UnifiedData | getRecords(): Array\<UnifiedRecord> | Obtains all data records from this **UnifiedData** object. The data obtained is of the **UnifiedRecord** type. You need to obtain the data type by using **getType** and convert the data type to a child class before using it.|
## How to Develop
The following describes how to create a **UnifiedData** object containing two data records: image and plain text.
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Create an image data record and initialize the **UnifiedData** object with the image data record.
(1) Create an image data record.
```ts
let image = new UDMF.Image();
```
(2) Modify object attributes.
```ts
// The Image object contains the imageUri attribute.
image.imageUri = '...';
```
(3) Access the object attributes.
```ts
console.info(`imageUri = ${image.imageUri}`);
```
(4) Create a **UnifiedData** instance.
```ts
let unifiedData = new UDMF.UnifiedData(image);
```
3. Create a plain text data record and add it to the **UnifiedData** instance created.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'this is textContent of plainText';
plainText.abstract = 'abstract of plainText';
plainText.details = {
plainKey1: 'plainValue1',
plainKey2: 'plainValue2',
};
unifiedData.addRecord(plainText);
```
4. Obtain all data records in this **UnifiedData** instance.
```ts
let records = unifiedData.getRecords();
```
5. Traverse each record, determine the data type of the record, and convert the record into a child class object to obtain the original data record.
```ts
for (let i = 0; i < records.length; i ++) {
// Read the type of the data record.
let type = records[i].getType();
switch (type) {
case UDMF.UnifiedDataType.IMAGE:
// Convert the data to obtain the original image data record.
let image = <UDMF.Image>(records[i]);
break;
case UDMF.UnifiedDataType.PLAIN_TEXT:
// Convert the data to obtain the original text record.
let plainText = <UDMF.PlainText>(records[i]);
break;
default:
break;
}
}
```
......@@ -4,6 +4,8 @@ When a user needs to download a file from the network to a local directory or sa
The operations for saving images, audio or video clips, and documents are similar. Call **save()** of the corresponding picker instance and pass in **saveOptions**.
The **save()** interface saves the file in the file manager, not in the Gallery.
## Saving Images or Video Files
......@@ -23,14 +25,14 @@ The operations for saving images, audio or video clips, and documents are simila
3. Create a **photoViewPicker** instance and call [save()](../reference/apis/js-apis-file-picker.md#save) to open the **FilePicker** page to save the files. After the user selects the target folder, the file saving operation is complete. After the files are saved successfully, the URIs of the files saved are returned.
<br>The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts
let URI = null;
let uri = null;
const photoViewPicker = new picker.PhotoViewPicker();
photoViewPicker.save(photoSaveOptions).then((photoSaveResult) => {
URI = photoSaveResult[0];
console.info('photoViewPicker.save to file succeed and URI is:' + URI);
uri = photoSaveResult[0];
console.info('photoViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke photoViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -39,7 +41,7 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE);
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd);
```
......@@ -72,11 +74,11 @@ The operations for saving images, audio or video clips, and documents are simila
The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts
let URI = null;
let uri = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => {
URI = documentSaveResult[0];
console.info('documentViewPicker.save to file succeed and URI is:' + URI);
uri = documentSaveResult[0];
console.info('documentViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -85,7 +87,7 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE);
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd);
```
......@@ -118,11 +120,11 @@ The operations for saving images, audio or video clips, and documents are simila
The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts
let URI = null;
let uri = null;
const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.save(audioSaveOptions).then((audioSelectResult) => {
URI = audioSelectResult[0];
console.info('audioViewPicker.save to file succeed and URI is:' + URI);
uri = audioSelectResult[0];
console.info('audioViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke audioViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -131,11 +133,11 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE);
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd);
```
5. Use [fs.writeSync](../reference/apis/js-apis-file-fs.md#writesync) to edit the file based on the FD, and then close the FD.
5. Use [fs.writeSync()](../reference/apis/js-apis-file-fs.md#writesync) to edit the file based on the FD, and then close the FD.
```ts
let writeLen = fs.writeSync(file.fd, 'hello, world');
......
......@@ -35,14 +35,14 @@ The **FilePicker** provides the following interfaces by file type:
4. Create a **photoPicker** instance and call [select()](../reference/apis/js-apis-file-picker.md#select) to open the **FilePicker** page for the user to select files. After the files are selected, [PhotoSelectResult](../reference/apis/js-apis-file-picker.md#photoselectresult) is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts
let URI = null;
let uri = null;
const photoViewPicker = new picker.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
URI = photoSelectResult.photoUris[0];
console.info('photoViewPicker.select to file succeed and URI is:' + URI);
uri = photoSelectResult.photoUris[0];
console.info('photoViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -51,7 +51,7 @@ The **FilePicker** provides the following interfaces by file type:
5. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY);
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd);
```
......@@ -81,20 +81,20 @@ The **FilePicker** provides the following interfaces by file type:
3. Create a **documentViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-3) to open the **FilePicker** page for the user to select documents. After the documents are selected, a result set containing the file URIs is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
<br>For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attribute information, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md).
For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attribute information, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md).
> **NOTE**
>
> Currently, **DocumentSelectOptions** is not configurable. By default, all types of user files are selected.
```ts
let URI = null;
let uri = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
URI = documentSelectResult[0];
console.info('documentViewPicker.select to file succeed and URI is:' + URI);
uri = documentSelectResult[0];
console.info('documentViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -129,7 +129,7 @@ The **FilePicker** provides the following interfaces by file type:
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY);
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd);
```
......@@ -160,20 +160,20 @@ The **FilePicker** provides the following interfaces by file type:
3. Create an **audioViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-6) to open the **FilePicker** page for the user to select audio files. After the files are selected, a result set containing the URIs of the audio files selected is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
<br>For example, use the [file management interface](../reference/apis/js-apis-file-fs.md) to obtain the file handle (FD) of the audio clip based on the URI, and then develop the audio playback function based on the media service. For details, see [Audio Playback Development](../media/audio-playback-overview.md).
For example, use the [file management interface](../reference/apis/js-apis-file-fs.md) to obtain the file handle (FD) of the audio clip based on the URI, and then develop the audio playback function based on the media service. For details, see [Audio Playback Development](../media/audio-playback-overview.md).
> **NOTE**
>
> Currently, **AudioSelectOptions** is not configurable. By default, all types of user files are selected.
```ts
let URI = null;
let uri = null;
const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.select(audioSelectOptions).then(audioSelectResult => {
URI = audioSelectOptions[0];
console.info('audioViewPicker.select to file succeed and URI is:' + URI);
uri = audioSelectOptions[0];
console.info('audioViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => {
console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
})
......@@ -182,7 +182,7 @@ The **FilePicker** provides the following interfaces by file type:
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY);
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd);
```
......
......@@ -180,7 +180,7 @@ Represents the text data. It is a child class of [UnifiedRecord](#unifiedrecord)
| Name | Type | Readable| Writable| Description |
| ------- | ------------------------- | ---- | ---- |-----------------------------------------------------------------------------------------------------------------------------------------------------|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe the text content. For example, a data object with the following content can be created to describe a text file:<br>{<br>"title":"Title",<br>"content":"Content"<br>}<br>This parameter is optional. The default value is an empty dictionary object.|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe the text content. For example, a data object with the following content can be created to describe a text file:<br>{<br>"title":"Title",<br>"content":"Content"<br>}<br> This parameter is optional. The default value is an empty dictionary object.|
**Example**
......@@ -258,7 +258,7 @@ Represents the file data. It is a child class of [UnifiedRecord](#unifiedrecord)
| Name | Type | Readable| Writable| Description |
|---------|---------------------------| ---- | ---- |------------------------------------------------------------------------------------------------------------------------------------------------------|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe file information. For example, a data object with the following content can be created to describe a file:<br>{<br>"name":"File name",<br>"type":"File type"<br>}<br>This parameter is optional. The default value is an empty dictionary object.|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe file information. For example, a data object with the following content can be created to describe a file:<br>{<br>"name":"File name",<br>"type":"File type"<br>}<br> This parameter is optional. The default value is an empty dictionary object.|
| uri | string | Yes | Yes | URI of the file data. |
**Example**
......@@ -484,13 +484,13 @@ let unifiedData = new UDMF.UnifiedData(record);
## Intention
Enumerates the types of the system abilities connected to the UDMF. It identifies the purpose of the data written by the user to the UDMF and the system abilities connected to the UDMF to implement data transmission between applications.
Enumerates the data channel types supported by the UDMF. It is used to identify different service scenarios, to which the UDMF data channels apply.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
| Name | Value | Description |
|----------|-----------|---------|
| DATA_HUB | 'DataHub' | Public data hub.|
| DATA_HUB | 'DataHub' | Public data channel.|
## Options
......@@ -499,9 +499,9 @@ Defines the data operation performed by the UDMF. It includes two optional param
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
| Name | Type | Readable| Writable| Mandatory| Description |
|-----------|-------------------------|----|----|----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| intention | [Intention](#intention) | Yes | Yes | No | Service tag related to the data operation. |
| Name | Type | Readable| Writable| Mandatory| Description |
|-----------|-------------------------|----|----|----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| intention | [Intention](#intention) | Yes | Yes | No | Type of the data channel related to the data operation. |
| key | string | Yes | Yes | No | Unique identifier of a data object in the UDMF, which can be obtained from the return value of [insertData](#udmfinsertdata).<br>The key consists of **udmf:/**, **intention**, **bundleName**, and **groupId** with a (/) in between, for example, **udmf://DataHub/com.ohos.test/0123456789**.<br>**udmf:/** is fixed, **DataHub** is an enum of **intention**, **com.ohos.test** is the bundle name, and **0123456789** is a group ID randomly generated.|
......@@ -510,7 +510,7 @@ Defines the data operation performed by the UDMF. It includes two optional param
insertData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;string&gt;): void
Inserts data to the UDMF. This API uses an asynchronous callback to return the result.
Inserts data to the UDMF public data channel. This API uses an asynchronous callback to return the unique identifier of the data inserted.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -552,7 +552,7 @@ try {
insertData(options: Options, data: UnifiedData): Promise&lt;string&gt;
Inserts data to the UDMF. This API uses a promise to return the result.
Inserts data to the UDMF public data channel. This API uses a promise to return the unique identifier of the data inserted.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -596,7 +596,7 @@ try {
updateData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;void&gt;): void
Updates data in the UDMF. This API uses an asynchronous callback to return the result.
Updates the data in the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -626,7 +626,7 @@ try {
if (err === undefined) {
console.info('Succeeded in updating data.');
} else {
console.error('Failed to update data. code is ${err.code},message is ${err.message} `);
console.error(`Failed to update data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
......@@ -638,7 +638,7 @@ try {
updateData(options: Options, data: UnifiedData): Promise&lt;void&gt;
Updates data in the UDMF. This API uses a promise to return the result.
Updates the data in the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -683,7 +683,7 @@ try {
queryData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void
Queries data in the UDMF. This API uses an asynchronous callback to return the result.
Queries data in the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -729,7 +729,7 @@ try {
queryData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt;
Queries data in the UDMF. This API uses a promise to return the result.
Queries data in the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -778,7 +778,7 @@ try {
deleteData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void
Deletes data from the UDMF. This API uses an asynchronous callback to return the result.
Deletes data from the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......@@ -824,7 +824,7 @@ try {
deleteData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt;
Deletes data in the UDMF. This API uses a promise to return the result.
Deletes data from the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......
......@@ -117,4 +117,59 @@ Data is added, deleted, and modified continuously without closing the read trans
**Solution**
1. Check for unclosed result sets or transactions.
2. Closes all result sets or transactions.
## 14800050 Failed to Obtain the Subscription Service
**Error Message**
Failed to obtain subscription service.
**Description**
The error code is returned when the subscription service failed to be obtained.
**Possible Causes**
The platform does not support service subscription.
**Solution**
Deploy the subscription service on the platform.
## 14801001 Stage Model Required
**Error Message**
Only supported in stage mode.
**Description**
This operation can be performed only on the stage model.
**Possible Causes**
The context is not a stage model.
**Solution**
Perform the operation on the stage model.
## 14801002 Invalid dataGroupId in storeConfig
**Error Message**
The data group id is not valid.
**Description**
The **dataGroupId** parameter is invalid.
**Possible Causes**
The **dataGroupId** is not obtained from the AppGallery.
**Solution**
Obtain **dataGroupId** from the AppGallery and pass it to **storeConfig** correctly.
......@@ -23,3 +23,57 @@ The possible causes are as follows:
1. Check that the file name is correct.
2. Check that the file path is correct.
## 15500019 Failed to Obtain the Subscription Service
**Error Message**
Failed to obtain subscription service.
**Description**
Failed to obtain the subscription service in inter-process event subscription.
**Possible Causes**
The platform does not support service subscription.
**Solution**
Deploy the subscription service on the platform.
## 14801001 Stage Model Required
**Error Message**
Only supported in stage mode.
**Description**
This operation can be performed only on the stage model.
**Possible Causes**
The context is not a stage model.
**Solution**
Perform the operation on the stage model.
## 15501002 The dataGroupId parameter in Options is invalid.
**Error Message**
The data group id is not valid.
**Description**
The **dataGroupId** parameter is invalid.
**Possible Causes**
The **dataGroupId** is not obtained from the AppGallery.
**Solution**
Obtain **dataGroupId** from the AppGallery and pass it correctly.
......@@ -158,7 +158,8 @@
- [Linux Kernel Overview](kernel-standard-overview.md)
- [Applying Patches on Development Boards](kernel-standard-patch.md)
- [Compiling and Building the Linux Kernel](kernel-standard-build.md)
- Enhanced Kernel Features
- [HCK](kernel-standard-hck.md)
- Enhanced kernel features
- [Enhanced Swap](kernel-standard-mm-eswap.md)
- [NewIP Kernel Protocol Stack](kernel-standard-newip.md)
- Task Scheduling
......
# HCK
## Background
Vendors have different kernel requirements and feature solutions when adapting their platforms to OpenHarmony.
If their code is directly integrated into the kernel repository or patches are applied, problems such as low build and development efficiency, complex routine maintenance, and poor feature portability will be caused.
To incorporate third-party kernel features into OpenHarmony without or with little interference with the kernel repository, OpenHarmony provides the OpenHarmony Common Kernel (HCK) framework, which provides a complete set of instrumentation, registration, and invoking interfaces to reduce modification to the kernel. The HCK provides instrumentation interfaces applicable to multiple platforms, implementing decoupling of the kernel.
This document describes how to implement the HCK based on the native hook mode.
## Application Scope
You can use the HCK framework when your Linux kernel patches, features, and modules require modification on the native kernel.
## Available APIs
**DECLARE_HCK_LITE_HOOK**: macro used to declare an instrumentation interface. It generates the registration and call interfaces.
**REGISTER_HCK_LITE_HOOK**: macro used to register an instrumentation interface instance. The HCK supports only the registration of a singleton for a singleton, and the registered interfaces cannot be deregistered.
**REGISTER_HCK_LITE_DATA_HOOK**: macro used to register an instrumentation interface instance with parameters. Only a singleton can be registered, and the registered interfaces cannot be deregistered.
**CALL_HCK_LITE_HOOK**: macro used to call an instrumentation interface, which replaces the code that intrudes into the native kernel code.
**Figure 1** HCK interface definition, registration, and invocation
![](figures/hck.png)
## Usage Guide
### Configuration
Before using the HCK, enable the following settings in the **config** file of your platform in the **kernel_linux_config** repository:
```c
CONFIG_HCK=y
CONFIG_HCK_VENDOR_HOOKS=y
```
### Interface Definition
Define the hook interface in **kernel-x.x/include/linux/hck/lite_hck_xxx.h** by using the following macro:
```c
#include <linux/hck/lite_vendor_hooks.h> // Include HCK header file.
// You can customize data types for passing in parameters in interface registration.
struct hck_data {
int stat;
char* name;
};
// Declare the hook interface. The declaration macro contains the EXPORT interface, and you do not need to call EXPORT separately to declare the interface.
DECLARE_HCK_LITE_HOOK(boot_config_info0_lhck, TP_PROTO(Type Parameter, ...), TP_ARGS(Parameter, ...));
```
### Interface Declaration
Add the interface declaration header file to the new vendor hck module. The file path is **drivers/hck/vendor_hooks.c**.
```c
#define CREATE_LITE_VENDOR_HOOK
#include <linux/hck/lite_hck_sample.h> // Add the interface declaration header file after the macro definition.
```
### Interface Registration
Define the instrumentation function and register the hook functions in the hook module.
```c
// Include the interface declaration header file.
#include <linux/hck/lite_hck_sample.h>
// Implement the interfaces:
boot_config_info0([void* data], ...)
{
// Call the input parameters for the registration.
((struct hck_data*)data)->stat ... ;
}
// Register the interface. Generally, register the interface in the init() function of the module. Ensure that the registration is completed before the interface is called.
xxx_init()
{
REGISTER_HCK_LITE_HOOK(boot_config_info0_lhck, boot_config_info0);
// Register the interface with parameters. The input parameters can be obtained and used in the interface instance.
REGISTER_HCK_LITE_DATA_HOOK(boot_config_info1_lhck, boot_config_info1, data);
}
```
### Interface Calling
Add the hook function in other kernel modules.
For example, add the previously defined interface to **linux-x.y/drivers/xxx/xxx.c**.
```c
#include <linux/hck/lite_hck_sample.h>
...
int foo(...)
{
CALL_HCK_LITE_HOOK(boot_config_info0_lhck, parameter...);
}
```
### Example
The sample code is located in **kernel/linux/linux-5.10/samples/hck**.
To enable the sample code, enable the following settings in **config** (do not enable the settings in the official version):
```c
CONFIG_SAMPLES=y
CONFIG_SAMPLE_HCK=y
CONFIG_SAMPLE_HCK_CALL=y
CONFIG_SAMPLE_HCK_REGISTER=y
```
## Specifications
### Naming Rules
1. Comply with the kernel interface naming rules. The interface names should be descriptive.
2. Add **suffix _lhck** to the end of the interface names of lite hck interfaces. For example:
```c
DECLARE_HCK_LITE_HOOK(boot_config_info0_lhck, TP_PROTO(int* s ), TP_ARGS(s));
```
3. Use English words and grammar, not Hanyu Pinyin.
4. Use commonly accepted abbreviations. Do not create abbreviations.
5. Avoid using negative expressions.
For example, name the interface for obtaining the boot configuration as follows:get_boot_config_lhck
### File Directory Specifications
1. Place the interface definition header file in **kernel-x.x/include/linux/hck/**.
2. Name header files in the **lite_hck_*module-description*.h** format, for example, **lite_hck_mmc.h**.
......@@ -7,7 +7,8 @@ OpenHarmony is an open OS that allows you to easily develop services and applica
This environment combines chip security and system security features with upper-layer security services to secure hardware, the system, data, device interconnection, applications, and updates.
**Figure 1** Security assurance
**Figure 1** Security assurance
![](figures/security-assurance-framework.png)
......@@ -18,32 +19,36 @@ This environment combines chip security and system security features with upper-
### Security Mechanism
- Boot root of trust
OpenHarmony devices use the public key infrastructure (PKI) to protect software integrity and ensure that the software source is valid and the software is not tampered with.
In the device boot process, software signature verification is performed at each phase to form a secure boot chain. If signature verification fails at any phase, the device boot will be terminated. The hardware or software entity that initially performs signature verification in the secure boot chain is the boot root of trust. It must be valid and should not be tampered with. The boot root of trust can be a built-in code segment in the read-only memory (ROM). This code segment is written in the chip during the chip manufacturing and cannot be modified later. When the device is powered on and initialized, this code segment is executed first and used to verify software signatures.
When you use the code for signature verification, ensure the validity of the PKI public keys. OpenHarmony devices use a storage medium such as eFUSE and one-time password (OTP) to store the public keys (for example, their hash values) and guarantee their validity. A public key is usually programed into the eFuse or OTP of a device during device manufacturing.
- Hardware-isolated trusted environment
The hardware-isolated trusted environment complies with the design concept of the trusted computing system. There is a clear boundary between the trusted environment and untrusted one. OpenHarmony devices protect core sensitive data in the trusted environment. Even if OS vulnerabilities in the untrusted environment are exploited, sensitive data in the trusted environment is secure.
The trusted environment of OpenHarmony devices is built based on a hardware security isolation mechanism. The chip isolation mechanism varies slightly on different OpenHarmony devices, and the most common mechanism is Arm TrustZone. On some RISC-V chip platforms, independent security cores may also be used to build a trusted environment.
A specific, simplified OS iTrustee lite runs in the trusted environment to manage resources and schedule tasks in the environment and provide security services for OpenHarmony devices. Key management and data security are the most common security services in the trusted environment. A device has a unique hardware root key in the eFuse/OTP. Based on this root key and service context, the trusted environment generates multiple keys that provide key management and data encryption/decryption services for applications. During their whole lifecycle, core keys of devices stay in the trusted environment. The trusted environment also provides security services such as identity authentication, system status monitoring, and secure data storage to enhance device security.
- Hardware key engine
Cryptography is the basis of information security. Data encryption/decryption requires high efficiency and security of computing devices. Hardware encryption/decryption technologies use computer hardware to assist or even replace software to encrypt or decrypt data. Hardware-based encryption/decryption is more efficient and secure than software-based encryption/decryption.
Since some dedicated hardware resources are used for data encryption/decryption, the CPU can concurrently execute other computing tasks, which greatly improves performance and reduces the CPU load. In addition, a well-designed hardware key engine protects keys from leak even if the software is cracked and even defends against electromagnetic and radiation attacks from physical channels.
OpenHarmony devices support the hardware key engine, which allows OpenHarmony to perform computing tasks such as data encryption and decryption, certificate signature verification, and hash value calculation. The hardware key engine supports popular algorithms such as Advanced Encryption Standard (AES) and Rivest-Shamir-Adleman (RSA).
OpenHarmony devices support the hardware key engine, which allows OpenHarmony to perform computing tasks such as data encryption and decryption, certificate signature verification, and hash value calculation. The hardware key engine supports popular algorithms such as Advanced Encryption Standard (AES) and Rivest-Shamir-Adleman (RSA).
### Recommended Practices
- The boot root of trust consists of a built-in code segment in the chip and the root key of the device. The root of trust is written into the chip during manufacturing and cannot be modified in the device lifecycle. It is used to verify software certificates in the device boot process. The root key is the public key matching the private key of the device certificate signature. The private key is maintained on the PKI signature server and the public key is written to the device. To prevent attackers from tampering with the public key to bypass signature authentication, you can write the public key to media such as fuses on OpenHarmony devices. Considering that the fuse space is limited, you can store only the hash value of the public key in the fuse and verify the validity of the public key using the boot code.
- Generally, a trusted execution environment (TEE) is built based on the Arm TrustZone technology, and can also adopt other isolation mechanisms, such as TrustZone-M and independent security cores, depending on the device form. A TEE OS must be deployed in the TEE to manage resources and schedule tasks. OpenHarmony provides iTrustee as the TEE OS. You can develop and deploy security services based on iTrustee.
Not all OpenHarmony devices need to support the TEE, for example, some devices with thin resources that run less sensitive services may not need the TEE. You can choose whether to support the TEE and how to implement the TEE based on service requirements.
- The hardware key engine must provide key algorithms related to true random numbers, public keys, symmetric keys, and hash values. By deploying required drivers in OpenHarmony, you can provide unified key management and key algorithms for applications.
......@@ -72,8 +77,8 @@ For device with 128 KB to 128 MB of memory, the OpenHarmony lite kernel is recom
The following figure shows how DAC works when a process accesses a file. The DAC first matches the process UID with the file UID, and then the process GID with the file GID. If the UID and GID both fail to match, DAC checks the **other** attribute of the file to determine whether the process is allowed to read, write, or execute the file. In addition, the system supports a privileged capability that is not subject to DAC mechanism (read, write, and execute) and can access files directly. Services with high permissions (such as system services) can manage files of applications with low permissions (such as third-party applications).
**Figure 2** How DAC works
**Figure 2** How DAC works
![](figures/how-dac-works.png)
- Capability mechanism
......@@ -94,14 +99,14 @@ For device with 128 KB to 128 MB of memory, the OpenHarmony lite kernel is recom
- Secure boot must be enabled, and the trusted root must be in the chip and cannot be modified. In addition, you must consider the impact of secure upgrade (if available) on secure boot, that is, the signature or hash value of an image file must be updated after a secure upgrade.
## Data Security
## Data security
### Security Mechanism
OpenHarmony Universal KeyStore (HUKS) provides key and certificate management. For OpenHarmony, it mainly provides key management for HiChain (device identity authentication platform). The figure below shows the HUKS functions.
**Figure 3** HUKS functions
**Figure 3** HUKS functions
![](figures/huks-functions.png)
......@@ -143,39 +148,35 @@ To use the device certification function, it is recommended that you use HiChain
To ensure secure transmit of user data between devices, a trust relationship and a secure data transmission channel must be established between the devices. The following figure shows how an IoT controller and an IoT device establish a trust relationship.
**Figure 4** Process of establishing a trust relationship between devices
**Figure 4** Process of establishing a trust relationship between devices
![](figures/how-an-iot-controller-and-an-iot-device-establish-a-trust-relationship.png)
- IoT device interconnection security
A trust relationship can be established between an IoT device that runs OpenHarmony (such as an AI speaker, smart home device, and wearable) and an IoT controller. Encrypted user data is transmitted between the IoT device and IoT controller through a secure connection.
A trust relationship can be established between an IoT device that runs OpenHarmony (such as an AI speaker, smart home device, and wearable) and an IoT controller. Encrypted user data is transmitted between the IoT device and IoT controller through a secure connection.
- IoT service identifier of the IoT controller
An IoT controller generates different identifiers for different IoT device management services to isolate these services. The identifier can be used for authentication and communication between an IoT controller and an IoT device. It is an Ed25519 public/private key pair generated using the elliptic curve cryptography.
An IoT controller generates different identifiers for different IoT device management services to isolate these services. The identifier can be used for authentication and communication between an IoT controller and an IoT device. It is an Ed25519 public/private key pair generated using the elliptic curve cryptography.
- IoT device identifier
An IoT device can generate its own device identifier for communicating with the IoT controller. It is also an Ed25519 public/private key pair generated using elliptic curve cryptography, with the private key stored on the IoT device. Each time the device is restored to factory settings, the public/private key pair will be reset.
The identifier can be used for secure communication between the IoT controller and IoT device. After the devices exchange the service identifier or device identifier, they can negotiate the key and establish a secure communication channel.
The identifier can be used for secure communication between the IoT controller and IoT device. After the devices exchange the service identifier or device identifier, they can negotiate the key and establish a secure communication channel.
- P2P trusted binding between devices
When an IoT controller and an IoT device establish a trust relationship, they exchange identifiers.
During this process, the user needs to enter or scan the PIN provided by the IoT device on the IoT controller. PIN is either dynamically generated if the IoT device has a screen, or preset by the manufacturer if it does not have a screen. A PIN can be a number or a QR code. Then the IoT controller and IoT device perform authentication and session key exchange based on password authenticated key exchange (PAKE), and use the session key to encrypt the channel for exchanging identity public keys.
During this process, the user needs to enter or scan the PIN provided by the IoT device on the IoT controller. PIN is either dynamically generated if the IoT device has a screen, or preset by the manufacturer if it does not have a screen. A PIN can be a number or a QR code. Then the IoT controller and IoT device perform authentication and session key exchange based on password authenticated key exchange (PAKE), and use the session key to encrypt the channel for exchanging identity public keys.
- Secure communication between the IoT controller and IoT device
When an IoT controller and an IoT device communicate with each other after establishing a trust relationship, they authenticate each other by using the locally stored identity public key of the peer. Bidirectional identity authentication and session key exchange are performed using the Station-to-Station (STS) protocol during each communication. The session key is used to encrypt the data transmission channel between the devices.
When an IoT controller and an IoT device communicate with each other after establishing a trust relationship, they authenticate each other by using the locally stored identity public key of the peer. Bidirectional identity authentication and session key exchange are performed using the Station-to-Station (STS) protocol during each communication. The session key is used to encrypt the data transmission channel between the devices.
## Application Security
......@@ -183,32 +184,30 @@ To ensure secure transmit of user data between devices, a trust relationship and
### Security Mechanism
- Application signature management
After developing and debugging an OpenHarmony application, sign the application installation package using a private key, which matches a public key. Generally, the OEM generates a public/private key pair, presets the public key in the device, and stores the private key on a local server that is not connected to the Internet to prevent private key leakage. After you finish developing an application, you can use an external device (such as a USB flash drive) to upload the installation package to the server where the private key is stored, calculate the signature, and download the signature to the external device. During application installation, the hash value of the bundle is calculated using the SHA-256 algorithm. The hash value, together with the signature and preset public key, is used for authentication. The application can be installed only after the authentication is successful.
In addition, the application source must be verified to ensure that the application is from a valid developer. As a developer, you must apply for a development certificate and use it to sign the application you have developed. During application installation, the upper-level certificate stored on the device is used to verify the signature to ensure validity of the developer.
- Application permission control
OpenHarmony allows users to install third-party applications and controls calls made by third-party applications to sensitive permissions. When developing an application, you need to declare the sensitive permissions that the application may require in the application configuration file. The permissions can be static or dynamic. Static permissions need to be registered during application installation, and dynamic permissions can be obtained only upon user authorization. Authorization modes include system settings, manual authorization by applications, and others. In addition, application signature control is used to ensure that the application installation package has been confirmed by the device vendor.
In addition, the application source must be verified to ensure that the application is from a valid developer. As a developer, you must apply for a development certificate and use it to sign the application you have developed. During application installation, the upper-level certificate stored on the device is used to verify the signature to ensure validity of the developer.
- Application permission control
**Table 1** OpenHarmony system permissions
OpenHarmony allows users to install third-party applications and controls calls made by third-party applications to sensitive permissions. When developing an application, you need to declare the sensitive permissions that the application may require in the application configuration file. The permissions can be static or dynamic. Static permissions need to be registered during application installation, and dynamic permissions can be obtained only upon user authorization. Authorization modes include system settings, manual authorization by applications, and others. In addition, application signature control is used to ensure that the application installation package has been confirmed by the device vendor.
| Permission| **Authorization Mode**| **Description**|
| -------- | -------- | -------- |
| ohos.permission.LISTEN_BUNDLE_CHANGE | system_grant (static permission)| Allows an application to listen for application changes.|
| ohos.permission.GET_BUNDLE_INFO | system_grant (static permission)| Allows an application to obtain information about other applications.|
| ohos.permission.INSTALL_BUNDLE | system_grant (static permission)| Allows an application to install other applications.|
| ohos.permission.CAMERA | user_grant (dynamic permission)| Allows an application to use the camera to take photos and record videos at any time.|
| ohos.permission.MODIFY_AUDIO_SETTINGS | system_grant (static permission)| Allows an application to modify global audio settings, such as the volume and speaker for output.|
| ohos.permission.READ_MEDIA | user_grant (dynamic permission)| Allows an application to read users' favorite videos.|
| ohos.permission.MICROPHONE | user_grant (dynamic permission)| Allows an application to use the microphone for audio recording at any time.|
| ohos.permission.WRITE_MEDIA | user_grant (dynamic permission)| Allows an application to write users' favorite music.|
| ohos.permission.DISTRIBUTED_DATASYNC | user_grant (dynamic permission)| Allows an application to manage distributed data transmission.|
| ohos.permission.DISTRIBUTED_VIRTUALDEVICE | user_grant (dynamic permission)| Allows an application to use distributed virtualization features.|
**Table 1** OpenHarmony system permissions
| Permission| **Authorization Mode**| **Description**|
| -------- | -------- | -------- |
| ohos.permission.LISTEN_BUNDLE_CHANGE | system_grant (static permission)| Allows an application to listen for application changes.|
| ohos.permission.GET_BUNDLE_INFO | system_grant (static permission)| Allows an application to obtain information about other applications.|
| ohos.permission.INSTALL_BUNDLE | system_grant (static permission)| Allows an application to install other applications.|
| ohos.permission.CAMERA | user_grant (dynamic permission)| Allows an application to use the camera to take photos and record videos at any time.|
| ohos.permission.MODIFY_AUDIO_SETTINGS | system_grant (static permission)| Allows an application to modify global audio settings, such as the volume and speaker for output.|
| ohos.permission.READ_MEDIA | user_grant (dynamic permission)| Allows an application to read users' favorite videos.|
| ohos.permission.MICROPHONE | user_grant (dynamic permission)| Allows an application to use the microphone for audio recording at any time.|
| ohos.permission.WRITE_MEDIA | user_grant (dynamic permission)| Allows an application to write users' favorite music.|
| ohos.permission.DISTRIBUTED_DATASYNC | user_grant (dynamic permission)| Allows an application to manage distributed data transmission.|
| ohos.permission.DISTRIBUTED_VIRTUALDEVICE | user_grant (dynamic permission)| Allows an application to use distributed virtualization features.|
### Recommended Practices
......@@ -217,4 +216,3 @@ When developing an application, determine the permissions required by your appli
> **NOTE**
>
> The application configuration file varies depending on the application model. It is **config.json** for the application based on the FA model and **module.json5** for the application based on the stage mode. For details about the application models, see [Interpretation of the Application Model](../../application-dev/application-models/application-model-description.md).
......@@ -35,7 +35,7 @@ XML模块提供XmlPullParser类对XML文件解析,输入为含有XML文本的A
```
2. 对XML文件编码后调用XmlPullParser。
可以基于Arraybuffer构造XmlPullParser对象, 也可以基于DataView构造XmlPullParser对象。
可以基于ArrayBuffer构造XmlPullParser对象, 也可以基于DataView构造XmlPullParser对象。
```js
......@@ -47,7 +47,7 @@ XML模块提供XmlPullParser类对XML文件解析,输入为含有XML文本的A
'</note>';
let textEncoder = new util.TextEncoder();
let arrBuffer = textEncoder.encodeInto(strXml); // 对数据编码,防止包含中文字符乱码
// 1.基于Arraybuffer构造XmlPullParser对象
// 1.基于ArrayBuffer构造XmlPullParser对象
let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8');
// 2.基于DataView构造XmlPullParser对象
......@@ -62,7 +62,7 @@ XML模块提供XmlPullParser类对XML文件解析,输入为含有XML文本的A
function func(name, value){
str = name + value;
console.info(str);
return true; //true:继续解析 flase:停止解析
return true; //true:继续解析 false:停止解析
}
```
......@@ -118,7 +118,7 @@ XML模块提供XmlPullParser类对XML文件解析,输入为含有XML文本的A
let str = '';
function func(name, value){
str += name + ' ' + value + ' ';
return true; // true:继续解析 flase:停止解析
return true; // true:继续解析 false:停止解析
}
```
......@@ -167,7 +167,7 @@ XML模块提供XmlPullParser类对XML文件解析,输入为含有XML文本的A
function func(name, value){
str = name + ' ' + value.getDepth(); // getDepth 获取元素的当前深度
console.info(str)
return true; //true:继续解析 flase:停止解析
return true; //true:继续解析 false:停止解析
}
```
......
......@@ -68,7 +68,7 @@
return;
}
console.info('Succeeded in getting preferences.');
// 进行相关数据操作
// 请确保获取到Preferences实例后,再进行相关数据操作
})
} catch (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
......@@ -93,7 +93,7 @@
return;
}
console.info('Succeeded in getting preferences.');
// 进行相关数据操作
// 请确保获取到Preferences实例后,再进行相关数据操作
})
} catch (err) {
console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`);
......
......@@ -97,7 +97,7 @@
store.version = 3;
}
// 这里执行数据库的增、删、改、查等操作
// 请确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
});
}
......@@ -151,7 +151,7 @@
store.version = 3;
}
// 这里执行数据库的增、删、改、查等操作
// 请确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
});
```
......
......@@ -171,7 +171,7 @@
return;
}
console.info('Succeeded in getting KVStore.');
// 进行相关数据操作
// 请确保获取到键值数据库实例后,再进行相关数据操作
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
......
......@@ -125,9 +125,9 @@ if (isSupported) {
| 接口 | 说明 |
| ---- | ---- |
| isPreLaunchSupported(camera: CameraDevice) : boolean | 判断指定cameraDevice是否支持预热启动。 |
| setPreLaunchConfig(preLaunchConfig: PreLaunchConfig) : void | 配置相机预热参数。 |
| preLaunch() : void | 用户点击系统相机图标,拉起相机应用的同时调用,下发预热请求,使能相机预热启动。 |
| isPrelaunchSupported(camera: CameraDevice) : boolean | 判断指定cameraDevice是否支持预热启动。 |
| setPrelaunchConfig(prelaunchConfig: PrelaunchConfig) : void | 配置相机预热参数。 |
| prelaunch() : void | 用户点击系统相机图标,拉起相机应用的同时调用,下发预热请求,使能相机预热启动。 |
### 开发示例
......@@ -142,7 +142,7 @@ if (isSupported) {
this.cameraManager = camera.getCameraManager(globalThis.abilityContext);
try {
this.cameraManager.preLaunch();
this.cameraManager.prelaunch();
} catch (error) {
console.error(`catch error: Code: ${error.code}, message: ${error.message}`)
}
......@@ -159,9 +159,9 @@ if (isSupported) {
this.cameraManager = camera.getCameraManager(globalThis.abilityContext);
let cameras = this.cameraManager.getSupportedCameras()
if(this.cameraManager.isPreLaunchSupported(cameras[0])) {
if(this.cameraManager.isPrelaunchSupported(cameras[0])) {
try {
this.cameraManager.setPreLaunchConfig({cameraDevice: cameras[0]});
this.cameraManager.setPrelaunchConfig({cameraDevice: cameras[0]});
} catch (error) {
console.error(`catch error: Code: ${error.code}, message: ${error.message}`)
}
......
......@@ -74,8 +74,11 @@
- [其他状态管理概述](arkts-other-state-mgmt-functions-overview.md)
- [\@Watch装饰器:状态变量更改通知](arkts-watch.md)
- [$$语法:内置组件双向同步](arkts-two-way-sync.md)
- [MVVM模式](arkts-mvvm.md)
- [状态管理优秀实践](arkts-state-management-best-practices.md)
- 渲染控制
- [渲染控制概述](arkts-rendering-control-overview.md)
- [if/else:条件渲染](arkts-rendering-control-ifelse.md)
- [ForEach:循环渲染](arkts-rendering-control-foreach.md)
- [LazyForEach:数据懒加载](arkts-rendering-control-lazyforeach.md)
- [渲染控制优秀实践](arkts-rendering-control-best-practices.md)
此差异已折叠。
# 渲染控制优秀实践
为了帮助应用程序开发人员提高其应用程序质量,本章节面向开发者提供了多个在开发ArkUI应用中常见场景和易错问题,并给出了对应的解决方案。此外,还提供了同一场景下,推荐用法和不推荐用法的对比和解释说明,更直观地展示两者区别,从而帮助开发者学习如果正确地在应用开发中使用渲染控制,进行高性能开发。
## 在ForEach数据源中添加元素
在ForEach数据源中添加元素导致数组项ID重复。
### 不推荐用法
下面示例使用ForEach方法迭代数组this.arr的每个元素,在Text组件进行显示,并在单击Text('Add arr element')时添加新的数组元素。
```ts
@Entry
@Component
struct Index {
@State arr: number[] = [1,2,3];
build() {
Column() {
ForEach(this.arr,
(item) => {
Text(`Item ${item}`)
},
item => item.toString())
Text('Add arr element')
.fontSize(20)
.onClick(()=>{
this.arr.push(4); // arr新增的元素,其在ForEach内的键值均为'4'
console.log("Arr elements: ", this.arr);
})
}
}
}
```
点击两次Text('Add arr element')时,数组this.arr每次都会添加新元素 4。但是在ForEach循环渲染中,第三个参数(item)=&gt; item.toString()需要生成Array每一个item对应的id值。该Array Id被要求是唯一的和稳定的。
- 唯一性:键值生成函数生成的每个数组项的id是不同的。
- 稳定性:当数组项ID发生变化时,ArkUI框架认为该数组项被替换或更改。
- ArkUI框架会对重复的ID告警,这种情况下框架的行为是未知的,特别是UI的更新在该场景下可能不起作用。
因此上述示例中,框架不会显示第二次及以后新添加的文本元素。因为这个元素不再是唯一的,他们都含有相同的id4。 但是如果删除ForEach第三个键值生成函数(item) =&gt; item.toString(),则触发onClick事件后每一个新添加的Text元素都会得到更新。这是因为框架使用了默认的Array id生成函数,即(item: any, index : number) =&gt; '${index}__${JSON.stringify(item)}'。它的兼容性更好但可能会导致不必要的UI更新,因此仍建议应用定义自己的键值生成函数。
## ForEach数据源更新
ForEach数据源更新时,数组项ID与原数组项ID重复不会重新创建该数组项。
### 不推荐用法
下面的示例定义了Index和Child两个组件。父组件Index有arr数组成员变量,初始值包含数字1、2、3。Child定义\@Prop value,接收父组件中arr数组中的一个元素。
```ts
@Component
struct Child {
@Prop value: number;
build() {
Text(`${this.value}`)
.fontSize(50)
.onClick(() => {
this.value++ // 点击改变@Prop的值
})
}
}
@Entry
@Component
struct Index {
@State arr: number[] = [1, 2, 3];
build() {
Row() {
Column() {
// 对照组
Child({ value: this.arr[0] })
Child({ value: this.arr[1] })
Child({ value: this.arr[2] })
Divider().height(5)
ForEach(this.arr,
item => {
Child({ value: item })
},
item => item.toString() // 键值,标识id
)
Text('Parent: replace entire arr')
.fontSize(50)
.onClick(() => {
// 两个数组项内均含有'3',ForEach内的id没有发生变化
// 意味着ForEach不会更新该Child实例,@Prop也不会在父组件处被更新
this.arr = (this.arr[0] == 1) ? [3, 4, 5] : [1, 2, 3];
})
}
}
}
}
```
当触发文本组件Parent: replace entire arr的onClick事件时,状态变量数组arr根据自身第一个元素的值将被[3, 4, 5]或[1, 2, 3]替换,但是ForEach里初始被创建的\@Prop传入值为3的Child组件并不会更新。
因为,老数组和新数组初始均含有同一个值的元素(数字3),而且该元素生成的标识id在父组件里没有变化。因此ForEach没有识别出对应的Child实例需要被新的输入值更新,对应的子组件内\@Prop也没有更新。
![zh-cn_image_0000001604900446](figures/zh-cn_image_0000001604900446.png)
可以在arr中用一个唯一的元素代替重复的元素3来观察本事件的行为表现。当恰当的替换掉数组时,接下来应用的表现才是符合预期的。
\ No newline at end of file
......@@ -582,11 +582,11 @@ cameraManager.on('cameraMute', (err, curMuetd) => {
})
```
### isPreLaunchSupported
### isPrelaunchSupported
isPreLaunchSupported(camera: CameraDevice): boolean
isPrelaunchSupported(camera: CameraDevice): boolean
在setPreLaunchConfig接口使用前调用,用于判断指定cameraDevice是否支持预热启动。
在setPrelaunchConfig接口使用前调用,用于判断指定cameraDevice是否支持预热启动。
**系统接口:** 此接口为系统接口。
......@@ -617,14 +617,14 @@ isPreLaunchSupported(camera: CameraDevice): boolean
```js
this.cameraManager = camera.getCameraManager(globalThis.abilityContext);
let cameras = this.cameraManager.getSupportedCameras()
if(this.cameraManager.isPreLaunchSupported(cameras[0])) {
this.cameraManager.setPreLaunchConfig({cameraDevice: cameras[0]});
if(this.cameraManager.isPrelaunchSupported(cameras[0])) {
this.cameraManager.setPrelaunchConfig({cameraDevice: cameras[0]});
}
```
### setPreLaunchConfig
### setPrelaunchConfig
setPreLaunchConfig(preLaunchConfig: PreLaunchConfig): void
setPrelaunchConfig(prelaunchConfig: PrelaunchConfig): void
**系统接口:** 此接口为系统接口。
......@@ -636,7 +636,7 @@ setPreLaunchConfig(preLaunchConfig: PreLaunchConfig): void
| 参数名 | 类型 | 必填 | 说明 |
| -------- | --------------- | ---- | --------- |
| preLaunchConfig | [PreLaunchConfig](#prelaunchconfig) | 是 | 预启动配置参数。|
| prelaunchConfig | [PrelaunchConfig](#prelaunchconfig) | 是 | 预启动配置参数。|
**错误码:**
......@@ -652,11 +652,18 @@ setPreLaunchConfig(preLaunchConfig: PreLaunchConfig): void
```js
this.cameraManager = camera.getCameraManager(globalThis.abilityContext);
let cameras = this.cameraManager.getSupportedCameras()
if(this.cameraManager.isPrelaunchSupported(cameras[0])) {
try {
this.cameraManager.setPrelaunchConfig({cameraDevice: cameras[0]});
} catch (error) {
console.error(`catch error: Code: ${error.code}, message: ${error.message}`);
}
}
```
### preLaunch
### prelaunch
preLaunch(): void
prelaunch(): void
用户点击系统相机图标,拉起相机应用同时调用,下发预热请求,使能相机预热启动。
......@@ -668,6 +675,11 @@ preLaunch(): void
```js
this.cameraManager = camera.getCameraManager(globalThis.abilityContext);
try {
this.cameraManager.prelaunch();
} catch (error) {
console.error(`catch error: Code: ${error.code}, message: ${error.message}`);
}
```
### createDeferredPreviewOutput
......@@ -710,7 +722,7 @@ function getDeferredPreviewOutput(context: Context, previewProfile: camera.Profi
}
```
## PreLaunchConfig
## PrelaunchConfig
相机预启动配置参数。
......
......@@ -553,12 +553,16 @@ cryptoCert.createX509Cert(encodingBlob, function (error, x509Cert) {
});
```
### getSerialNumber
### getSerialNumber<sup>(deprecated)</sup>
getSerialNumber() : number
表示获取X509证书序列号。
> **说明:**
>
> 从API version 9开始支持,从API version 10开始废弃,建议使用[getCertSerialNumber](#getcertserialnumber10)替代。
**系统能力:** SystemCapability.Security.Cert
**返回值**
......@@ -589,6 +593,48 @@ cryptoCert.createX509Cert(encodingBlob, function (error, x509Cert) {
});
```
### getCertSerialNumber<sup>10+</sup>
getCertSerialNumber() : bigint
表示获取X509证书序列号。
**系统能力:** SystemCapability.Security.Cert
**返回值**
| 类型 | 说明 |
| ------ | ------------------ |
| bigint | 表示X509证书序列号 |
**错误码:**
| 错误码ID | 错误信息 |
| -------- | ------------------------------------------------- |
| 19020002 | runtime error. |
**示例:**
```js
import cryptoCert from '@ohos.security.cert';
// 证书二进制数据,需业务自行赋值
let encodingData = null;
let encodingBlob = {
data: encodingData,
// 根据encodingData的格式进行赋值,支持FORMAT_PEM和FORMAT_DER
encodingFormat: cryptoCert.EncodingFormat.FORMAT_PEM
};
cryptoCert.createX509Cert(encodingBlob, function (error, x509Cert) {
if (error != null) {
console.log("createX509Cert failed, errCode: " + error.code + ", errMsg: " + error.message);
} else {
console.log("createX509Cert success");
let serialNumber = x509Cert.getCertSerialNumber();
}
});
```
### getIssuerName
getIssuerName() : DataBlob
......
......@@ -16,7 +16,7 @@
import cloudData from '@ohos.data.cloudData';
```
## Action
## ClearAction
清除本地下载的云端数据的行为枚举。
......@@ -347,9 +347,9 @@ try {
}
```
### clean
### clear
static clean(accountId: string, appActions: {[bundleName: string]: Action}, callback: AsyncCallback&lt;void&gt;):void
static clear(accountId: string, appActions: {[bundleName: string]: ClearAction}, callback: AsyncCallback&lt;void&gt;):void
清除本地下载的云端数据,使用callback异步回调。
......@@ -361,26 +361,26 @@ static clean(accountId: string, appActions: {[bundleName: string]: Action}, cal
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ---------- | ----------------------------------------- | ---- | -------------------------------- |
| accountId | string | 是 | 具体打开的云帐号ID。 |
| appActions | {[bundleName: string]: [Action](#action)} | 是 | 要清除数据的应用信息及清除规则。 |
| callback | AsyncCallback&lt;void&gt; | 是 | 回调函数。 |
| 参数名 | 类型 | 必填 | 说明 |
| ---------- | --------------------------------------------------- | ---- | -------------------------------- |
| accountId | string | 是 | 具体打开的云帐号ID。 |
| appActions | {[bundleName: string]: [ClearAction](#clearaction)} | 是 | 要清除数据的应用信息及清除规则。 |
| callback | AsyncCallback&lt;void&gt; | 是 | 回调函数。 |
**示例:**
```js
let action = cloudData.Action;
let action = cloudData.ClearAction;
let account = "test_id";
let bundleName1 = "test_bundleName1";
let bundleName2 = "test_bundleName2";
let appActions = { [bundleName1]: action.CLEAR_CLOUD_INFO, [bundleName2]: action.CLEAR_CLOUD_DATA_AND_INFO };
try {
cloudData.Config.clean(account, appActions, function (err) {
cloudData.Config.clear(account, appActions, function (err) {
if (err === undefined) {
console.info('Succeeding in cleaning cloud data');
console.info('Succeeding in clearing cloud data');
} else {
console.error(`Failed to clean cloud data. Code: ${err.code}, message: ${err.message}`);
console.error(`Failed to clear cloud data. Code: ${err.code}, message: ${err.message}`);
}
});
} catch (error) {
......@@ -388,9 +388,9 @@ try {
}
```
### clean
### clear
static clean(accountId: string, appActions: {[bundleName: string]: Action}): Promise&lt;void&gt;
static clear(accountId: string, appActions: {[bundleName: string]: ClearAction}): Promise&lt;void&gt;
清除本地下载的云端数据,使用Promise异步回调。
......@@ -402,10 +402,10 @@ static clean(accountId: string, appActions: {[bundleName: string]: Action}): Pro
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| ---------- | ----------------------------------------- | ---- | -------------------------------- |
| accountId | string | 是 | 具体打开的云帐号ID。 |
| appActions | {[bundleName: string]: [Action](#action)} | 是 | 要清除数据的应用信息及清除规则。 |
| 参数名 | 类型 | 必填 | 说明 |
| ---------- | --------------------------------------------------- | ---- | -------------------------------- |
| accountId | string | 是 | 具体打开的云帐号ID。 |
| appActions | {[bundleName: string]: [ClearAction](#clearaction)} | 是 | 要清除数据的应用信息及清除规则。 |
**返回值:**
......@@ -416,16 +416,16 @@ static clean(accountId: string, appActions: {[bundleName: string]: Action}): Pro
**示例:**
```js
let action = cloudData.Action;
let action = cloudData.ClearAction;
let account = "test_id";
let bundleName1 = "test_bundleName1";
let bundleName2 = "test_bundleName2";
let appActions = { [bundleName1]: action.CLEAR_CLOUD_INFO, [bundleName2]: action.CLEAR_CLOUD_DATA_AND_INFO };
try {
cloudData.Config.clean(account, appActions).then(() => {
console.info('Succeeding in cleaning cloud data');
cloudData.Config.clear(account, appActions).then(() => {
console.info('Succeeding in clearing cloud data');
}).catch((err) => {
console.error(`Failed to clean cloud data. Code: ${err.code}, message: ${err.message}`);
console.error(`Failed to clear cloud data. Code: ${err.code}, message: ${err.message}`);
});
} catch (error) {
console.error(`An unexpected error occurred. Code: ${error.code}, message: ${error.message}`);
......
......@@ -704,12 +704,12 @@ class EntryAbility extends UIAbility {
**系统能力:** SystemCapability.DistributedDataManager.RelationalStore.Core
| 名称 | 类型 | 必填 | 说明 |
| -------- | ------ | ---- | ---------------------------------------- |
| total | number | 是 | 表示数据库表中需要端云同步的总行数。 |
| success | number | 是 | 表示数据库表中端云同步成功的行数。 |
| failed | number | 是 | 表示数据库表中端云同步失败的行数。 |
| remained | number | 是 | 表示数据库表中端云同步剩余未执行的行数。 |
| 名称 | 类型 | 必填 | 说明 |
| ---------- | ------ | ---- | ---------------------------------------- |
| total | number | 是 | 表示数据库表中需要端云同步的总行数。 |
| successful | number | 是 | 表示数据库表中端云同步成功的行数。 |
| failed | number | 是 | 表示数据库表中端云同步失败的行数。 |
| remained | number | 是 | 表示数据库表中端云同步剩余未执行的行数。 |
## TableDetails<sup>10+</sup>
......@@ -3533,7 +3533,48 @@ promise.then(() => {
### setDistributedTables<sup>10+</sup>
setDistributedTables(tables: Array&lt;string&gt;, type: number, config: DistributedConfig, callback: AsyncCallback&lt;void&gt;): void
setDistributedTables(tables: Array&lt;string&gt;, type: DistributedType, callback: AsyncCallback&lt;void&gt;): void
设置分布式数据库表,使用callback异步回调。
**需要权限:** ohos.permission.DISTRIBUTED_DATASYNC
**系统能力:** SystemCapability.DistributedDataManager.RelationalStore.Core
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| -------- | ------------------------------------- | ---- | ---------------------------- |
| tables | Array&lt;string&gt; | 是 | 要设置的分布式数据库表表名。 |
| type | [DistributedType](#distributedtype10) | 是 | 表的分布式类型。 |
| callback | AsyncCallback&lt;void&gt; | 是 | 指定callback回调函数。 |
**错误码:**
以下错误码的详细介绍请参见[关系型数据库错误码](../errorcodes/errorcode-data-rdb.md)
| **错误码ID** | **错误信息** |
| ------------ | ------------ |
| 14800000 | Inner error. |
| 14800051 |The type of the distributed table does not match.|
**示例:**
```js
store.setDistributedTables(["EMPLOYEE"], relationalStore.DistributedType.DISTRIBUTED_CLOUD, function (err) {
if (err) {
console.error(`SetDistributedTables failed, code is ${err.code},message is ${err.message}`);
return;
}
console.info(`SetDistributedTables successfully.`);
})
```
###
### setDistributedTables<sup>10+</sup>
setDistributedTables(tables: Array&lt;string&gt;, type: DistributedType, config: DistributedConfig, callback: AsyncCallback&lt;void&gt;): void
设置分布式数据库表,使用callback异步回调。
......@@ -3546,10 +3587,19 @@ setDistributedTables(tables: Array&lt;string&gt;, type: number, config: Distribu
| 参数名 | 类型 | 必填 | 说明 |
| -------- | ----------------------------------- | --- | --------------- |
| tables | Array&lt;string&gt; | 是 | 要设置的分布式数据库表表名。 |
| type | number | 是 | 表的分布式类型。目前支持的入参值为: relationalStore.DistributedType.DISTRIBUTED_DEVICE、relationalStore.DistributedType.DISTRIBUTED_CLOUD。<br> 当type为relationalStore.DistributedType.DISTRIBUTED_DEVICE时,表示表在不同设备之间分布式。<br> 当type为relationalStore.DistributedType.DISTRIBUTED_CLOUD时,表示表在设备和云端之间分布式。 |
| type | [DistributedType](#distributedtype10) | 是 | 表的分布式类型。 |
| config | [DistributedConfig](#distributedconfig10) | 是 | 表的分布式配置信息。 |
| callback | AsyncCallback&lt;void&gt; | 是 | 指定callback回调函数。 |
**错误码:**
以下错误码的详细介绍请参见[关系型数据库错误码](../errorcodes/errorcode-data-rdb.md)
| **错误码ID** | **错误信息** |
| ------------ | ------------------------------------------------- |
| 14800000 | Inner error. |
| 14800051 | The type of the distributed table does not match. |
**示例:**
```js
......@@ -3566,7 +3616,7 @@ store.setDistributedTables(["EMPLOYEE"], relationalStore.DistributedType.DISTRIB
### setDistributedTables<sup>10+</sup>
setDistributedTables(tables: Array&lt;string>, type?: number, config?: DistributedConfig): Promise&lt;void>
setDistributedTables(tables: Array&lt;string>, type?: DistributedType, config?: DistributedConfig): Promise&lt;void>
设置分布式数据库表,使用Promise异步回调。
......@@ -3578,8 +3628,8 @@ store.setDistributedTables(["EMPLOYEE"], relationalStore.DistributedType.DISTRIB
| 参数名 | 类型 | 必填 | 说明 |
| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
| tables | Array&lt;string&gt; | 是 | 要设置的分布式数据库表表名。 |
| type | number | 否 | 表的分布式类型。默认值是relationalStore.DistributedType.DISTRIBUTED_DEVICE。<br> 目前支持的入参值为: relationalStore.DistributedType.DISTRIBUTED_DEVICE、relationalStore.DistributedType.DISTRIBUTED_CLOUD。<br/> 当type为relationalStore.DistributedType.DISTRIBUTED_DEVICE时,表示表在不同设备之间分布式。<br/> 当type为relationalStore.DistributedType.DISTRIBUTED_CLOUD时,表示表在设备和云端之间分布式。 |
| tables | Array&lt;string&gt; | 是 | 要设置的分布式数据库表表名。 |
| type | [DistributedType](#distributedtype10) | 否 | 表的分布式类型。默认值是relationalStore.DistributedType.DISTRIBUTED_DEVICE。 |
| config | [DistributedConfig](#distributedconfig10) | 否 | 表的分布式配置信息。不传入时默认autoSync为false,即只支持手动同步。 |
**返回值**
......@@ -3588,6 +3638,15 @@ store.setDistributedTables(["EMPLOYEE"], relationalStore.DistributedType.DISTRIB
| ------------------- | ------------------------- |
| Promise&lt;void&gt; | 无返回结果的Promise对象。 |
**错误码:**
以下错误码的详细介绍请参见[关系型数据库错误码](../errorcodes/errorcode-data-rdb.md)
| **错误码ID** | **错误信息** |
| ------------ | ------------------------------------------------- |
| 14800000 | Inner error. |
| 14800051 | The type of the distributed table does not match. |
**示例:**
```js
......
......@@ -372,6 +372,8 @@ deleteAsset(uri: string): Promise\<void>
删除媒体文件资源。
调用该接口彻底删除文件前,需要先调用[FileAsset.trash](#trash8)将文件放入回收站,否则文件会删除失败。
> **说明:**
> 此接口从API Version 9开始废弃。请使用[deleteAssets](js-apis-photoAccessHelper.md#deleteassets-3)替代。
......@@ -424,6 +426,8 @@ deleteAsset(uri: string, callback: AsyncCallback\<void>): void
删除媒体文件资源。
调用该接口彻底删除文件前,需要先调用[FileAsset.trash](#trash8)将文件放入回收站,否则文件会删除失败。
> **说明:**
> 此接口从API Version 9开始废弃。请使用[deleteAssets](js-apis-photoAccessHelper.md#deleteassets-2)替代。
......
......@@ -48,10 +48,10 @@ createData(mimeType: string, value: ValueType): PasteData
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- | -------- |
| mimeType | string | 是 | 自定义数据的MIME类型。 |
| value | [ValueType](#valuetype9) | 是 | 自定义数据内容。 |
| 参数名 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- |--------------------------------------------------------------------------------------------------------|
| mimeType | string | 是 | 剪贴板数据对应的MIME类型,可以是[常量](#常量)中已定义的类型,包括HTML类型,WANT类型,纯文本类型,URI类型,PIXELMAP类型;也可以是自定义的MIME类型,开发者可自定义此参数值。 |
| value | [ValueType](#valuetype9) | 是 | 自定义数据内容。 |
**返回值:**
......@@ -59,13 +59,21 @@ createData(mimeType: string, value: ValueType): PasteData
| -------- | -------- |
| [PasteData](#pastedata) | 剪贴板内容对象。 |
**示例:**
**示例1:**
```js
let dataXml = new ArrayBuffer(256);
let pasteData = pasteboard.createData('app/xml', dataXml);
let pasteData = pasteboard.createData('app/xml', dataXml);
```
**示例2:**
```js
let dataText = 'hello';
let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, dataText);
```
## pasteboard.createRecord<sup>9+</sup>
createRecord(mimeType: string, value: ValueType):PasteDataRecord;
......@@ -76,10 +84,10 @@ createRecord(mimeType: string, value: ValueType):PasteDataRecord;
**参数:**
| 参数名 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- | -------- |
| mimeType | string | 是 | 自定义数据的MIME类型。 |
| value | [ValueType](#valuetype9) | 是 | 自定义数据内容。 |
| 参数名 | 类型 | 必填 | 说明 |
| -------- | -------- | -------- |-------------------|
| mimeType | string | 是 | 剪贴板数据对应的MIME类型,可以是[常量](#常量)中已定义的类型,包括HTML类型,WANT类型,纯文本类型,URI类型,PIXELMAP类型;也可以是自定义的MIME类型,开发者可自定义此参数值。 |
| value | [ValueType](#valuetype9) | 是 | 自定义数据内容。 |
**返回值:**
......@@ -87,13 +95,20 @@ createRecord(mimeType: string, value: ValueType):PasteDataRecord;
| -------- | -------- |
| [PasteDataRecord](#pastedatarecord7) | 一条新建的自定义数据内容条目。 |
**示例:**
**示例1:**
```js
let dataXml = new ArrayBuffer(256);
let pasteDataRecord = pasteboard.createRecord('app/xml', dataXml);
```
**示例2:**
```js
let dataUri = 'dataability:///com.example.myapplication1/user.txt';
let record = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_URI, dataUri);
```
## pasteboard.getSystemPasteboard
getSystemPasteboard(): SystemPasteboard
......
......@@ -4324,6 +4324,10 @@ export default class EntryAbility extends UIAbility {
通过WebCookie可以控制Web组件中的cookie的各种行为,其中每个应用中的所有web组件共享一个WebCookieManager实例。
> **说明:**
>
> 目前调用WebCookieManager下的方法,都需要先加载Web组件。
### getCookie
static getCookie(url: string): string
......@@ -4785,6 +4789,10 @@ struct WebComponent {
通过WebStorage可管理Web SQL数据库接口和HTML5 Web存储接口,每个应用中的所有Web组件共享一个WebStorage。
> **说明:**
>
> 目前调用WebStorage下的方法,都需要先加载Web组件。
### deleteOrigin
static deleteOrigin(origin : string): void
......@@ -5245,6 +5253,10 @@ struct WebComponent {
web组件数据库管理对象。
> **说明:**
>
> 目前调用WebDataBase下的方法,都需要先加载Web组件。
### getHttpAuthCredentials
static getHttpAuthCredentials(host: string, realm: string): Array\<string>
......@@ -5423,6 +5435,10 @@ struct WebComponent {
web组件地理位置权限管理对象。
> **说明:**
>
> 目前调用GeolocationPermissions下的方法,都需要先加载Web组件。
### 需要权限
访问地理位置时需添加权限:ohos.permission.LOCATION、ohos.permission.APPROXIMATELY_LOCATION、ohos.permission.LOCATION_IN_BACKGROUND,具体权限说明请参考[位置服务](./js-apis-geolocation.md)
......
......@@ -165,7 +165,7 @@
strokeColor: '#0081ff',
fillColor: '#cce5ff',
data: [763, 550, 551, 554, 731, 654, 525, 696, 595, 628, 791, 505, 613, 575, 475, 553, 491, 680, 657, 716],
gradient: true,
gradient: false,
}
],
lineOps: {
......
......@@ -45,8 +45,8 @@ Grid(scroller?: Scroller)
| 名称 | 参数类型 | 描述 |
| -------- | -------- | -------- |
| columnsTemplate | string | 设置当前网格布局列的数量,不设置时默认1列。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'&nbsp;是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。<br/>‘repeat(auto-fit, 90px)’是设置最小列宽值为90,自动计算列数和实际列宽。<br/>**说明:** <br/>设置为'0fr'时,该列的列宽为0,不显示GridItem。设置为其他非法值时,GridItem显示为固定1列。 |
| rowsTemplate | string | 设置当前网格布局行的数量,不设置时默认1行。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。<br/>‘repeat(auto-fit, 90px)’是设置最小行高值为90,自动计算行数和实际行高。<br/>**说明:** <br/>设置为'0fr',则这一行的行宽为0,这一行GridItem不显示。设置为其他非法值,按固定1行处理。 |
| columnsTemplate | string | 设置当前网格布局列的数量或最小列宽值,不设置时默认1列。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'&nbsp;是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。<br/>‘repeat(auto-fit, 90px)’是设置最小列宽值为90,自动计算列数和实际列宽。<br/>**说明:** <br/>设置为'0fr'时,该列的列宽为0,不显示GridItem。设置为其他非法值时,GridItem显示为固定1列。 |
| rowsTemplate | string | 设置当前网格布局行的数量或最小行高值,不设置时默认1行。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。<br/>‘repeat(auto-fit, 90px)’是设置最小行高值为90,自动计算行数和实际行高。<br/>**说明:** <br/>设置为'0fr',则这一行的行宽为0,这一行GridItem不显示。设置为其他非法值,按固定1行处理。 |
| columnsGap | [Length](ts-types.md#length) | 设置列与列的间距。<br/>默认值:0<br/>**说明:** <br/>设置为小于0的值时,按默认值显示。 |
| rowsGap | [Length](ts-types.md#length) | 设置行与行的间距。<br/>默认值:0<br/>**说明:** <br/>设置为小于0的值时,按默认值显示。 |
| scrollBar | [BarState](ts-appendix-enums.md#barstate) | 设置滚动条状态。<br/>默认值:BarState.Off<br/>**说明:** <br/>API version 9及以下版本默认值为BarState.Off,API version 10的默认值为BarState.Auto。 |
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册