diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..35410cacdc5e87f985c93a96520f5e11a5c822e4 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..08c3be96e3e711e0801af36f5fae65bb157af76d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/rust_proj_mock.iml b/.idea/rust_proj_mock.iml new file mode 100644 index 0000000000000000000000000000000000000000..0f9a3c60115f9c8d606d87063c995759c868aee6 --- /dev/null +++ b/.idea/rust_proj_mock.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..35eb1ddfbbc029bcab630581847471d7f238ec53 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..c85902c821c0e17b288c4e38b8b7889c60557b72 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,22 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "hilog_rust" +version = "0.1.0" + +[[package]] +name = "ipc_rust" +version = "0.1.0" +dependencies = [ + "hilog_rust", +] + +[[package]] +name = "ylong_cloud_extension" +version = "1.0.0" +dependencies = [ + "hilog_rust", + "ipc_rust", +] diff --git a/src/cloud_extension/BUILD.gn b/src/cloud_extension/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bd6b46342dcf11536c9a4e8dd029ef51c6d1e1be --- /dev/null +++ b/src/cloud_extension/BUILD.gn @@ -0,0 +1,46 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("//build/test.gni") + +ohos_rust_shared_ffi("ylong_cloud_extension") { + crate_name = "ylong_cloud_extension" + crate_root = "src/lib.rs" + + sources = [ "src/lib.rs" ] + external_deps = [ + "hilog:hilog_rust", + "ipc:ipc_rust" + ] + deps = [ + "../connect_adapter:conn_adapter" + ] + subsystem_name = "distributeddatamgr" + + part_name = "datamgr_service" +} + +ohos_rust_unittest("rust_ylong_cloud_ext_unit_test") { + module_out_path = "distributeddatamgr/datamgr_service/services/rust/ylong_cloud_extension" + sources = [ "src/lib.rs" ] + + external_deps = [ + "hilog:hilog_rust", + "ipc:ipc_rust" + ] + deps = [ + ":ylong_cloud_extension", + "../connect_adapter:conn_adapter" + ] +} \ No newline at end of file diff --git a/src/cloud_extension/Cargo.toml b/src/cloud_extension/Cargo.toml index cd717be66c2826bdd3f10df30088cf0c47ec766f..0dff70769bc216e61bb6ad0ad9b036f5d1c6b342 100644 --- a/src/cloud_extension/Cargo.toml +++ b/src/cloud_extension/Cargo.toml @@ -1,10 +1,23 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + [package] -name = "cloud_extension" +name = "ylong_cloud_extension" version = "1.0.0" edition = "2021" description = "Cloud and Local End Synchronization General Implementation in Rust. Belong to OH Distributed Data Service." license = "Apache-2.0" -repository = "https://gitee.com/openharmony-sig/commonlibrary_rust_ylong_runtime" +repository = "https://gitee.com/openharmony-sig/" keywords = ["cloud", "distributeddataservice"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -13,10 +26,6 @@ keywords = ["cloud", "distributeddataservice"] name = "cloud_extension" crate-type = ["cdylib", "staticlib", "lib"] -[features] -default = [] -c_adapter = [] - [dependencies] ipc_rust = {path = "../ipc_rust", version = "0.1.0"} hilog_rust = { path = "../hilog_rust", version = "0.1.0" } \ No newline at end of file diff --git a/src/cloud_extension/include/basic_rust_types.h b/src/cloud_extension/include/basic_rust_types.h new file mode 100644 index 0000000000000000000000000000000000000000..dd3df8655bdaadce49af756b0e2d06de170e60a2 --- /dev/null +++ b/src/cloud_extension/include/basic_rust_types.h @@ -0,0 +1,180 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H +#define CLOUD_EXTENSION_BASIC_RUST_TYPES_H + +#ifndef CLOUD_EXTENSION_ERROR_H +#include "error.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +typedef enum ValueType { + NULL = 0, + U32, + I32, + STRING, + VALUE, + VALUE_BUCKET, + DATABASE, + TABLE, + FIELD, + RELATION, + RELATION_SET, + ASSET, + APP_INFO, + VEC_U32, + VEC_DATABASE, + HASHMAP_VALUE, +}; + +/** +* @brief Type declaration of Rust struct Vector. +* @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `VectorFree` to release +* the memory it occupies. +*/ +typedef void Vector; + +/** + * @brief Create a Vector enum according to ValueType. + * @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `VectorFree` to release + * the memory it occupies. + */ +Vector *VectorNew(ValueTyp typ); + +/** + * @breif Get ValueType of the Vector pointer. + */ +ValueTyp VectorGetValueTyp(Vector *src); + +/** + * @breif Push value into the Vector pointer. + * @attention For Values pushed in, if they're created by other functions and allocated in Rust side, + * pushing them will transfer the management to the Vector. Therefore, no more free is needed. + */ +int VectorPush( + Vector *src, + void *value, + size_t valueLen, +); + +/** + * @breif Get value from the Vector pointer. + * @attention If value type is Vector or HashMap, value will be copied so that the C side can get information stored + * in Rust side. In this case, the pointer returned back should be freed by VectorFree or HashMapFree. + */ +int VectorGet( + Vector *src, + size_t idx, + void **value, + size_t *valueLen, +); + +/** + * @brief Get length of a Vector pointer. + */ +int VectorGetLength(Vector *src, size_t *len); + +/** + * @brief Free a Vector pointer. + */ +void VectorFree(Vector *src); + +/** +* @brief Type declaration of Rust struct HashMap. +* @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `HashMapFree` to release +* the memory it occupies. +*/ +typedef void HashMap; + +/** + * @brief Initialize a HashMap enum by its ValueType. The key type is fixed to be String. + * @attention The memory is managed by Rust. Therefore, to memory leaks, users should call `HashMapFree` to release + * the memory it occupies. + */ +HashMap *HashMapNew(ValueTyp valueTyp); + +/** + * @brief Get key type of a Hashmap pointer. + */ +ValueTyp HashMapGetKeyTyp(HashMap *src); + +/** + * @brief Get value type of a Hashmap pointer. + */ +ValueTyp HashMapGetValueTyp(HashMap *src); + +/** + * @brief Get length of a Hashmap pointer. + */ +int HashMapGetLength(HashMap *src, size_t *len); + +/** + * @brief Insert key, value pairs into the Hashmap pointer. + * @attention For Values pushed in, if they're created by other functions and allocated in Rust side, + * pushing them will transfer the management to the HashMap. Therefore, no more free is needed. + */ +int HashMapInsert( + HashMap *src, + void *key, + size_t keyLen, + void *value, + size_t valueLen, +); + +/** + * @brief Get key, value pair from the Hashmap pointer. + * @param src [IN] + * key [OUT] Vec, here fixed to be Vec now + * value [OUT] Vec + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention The vectors output should be freed by VectorFree. + */ +int HashMapIterGetKeyValuePair( + HashMap *src, + Vector **key, + Vector **value, +); + +/** + * @brief According to key, get value from the Hashmap pointer. + * @attention If value type is Vector or HashMap, value will be copied so that the C side can get information stored + * in Rust side. In this case, the pointer returned back should be freed by VectorFree or HashMapFree. + */ +int HashMapGet( + HashMap *src, + void *key, + size_t keyLen, + void **value, + size_t *valueLen, +); + +/** + * @brief Free a Hashmap pointer. + */ +void HashMapFree(HashMap *src); + + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //CLOUD_EXTENSION_BASIC_RUST_TYPES_H diff --git a/src/cloud_extension/include/cloud_ext_types.h b/src/cloud_extension/include/cloud_ext_types.h new file mode 100644 index 0000000000000000000000000000000000000000..f9493b6cac789ba7cda85785419f77fe6e1edcf2 --- /dev/null +++ b/src/cloud_extension/include/cloud_ext_types.h @@ -0,0 +1,633 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#ifndef CLOUD_EXTENSION_TYPES_H +#define CLOUD_EXTENSION_TYPES_H + +#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H +#include "basic_rust_types.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +/** + * @brief Type declaration of Rust cloud extension struct Value. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to + * release the memory occupied + */ +typedef void Value; + +/** + * @brief Enumeration represents inner type of contents in Value enum. + */ +typedef enum ValueInnerType { + EMPTY = 0, + INT = 1, + DOUBLE = 2, + BOOL = 3, + STRING = 4, + BYTES = 5, + ASSET = 6, + ASSETS = 7 +}; + +/** + * @brief Create an Value instance according to ValueInnerType. + * @retval != NULL, a valid pointer of Value. + */ +Value *ValueNew(ValueInnerType typ, void *content, size_t contentLen); + +/** + * @brief Get content from a Value pointer. + * @param src [IN] + * typ [OUT] User should use this value to cast the output pointer. + * content [OUT] The pointer value will be updated to point to the content of the Value. + * contentLen [OUT] + * @attention If the type is Assets, the Vector pointer returned should be freed by VectorFree. + */ +int ValueGetContent(Value *src, ValueInnerType *typ, void **content, size_t *contentLen); + +/** + * @brief Free a Value pointer. + */ +void ValueFree(Value *src); + +/** + * @brief AssetStutus enumeration. + */ +typedef enum AssetStatus { + UNKNOWN = 0, + NORMAL, + INSERT, + UPDATE, + DELETE, + ABNORMAL, + DOWNLOADING, + BUTT, +}; + +/** +* @brief Asset builder that will used to build an Asset in Rust side +*/ +typedef struct { + unsigned int version; + AssetStatus status; + unsigned int expiresTime; + unsigned char *id; + size_t idLen; + unsigned char *name; + size_t nameLen; + unsigned char *uri; + size_t uriLen; + unsigned char *localPath; + size_t localPathLen; + unsigned char *createTime; + size_t createTimeLen; + unsigned char *modifyTime; + size_t modifyTimeLen; + unsigned char *size; + size_t sizeLen; + unsigned char *hash; + size_t hashLen; +} AssetBuilder; + +/** + * @brief Type declaration of Rust cloud extension struct Value. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to + * release the memory occupied + */ +typedef void Asset; + +/** + * @brief Initialize an Asset by the AssetBuilder. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `ValueFree` to + * release the memory occupied + */ +Asset *AssetNew(AssetBuilder *builder); + +/** + * @brief Get id from an Asset pointer. + */ +int AssetGetId(Asset *asset, unsigned char **id, size_t *len); + +/** + * @brief Get name from an Asset pointer. + */ +int AssetGetName(Asset *asset, unsigned char **name, size_t *len); + +/** + * @brief Get uri from an Asset pointer. + */ +int AssetGetUri(Asset *asset, unsigned char **uri, size_t *len); + +/** + * @brief Get local path from an Asset pointer. + */ +int AssetGetLocalPath(Asset *asset, unsigned char **localPath, size_t *len); + +/** + * @brief Get create time from an Asset pointer. + */ +int AssetGetCreateTime(Asset *asset, unsigned char **createTime, size_t *len); + +/** + * @brief Get modified time from an Asset pointer. + */ +int AssetGetModifiedTime(Asset *asset, unsigned char **modifiedTime, size_t *len); + +/** + * @brief Get size from an Asset pointer. + */ +int AssetGetSize(Asset *asset, unsigned char **size, size_t *len); + +/** + * @brief Get hash from an Asset pointer. + */ +int AssetGetHash(Asset *asset, unsigned char **hash, size_t *len); + +/** + * @brief Free an Asset pointer. + */ +void AssetFree(Asset *src); + +/** + * @brief Type declaration of Rust cloud extension struct Database. Can be obtained from Schema. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `DatabaseFree` to + * release the memory occupied + */ +typedef void Database; + +/** + * @brief Create a Database instance. + * @param tables [IN] HashMap, table_name as key + * @retval != NULL, a valid pointer of Database. + * @attention When passed in, database will take control of the memory management of the vector tables. No more free + * is needed. For the database pointer, the memory is managed by Rust. Therefore, to prevent memory leaks, + * users should call `DatabaseFree` to release the memory occupied + */ +Database *DatabaseNew( + unsigned char *name, + size_t nameLen, + unsigned char *alias, + size_t aliasLen, + HashMap *tables +) + +/** + * @brief Get name from a Database instance. + * @param db [IN] + * name [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Name returned shouldn't be freed, or issues of double free is possible. + */ +int DatabaseGetName(Database *db, unsigned char **name, size_t *len); + +/** + * @brief Get alias from a Database instance. + * @param db [IN] + * alias [OUT] + * len [OUT] + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention Alias returned shouldn't be freed, or issues of double free is possible. + */ +int DatabaseGetAlias(Database *db, unsigned char **alias, size_t *len); + +/** + * @brief Get tables from a Database instance. + * @param db [IN] + * tables [OUT] HashMap, table name as key + * @retval SUCCESS + * ERRNO_NULLPTR + * @attention The HashMap returned should be freed by HashMapFree. + */ +int DatabaseGetTable(Database *db, HashMap **tables); + +/** + * @brief Free a Database pointer. + */ +void DatabaseFree(Database *db); + +/** + * @brief Type declaration of Rust cloud extension struct Table. Can be obtained from Database. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `TableFree` to + * release the memory occupied + */ +typedef void Table; + +/** + * @brief Create a Table instance. + * fields [IN] Vec + * @attention When passed in, table will take control of the memory management of the vector field. No more free is + * needed. + */ +Table *TableNew( + unsigned char *name, + size_t nameLen, + unsigned char *alias, + size_t aliasLen, + Vector *fields +) + +/** + * @brief Get name from a Table instance. + * @param tb [IN] + * name [OUT] + * len [OUT] + * @attention Name returned shouldn't be freed, or issues of double free is possible. + */ +int TableGetName(Table *tb, unsigned char **name, size_t *len); + +/** + * @brief Get alias from a Table instance. + * @param tb [IN] + * alias [OUT] + * len [OUT] + * @attention Alias returned shouldn't be freed, or issues of double free is possible. + */ +int TableGetAlias(Table *tb, unsigned char **alias, size_t *len); + +/** + * @brief Get fields from a Database instance. + * @param tb [IN] + * fields [OUT] Vec + * len [OUT] + * @attention The Vector returned should be freed by VectorFree. + */ +int TableGetFields(Table *tb, Vector **fields); + +/** + * @brief Free a Table pointer. + */ +void TableFree(Table *db); + +/** + * @brief Type declaration of Rust cloud extension struct Field. Can be obtained from Table. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `FieldFree` to + * release the memory occupied + */ +typedef void Field; + +/** + * @brief FieldBuilder providing necessary information when create a Field. + */ +typedef struct { + unsigned char *colName; + size_t colNameLen; + unsigned char *alias; + size_t aliasLen; + unsigned int typ; + bool primary; + bool nullable; +} FieldBuilder; + +/** + * @brief Initialize a Field instance. + * @attention The memory is managed by Rust. Therefore, to prevent memory leaks, users should call `FieldFree` to + * release the memory occupied + */ +Field *FieldNew(FieldBuilder *builder); + +/** + * @brief Get name from a Field instance. + * @param fd [IN] + * name [OUT] + * len [OUT] + * @attention Name returned shouldn't be freed, or issues of double free is possible. + */ +int FieldGetColName(Field *fd, unsigned char **name, size_t *len); + +/** + * @brief Get alias from a Field instance. + * @param fd [IN] + * alias [OUT] + * len [OUT] + * @attention Alias returned shouldn't be freed, or issues of double free is possible. + */ +int FieldGetAlias(Field *fd, unsigned char **alias, size_t *len); + +/** + * @brief Get type of a Field instance. + * @param fd [IN] + * typ [OUT] + */ +int FieldGetTyp(Field *fd, unsigned int *typ); + +/** + * @brief Get primacy of a Field instance. + * @param fd [IN] + * primary [OUT] + */ +int FieldGetPrimary(Field *fd, bool *primary); + +/** + * @brief Get nullability of a Field instance. + * @param fd [IN] + * nullable [OUT] + */ +int FieldGetNullable(Field *fd, bool *nullable); + +/** + * @brief Free a Field pointer. + */ +void FieldFree(Table *db); + +/** + * @brief Type declaration of Rust cloud extension struct CloudInfo. Can be obtained from + * CloudServerGetServiceInfo. + * @attention CloudInfo is obtained from CloudServer to provide relevant information to users. When done, users should + * call CloudInfoFree to release memory and prevent memory leak. + */ +typedef void CloudInfo; + +/** + * @brief Get user from a CloudInfo pointer. + */ +int CloudInfoGetUser(CloudInfo *info, int *user); + +/** + * @brief Get user from a CloudInfo pointer. + */ +int CloudInfoGetId( + CloudInfo *info, + unsigned char **id, + size_t *idLen, +); + +/** + * @brief Get total space from a CloudInfo pointer. + */ +int CloudInfoGetTotalSpace( + CloudInfo *info, + int *totalSpace +); + +/** + * @brief Get remain space from a CloudInfo pointer. + */ +int CloudInfoGetRemainSpace( + CloudInfo *info, + int *remainSpace +); + +/** + * @brief Check whether a CloudInfo enables cloud sync. + */ +int CloudInfoGetEnableCloud( + CloudInfo *info, + bool *enableCloud +); + +/** + * @brief Get hashmap of AppInfo from a CloudInfo pointer. + * @param appInfo [OUT] HashMap, bundle name as key + */ +int CloudInfoGetAppInfo( + CloudInfo *info, + HashMap **appInfo +); + +/** + * @brief Destroy an CloudInfo instance. + * @param ptr [IN] The pointer of CloudInfo that should be destroyed. + */ +void CloudInfoFree(CloudInfo *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct AppInfo. + * @attention AppInfo is obtained from CloudInfo. When CloudInfo is freed, AppInfo will also be + * freed. + */ +typedef void AppInfo; + +/** + * @brief Get id from an AppInfo pointer. + */ +int AppInfoGetAppId( + AppInfo *appInfo, + unsigned char **appId, + size_t *idLen, +); + +/** + * @brief Get bundle name from an AppInfo pointer. + */ +int AppInfoGetBundleName( + AppInfo *appInfo, + unsigned char **bundleName, + size_t *len, +); + +/** + * @brief Check whether an AppInfo pointer allows cloud switch. + */ +int AppInfoGetCloudSwitch( + AppInfo *appInfo, + bool *cloudSwitch +); + +/** + * @brief Get instance id from an AppInfo pointer. + */ +int AppInfoGetInstanceId( + AppInfo *appInfo, + int *id +); + +/** + * @brief Type declaration of Rust cloud extension struct SchemaMeta. Can be obtained from + * CloudServerGetServiceInfo. + * @attention SchemaMeta is obtained from CloudServer to provide relevant information to users. When done, users + * should call SchemaMetaFree to release memory and prevent memory leak. + */ +typedef void SchemaMeta; + +/** + * @brief Get version from a SchemaMeta pointer. + */ +int SchemaMetaGetVersion(SchemaMeta *schema, int *version); + +/** + * @brief Get bundle name from a SchemaMeta pointer. + */ +int SchemaMetaGetBundleName(SchemaMeta *schema, unsigned char **name, size_t *len); + +/** + * @brief Get databases from a SchemaMeta instance. + * @param schema [IN] + * db [IN] Vec + * @attention The Vector returned should be freed by VectorFree. + */ +int SchemaMetaGetDatabases( + SchemaMeta *schema, + Vector **db +); + +/** + * @brief Destroy an SchemaMeta instance. + * @param ptr [IN] The pointer of SchemaMeta that should be destroyed. + */ +void SchemaMetaFree(SchemaMeta *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct RelationSet. + * @attention When done, users should call `RelationSetFree` to release memory and prevent memory leak. + */ +typedef void RelationSet; + +/** + * @brief Create a RelationSet instance by bundle name, and relations. + * @param relations [IN] HashMap, Database alias as key, subscription id and time generated + * by the cloud as value + * @attention The hashmap passed in will be managed in the Rust side, so don't call free on this pointer again. + */ +RelationSet *RelationSetNew( + const unsigned char *bundleName, + size_t nameLen, + c_int expireTime, + HashMap *relations +) + +/** + * @brief Get bundle name from a RelationSet pointer. + */ +int RelationSetGetBundleName(RelationSet *re, unsigned char **name, size_t *len); + +/** + * @brief Get expire time from a RelationSet pointer. + */ +int RelationSetGetExpireTime(RelationSet *re, unsigned int *time); + +/** + * @brief Get relations from a RelationSet pointer. + * @param relations [OUT] HashMap, Database alias as key, subscription id and time generated + * by the cloud as value + * @attention The hashmap returned should be freed by HashMapFree. + */ +int RelationSetGetRelations(RelationSet *re, HashMap **relations); + +/** + * @brief Free a RelationSet pointer. + */ +void RelationSetFree(RelationSet *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct CloudData. Can be obtained from CloudDbBatchQuery. + * @attention CloudData is obtained from CloudDatabase to provide relevant information to users. When done, users + * should call `CloudDataFree` to release memory and prevent memory leak. + */ +typedef void CloudData; + +/** + * @brief Get the next cursor from a CloudData pointer. + */ +int CloudDataGetNextCursor(CloudData *data, unsigned char **cursor, size_t *len); + +/** + * @brief Check whether a CloudData has more data. + */ +int CloudDataGetHasMore(CloudData *data, bool *hasMore); + +/** + * @brief Get vector of values from a CloudData pointer. + * @param values [OUT] Vec + * @attention The vector returned should be freed by VectorFree. + */ +int CloudDataGetValues(CloudData *data, Vector **values); + +/** + * @brief Free a CloudData pointer. + */ +void CloudDataFree(CloudData *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct ValueBucket. + */ +typedef void ValueBucket; + +/** + * @brief Get id from a ValueBucket pointer. + */ +int ValueBucketGetId(ValueBucket *vb, unsigned char **id, size_t *len); + +/** + * @brief Get type string from a ValueBucket pointer. + */ +int ValueBucketGetTyp(ValueBucket *vb, unsigned char **typ, size_t *len); + +/** + * @brief Get create info from a ValueBucket pointer. + */ +int ValueBucketGetCreateInfo(ValueBucket *vb, VBInfo **info); + +/** + * @brief Get modified info from a ValueBucket pointer. + */ +int ValueBucketGetModifiedInfo(ValueBucket *vb, VBInfo **info); + +/** + * @brief Get data from a ValueBucket pointer. + * @param data [OUT] HashMap + * @attention The HashMap returned should be freed by `HashMapFree`. + */ +int ValueBucketGetData(ValueBucket *vb, HashMap **data); + +/** + * @brief Check whether a ValueBucket is deleted. + */ +int ValueBucketIsDelete(ValueBucket *vb, bool *isDelete); + +/** + * @brief Get version from a ValueBucket pointer. + */ +int ValueBucketGetVersion(ValueBucket *vb, unsigned int *version); + +/** + * @brief Check whether a ValueBucket is deleted. + */ +int ValueBucketIsNewCreate(ValueBucket *vb, bool *isNewCreate); + +/** + * @brief Type declaration of Rust cloud extension struct VBInfo. + */ +typedef void VBInfo; + +/** + * @brief Get time from a VBInfo pointer. + */ +int VBInfoGetTime(VBInfo *info, int *time); + +/** + * @brief Get device name from a VBInfo pointer. + */ +int VBInfoGetDeviceName(VBInfo *info, unsigned char **name, size_t *len); + +/** + * @brief Get app id from a VBInfo pointer. + */ +int VBInfoGetAppId(VBInfo *info, unsigned char **id, size_t *len); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //CLOUD_EXTENSION_TYPES_H diff --git a/src/cloud_extension/include/cloud_extension.h b/src/cloud_extension/include/cloud_extension.h index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3f075f0ecab614d09c266638ceeeb35ce057ab65 100644 --- a/src/cloud_extension/include/cloud_extension.h +++ b/src/cloud_extension/include/cloud_extension.h @@ -0,0 +1,336 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @defgroup cloud_extension C headers + * @ingroup cloud_extension + */ + +#ifndef CLOUD_EXTENSION_H +#define CLOUD_EXTENSION_H + +#ifndef CLOUD_EXTENSION_BASIC_RUST_TYPES_H +#include "basic_rust_types.h" +#endif + +#ifndef CLOUD_EXTENSION_TYPES_H +#include "cloud_ext_types.h" +#endif + +#ifndef CLOUD_EXTENSION_ERROR_H +#include "error.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +// AssetLoader, CloudDatabase, CloudServer +/** + * @brief Type declaration of Rust cloud extension struct AssetLoader. + * @attention When an instance is created by AssetLoaderNew,memory is managed by Rust and it will return a pointer. + * When destroying this instance, users should call AssetLoaderFree to release memory and prevent leak. + * + * Besides, AssetLoader is only valid when the database passed in its initialization function is valid. + */ +typedef void AssetLoader; + +/** + * @brief Information needed when upload or download assets through AssetLoader. + */ +typedef struct { + unsigned char *tableName; + size_t tableNameLen; + unsigned char *gid; + size_t gidLen; + unsigned char *prefix; + size_t prefixLen; +} UpDownloadInfo; + +/** + * @brief Create an AssetLoader instance. + * @param userId [IN] + * bundleName [IN] + * nameLen [IN] + * db [IN] + */ +AssetLoader *AssetLoaderNew(int userId, const unsigned char *bundleName, size_t nameLen, Database* db); + +/** + * @brief Upload assets. + * @param loader [IN] + * tableName [IN] + * tableNameLen [IN] + * gid [IN] + * gidLen [IN] + * prefix [IN] + * prefixLen [IN] + * assets [IN/OUT] HashMap + */ +int AssetLoaderUpload(AssetLoader *loader, UpDownloadInfo *info, HashMap *assets); + +/** + * @brief Download assets. + * @param loader [IN] + * tableName [IN] + * tableNameLen [IN] + * gid [IN] + * gidLen [IN] + * prefix [IN] + * prefixLen [IN] + * assets [IN/OUT] HashMap + */ +int AssetLoaderDownload(AssetLoader *loader, UpDownloadInfo *info, HashMap *assets); + +/** + * @brief Remove one asset from the local path. + * @param asset [IN] + */ +int AssetLoaderRemoveLocalAssets(Asset *asset); + +/** + * @brief Destroy an AssetLoader instance. + * @param ptr [IN] The pointer of AssetLoader that should be destroyed. + */ +void AssetLoaderFree(AssetLoader *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct CloudDatabase. + * @attention When an instance is created by CloudDbNew,memory is managed by Rust and it will return a pointer. + * When destroying this instance, users should call CloudDatabaseFree to release memory and prevent leak. + */ +typedef void CloudDatabase; + +/** + * @brief Create an CloudDatabase instance. + * @retval != NULL, a valid pointer of CloudDatabase. + * @attention Database passed in will be managed by CloudDb instead. No free function is needed to call on it. + */ +CloudDatabase *CloudDbNew( + int userId, + const unsigned char *bundleName, + size_t nameLen, + Database *database +); + +/** + * @brief Sql that will passed to CloudDatabase and executed. + */ +typedef struct { + unsigned char *table; + size_t tableLen; + unsigned char *sql; + size_t sqlLen; +} Sql; + +/** + * @brief Execute a sql on a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * sql [IN] + * sqlLen [IN] + * extend [IN/OUT] HashMap + * @retval SUCCESS + * ERRNO_Unsupported + */ +int CloudDbExecuteSql( + CloudDatabase *cdb, + const Sql *sql, + HashMap *extend +); + +/** + * @brief Insert batches of value buckets into a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * value [IN] Vec> + * extend [IN/OUT] Vec> + * @retval SUCCESS + */ +int CloudDbBatchInsert( + CloudDatabase *cdb, + const unsigned char *table, + size_t tableLen, + const Vector *value, + Vector *extend, +); + +/** + * @brief Update batches of value buckets in a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * value [IN] Vec> + * extend [IN/OUT] Vec> + * @retval SUCCESS + */ +int CloudDbBatchUpdate( + CloudDatabase *cdb, + const unsigned char *table, + size_t tableLen, + const Vector *value, + Vector *extend +); + +/** + * @brief Delete batches of value buckets from a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * extend [IN/OUT] Vec> + * @retval SUCCESS + */ +int CloudDbBatchDelete( + CloudDatabase *cdb, + const unsigned char *table, + size_t tableLen, + Vector *extend +); + +/** + * @brief Query info that will passed to CloudDatabase. + */ +typedef struct { + unsigned char *table; + size_t tableLen; + unsigned char *cursor; + size_t cursorLen; +} QueryInfo; + + +/** + * @brief Search batches of value buckets from a CloudDatabase instance. + * @param cdb [IN] + * table [IN] + * tableLen [IN] + * cursor [IN] + * cursorLen [IN] + * out [OUT] The address of a pointer to a Cursor. The pointer value will be updated when + * query function successfully returns. + * @attention The CloudData returned should be freed by `CloudDataFree` if no longer in use. + */ +int CloudDbBatchQuery( + CloudDatabase *cdb, + const QueryInfo *info, + CloudData **out +); + +/** + * @brief Lock a CloudDatabase instance. + * @param cdb [IN] + * expire [OUT] + */ +int CloudDbLock(CloudDatabase *cdb, int *expire); + +/** + * @brief Unlock a CloudDatabase instance. + * @param cdb [IN] + */ +int CloudDbUnlock(CloudDatabase *cdb); + +/** + * @brief Heartbeat function of a CloudDatabase. Extend the current locking session. + * @param cdb [IN] + */ +int CloudDbHeartbeat(CloudDatabase *cdb); + +/** + * @brief Destroy an CloudDatabase instance. + * @param ptr [IN] The pointer of CloudDatabase that should be destroyed. + */ +void CloudDatabaseFree(CloudDatabase *ptr); + +/** + * @brief Type declaration of Rust cloud extension struct CloudServer. + * @attention When an instance is created by CloudServerNew,memory is managed by Rust and it will return a pointer. + * When destroying this instance, users should call CloudServerFree to release memory and prevent leak. + */ +typedef void CloudServer; + +/** + * @brief Create an CloudServer instance. + * @param userId [IN] + * @retval != NULL, a valid pointer of CloudServer. + */ +CloudServer *CloudServerNew(int userId); + +/** + * @brief Get service info from a CloudServer pointer. + * @param server [IN] + * info [OUT] + * @attention The CloudInfo returned should be freed by `CloudInfoFree` if no longer in use. + */ +int CloudServerGetServiceInfo(CloudServer *server, CloudInfo **info); + +/** + * @brief Get app schema from a CloudServer pointer, with a bundle name. + * @param server [IN] + * bundleName [IN] + * bundleNameLen [IN] + * schema [OUT] + * @attention The SchemaMeta returned should be freed by `SchemaMetaFree` if no longer in use. + */ +int CloudServerGetAppSchema( + CloudServer *server, + const unsigned char *bundleName, + size_t bundleNameLen, + SchemaMeta **schema +); + +/** + * @brief Pass a batch of subscription orders to a CloudServer pointer, with target database information + * and expected expire time. + * @param server [IN] + * dbs [IN] HashMap>, bundle name as key + * relations [OUT] HashMap, bundle name as key + * err [OUT] Vec + * @attention The Vector returned should be freed by `VectorFree`. Similar for the HashMap. + */ +int CloudServerSubscribe( + CloudServer *server, + const HashMap *dbs, + int expire, + HashMap **relations, + Vector **err, +); + +/** + * @brief Pass a batch of unsubscription orders to a CloudServer pointer, with target relations. + * @param server [IN] + * relations [IN] HashMap>, bundle name as key, ids as value + * err [OUT] Vec + * @attention The Vector returned should be freed by `VectorFree`. + */ +int CloudServerUnsubscribe( + CloudServer *server, + const HashMap *relations, + Vector **err, +); + +/** + * @brief Destroy an CloudServer instance. + * @param ptr [IN] The pointer of CloudServer that should be destroyed. + */ +void CloudServerFree(CloudServer *ptr); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif // CLOUD_EXTENSION_H \ No newline at end of file diff --git a/src/cloud_extension/include/error.h b/src/cloud_extension/include/error.h new file mode 100644 index 0000000000000000000000000000000000000000..c2ff0d8d175777007dfab9802c023cb22fbbcd41 --- /dev/null +++ b/src/cloud_extension/include/error.h @@ -0,0 +1,49 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUD_EXTENSION_ERROR_H +#define CLOUD_EXTENSION_ERROR_H + + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif + +#define SUCCESS 0 +#define ERRNO_NULLPTR 1 +#define ERRNO_UNKNOWN 2 +#define ERRNO_INVALID_INPUT_TYPE 3 +#define ERRNO_ASSET_DOWNLOAD_FAILURE 4 +#define ERRNO_ASSET_UPLOAD_FAILURE 5 +#define ERRNO_UNSUPPORTED 6 +#define ERRNO_NETWORK_ERROR 7 +#define ERRNO_LOCKED_BY_OTHERS 8 +#define ERRNO_UNLOCKED 9 +#define ERRNO_RECORD_LIMIT_EXCEEDED 10 +#define ERRNO_NO_SPACE_FOR_ASSET 11 +#define ERRNO_OTHER_IPC_ERROR 12 +#define ERRNO_IPC_ERRORS 13 +#define ERRNO_OTHER_IO_ERROR 14 +#define ERRNO_OUT_OF_RANGE 15 +#define ERRNO_NO_SUCH_TABLE_IN_DB 16 + + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif + +#endif //CLOUD_EXTENSION_ERROR_H diff --git a/src/cloud_extension/src/c_adapter/basic_rust_types.rs b/src/cloud_extension/src/c_adapter/basic_rust_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..484d31b3d4e6573ff1df66e6b0fc3f17612f5e78 --- /dev/null +++ b/src/cloud_extension/src/c_adapter/basic_rust_types.rs @@ -0,0 +1,751 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::c_adapter::*; +use crate::service_impl::{asset_loader, cloud_db, cloud_service, types}; +use std::collections; +use std::ffi::{c_int, c_uchar, c_void}; +use std::ptr::{null, null_mut}; + +pub type Vector = c_void; +pub type HashMap = c_void; + +/// Value type enum in C. Used to represent returning type value, so C side can cast its pointer. +#[repr(C)] +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +pub enum ValueType { + NULL = 0, + U32, + I32, + STRING, + VALUE, + VALUE_BUCKET, + DATABASE, + TABLE, + FIELD, + RELATION, + RELATION_SET, + ASSET, + APP_INFO, + VEC_U32, + VEC_DATABASE, + HASHMAP_VALUE, +} + +/// Vector enum in C side to adapt Vector generic types. +pub enum VectorCffi { + I32(Vec), + U32(Vec), + String(Vec), + Value(Vec), + ValueBucket(Vec), + Database(Vec), + Table(Vec), + Field(Vec), + RelationSet(Vec), + Asset(Vec), + AppInfo(Vec), + VecU32(Vec>), + VecDatabase(Vec>), + HashMapValue(Vec>), +} + +/// Hashmap enum in C side to adapt Vector generic types. String as the key, enum type only +/// marks value type. +pub enum HashMapCffi { + U32(collections::HashMap), + Value(collections::HashMap), + Table(collections::HashMap), + RelationSet(collections::HashMap), + AppInfo(collections::HashMap), + VecU32(collections::HashMap>), + VecDatabase(collections::HashMap>), +} + +/// Create a Vector enum according to ValueType. +#[no_mangle] +pub unsafe extern "C" fn VectorNew(typ: ValueType) -> *mut Vector { + let vec = match typ { + ValueType::I32 => VectorCffi::I32(vec![]), + ValueType::U32 => VectorCffi::U32(vec![]), + ValueType::STRING => VectorCffi::String(vec![]), + ValueType::VALUE => VectorCffi::Value(vec![]), + ValueType::VALUE_BUCKET => VectorCffi::ValueBucket(vec![]), + ValueType::DATABASE => VectorCffi::Database(vec![]), + ValueType::TABLE => VectorCffi::Table(vec![]), + ValueType::FIELD => VectorCffi::Field(vec![]), + ValueType::RELATION_SET => VectorCffi::RelationSet(vec![]), + ValueType::ASSET => VectorCffi::Asset(vec![]), + ValueType::APP_INFO => VectorCffi::AppInfo(vec![]), + ValueType::VEC_U32 => VectorCffi::VecU32(vec![]), + ValueType::VEC_DATABASE => VectorCffi::VecDatabase(vec![]), + ValueType::HASHMAP_VALUE => VectorCffi::HashMapValue(vec![]), + // Not supported + _ => return null_mut(), + }; + Box::into_raw(Box::from(vec)) as *mut Vector +} + +/// Get ValueType of the Vector pointer. +#[no_mangle] +pub unsafe extern "C" fn VectorGetValueTyp(vector: *const Vector) -> ValueType { + if vector.is_null() { + return ValueType::NULL; + } + let vector = &*(vector as *mut VectorCffi); + match vector { + VectorCffi::I32(_) => ValueType::I32, + VectorCffi::U32(_) => ValueType::U32, + VectorCffi::String(_) => ValueType::STRING, + VectorCffi::Value(_) => ValueType::VALUE, + VectorCffi::ValueBucket(_) => ValueType::VALUE_BUCKET, + VectorCffi::Database(_) => ValueType::DATABASE, + VectorCffi::Table(_) => ValueType::TABLE, + VectorCffi::Field(_) => ValueType::FIELD, + VectorCffi::RelationSet(_) => ValueType::RELATION_SET, + VectorCffi::Asset(_) => ValueType::ASSET, + VectorCffi::AppInfo(_) => ValueType::APP_INFO, + VectorCffi::VecU32(_) => ValueType::VEC_U32, + VectorCffi::VecDatabase(_) => ValueType::VEC_DATABASE, + VectorCffi::HashMapValue(_) => ValueType::HASHMAP_VALUE, + } +} + +/// Push value into the Vector pointer. If the value pushed is allocated in the Rust side before, +/// pushing them means to transfer their management to the Vector, so no more free is needed. +#[no_mangle] +pub unsafe extern "C" fn VectorPush( + vector: *mut Vector, + value: *mut c_void, + value_len: usize, +) -> c_int { + if vector.is_null() | value.is_null() { + return ERRNO_NULLPTR; + } + + let vector = &mut *(vector as *mut VectorCffi); + match vector { + VectorCffi::I32(vec) => { + let ptr = value as *mut i32; + vec.push(*ptr); + } + VectorCffi::U32(vec) => { + let ptr = value as *mut u32; + vec.push(*ptr); + } + VectorCffi::String(vec) => { + let str = char_ptr_to_string(value as *const c_uchar, value_len); + vec.push(str); + } + VectorCffi::Value(vec) => { + let val = *Box::from_raw(value as *mut types::Value); + vec.push(val); + } + VectorCffi::ValueBucket(vec) => { + let vb = *Box::from_raw(value as *mut cloud_db::ValueBucket); + vec.push(vb); + } + VectorCffi::Database(vec) => { + let db = *Box::from_raw(value as *mut types::Database); + vec.push(db); + } + VectorCffi::Table(vec) => { + let val = *Box::from_raw(value as *mut types::Table); + vec.push(val); + } + VectorCffi::Field(vec) => { + let fd = *Box::from_raw(value as *mut types::Field); + vec.push(fd); + } + VectorCffi::RelationSet(vec) => { + let val = *Box::from_raw(value as *mut cloud_service::RelationSet); + vec.push(val); + } + VectorCffi::Asset(vec) => { + let asset = *Box::from_raw(value as *mut asset_loader::Asset); + vec.push(asset); + } + VectorCffi::AppInfo(vec) => { + let val = *Box::from_raw(value as *mut cloud_service::AppInfo); + vec.push(val); + } + VectorCffi::VecU32(vec) => { + let vec_struct = *Box::from_raw(value as *mut VectorCffi); + match vec_struct { + VectorCffi::U32(v) => vec.push(v), + _ => return ERRNO_INVALID_INPUT_TYPE, + } + } + VectorCffi::VecDatabase(vec) => { + let vec_struct = *Box::from_raw(value as *mut VectorCffi); + match vec_struct { + VectorCffi::Database(v) => vec.push(v), + _ => return ERRNO_INVALID_INPUT_TYPE, + } + } + VectorCffi::HashMapValue(vec) => { + let map = *Box::from_raw(value as *mut HashMapCffi); + match map { + HashMapCffi::Value(m) => vec.push(m), + _ => return ERRNO_INVALID_INPUT_TYPE, + } + } + } + SUCCESS +} + +/// Get value from the Vector pointer. If returning type is `Vector` or `HashMap` pointer, +/// these pointers should be freed by `VectorFree` or `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn VectorGet( + vector: *mut Vector, + index: usize, + value: *mut *const c_void, + _value_len: *mut usize, +) -> c_int { + macro_rules! get_content { + ($vec: ident, $index: ident, $value: ident) => { + if $index < $vec.len() { + *$value = (&$vec[index]) as *const _ as *const c_void; + } else { + return ERRNO_OUT_OF_RANGE; + } + }; + } + + if vector.is_null() { + return ERRNO_NULLPTR; + } + let vector = &mut *(vector as *mut VectorCffi); + match vector { + VectorCffi::I32(vec) => get_content!(vec, index, value), + VectorCffi::U32(vec) => get_content!(vec, index, value), + VectorCffi::String(vec) => get_content!(vec, index, value), + VectorCffi::Value(vec) => get_content!(vec, index, value), + VectorCffi::ValueBucket(vec) => get_content!(vec, index, value), + VectorCffi::Database(vec) => get_content!(vec, index, value), + VectorCffi::Table(vec) => get_content!(vec, index, value), + VectorCffi::Field(vec) => get_content!(vec, index, value), + VectorCffi::RelationSet(vec) => get_content!(vec, index, value), + VectorCffi::Asset(vec) => get_content!(vec, index, value), + VectorCffi::AppInfo(vec) => get_content!(vec, index, value), + VectorCffi::VecU32(vec) => { + if index < vec.len() { + let src = &vec[index]; + *value = Box::into_raw(Box::new(VectorCffi::U32(src.clone()))) as *const c_void; + } else { + return ERRNO_OUT_OF_RANGE; + } + } + VectorCffi::VecDatabase(vec) => { + if index < vec.len() { + let src = &vec[index]; + *value = + Box::into_raw(Box::new(VectorCffi::Database(src.clone()))) as *const c_void; + } else { + return ERRNO_OUT_OF_RANGE; + } + } + VectorCffi::HashMapValue(vec) => { + if index < vec.len() { + let src = &vec[index]; + let mut inner = collections::HashMap::new(); + for (key, value) in src { + inner.insert(key.clone(), value.clone()); + } + *value = Box::into_raw(Box::new(HashMapCffi::Value(inner))) as *const c_void; + } else { + return ERRNO_OUT_OF_RANGE; + } + } + } + SUCCESS +} + +/// Get length of a Vector pointer. +#[no_mangle] +pub unsafe extern "C" fn VectorGetLength(vector: *mut Vector, len: *mut usize) -> c_int { + if vector.is_null() { + return ERRNO_NULLPTR; + } + let vector = &mut *(vector as *mut VectorCffi); + match vector { + VectorCffi::I32(vec) => *len = vec.len(), + VectorCffi::U32(vec) => *len = vec.len(), + VectorCffi::String(vec) => *len = vec.len(), + VectorCffi::Value(vec) => *len = vec.len(), + VectorCffi::ValueBucket(vec) => *len = vec.len(), + VectorCffi::Database(vec) => *len = vec.len(), + VectorCffi::Table(vec) => *len = vec.len(), + VectorCffi::Field(vec) => *len = vec.len(), + VectorCffi::RelationSet(vec) => *len = vec.len(), + VectorCffi::Asset(vec) => *len = vec.len(), + VectorCffi::AppInfo(vec) => *len = vec.len(), + VectorCffi::VecU32(vec) => *len = vec.len(), + VectorCffi::VecDatabase(vec) => *len = vec.len(), + VectorCffi::HashMapValue(vec) => *len = vec.len(), + } + SUCCESS +} + +/// Free a Vector pointer. +#[no_mangle] +pub unsafe extern "C" fn VectorFree(vector: *mut Vector) { + if !vector.is_null() { + let _ = Box::from_raw(vector as *mut VectorCffi); + } +} + +/// Initialize a HashMap enum by its ValueType. The key type is fixed to be String. +#[no_mangle] +pub unsafe extern "C" fn HashMapNew(value_type: ValueType) -> *mut HashMap { + let map = match value_type { + ValueType::U32 => HashMapCffi::U32(collections::HashMap::default()), + ValueType::VALUE => HashMapCffi::Value(collections::HashMap::default()), + ValueType::TABLE => HashMapCffi::Table(collections::HashMap::default()), + ValueType::RELATION => HashMapCffi::RelationSet(collections::HashMap::default()), + ValueType::APP_INFO => HashMapCffi::AppInfo(collections::HashMap::default()), + ValueType::VEC_U32 => HashMapCffi::VecU32(collections::HashMap::default()), + ValueType::VEC_DATABASE => HashMapCffi::VecDatabase(collections::HashMap::default()), + // Not supported + _ => return null_mut(), + }; + Box::into_raw(Box::from(map)) as *mut HashMap +} + +/// Get key type of a Hashmap pointer. +#[no_mangle] +pub unsafe extern "C" fn HashMapGetKeyTyp(hash_map: *mut HashMap) -> ValueType { + if hash_map.is_null() { + ValueType::NULL + } else { + ValueType::STRING + } +} + +/// Get value type of a Hashmap pointer. +#[no_mangle] +pub unsafe extern "C" fn HashMapGetValueTyp(hash_map: *mut HashMap) -> ValueType { + if hash_map.is_null() { + return ValueType::NULL; + } + let hash_map = &*(hash_map as *mut HashMapCffi); + match hash_map { + HashMapCffi::U32(_) => ValueType::U32, + HashMapCffi::Value(_) => ValueType::VALUE, + HashMapCffi::Table(_) => ValueType::TABLE, + HashMapCffi::RelationSet(_) => ValueType::RELATION, + HashMapCffi::AppInfo(_) => ValueType::APP_INFO, + HashMapCffi::VecU32(_) => ValueType::VEC_U32, + HashMapCffi::VecDatabase(_) => ValueType::VEC_DATABASE, + } +} + +/// Get length of a Hashmap pointer. +#[no_mangle] +pub unsafe extern "C" fn HashMapGetLength(hash_map: *mut HashMap, len: *mut usize) -> c_int { + if hash_map.is_null() { + return ERRNO_NULLPTR; + } + let hash_map = &mut *(hash_map as *mut HashMapCffi); + match hash_map { + HashMapCffi::U32(map) => *len = map.len(), + HashMapCffi::Value(map) => *len = map.len(), + HashMapCffi::Table(map) => *len = map.len(), + HashMapCffi::RelationSet(map) => *len = map.len(), + HashMapCffi::AppInfo(map) => *len = map.len(), + HashMapCffi::VecU32(map) => *len = map.len(), + HashMapCffi::VecDatabase(map) => *len = map.len(), + } + SUCCESS +} + +/// Insert key, value pairs into the Hashmap pointer. If the value pushed is allocated in the Rust +/// side before, pushing them means to transfer their management to the Hashmap, so no more free +/// is needed. +#[no_mangle] +pub unsafe extern "C" fn HashMapInsert( + hash_map: *mut HashMap, + key: *mut c_uchar, + key_len: usize, + value: *mut c_void, + _value_len: usize, +) -> c_int { + if hash_map.is_null() | key.is_null() | value.is_null() { + return ERRNO_NULLPTR; + } + let hash_map = &mut *(hash_map as *mut HashMapCffi); + let key = char_ptr_to_string(key as *const c_uchar, key_len); + match hash_map { + HashMapCffi::U32(map) => { + let ptr = value as *mut u32; + map.insert(key, *ptr); + } + HashMapCffi::Value(map) => { + let val = *Box::from_raw(value as *mut types::Value); + map.insert(key, val); + } + HashMapCffi::Table(map) => { + let val = *Box::from_raw(value as *mut types::Table); + map.insert(key, val); + } + HashMapCffi::RelationSet(map) => { + let val = *Box::from_raw(value as *mut cloud_service::RelationSet); + map.insert(key, val); + } + HashMapCffi::AppInfo(map) => { + let val = *Box::from_raw(value as *mut cloud_service::AppInfo); + map.insert(key, val); + } + HashMapCffi::VecU32(map) => { + let val = *Box::from_raw(value as *mut VectorCffi); + match val { + VectorCffi::U32(vec) => map.insert(key, vec), + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + } + HashMapCffi::VecDatabase(map) => { + let val = *Box::from_raw(value as *mut VectorCffi); + match val { + VectorCffi::Database(vec) => map.insert(key, vec), + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + } + } + SUCCESS +} + +/// Get key, value pair from the Hashmap pointer. The returning type is `Vector`, and should be +/// freed by `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn HashMapIterGetKeyValuePair( + hash_map: *mut HashMap, + key: *mut *const Vector, + value: *mut *const Vector, +) -> c_int { + if hash_map.is_null() { + return ERRNO_NULLPTR; + } + let hash_map = &mut *(hash_map as *mut HashMapCffi); + match hash_map { + HashMapCffi::U32(map) => { + let key_vec = map.keys().cloned().collect(); + let value_vec: Vec = map.values().cloned().collect(); + + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::U32(value_vec))) as *const Vector; + } + HashMapCffi::Value(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let value_vec: Vec = map.values().cloned().collect(); + + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::Value(value_vec))) as *const Vector; + } + HashMapCffi::Table(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let mut value_vec = vec![]; + for src in map.values() { + value_vec.push(src.clone()); + } + + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::Table(value_vec))) as *const Vector; + } + HashMapCffi::RelationSet(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let mut value_vec = vec![]; + for src in map.values() { + value_vec.push(src.clone()); + } + + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::RelationSet(value_vec))) as *const Vector; + } + HashMapCffi::AppInfo(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let mut value_vec = vec![]; + for src in map.values() { + value_vec.push(src.clone()); + } + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::AppInfo(value_vec))) as *const Vector; + } + HashMapCffi::VecU32(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let value_vec: Vec> = map.values().cloned().collect(); + + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::VecU32(value_vec))) as *const Vector; + } + HashMapCffi::VecDatabase(map) => { + let key_vec: Vec = map.keys().cloned().collect(); + let mut value_vec = vec![]; + for src in map.values() { + value_vec.push(src.clone()); + } + *key = Box::into_raw(Box::new(VectorCffi::String(key_vec))) as *const Vector; + *value = Box::into_raw(Box::new(VectorCffi::VecDatabase(value_vec))) as *const Vector; + } + } + SUCCESS +} + +/// According to key, get value from the Hashmap pointer. If returning type is `Vector` or `HashMap` +/// pointer, these pointers should be freed by `VectorFree` or `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn HashMapGet( + hash_map: *mut HashMap, + key: *mut c_uchar, + key_len: usize, + value: *mut *const c_void, + value_len: *mut usize, +) -> c_int { + if hash_map.is_null() | key.is_null() { + return ERRNO_NULLPTR; + } + let hash_map = &mut *(hash_map as *mut HashMapCffi); + let key = String::from_raw_parts(key as *mut u8, key_len, key_len); + *value = null(); + *value_len = 0; + + match hash_map { + HashMapCffi::U32(map) => { + if let Some(val) = map.get(&key) { + *value = val as *const _ as *const c_void; + } + } + HashMapCffi::Value(map) => { + if let Some(val) = map.get(&key) { + *value = val as *const _ as *const c_void; + } + } + HashMapCffi::Table(map) => { + if let Some(val) = map.get(&key) { + *value = val as *const _ as *const c_void; + } + } + HashMapCffi::RelationSet(map) => { + if let Some(val) = map.get(&key) { + *value = val as *const _ as *const c_void; + } + } + HashMapCffi::AppInfo(map) => { + if let Some(val) = map.get(&key) { + *value = val as *const _ as *const c_void; + } + } + HashMapCffi::VecU32(map) => { + if let Some(val) = map.get(&key) { + *value = Box::into_raw(Box::new(VectorCffi::U32(val.clone()))) as *const c_void; + } + } + HashMapCffi::VecDatabase(map) => { + if let Some(val) = map.get(&key) { + *value = + Box::into_raw(Box::new(VectorCffi::Database(val.clone()))) as *const c_void; + } + } + } + SUCCESS +} + +/// Free a Hashmap pointer. +#[no_mangle] +pub unsafe extern "C" fn HashMapFree(hash_map: *mut HashMap) { + if !hash_map.is_null() { + let _ = Box::from_raw(hash_map as *mut HashMapCffi); + } +} + +#[cfg(tests)] +mod test { + use crate::c_adapter::basic_rust_types::*; + use crate::c_adapter::*; + use crate::service_impl::{asset_loader, cloud_db, cloud_service, types}; + + /// UT test for vec use and create. + /// + /// # Brief + /// 1. Create a vec of different type. + /// 2. Push and get values from it. + /// 3. Free vec. + /// 4. No error and memory leak should happen. + macro_rules! ut_vec { + ( + $typ: ident, + $push: ident, + $value_typ: ty, + $value: ident + ) => { + unsafe { + let typ = ValueType::$typ; + let new_vec = VectorNew(typ); + assert!(!new_vec.is_null()); + match VectorGetValueTyp(new_vec) { + ValueType::$typ => {} + _ => panic!("Vector Type wrong"), + } + + let mut length; + assert_eq!( + VectorGetLength(new_vec, &mut length as *mut _ as *mut usize), + SUCCESS + ); + assert_eq!(length, 0); + + assert_eq!( + VectorPush(new_vec, $push as *const _ as *mut c_void, 0), + SUCCESS + ); + assert_eq!( + VectorGetLength(new_vec, &mut length as *mut _ as *mut usize), + SUCCESS + ); + assert_eq!(length, 1); + + let mut val = null(); + assert_eq!( + VectorGet( + new_vec, + 0, + &mut val as *mut _ as *mut *const c_void, + &mut length as *mut _ as *mut usize + ), + SUCCESS + ); + let value = &*(val as *const $value_typ); + assert_eq!(value, $value); + VectorFree(new_vec); + } + }; + } + + /// UT test for hashmap use and create. + /// + /// # Brief + /// 1. Create a hashmap of different type. + /// 2. Push and get values from it. + /// 3. Free vec. + /// 4. No error and memory leak should happen. + macro_rules! ut_vec { + ( + $typ: ident, + $push: ident, + $value_typ: ty, + $value: ident + ) => { + unsafe { + let typ = ValueType::$typ; + let new_vec = VectorNew(typ); + assert!(!new_vec.is_null()); + match VectorGetValueTyp(new_vec) { + ValueType::$typ => {} + _ => panic!("Vector Type wrong"), + } + + let mut length; + assert_eq!( + VectorGetLength(new_vec, &mut length as *mut _ as *mut usize), + SUCCESS + ); + assert_eq!(length, 0); + + assert_eq!( + VectorPush(new_vec, $push as *const _ as *mut c_void, 0), + SUCCESS + ); + assert_eq!( + VectorGetLength(new_vec, &mut length as *mut _ as *mut usize), + SUCCESS + ); + assert_eq!(length, 1); + + let mut val = null(); + assert_eq!( + VectorGet( + new_vec, + 0, + &mut val as *mut _ as *mut *const c_void, + &mut length as *mut _ as *mut usize + ), + SUCCESS + ); + let value = &*(val as *const $value_typ); + assert_eq!(value, $value); + VectorFree(new_vec); + } + }; + } + + #[test] + fn ut_vec_i32() { + let src: i32 = 3; + let borrow = &src; + ut_vec!(I32, borrow, i32, src); + } + + #[test] + fn ut_vec_u32() { + let src: u32 = 3; + let borrow = &src; + ut_vec!(U32, borrow, u32, src); + } + + #[test] + fn ut_vec_string() { + let src = "hello".to_string(); + let borrow = &src; + ut_vec!(String, borrow, String, src); + } + + #[test] + fn ut_vec_value() { + let src = types::Value::Empty; + let borrow = &src; + ut_vec!(Value, borrow, types::Value, src); + } + + #[test] + fn ut_vec_vecu32() { + unsafe { + let vec = VectorNew(ValueType::U32); + let src: u32 = 3; + assert_eq!(VectorPush(vec, &src as *mut _ as *mut c_void, 1), SUCCESS); + } + + let src = vec![1_u32]; + ut_vec!(VecU32, vec, Vec, src); + } + + #[test] + fn ut_vec_null() { + unsafe { + assert!(VectorNew(ValueType::NULL).is_null()); + assert_eq!( + VectorPush(null_mut(), &3 as *mut _ as *mut c_void, 0), + ERRNO_NULLPTR + ); + + let typ = ValueType::I32; + let new_vec = VectorNew(typ); + let mut length; + assert_eq!( + VectorGetLength(null_mut(), &mut length as *mut _ as *mut usize), + ERRNO_NULLPTR + ); + assert_eq!(VectorPush(new_vec, null_mut(), 0), ERRNO_NULLPTR); + VectorFree(new_vec); + } + } +} diff --git a/src/cloud_extension/src/c_adapter/cloud_ext_types.rs b/src/cloud_extension/src/c_adapter/cloud_ext_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..3ad7eda96772e1e275d33703e64d171cff2a1b52 --- /dev/null +++ b/src/cloud_extension/src/c_adapter/cloud_ext_types.rs @@ -0,0 +1,1166 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::c_adapter::basic_rust_types; +use crate::c_adapter::basic_rust_types::{HashMapCffi, VectorCffi}; +use crate::c_adapter::*; +use crate::service_impl::{asset_loader, cloud_db, cloud_service, types}; +use std::ffi::{c_double, c_int, c_uchar, c_uint, c_void}; +use std::ptr::{null, null_mut, slice_from_raw_parts}; + +/// AssetBuilder defined in C struct. It will be used to build a Rust Asset struct in use. +#[repr(C)] +pub struct AssetBuilder { + pub version: u64, + pub status: asset_loader::AssetStatus, + pub expires_time: u64, + pub id: *const c_uchar, + pub id_len: usize, + pub name: *const c_uchar, + pub name_len: usize, + pub uri: *const c_uchar, + pub uri_len: usize, + pub local_path: *const c_uchar, + pub local_path_len: usize, + pub create_time: *const c_uchar, + pub create_time_len: usize, + pub modify_time: *const c_uchar, + pub modify_time_len: usize, + pub size: *const c_uchar, + pub size_len: usize, + pub hash: *const c_uchar, + pub hash_len: usize, +} + +/// Enumeration represents inner type of contents in Value enum. +#[repr(C)] +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +pub enum ValueInnerType { + EMPTY = 0, + INT, + DOUBLE, + BOOL, + STRING, + BYTES, + ASSET, + ASSETS, +} + +/// Value pointer passed to C side. +pub type Value = c_void; +/// Asset pointer passed to C side. +pub type Asset = c_void; +/// Database pointer passed to C side. +pub type Database = c_void; +/// CloudInfo pointer passed to C side. +pub type CloudInfo = c_void; +/// AppInfo pointer passed to C side. +pub type AppInfo = c_void; +/// Table pointer passed to C side. +pub type Table = c_void; +/// Field pointer passed to C side. +pub type Field = c_void; +/// SchemaMeta pointer passed to C side. +pub type SchemaMata = c_void; +/// RelationSet pointer passed to C side. +pub type RelationSet = c_void; +/// CloudData pointer passed to C side. +pub type CloudData = c_void; +/// ValueBucket pointer passed to C side. +pub type ValueBucket = c_void; +/// VBInfo pointer passed to C side. +pub type VBInfo = c_void; + +/// Create a Value instance according to ValueInnerType. +#[no_mangle] +pub unsafe extern "C" fn ValueNew( + typ: ValueInnerType, + content: *mut c_void, + len: usize, +) -> *mut Value { + if content.is_null() { + return null_mut(); + } + + let val = match typ { + ValueInnerType::EMPTY => types::Value::Empty, + ValueInnerType::INT => { + let int = &*(content as *mut c_int as *mut i64); + types::Value::Int(*int) + } + ValueInnerType::DOUBLE => { + let float = &*(content as *mut c_int as *mut f64); + types::Value::Double(*float) + } + ValueInnerType::BOOL => { + let bool = &*(content as *mut c_int); + types::Value::Bool(c_int_to_bool(*bool)) + } + ValueInnerType::STRING => { + let str = char_ptr_to_string(content as *const c_uchar, len); + types::Value::String(str) + } + ValueInnerType::BYTES => { + let slice = &(*slice_from_raw_parts(content as *const u8, len)); + types::Value::Bytes(slice.to_vec()) + } + ValueInnerType::ASSET => { + let src = &*(content as *mut AssetBuilder); + let asset = get_asset(src); + types::Value::Asset(asset) + } + ValueInnerType::ASSETS => { + let asset_slice = &*slice_from_raw_parts(content as *const AssetBuilder, len); + let mut assets = vec![]; + for asset_cffi in asset_slice { + assets.push(get_asset(asset_cffi)); + } + types::Value::Assets(assets) + } + }; + + Box::into_raw(Box::new(val)) as *mut Value +} + +/// Get content from a Value pointer. +/// +/// If the Value is Value::Assets, the Vector pointer returned should be freed by +/// `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn ValueGetContent( + src: *mut Value, + typ: *mut ValueInnerType, + content: *mut *const c_void, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let value_struct = &*(src as *const types::Value); + match value_struct { + types::Value::Empty => { + *typ = ValueInnerType::EMPTY; + *content = null(); + *len = 0; + } + types::Value::Int(c) => { + *typ = ValueInnerType::INT; + let ptr = &mut *(content as *mut c_int); + *ptr = *c as c_int; + } + types::Value::Double(c) => { + *typ = ValueInnerType::DOUBLE; + let ptr = &mut *(content as *mut c_double); + *ptr = *c; + } + types::Value::String(c) => { + *typ = ValueInnerType::STRING; + *content = c.as_ptr() as *const c_void; + *len = c.len(); + } + types::Value::Bool(c) => { + *typ = ValueInnerType::BOOL; + let ptr = &mut *(content as *mut c_int); + *ptr = *c as c_int; + } + types::Value::Bytes(c) => { + *typ = ValueInnerType::BYTES; + *content = c.as_ptr() as *const c_void; + *len = c.len(); + } + types::Value::Asset(c) => { + *typ = ValueInnerType::ASSET; + *content = c as *const asset_loader::Asset as *const c_void; + } + types::Value::Assets(c) => { + *typ = ValueInnerType::ASSETS; + let vec = VectorCffi::Asset(c.to_vec()); + *content = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector; + *len = c.len(); + } + } + SUCCESS +} + +/// Free a Value pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueFree(src: *mut Value) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut types::Value); + } +} + +unsafe fn get_asset(builder: *const AssetBuilder) -> asset_loader::Asset { + let builder = &*builder; + let id = char_ptr_to_string(builder.id, builder.id_len); + let name = char_ptr_to_string(builder.name, builder.name_len); + let uri = char_ptr_to_string(builder.uri, builder.uri_len); + let local_path = char_ptr_to_string(builder.local_path, builder.local_path_len); + let create_time = char_ptr_to_string(builder.create_time, builder.create_time_len); + let modify_time = char_ptr_to_string(builder.modify_time, builder.modify_time_len); + let size = char_ptr_to_string(builder.size, builder.size_len); + let hash = char_ptr_to_string(builder.hash, builder.hash_len); + + asset_loader::Asset { + status: builder.status.clone(), + id, + name, + uri, + local_path, + create_time, + modify_time, + size, + hash, + } +} + +/// Initialize an Asset by the AssetBuilder. +#[no_mangle] +pub unsafe extern "C" fn AssetNew(builder: *mut AssetBuilder) -> *mut Asset { + if builder.is_null() { + return null_mut(); + } + + let asset = get_asset(builder); + Box::into_raw(Box::new(asset)) as *mut Asset +} + +/// Get id from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetId( + src: *const Asset, + id: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *id = src_struct.id.as_ptr() as *const c_uchar; + *len = src_struct.id.len(); + SUCCESS +} + +/// Get name from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetName( + src: *const Asset, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *name = src_struct.name.as_ptr() as *const c_uchar; + *len = src_struct.name.len(); + SUCCESS +} + +/// Get uri from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetUri( + src: *const Asset, + uri: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *uri = src_struct.uri.as_ptr() as *const c_uchar; + *len = src_struct.uri.len(); + SUCCESS +} + +/// Get local path from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetLocalPath( + src: *const Asset, + local_path: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *local_path = src_struct.local_path.as_ptr() as *const c_uchar; + *len = src_struct.local_path.len(); + SUCCESS +} + +/// Get create time from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetCreateTime( + src: *const Asset, + create_time: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *create_time = src_struct.create_time.as_ptr() as *const c_uchar; + *len = src_struct.create_time.len(); + SUCCESS +} + +/// Get modified time from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetModifiedTime( + src: *const Asset, + modify_time: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *modify_time = src_struct.modify_time.as_ptr() as *const c_uchar; + *len = src_struct.modify_time.len(); + SUCCESS +} + +/// Get size from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetSize( + src: *const Asset, + size: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *size = src_struct.size.as_ptr() as *const c_uchar; + *len = src_struct.size.len(); + SUCCESS +} + +/// Get hash from an Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetGetHash( + src: *const Asset, + hash: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if src.is_null() { + return ERRNO_NULLPTR; + } + + let src_struct = &*(src as *const asset_loader::Asset); + *hash = src_struct.hash.as_ptr() as *const c_uchar; + *len = src_struct.hash.len(); + SUCCESS +} + +/// Free a Asset pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetFree(src: *mut Asset) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut asset_loader::Asset); + } +} + +/// Create a Database instance by database name, alias, and tables. Tables can be created by +/// basic_rust_types::HashMap, and its type should be HashMap. When passed in, +/// tables don't need to be freed again, because their management will be transferred to Database +/// instance. +#[no_mangle] +pub unsafe extern "C" fn DatabaseNew( + name: *const c_uchar, + name_len: usize, + alias: *const c_uchar, + alias_len: usize, + tables: *const basic_rust_types::HashMap, +) -> *mut Database { + let name = char_ptr_to_string(name, name_len); + let alias = char_ptr_to_string(alias, alias_len); + let tables = *Box::from_raw(tables as *mut HashMapCffi); + match tables { + HashMapCffi::Table(t) => { + let db = types::Database::new(name, alias, t); + Box::into_raw(Box::new(db)) as *mut Database + } + _ => null_mut(), + } +} + +/// Get name from a Database pointer. +#[no_mangle] +pub unsafe extern "C" fn DatabaseGetName( + db: *const Database, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if db.is_null() { + return ERRNO_NULLPTR; + } + + let db_struct = &*(db as *const types::Database); + *name = db_struct.name.as_ptr() as *const c_uchar; + *len = db_struct.name.len(); + SUCCESS +} + +/// Get alias from a Database pointer. +#[no_mangle] +pub unsafe extern "C" fn DatabaseGetAlias( + db: *const Database, + alias: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if db.is_null() { + return ERRNO_NULLPTR; + } + + let db_struct = &*(db as *const types::Database); + *alias = db_struct.alias.as_ptr() as *const c_uchar; + *len = db_struct.alias.len(); + SUCCESS +} + +/// Get hashmap of tables from a Database pointer. Parameter `tables` will be updated +/// to hold the output HashMap, and it should be freed by `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn DatabaseGetTable( + db: *const Database, + tables: *mut *const basic_rust_types::HashMap, +) -> c_int { + if db.is_null() { + return ERRNO_NULLPTR; + } + + let db_struct = &*(db as *const types::Database); + let tb_hashmap = HashMapCffi::Table(db_struct.tables.clone()); + *tables = Box::into_raw(Box::new(tb_hashmap)) as *mut basic_rust_types::HashMap; + SUCCESS +} + +/// Free a Database pointer. +#[no_mangle] +pub unsafe extern "C" fn DatabaseFree(src: *mut Database) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut types::Database); + } +} + +/// Create a Table instance by table name, table alias, and vector of fields. Fields can be created +/// by basic_rust_types::Vector, and its type should be Vec. When passed in, fields don't +/// need to be freed again, because their management will be transferred to Table instance. +#[no_mangle] +pub unsafe extern "C" fn TableNew( + name: *const c_uchar, + name_len: usize, + alias: *const c_uchar, + alias_len: usize, + fields: *const basic_rust_types::Vector, +) -> *mut Table { + let name = char_ptr_to_string(name, name_len); + let alias = char_ptr_to_string(alias, alias_len); + let fields = *Box::from_raw(fields as *mut VectorCffi); + match fields { + VectorCffi::Field(f) => { + let tb = types::Table::new(name, alias, f); + Box::into_raw(Box::new(tb)) as *mut Table + } + _ => null_mut(), + } +} + +/// Get name from a Table pointer. +#[no_mangle] +pub unsafe extern "C" fn TableGetName( + tb: *const Table, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if tb.is_null() { + return ERRNO_NULLPTR; + } + + let tb_struct = &*(tb as *const types::Table); + *name = tb_struct.name.as_ptr() as *const c_uchar; + *len = tb_struct.name.len(); + SUCCESS +} + +/// Get alias from a Table pointer. +#[no_mangle] +pub unsafe extern "C" fn TableGetAlias( + tb: *const Table, + alias: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if tb.is_null() { + return ERRNO_NULLPTR; + } + + let tb_struct = &*(tb as *const types::Table); + *alias = tb_struct.alias.as_ptr() as *const c_uchar; + *len = tb_struct.alias.len(); + SUCCESS +} + +/// Get vector of fields from a Table pointer. Parameter `fields` be updated +/// to hold the output Vector, and it should be freed by `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn TableGetFields( + tb: *const Table, + fields: *mut *const basic_rust_types::Vector, +) -> c_int { + if tb.is_null() { + return ERRNO_NULLPTR; + } + + let tb_struct = &*(tb as *const types::Table); + let vec = VectorCffi::Field(tb_struct.fields.clone()); + *fields = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector; + SUCCESS +} + +/// Free a Table pointer. +#[no_mangle] +pub unsafe extern "C" fn TableFree(src: *mut Table) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut types::Table); + } +} + +#[inline] +fn c_int_to_bool(src: c_int) -> bool { + src != 0 +} + +/// FieldBuilder providing necessary information when create a Field. +#[repr(C)] +pub struct FieldBuilder { + col_name: *const c_uchar, + col_name_len: usize, + alias: *const c_uchar, + alias_len: usize, + typ: c_uint, + primary: c_int, + nullable: c_int, +} + +/// Create a Field by column name, alias, type, primary, and nullable. +#[no_mangle] +pub unsafe extern "C" fn FieldNew(builder: *const FieldBuilder) -> *mut Field { + let builder = &*builder; + let col_name = char_ptr_to_string(builder.col_name, builder.col_name_len); + let alias = char_ptr_to_string(builder.alias, builder.alias_len); + let typ = builder.typ as u8; + let primary = c_int_to_bool(builder.primary); + let nullable = c_int_to_bool(builder.nullable); + let fd = types::Field::new(col_name, alias, typ, primary, nullable); + Box::into_raw(Box::new(fd)) as *mut Field +} + +/// Get column name from a Field pointer. +#[no_mangle] +pub unsafe extern "C" fn FieldGetColName( + fd: *const Field, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *name = fd_struct.col_name.as_ptr() as *const c_uchar; + *len = fd_struct.col_name.len(); + SUCCESS +} + +/// Get alias from a Field pointer. +#[no_mangle] +pub unsafe extern "C" fn FieldGetAlias( + fd: *const Field, + alias: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *alias = fd_struct.alias.as_ptr() as *const c_uchar; + *len = fd_struct.alias.len(); + SUCCESS +} + +/// Get type from a Field pointer. +#[no_mangle] +pub unsafe extern "C" fn FieldGetTyp(fd: *const Field, typ: *mut c_uint) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *typ = fd_struct.typ as c_uint; + SUCCESS +} + +/// Check whether the Field is primary. +#[no_mangle] +pub unsafe extern "C" fn FieldGetPrimary(fd: *const Field, primary: *mut c_int) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *primary = fd_struct.primary as c_int; + SUCCESS +} + +/// Check whether the Field is nullable. +#[no_mangle] +pub unsafe extern "C" fn FieldGetNullable(fd: *const Field, nullable: *mut c_int) -> c_int { + if fd.is_null() { + return ERRNO_NULLPTR; + } + + let fd_struct = &*(fd as *const types::Field); + *nullable = fd_struct.nullable as c_int; + SUCCESS +} + +/// Free a Field pointer. +#[no_mangle] +pub unsafe extern "C" fn FieldFree(src: *mut Field) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut types::Field); + } +} + +/// Get user from a CloudInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoGetUser(info: *const CloudInfo, user: *mut c_int) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + *user = info_struct.user; + SUCCESS +} + +/// Get id from a CloudInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoGetId( + info: *const CloudInfo, + id: *mut *const c_uchar, + id_len: *mut usize, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + *id = info_struct.id.as_ptr() as *const c_uchar; + *id_len = info_struct.id.len(); + SUCCESS +} + +/// Get total space from a CloudInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoGetTotalSpace( + info: *const CloudInfo, + total_space: *mut c_int, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + *total_space = info_struct.total_space as c_int; + SUCCESS +} + +/// Get remain space from a CloudInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoGetRemainSpace( + info: *const CloudInfo, + remain_space: *mut c_int, +) -> c_int { + if info.is_null() { + return false as c_int; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + *remain_space = info_struct.remain_space as c_int; + SUCCESS +} + +/// Check whether a CloudInfo enables cloud sync. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoEnableCloud(info: *const CloudInfo, enable: *mut c_int) -> c_int { + if info.is_null() { + return false as c_int; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + *enable = info_struct.enable_cloud as c_int; + SUCCESS +} + +/// Get hashmap of AppInfo from a CloudInfo pointer. Parameter `app_info` will be updated +/// to hold the output HashMap, and it should be freed by `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoGetAppInfo( + info: *const CloudInfo, + app_info: *mut *const basic_rust_types::HashMap, +) -> c_int { + if info.is_null() { + return false as c_int; + } + + let info_struct = &*(info as *const cloud_service::CloudInfo); + let apps = HashMapCffi::AppInfo(info_struct.apps.clone()); + *app_info = Box::into_raw(Box::new(apps)) as *mut basic_rust_types::HashMap; + SUCCESS +} + +/// Free a CloudInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudInfoFree(info: *const CloudInfo) { + if !info.is_null() { + let _ = Box::from_raw(info as *mut cloud_service::CloudInfo); + } +} + +/// Get app id from an AppInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn AppInfoGetAppId( + info: *const AppInfo, + id: *mut *const c_uchar, + id_len: *mut usize, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *const cloud_service::AppInfo); + *id = info_struct.app_id.as_ptr() as *const c_uchar; + *id_len = info_struct.app_id.len(); + SUCCESS +} + +/// Get bundle name from an AppInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn AppInfoGetBundleName( + info: *const AppInfo, + id: *mut *const c_uchar, + id_len: *mut usize, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *const cloud_service::AppInfo); + *id = info_struct.bundle_name.as_ptr() as *const c_uchar; + *id_len = info_struct.bundle_name.len(); + SUCCESS +} + +/// Check whether an AppInfo pointer allows cloud switch. +#[no_mangle] +pub unsafe extern "C" fn AppInfoGetCloudSwitch(info: *const AppInfo, switch: *mut c_int) -> c_int { + if info.is_null() { + return false as c_int; + } + + let info_struct = &*(info as *const cloud_service::AppInfo); + *switch = info_struct.cloud_switch as c_int; + SUCCESS +} + +/// Get instance id from an AppInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn AppInfoGetInstanceId(info: *const AppInfo, id: *mut c_int) -> c_int { + if info.is_null() { + return false as c_int; + } + + let info_struct = &*(info as *const cloud_service::AppInfo); + *id = info_struct.instance_id; + SUCCESS +} + +/// Get version from a SchemaMeta pointer. +#[no_mangle] +pub unsafe extern "C" fn SchemaMataGetVersion( + schema: *mut SchemaMata, + version: *mut c_int, +) -> c_int { + if schema.is_null() { + return ERRNO_NULLPTR; + } + + let schema_struct = &mut *(schema as *mut cloud_service::SchemaMeta); + *version = schema_struct.version(); + SUCCESS +} + +/// Get bundle name from a SchemaMeta pointer. +#[no_mangle] +pub unsafe extern "C" fn SchemaMataGetBundleName( + schema: *mut SchemaMata, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if schema.is_null() { + return false as c_int; + } + + let schema_struct = &*(schema as *const cloud_service::SchemaMeta); + *name = schema_struct.bundle_name().as_ptr() as *const c_uchar; + *len = schema_struct.bundle_name().len(); + SUCCESS +} + +/// Get a vector of databases from a SchemaMeta pointer. Parameter `dbs` be updated +/// to hold the output Vector, and it should be freed by `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn SchemaMataGetDatabases( + schema: *mut SchemaMata, + dbs: *mut *const basic_rust_types::Vector, +) -> c_int { + if schema.is_null() { + return ERRNO_NULLPTR; + } + + let schema_struct = &*(schema as *const cloud_service::SchemaMeta); + let databases = schema_struct.databases(); + let vec = VectorCffi::Database(databases.to_vec()); + *dbs = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector; + SUCCESS +} + +/// Free a SchemaMeta pointer. +#[no_mangle] +pub unsafe extern "C" fn SchemaMataFree(schema: *mut SchemaMata) { + if !schema.is_null() { + let _ = Box::from_raw(schema as *mut cloud_service::SchemaMeta); + } +} + +/// Create a RelationSet instance by bundle name, and relations. Relations can be created by +/// basic_rust_types::HashMap, and its type should be HashMap. When passed in, +/// tables don't need to be freed again, because their management will be transferred to RelationSet +/// instance. +#[no_mangle] +pub unsafe extern "C" fn RelationSetNew( + bundle_name: *const c_uchar, + len: usize, + expire_time: c_int, + relations: *mut basic_rust_types::HashMap, +) -> *mut RelationSet { + let name = char_ptr_to_string(bundle_name, len); + let relations = *Box::from_raw(relations as *mut HashMapCffi); + match relations { + HashMapCffi::U32(re) => { + let relation = cloud_service::RelationSet::new(name, expire_time as u32, re); + Box::into_raw(Box::new(relation)) as *mut RelationSet + } + _ => null_mut(), + } +} + +/// Get bundle name from a RelationSet pointer. +#[no_mangle] +pub unsafe extern "C" fn RelationSetGetBundleName( + relation: *mut RelationSet, + bundle_name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if relation.is_null() { + return ERRNO_NULLPTR; + } + + let relation_struct = &*(relation as *mut cloud_service::RelationSet); + *bundle_name = relation_struct.bundle_name.as_ptr() as *const c_uchar; + *len = relation_struct.bundle_name.len(); + SUCCESS +} + +/// Get expire time from a RelationSet pointer. +#[no_mangle] +pub unsafe extern "C" fn RelationSetGetExpireTime( + relation: *mut RelationSet, + expire_time: *mut c_uint, +) -> c_int { + if relation.is_null() { + return ERRNO_NULLPTR; + } + + let relation_struct = &*(relation as *mut cloud_service::RelationSet); + *expire_time = relation_struct.expire_time as c_uint; + SUCCESS +} + +/// Get relations from a RelationSet pointer. Parameter `relations` be updated +/// to hold the output HashMap, and it should be freed by `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn RelationSetGetRelations( + relation: *mut RelationSet, + relations: *mut *const basic_rust_types::HashMap, +) -> c_int { + if relation.is_null() { + return ERRNO_NULLPTR; + } + + let relation_struct = &*(relation as *const cloud_service::RelationSet); + let res = relation_struct.relations(); + let hashmap = HashMapCffi::U32(res.clone()); + *relations = Box::into_raw(Box::new(hashmap)) as *const basic_rust_types::HashMap; + SUCCESS +} + +/// Free a RelationSet pointer. +#[no_mangle] +pub unsafe extern "C" fn RelationSetFree(re: *mut RelationSet) { + if !re.is_null() { + let _ = Box::from_raw(re as *mut cloud_service::RelationSet); + } +} + +/// Get the next cursor from a CloudData pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudDataGetNextCursor( + data: *mut CloudData, + cursor: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if data.is_null() { + return ERRNO_NULLPTR; + } + + let data_struct = &*(data as *mut cloud_db::CloudData); + *cursor = data_struct.next_cursor.as_ptr() as *const c_uchar; + *len = data_struct.next_cursor.len(); + SUCCESS +} + +/// Check whether a CloudData has more data. +#[no_mangle] +pub unsafe extern "C" fn CloudDataGetHasMore(data: *mut CloudData, has_more: *mut c_int) -> c_int { + if data.is_null() { + return ERRNO_NULLPTR; + } + + let data_struct = &*(data as *mut cloud_db::CloudData); + *has_more = data_struct.has_more as c_int; + SUCCESS +} + +/// Get vector of values from a CloudData pointer. Parameter `values` will be updated +/// to hold the output Vec, and it should be freed by `VectorFree`. +#[no_mangle] +pub unsafe extern "C" fn CloudDataGetGetValues( + data: *mut CloudData, + values: *mut *const basic_rust_types::Vector, +) -> c_int { + if data.is_null() { + return ERRNO_NULLPTR; + } + + let data_struct = &*(data as *mut cloud_db::CloudData); + let vec = VectorCffi::ValueBucket(data_struct.values.clone()); + *values = Box::into_raw(Box::new(vec)) as *const basic_rust_types::Vector; + SUCCESS +} + +/// Free a CloudData pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudDataFree(src: *mut CloudData) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut cloud_db::CloudData); + } +} + +/// Get id from a ValueBucket pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetId( + vb: *mut ValueBucket, + id: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *id = vb_struct.id.as_ptr() as *const c_uchar; + *len = vb_struct.id.len(); + SUCCESS +} + +/// Get type string from a ValueBucket pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetTyp( + vb: *mut ValueBucket, + typ: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *typ = vb_struct.typ.as_ptr() as *const c_uchar; + *len = vb_struct.typ.len(); + SUCCESS +} + +/// Get create info from a ValueBucket pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetCreateInfo( + vb: *mut ValueBucket, + info: *mut *const VBInfo, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *info = (&vb_struct.create_info) as *const cloud_db::VBInfo as *const VBInfo; + SUCCESS +} + +/// Get modified info from a ValueBucket pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetModifiedInfo( + vb: *mut ValueBucket, + info: *mut *const VBInfo, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *info = (&vb_struct.modified_info) as *const cloud_db::VBInfo as *const VBInfo; + SUCCESS +} + +/// Get hashmap of data from a ValueBucket pointer. Parameter `data` will be updated +/// to hold the output HashMap, and it should be freed by `HashMapFree`. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetData( + vb: *mut ValueBucket, + data: *mut *const basic_rust_types::HashMap, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + let vec = HashMapCffi::Value(vb_struct.data.clone()); + *data = Box::into_raw(Box::new(vec)) as *const basic_rust_types::HashMap; + SUCCESS +} + +/// Check whether a ValueBucket is deleted. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketIsDelete(vb: *mut ValueBucket, deleted: *mut c_int) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *deleted = vb_struct.is_delete as c_int; + SUCCESS +} + +/// Get version from a ValueBucket pointer. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketGetVersion( + vb: *mut ValueBucket, + version: *mut c_uint, +) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *version = vb_struct.version; + SUCCESS +} + +/// Check whether a ValueBucket is deleted. +#[no_mangle] +pub unsafe extern "C" fn ValueBucketIsNewCreate(vb: *mut ValueBucket, is_new: *mut c_int) -> c_int { + if vb.is_null() { + return ERRNO_NULLPTR; + } + + let vb_struct = &*(vb as *mut cloud_db::ValueBucket); + *is_new = vb_struct.is_new_create as c_int; + SUCCESS +} + +/// Get time from a VBInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn VBInfoGetTime(info: *mut VBInfo, time: *mut c_int) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *mut cloud_db::VBInfo); + *time = info_struct.time() as c_int; + SUCCESS +} + +/// Get device name from a VBInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn VBInfoGetDeviceName( + info: *mut VBInfo, + name: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *mut cloud_db::VBInfo); + *name = info_struct.device_name().as_ptr() as *const c_uchar; + *len = info_struct.device_name().len(); + SUCCESS +} + +/// Get app id from a VBInfo pointer. +#[no_mangle] +pub unsafe extern "C" fn VBInfoGetAppId( + info: *mut VBInfo, + id: *mut *const c_uchar, + len: *mut usize, +) -> c_int { + if info.is_null() { + return ERRNO_NULLPTR; + } + + let info_struct = &*(info as *mut cloud_db::VBInfo); + *id = info_struct.app_id().as_ptr() as *const c_uchar; + *len = info_struct.app_id().len(); + SUCCESS +} diff --git a/src/cloud_extension/src/c_adapter/cloud_extension.rs b/src/cloud_extension/src/c_adapter/cloud_extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f2f9ab5fa447197dd1cda3140be9e8be0a34eac --- /dev/null +++ b/src/cloud_extension/src/c_adapter/cloud_extension.rs @@ -0,0 +1,572 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::c_adapter::basic_rust_types; +use crate::c_adapter::basic_rust_types::{HashMapCffi, VectorCffi}; +use crate::c_adapter::cloud_ext_types::*; +use crate::c_adapter::*; +use crate::ipc_conn; +use crate::service_impl::error::SyncError; +use crate::service_impl::{asset_loader, cloud_db, cloud_service, types}; +use std::ffi::{c_int, c_uchar, c_uint, c_void}; +use std::io::ErrorKind; +use std::ptr::null_mut; + +/// AssetLoader pointer passed to C side. +pub type AssetLoader = c_void; +/// CloudDatabase pointer passed to C side. +pub type CloudDatabase = c_void; +/// CloudServer pointer passed to C side. +pub type CloudServer = c_void; + +fn map_ipc_err(e: &ipc_conn::Error) -> c_int { + match e { + ipc_conn::Error::CreateMsgParcelFailed + | ipc_conn::Error::WriteMsgParcelFailed + | ipc_conn::Error::GetProxyObjectFailed + | ipc_conn::Error::SendRequestFailed => ERRNO_NETWORK_ERROR, + ipc_conn::Error::ReadMsgParcelFailed | ipc_conn::Error::InvalidCloudStatus => { + ERRNO_OTHER_IPC_ERROR + } + ipc_conn::Error::UnlockFailed => ERRNO_UNLOCKED, + ipc_conn::Error::InvalidSpaceStatus => ERRNO_NO_SPACE_FOR_ASSET, + ipc_conn::Error::DownloadFailed => ERRNO_ASSET_DOWNLOAD_FAILURE, + ipc_conn::Error::UploadFailed => ERRNO_ASSET_UPLOAD_FAILURE, + } +} + +fn map_single_sync_err(e: &SyncError) -> c_int { + match e { + SyncError::Unknown => ERRNO_UNKNOWN, + SyncError::NoSuchTableInDb => ERRNO_NO_SUCH_TABLE_IN_DB, + SyncError::NotAnAsset => ERRNO_INVALID_INPUT_TYPE, + SyncError::AssetDownloadFailure => ERRNO_ASSET_DOWNLOAD_FAILURE, + SyncError::AssetUploadFailure => ERRNO_ASSET_UPLOAD_FAILURE, + SyncError::SessionUnlocked => ERRNO_UNLOCKED, + SyncError::Unsupported => ERRNO_UNSUPPORTED, + SyncError::IPCError(ipc_e) => map_ipc_err(ipc_e), + // Not deal here to involve vec in every branch + SyncError::IPCErrors(_) => ERRNO_IPC_ERRORS, + SyncError::IO(io_e) => match io_e.kind() { + ErrorKind::ConnectionRefused + | ErrorKind::ConnectionReset + | ErrorKind::ConnectionAborted + | ErrorKind::NotConnected + | ErrorKind::AddrInUse + | ErrorKind::AddrNotAvailable + | ErrorKind::BrokenPipe + | ErrorKind::UnexpectedEof => ERRNO_NETWORK_ERROR, + ErrorKind::OutOfMemory => ERRNO_NO_SPACE_FOR_ASSET, + _ => ERRNO_OTHER_IO_ERROR, + }, + } +} + +/// Create an AssetLoader instance by bundle name and database pointer. This function only borrows +/// database, so this pointer won't be taken by AssetLoader. Users should free it if the database +/// is no longer in need. +#[no_mangle] +pub unsafe extern "C" fn AssetLoaderNew( + user_id: c_int, + bundle_name: *mut c_uchar, + name_len: usize, + db: *mut Database, +) -> *mut AssetLoader { + if bundle_name.is_null() | bundle_name.is_null() | db.is_null() { + return null_mut(); + } + let bundle_name = String::from_raw_parts(bundle_name, name_len, name_len); + let asset_loader = + asset_loader::AssetLoader::new(user_id, &bundle_name, &*(db as *mut types::Database)); + Box::into_raw(Box::from(asset_loader)) as *mut AssetLoader +} + +/// Information needed when upload or download assets through AssetLoader. +#[repr(C)] +pub struct UpDownloadInfo { + table_name: *const c_uchar, + table_name_len: usize, + gid: *const c_uchar, + gid_len: usize, + prefix: *const c_uchar, + prefix_len: usize, +} + +/// Upload assets, with table name, gid, and prefix. This function only mutably borrows +/// assets, so this pointer won't be taken by AssetLoader. Users should free it if the hashmap of +/// assets is no longer in need. +/// +/// Assets should be in type HashMap. +#[no_mangle] +pub unsafe extern "C" fn AssetLoaderUpload( + loader: *mut AssetLoader, + info: *const UpDownloadInfo, + assets: *mut basic_rust_types::HashMap, +) -> c_int { + if loader.is_null() | info.is_null() | assets.is_null() { + return ERRNO_NULLPTR; + } + let info = &*info; + let loader = &*(loader as *mut asset_loader::AssetLoader); + let table_name = String::from_raw_parts( + info.table_name as *mut u8, + info.table_name_len, + info.table_name_len, + ); + let gid = String::from_raw_parts(info.gid as *mut u8, info.gid_len, info.gid_len); + let prefix = String::from_raw_parts(info.prefix as *mut u8, info.prefix_len, info.prefix_len); + let assets = &mut *(assets as *mut HashMapCffi); + let assets = &mut (*match assets { + HashMapCffi::Value(re) => re, + _ => return ERRNO_INVALID_INPUT_TYPE, + }); + match loader.upload(&table_name, &gid, &prefix, assets) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Download assets, with table name, gid, and prefix. This function only mutably borrows +/// assets, so this pointer won't be taken by AssetLoader. Users should free it if the hashmap of +/// assets is no longer in need. +/// +/// Assets should be in type HashMap. +#[no_mangle] +pub unsafe extern "C" fn AssetLoaderDownload( + loader: *mut AssetLoader, + info: *const UpDownloadInfo, + assets: *mut basic_rust_types::HashMap, +) -> c_int { + if loader.is_null() | info.is_null() | assets.is_null() { + return ERRNO_NULLPTR; + } + let info = &*info; + let loader = &*(loader as *mut asset_loader::AssetLoader); + let table_name = String::from_raw_parts( + info.table_name as *mut u8, + info.table_name_len, + info.table_name_len, + ); + let gid = String::from_raw_parts(info.gid as *mut u8, info.gid_len, info.gid_len); + let prefix = String::from_raw_parts(info.prefix as *mut u8, info.prefix_len, info.prefix_len); + let assets = &mut *(assets as *mut HashMapCffi); + let assets = &mut (*match assets { + HashMapCffi::Value(re) => re, + _ => return ERRNO_INVALID_INPUT_TYPE, + }); + match loader.download(&table_name, &gid, &prefix, assets) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Remove local asset. This function will use file system and remove a local file. This function +/// only mutably borrows asset, so this pointer won't be taken by AssetLoader. Users should free +/// it if the asset is no longer in need. +#[no_mangle] +pub unsafe extern "C" fn AssetLoaderRemoveLocalAssets(asset: *mut Asset) -> c_int { + if asset.is_null() { + return -1; + } + let asset = &*(asset as *mut asset_loader::Asset); + match asset_loader::AssetLoader::remove_local_assets(asset) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Free an AssetLoader pointer. +#[no_mangle] +pub unsafe extern "C" fn AssetLoaderFree(src: *mut AssetLoader) { + if !src.is_null() { + let _ = Box::from_raw(src as *mut asset_loader::AssetLoader); + } +} + +/// Initialize a CloudDatabase instance with bundle name and database. The database passed in will +/// be stored in CloudDatabase, so its management will be transferred and it should not be freed +/// by the users. +#[no_mangle] +pub unsafe extern "C" fn CloudDbNew( + user_id: c_int, + bundle_name: *mut c_uchar, + name_len: usize, + database: *mut Database, +) -> *mut CloudDatabase { + if database.is_null() | bundle_name.is_null() { + return null_mut(); + } + let database = *Box::from_raw(database as *mut types::Database); + let bundle_name = String::from_raw_parts(bundle_name as *mut u8, name_len, name_len); + + if let Ok(db) = cloud_db::CloudDatabase::new(user_id, &bundle_name, database) { + return Box::into_raw(Box::from(db)) as *mut CloudDatabase; + } + null_mut() +} + +/// Sql that will passed to CloudDatabase and executed. +#[repr(C)] +pub struct Sql { + table: *mut c_uchar, + table_len: usize, + sql: *mut c_uchar, + sql_len: usize, +} + +/// Execute sql on a CloudDatabase. +#[no_mangle] +pub unsafe extern "C" fn CloudDbExecuteSql( + cdb: *mut CloudDatabase, + sql: *const Sql, + extend: *mut basic_rust_types::HashMap, +) -> c_int { + if cdb.is_null() | sql.is_null() | extend.is_null() { + return ERRNO_NULLPTR; + } + let sql = &*sql; + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + let table = String::from_raw_parts(sql.table as *mut u8, sql.table_len, sql.table_len); + let sql = String::from_raw_parts(sql.sql as *mut u8, sql.sql_len, sql.sql_len); + + let extend = &mut *(extend as *mut HashMapCffi); + let extend = &mut (*match extend { + HashMapCffi::Value(re) => re, + _ => return ERRNO_INVALID_INPUT_TYPE, + }); + match cdb.execute(&table, &sql, extend) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Insert records into a CloudDatabase, with table name, and two Vector of HashMap. +/// Those two vectors passed in are only borrowed, and they should be freed by `VectorFree` if they +/// are no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudDbBatchInsert( + cdb: *mut CloudDatabase, + table: *mut c_uchar, + table_len: usize, + value: *const basic_rust_types::Vector, + extend: *mut basic_rust_types::Vector, +) -> c_int { + if cdb.is_null() | table.is_null() | value.is_null() | extend.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + let table = String::from_raw_parts(table as *mut u8, table_len, table_len); + + let value = &mut *(value as *mut VectorCffi); + let value_vec = match value { + VectorCffi::HashMapValue(val) => val, + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + let extend = &mut *(extend as *mut VectorCffi); + let extend_vec = match extend { + VectorCffi::HashMapValue(val) => val, + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + match cdb.batch_insert(&table, value_vec, extend_vec) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Update records in a CloudDatabase, with table name, and two Vector of HashMap. +/// Those two vectors passed in are only borrowed, and they should be freed by `VectorFree` if they +/// are no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudDbBatchUpdate( + cdb: *mut CloudDatabase, + table: *mut c_uchar, + table_len: usize, + value: *const basic_rust_types::Vector, + extend: *mut basic_rust_types::Vector, +) -> c_int { + if cdb.is_null() | table.is_null() | value.is_null() | extend.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + let table = String::from_raw_parts(table as *mut u8, table_len, table_len); + + let value = &mut *(value as *mut VectorCffi); + let value_vec = match value { + VectorCffi::HashMapValue(val) => val, + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + let extend = &mut *(extend as *mut VectorCffi); + let extend_vec = match extend { + VectorCffi::HashMapValue(val) => val, + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + + match cdb.batch_update(&table, value_vec, extend_vec) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Delete records from a CloudDatabase, with table name, and a Vector of HashMap. +/// The vector passed in are only borrowed, and it should be freed by `VectorFree` if it +/// is no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudDbBatchDelete( + cdb: *mut CloudDatabase, + table: *mut c_uchar, + table_len: usize, + extend: *mut basic_rust_types::Vector, +) -> c_int { + if cdb.is_null() | table.is_null() | extend.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + let table = String::from_raw_parts(table as *mut u8, table_len, table_len); + + let extend = &mut *(extend as *mut VectorCffi); + let extend_vec = match extend { + VectorCffi::HashMapValue(val) => val, + _ => return ERRNO_INVALID_INPUT_TYPE, + }; + + match cdb.batch_delete(&table, extend_vec) { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Query info that will passed to CloudDatabase. +#[repr(C)] +pub struct QueryInfo { + table: *mut c_uchar, + table_len: usize, + cursor: *mut c_uchar, + cursor_len: usize, +} + +/// Query records from a CloudDatabase, with table name and cursor. Return a CloudData pointer, which +/// should be freed by `CloudDataFree` if it's no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudDbBatchQuery( + cdb: *mut CloudDatabase, + info: *const QueryInfo, + out: *mut *const CloudData, +) -> c_int { + if cdb.is_null() | info.is_null() { + return -1; + } + let info = &*info; + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + let table = String::from_raw_parts(info.table as *mut u8, info.table_len, info.table_len); + let cursor = String::from_raw_parts(info.cursor as *mut u8, info.cursor_len, info.cursor_len); + + match cdb.batch_query(&table, &cursor) { + Ok(cloud_data) => { + *out = Box::into_raw(Box::new(cloud_data)) as *const CloudData; + SUCCESS + } + Err(e) => map_single_sync_err(&e), + } +} + +/// Lock a CloudDatabase. Return expire time. +#[no_mangle] +pub unsafe extern "C" fn CloudDbLock(cdb: *mut CloudDatabase, expire: *mut c_int) -> c_int { + if cdb.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + match cdb.lock() { + Ok(time) => { + *expire = time; + SUCCESS + } + Err(e) => map_single_sync_err(&e), + } +} + +/// Unlock a CloudDatabase. +#[no_mangle] +pub unsafe extern "C" fn CloudDbUnlock(cdb: *mut CloudDatabase) -> c_int { + if cdb.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + match cdb.unlock() { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Heartbeat function of a CloudDatabase. Extend the current locking session. +#[no_mangle] +pub unsafe extern "C" fn CloudDbHeartbeat(cdb: *mut CloudDatabase) -> c_int { + if cdb.is_null() { + return ERRNO_NULLPTR; + } + let cdb = &mut *(cdb as *mut cloud_db::CloudDatabase); + match cdb.heartbeat() { + Ok(()) => SUCCESS, + Err(e) => map_single_sync_err(&e), + } +} + +/// Free a CloudDatabase pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudDatabaseFree(cdb: *mut CloudDatabase) { + if !cdb.is_null() { + let _ = Box::from_raw(cdb as *mut cloud_db::CloudDatabase); + } +} + +/// Create a CloudServer instance by user id. +#[no_mangle] +pub unsafe extern "C" fn CloudServerNew(user_id: c_int) -> *mut CloudServer { + if let Ok(service) = cloud_service::CloudServer::new(user_id) { + return Box::into_raw(Box::from(service)) as *mut CloudServer; + } + null_mut() +} + +/// Get service info from a CloudServer pointer. Return a CloudInfo pointer, which +/// should be freed by `CloudInfoFree` if it's no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudServerGetServiceInfo( + server: *mut CloudServer, + info: *mut *const CloudInfo, +) -> c_int { + if server.is_null() { + return ERRNO_NULLPTR; + } + let cloud_server = &mut *(server as *mut cloud_service::CloudServer); + match cloud_server.get_service_info() { + Ok(cloud_info) => { + *info = Box::into_raw(Box::new(cloud_info)) as *const CloudInfo; + SUCCESS + } + Err(e) => map_single_sync_err(&e), + } +} + +/// Get app schema from a CloudServer pointer, with a bundle name. Return a SchemaMata pointer, which +/// should be freed by `SchemaMataFree` if it's no longer in use. +#[no_mangle] +pub unsafe extern "C" fn CloudServerGetAppSchema( + server: *mut CloudServer, + bundle_name: *mut c_uchar, + bundle_name_len: usize, + schema: *mut *const SchemaMata, +) -> c_int { + if server.is_null() | bundle_name.is_null() { + return ERRNO_NULLPTR; + } + let cloud_server = &mut *(server as *mut cloud_service::CloudServer); + let bundle_name = String::from_raw_parts(bundle_name, bundle_name_len, bundle_name_len); + match cloud_server.get_app_schema(&bundle_name) { + Ok(schema_meta) => { + *schema = Box::into_raw(Box::new(schema_meta)) as *const SchemaMata; + SUCCESS + } + Err(e) => map_single_sync_err(&e), + } +} + +/// Pass a batch of subscription orders to a CloudServer pointer, with target database information +/// and expected expire time. Database information should be in type HashMap>. +/// Return a Hashmap of subscription relations, in the form HashMap, and Vector +/// of errors in c_int, if failure happens. The Hashmap and Vector returned should be freed by +/// `HashMapFree` and `VectorFree` respectively, if not in use anymore. +#[no_mangle] +pub unsafe extern "C" fn CloudServerSubscribe( + server: *mut CloudServer, + dbs: *const basic_rust_types::HashMap, + expire: c_uint, + relations: *mut *const basic_rust_types::HashMap, + err: *mut *const basic_rust_types::Vector, +) -> c_int { + if server.is_null() | dbs.is_null() { + return ERRNO_NULLPTR; + } + + let cloud_server = &mut *(server as *mut cloud_service::CloudServer); + + let dbs = &mut *(dbs as *mut HashMapCffi); + let dbs = &mut (*match dbs { + HashMapCffi::VecDatabase(databases) => databases, + _ => return ERRNO_INVALID_INPUT_TYPE, + }); + + match cloud_server.subscribe(dbs, expire) { + Ok(ret) => { + let result = HashMapCffi::RelationSet(ret); + *relations = Box::into_raw(Box::new(result)) as *const basic_rust_types::HashMap; + *err = null_mut(); + SUCCESS + } + Err(e) => { + let errno = map_single_sync_err(&e); + if errno == ERRNO_IPC_ERRORS { + if let SyncError::IPCErrors(vec) = e { + let ret = VectorCffi::I32(vec.iter().map(map_ipc_err).collect()); + *err = Box::into_raw(Box::new(ret)) as *const basic_rust_types::Vector; + } + } + errno + } + } +} + +/// Pass a batch of unsubscription orders to a CloudServer pointer, with target relations. +/// Relations should be in type HashMap>, with bundle name as keys, vector of +/// subscription ids as value. Return a Vector of errors in c_int, if failure happens. The Vector +/// returned should be freed by `VectorFree` respectively, if not in use anymore. +#[no_mangle] +pub unsafe extern "C" fn CloudServerUnsubscribe( + server: *mut CloudServer, + relations: *const basic_rust_types::HashMap, + err: *mut *const basic_rust_types::Vector, +) -> c_int { + if server.is_null() | relations.is_null() { + return ERRNO_NULLPTR; + } + + let cloud_server = &mut *(server as *mut cloud_service::CloudServer); + + let relations = &mut *(relations as *mut HashMapCffi); + let relations = &mut (*match relations { + HashMapCffi::VecU32(res) => res, + _ => return ERRNO_INVALID_INPUT_TYPE, + }); + + match cloud_server.unsubscribe(relations) { + Ok(()) => SUCCESS, + Err(e) => { + let errno = map_single_sync_err(&e); + if errno == ERRNO_IPC_ERRORS { + if let SyncError::IPCErrors(vec) = e { + let ret = VectorCffi::I32(vec.iter().map(map_ipc_err).collect()); + *err = Box::into_raw(Box::new(ret)) as *const basic_rust_types::Vector; + } + } + errno + } + } +} + +/// Free a CloudServer pointer. +#[no_mangle] +pub unsafe extern "C" fn CloudServerFree(server: *mut CloudServer) { + if !server.is_null() { + let _ = Box::from_raw(server as *mut cloud_service::CloudServer); + } +} diff --git a/src/cloud_extension/src/c_adapter/mod.rs b/src/cloud_extension/src/c_adapter/mod.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9aecf05b79d6cb538d72a72446a05b2a59a85074 100644 --- a/src/cloud_extension/src/c_adapter/mod.rs +++ b/src/cloud_extension/src/c_adapter/mod.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub(crate) mod basic_rust_types; +pub(crate) mod cloud_ext_types; +pub(crate) mod cloud_extension; + +use std::ffi::{c_int, c_uchar}; +use std::ptr::slice_from_raw_parts; + +/// Errno of success. +pub const SUCCESS: c_int = 0; +/// Errno of null pointer. +pub const ERRNO_NULLPTR: c_int = 1; +/// Errno of unknown error. +pub const ERRNO_UNKNOWN: c_int = 2; +/// Errno of invalid input type. +pub const ERRNO_INVALID_INPUT_TYPE: c_int = 3; +/// Errno of asset download failure. +pub const ERRNO_ASSET_DOWNLOAD_FAILURE: c_int = 4; +/// Errno of asset upload failure. +pub const ERRNO_ASSET_UPLOAD_FAILURE: c_int = 5; +/// Errno of unsupported feature. +pub const ERRNO_UNSUPPORTED: c_int = 6; +/// Errno of network error. +pub const ERRNO_NETWORK_ERROR: c_int = 7; +/// Errno of lock conflict. +pub const ERRNO_LOCKED_BY_OTHERS: c_int = 8; +/// Errno of locking action on unlocked cloud database. +pub const ERRNO_UNLOCKED: c_int = 9; +/// Errno of record limit exceeding. +pub const ERRNO_RECORD_LIMIT_EXCEEDED: c_int = 10; +/// Errno of no space for assets. +pub const ERRNO_NO_SPACE_FOR_ASSET: c_int = 11; +/// Errno of other ipc error. +pub const ERRNO_OTHER_IPC_ERROR: c_int = 12; +/// Errno of batch of ipc errors. +pub const ERRNO_IPC_ERRORS: c_int = 13; +/// Errno of other io error. +pub const ERRNO_OTHER_IO_ERROR: c_int = 14; +/// Errno of array out of range. +pub const ERRNO_OUT_OF_RANGE: c_int = 15; +/// Errno of no target table in source database. +pub const ERRNO_NO_SUCH_TABLE_IN_DB: c_int = 16; + +pub(crate) unsafe fn char_ptr_to_string(ptr: *const c_uchar, len: usize) -> String { + if ptr.is_null() { + return String::default(); + } + let slice = &(*slice_from_raw_parts(ptr as *const u8, len)); + String::from_utf8_unchecked(slice.to_vec()) +} diff --git a/src/cloud_extension/src/ipc_conn/asset.rs b/src/cloud_extension/src/ipc_conn/asset.rs new file mode 100644 index 0000000000000000000000000000000000000000..ba192ccd339ebdd2756673285ec15cb1306c93d5 --- /dev/null +++ b/src/cloud_extension/src/ipc_conn/asset.rs @@ -0,0 +1,562 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn::error::Error; +use crate::ipc_conn::ffi::ConnectService; +use crate::ipc_conn::function::AssetLoaderFunc::{ConnectAssetLoader, Download, Upload}; +use crate::ipc_conn::{vec_raw_read, vec_raw_write}; +use ipc_rust::{ + BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcStatusCode, MsgParcel, RemoteObj, + Serialize, +}; + +#[derive(Debug, PartialEq)] +pub(crate) enum OperationType { + None, + Add, + Update, + Delete, +} + +impl Default for OperationType { + fn default() -> Self { + Self::None + } +} + +impl Serialize for OperationType { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + match self { + OperationType::None => parcel.write(&0_u32), + OperationType::Add => parcel.write(&1_u32), + OperationType::Update => parcel.write(&2_u32), + OperationType::Delete => parcel.write(&3_u32), + } + } +} + +impl Deserialize for OperationType { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let index = parcel.read::()?; + match index { + 0 => Ok(OperationType::None), + 1 => Ok(OperationType::Add), + 2 => Ok(OperationType::Update), + 3 => Ok(OperationType::Delete), + _ => Err(IpcStatusCode::Failed), + } + } +} + +#[derive(Debug, PartialEq)] +pub(crate) enum SwitchStatus { + Close, + Open, + NotEnable, +} + +impl Default for SwitchStatus { + fn default() -> Self { + Self::Close + } +} + +impl Serialize for SwitchStatus { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + match self { + SwitchStatus::Close => parcel.write(&0_u32), + SwitchStatus::Open => parcel.write(&1_u32), + SwitchStatus::NotEnable => parcel.write(&2_u32), + } + } +} + +impl Deserialize for SwitchStatus { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let index = parcel.read::()?; + match index { + 0 => Ok(SwitchStatus::Close), + 1 => Ok(SwitchStatus::Open), + 2 => Ok(SwitchStatus::NotEnable), + _ => Err(IpcStatusCode::Failed), + } + } +} + +#[derive(Default, PartialEq)] +pub(crate) struct Asset { + pub(crate) remote_obj: Option, + pub(crate) uri: String, + pub(crate) asset_name: String, + pub(crate) operation_type: OperationType, + pub(crate) hash: String, + pub(crate) version: u64, + pub(crate) asset_id: String, + pub(crate) sub_path: String, + pub(crate) prefix: String, + pub(crate) alias: String, + pub(crate) gid: String, + pub(crate) switch_status: SwitchStatus, +} + +impl Serialize for Asset { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + parcel.write(&self.uri)?; + parcel.write(&self.asset_name)?; + parcel.write(&self.operation_type)?; + parcel.write(&self.hash)?; + parcel.write(&self.version)?; + parcel.write(&self.asset_id)?; + parcel.write(&self.sub_path)?; + parcel.write(&self.prefix)?; + parcel.write(&self.alias)?; + parcel.write(&self.gid)?; + parcel.write(&self.switch_status)?; + Ok(()) + } +} + +impl Deserialize for Asset { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let uri = parcel.read::()?; + let asset_name = parcel.read::()?; + let operation_type = parcel.read::()?; + let hash = parcel.read::()?; + let version = parcel.read::()?; + let asset_id = parcel.read::()?; + let sub_path = parcel.read::()?; + let prefix = parcel.read::()?; + let alias = parcel.read::()?; + let gid = parcel.read::()?; + let switch_status = parcel.read::()?; + + let result = Asset { + remote_obj: None, + uri, + asset_name, + operation_type, + hash, + version, + asset_id, + sub_path, + prefix, + alias, + gid, + switch_status, + }; + Ok(result) + } +} + +impl Asset { + pub(crate) fn new(user_id: i32) -> Result { + let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + let function_number = ConnectAssetLoader as u32; + let remote_obj = + unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) } + .ok_or(Error::GetProxyObjectFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let mut result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + result.remote_obj = Some(remote_obj); + Ok(result) + } + + // Responsible for telling the cloud to perform file downloads. + pub(crate) fn download(&self, table: &str) -> Result<(), Error> { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Download as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::GetProxyObjectFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + if !result { + return Err(Error::DownloadFailed); + } + Ok(()) + } + + // Responsible for telling the cloud to perform file uploads. + pub(crate) fn upload(&self, table: &str) -> Result<(), Error> { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Upload as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::GetProxyObjectFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + if !result { + return Err(Error::UploadFailed); + } + Ok(()) + } +} + +#[derive(Default, PartialEq)] +pub(crate) struct Assets(pub(crate) Vec); + +impl Serialize for Assets { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + vec_raw_write(parcel, &self.0) + } +} + +impl Deserialize for Assets { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = Assets(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[cfg(all(test))] +mod test { + use crate::ipc_conn::asset::SwitchStatus; + use crate::ipc_conn::{Asset, Assets, OperationType}; + use ipc_rust::MsgParcel; + + /// UT test for operation_type_serialize. + /// + /// # Title + /// ut_operation_type_serialize + /// + /// # Brief + /// 1. Create a `OperationType` enum. + /// 2. Serialize the enum. + /// 3. Check if it is correct. + #[test] + fn ut_operation_type_serialize() { + let none = OperationType::None; + let add = OperationType::Add; + let update = OperationType::Update; + let delete = OperationType::Delete; + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&none).unwrap(); + msg_parcel.write(&add).unwrap(); + msg_parcel.write(&update).unwrap(); + msg_parcel.write(&delete).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 0_u32); + assert_eq!(msg_parcel.read::().unwrap(), 1_u32); + assert_eq!(msg_parcel.read::().unwrap(), 2_u32); + assert_eq!(msg_parcel.read::().unwrap(), 3_u32); + } + + /// UT test for operation_type_deserialize. + /// + /// # Title + /// ut_operation_type_deserialize + /// + /// # Brief + /// 1. Create a `OperationType` enum. + /// 2. Serialize the enum. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `OperationType`. + /// 5. Check if it is correct. + #[test] + fn ut_operation_type_deserialize() { + let none = OperationType::None; + let add = OperationType::Add; + let update = OperationType::Update; + let delete = OperationType::Delete; + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&none).unwrap(); + msg_parcel.write(&add).unwrap(); + msg_parcel.write(&update).unwrap(); + msg_parcel.write(&delete).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), none); + assert_eq!(msg_parcel.read::().unwrap(), add); + assert_eq!(msg_parcel.read::().unwrap(), update); + assert_eq!(msg_parcel.read::().unwrap(), delete); + } + + /// UT test for switch_status_serialize. + /// + /// # Title + /// ut_switch_status_serialize + /// + /// # Brief + /// 1. Create a `SwitchStatus` enum. + /// 2. Serialize the enum. + /// 3. Check if it is correct. + #[test] + fn ut_switch_status_serialize() { + let close = SwitchStatus::Close; + let open = SwitchStatus::Open; + let not_enable = SwitchStatus::NotEnable; + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&close).unwrap(); + msg_parcel.write(&open).unwrap(); + msg_parcel.write(¬_enable).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 0_u32); + assert_eq!(msg_parcel.read::().unwrap(), 1_u32); + assert_eq!(msg_parcel.read::().unwrap(), 2_u32); + } + + /// UT test for switch_status_deserialize. + /// + /// # Title + /// ut_switch_status_deserialize + /// + /// # Brief + /// 1. Create a `SwitchStatus` enum. + /// 2. Serialize the enum. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `SwitchStatus`. + /// 5. Check if it is correct. + #[test] + fn ut_switch_status_deserialize() { + let close = SwitchStatus::Close; + let open = SwitchStatus::Open; + let not_enable = SwitchStatus::NotEnable; + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&close).unwrap(); + msg_parcel.write(&open).unwrap(); + msg_parcel.write(¬_enable).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), close); + assert_eq!(msg_parcel.read::().unwrap(), open); + assert_eq!(msg_parcel.read::().unwrap(), not_enable); + } + + /// UT test for asset_serialize. + /// + /// # Title + /// ut_asset_serialize + /// + /// # Brief + /// 1. Create a `Asset` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_asset_serialize() { + let asset = Asset::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&asset).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!( + msg_parcel.read::().unwrap(), + OperationType::None + ); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), 0_u64); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!( + msg_parcel.read::().unwrap(), + SwitchStatus::Close + ); + } + + /// UT test for asset_deserialize. + /// + /// # Title + /// ut_asset_deserialize + /// + /// # Brief + /// 1. Create a `Asset` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Asset`. + /// 5. Check if it is correct. + #[test] + fn ut_asset_deserialize() { + let asset = Asset::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&asset).unwrap(); + + let result = msg_parcel.read::().unwrap(); + assert_eq!(result.uri, asset.uri); + assert_eq!(result.asset_name, asset.asset_name); + assert_eq!(result.operation_type, asset.operation_type); + assert_eq!(result.hash, asset.hash); + assert_eq!(result.version, asset.version); + assert_eq!(result.asset_id, asset.asset_id); + assert_eq!(result.sub_path, asset.sub_path); + assert_eq!(result.prefix, asset.prefix); + assert_eq!(result.alias, asset.alias); + assert_eq!(result.gid, asset.gid); + assert_eq!(result.switch_status, asset.switch_status); + } + + /// UT test for assets_serialize. + /// + /// # Title + /// ut_assets_serialize + /// + /// # Brief + /// 1. Create a `Assets` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_assets_serialize() { + let mut assets = Assets::default(); + let asset_one = Asset::default(); + let asset_two = Asset::default(); + assets.0.push(asset_one); + assets.0.push(asset_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&assets).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 2); + + let default_one = Asset::default(); + let result_one = msg_parcel.read::().unwrap(); + assert_eq!(result_one.uri, default_one.uri); + assert_eq!(result_one.asset_name, default_one.asset_name); + assert_eq!(result_one.operation_type, default_one.operation_type); + assert_eq!(result_one.hash, default_one.hash); + assert_eq!(result_one.version, default_one.version); + assert_eq!(result_one.asset_id, default_one.asset_id); + assert_eq!(result_one.sub_path, default_one.sub_path); + assert_eq!(result_one.prefix, default_one.prefix); + assert_eq!(result_one.alias, default_one.alias); + assert_eq!(result_one.gid, default_one.gid); + assert_eq!(result_one.switch_status, default_one.switch_status); + + let default_two = Asset::default(); + let result_two = msg_parcel.read::().unwrap(); + assert_eq!(result_two.uri, default_two.uri); + assert_eq!(result_two.asset_name, default_two.asset_name); + assert_eq!(result_two.operation_type, default_two.operation_type); + assert_eq!(result_two.hash, default_two.hash); + assert_eq!(result_two.version, default_two.version); + assert_eq!(result_two.asset_id, default_two.asset_id); + assert_eq!(result_two.sub_path, default_two.sub_path); + assert_eq!(result_two.prefix, default_two.prefix); + assert_eq!(result_two.alias, default_two.alias); + assert_eq!(result_two.gid, default_two.gid); + assert_eq!(result_two.switch_status, default_two.switch_status); + } + + /// UT test for assets_deserialize. + /// + /// # Title + /// ut_assets_deserialize + /// + /// # Brief + /// 1. Create a `Assets` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Assets`. + /// 5. Check if it is correct. + #[test] + fn ut_assets_deserialize() { + let mut assets = Assets::default(); + let asset_one = Asset::default(); + let asset_two = Asset::default(); + assets.0.push(asset_one); + assets.0.push(asset_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&assets).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for asset_new. + /// + /// # Title + /// ut_asset_new + /// + /// # Brief + /// 1. Create a `Asset` struct. + /// 2. Get self info. + /// 3. Check if it is correct. + #[test] + fn ut_asset_new() { + let user_id = 100; + let mut asset = Asset::new(user_id).unwrap(); + assert!(asset.remote_obj.is_none()); + } + + /// UT test for asset_new. + /// + /// # Title + /// ut_asset_new + /// + /// # Brief + /// 1. Create a `Asset` struct. + /// 2. Download info. + /// 3. Check if it is correct. + #[test] + fn ut_asset_download() { + let user_id = 100; + let mut asset = Asset::new(user_id).unwrap(); + let table = ""; + let result = asset.download(table); + assert!(result.is_ok()); + } + + /// UT test for asset_new. + /// + /// # Title + /// ut_asset_new + /// + /// # Brief + /// 1. Create a `Asset` struct. + /// 2. Upload info. + /// 3. Check if it is correct. + #[test] + fn ut_asset_upload() { + let user_id = 100; + let mut asset = Asset::new(user_id).unwrap(); + let table = ""; + let result = asset.upload(table); + assert!(result.is_ok()); + } +} diff --git a/src/cloud_extension/src/ipc_conn/connect.rs b/src/cloud_extension/src/ipc_conn/connect.rs index 535265a51912d7bd08851724ab9d1eb9f62fccf8..62880d6ff95f69148d4972f818836fa268d321c1 100644 --- a/src/cloud_extension/src/ipc_conn/connect.rs +++ b/src/cloud_extension/src/ipc_conn/connect.rs @@ -11,180 +11,1237 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::ipc_conn::error::{Error, Errors}; +use crate::ipc_conn::ffi::ConnectService; +use crate::ipc_conn::function::CloudServiceFunc::{ + GetAppBriefInfo, GetAppSchema, GetServiceInfo, Subscribe, Unsubscribe, +}; +use crate::ipc_conn::{ + hash_map_raw_read, hash_map_raw_write, vec_raw_read, vec_raw_write, Database, +}; +use ipc_rust::{ + BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcResult, IpcStatusCode, MsgParcel, + RemoteObj, Serialize, +}; use std::collections::HashMap; +use std::sync::{RwLock, RwLockReadGuard}; -pub struct AppInfo { - app_bundle_name: String, - app_id: String, - app_fingerprint: String, - // And more... +/************** All AppSchema struct-related structures and methods **************/ + +#[derive(Debug, PartialEq)] +pub(crate) enum FieldType { + Null, + Number, + Real, + Text, + Bool, + Blob, + Asset, + Assets, } -pub enum CloudStatus { - Unknown, - UnActivated, - Login, - Logout, +impl Serialize for FieldType { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + match self { + FieldType::Null => parcel.write(&0_u32), + FieldType::Number => parcel.write(&1_u32), + FieldType::Real => parcel.write(&2_u32), + FieldType::Text => parcel.write(&3_u32), + FieldType::Bool => parcel.write(&4_u32), + FieldType::Blob => parcel.write(&5_u32), + FieldType::Asset => parcel.write(&6_u32), + FieldType::Assets => parcel.write(&7_u32), + } + } } -pub enum SpaceStatus { - Normal, - Full, + +impl Deserialize for FieldType { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + let index = parcel.read::()?; + match index { + 0 => Ok(FieldType::Null), + 1 => Ok(FieldType::Number), + 2 => Ok(FieldType::Real), + 3 => Ok(FieldType::Text), + 4 => Ok(FieldType::Bool), + 5 => Ok(FieldType::Blob), + 6 => Ok(FieldType::Asset), + 7 => Ok(FieldType::Assets), + _ => Err(IpcStatusCode::Failed), + } + } } -pub struct UserInfo { - account_id: String, - cloud_status: CloudStatus, - space_status: SpaceStatus, - total_space: u64, - left_space: u64, - // And more... + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct Field { + pub(crate) col_name: String, + pub(crate) alias: String, + pub(crate) typ: FieldType, + pub(crate) primary: bool, + pub(crate) nullable: bool, } -pub struct Connect { - infos: HashMap, +impl Serialize for Field { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + parcel.write(&self.col_name)?; + parcel.write(&self.alias)?; + parcel.write(&self.typ)?; + parcel.write(&self.primary)?; + parcel.write(&self.nullable)?; + Ok(()) + } } -impl Connect { - pub fn new() -> Self { - todo!() +impl Deserialize for Field { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + let col_name = parcel.read::()?; + let cloud_name = parcel.read::()?; + let typ = parcel.read::()?; + let primary = parcel.read::()?; + let nullable = parcel.read::()?; + + let result = Field { + col_name, + alias: cloud_name, + typ, + primary, + nullable, + }; + Ok(result) } +} + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct Fields(pub(crate) Vec); - pub fn get(user_id: i32) -> Infos { - todo!() +impl Serialize for Fields { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + vec_raw_write(parcel, &self.0) } } -pub struct Infos { - app_infos: HashMap, - user_infos: UserInfo, +impl Deserialize for Fields { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + let result = Fields(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct OrderTable { + pub(crate) alias: String, + pub(crate) table_name: String, + pub(crate) fields: Fields, } -impl Infos { - pub fn get_app_info(&self) -> &AppInfo { - todo!() +impl Serialize for OrderTable { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + parcel.write(&self.alias)?; + parcel.write(&self.table_name)?; + parcel.write(&self.fields)?; + Ok(()) } +} - pub fn get_user_info(&self) -> &UserInfo { - todo!() +impl Deserialize for OrderTable { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = OrderTable { + alias: parcel.read::()?, + table_name: parcel.read::()?, + fields: parcel.read::()?, + }; + Ok(result) } } -pub struct RecordResponse { - time: i64, - device_name: String, - app_id: String, +#[derive(Default, Debug, PartialEq)] +pub(crate) struct SchemaOrderTables(pub(crate) Vec); + +impl Serialize for SchemaOrderTables { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + vec_raw_write(parcel, &self.0) + } +} + +impl Deserialize for SchemaOrderTables { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = SchemaOrderTables(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Default)] +pub(crate) struct Databases(pub(crate) Vec); + +impl Serialize for Databases { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + vec_raw_write(parcel, &self.0) + } } -pub enum OperationType { - None, - Add, - Update, - Delete, +impl Deserialize for Databases { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let databases = Databases(vec_raw_read(parcel)?); + Ok(databases) + } } -pub struct Asset { - uri: String, - asset_name: String, - operation_type: OperationType, - hash: String, - version: u64, - asset_id: String, - sub_path: String, +#[derive(Default)] +pub(crate) struct Schema { + pub(crate) version: i32, + pub(crate) bundle_name: String, + pub(crate) databases: Databases, } -pub struct Reference { - record_id: String, - record_type: String, +impl Schema { + fn read(&mut self, msg_parcel: &mut MsgParcel) -> Result<(), Error> { + self.version = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + self.bundle_name = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + self.databases = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(()) + } } -pub enum RecordField { - Null(), - Int(i64), - Double(f64), - String(String), - Bool(bool), - Blob(Vec), - List(Vec), - Map(HashMap), - Asset(Asset), - Reference(Reference), +impl Deserialize for Schema { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let version = parcel.read::()?; + let bundle_name = parcel.read::()?; + let databases = parcel.read::()?; + + let result = Schema { + version, + bundle_name, + databases, + }; + Ok(result) + } } -pub struct Record { - record_id: String, - record_type: String, - create_info: RecordResponse, - modified_info: RecordResponse, - record_data: HashMap, - is_delete: bool, - version: u32, - is_new_create: bool, +/************** All AppInfo struct-related structures and methods **************/ + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct AppInfo { + pub(crate) bundle_name: String, + pub(crate) id: String, + pub(crate) fingerprint: String, + pub(crate) enable_cloud: bool, } -impl Record { - pub fn get_record_id(&self) -> &str { - self.record_id.as_str() +impl Deserialize for AppInfo { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let bundle_name = parcel.read::()?; + let id = parcel.read::()?; + let fingerprint = parcel.read::()?; + let enable_cloud = parcel.read::()?; + + let result = Self { + bundle_name, + id, + fingerprint, + enable_cloud, + }; + Ok(result) } +} + +/************** All ServiceInfo struct-related structures and methods **************/ +#[derive(Ord, PartialOrd, Eq, PartialEq)] +pub(crate) enum CloudStatus { + Unknown, + UnActivated, + Login, + Logout, +} - pub fn get_record_type(&self) -> &str { - self.record_type.as_str() +impl CloudStatus { + fn from_u8(data: u8) -> Result { + match data { + 0_u8 => Ok(Self::Unknown), + 1_u8 => Ok(Self::UnActivated), + 2_u8 => Ok(Self::Login), + 3_u8 => Ok(Self::Logout), + _ => Err(Error::InvalidCloudStatus), + } } +} - pub fn get_create_info(&self) -> &RecordResponse { - &self.create_info +impl Default for CloudStatus { + fn default() -> Self { + Self::Unknown } +} + +#[derive(Default)] +pub(crate) struct ServiceInfo { + pub(crate) account_id: String, + pub(crate) cloud_status: CloudStatus, + pub(crate) total_space: u64, + pub(crate) left_space: u64, +} - pub fn get_modified_info(&self) -> &RecordResponse { - &self.modified_info +impl ServiceInfo { + fn read(&mut self, msg_parcel: &mut MsgParcel) -> Result<(), Error> { + self.account_id = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + self.cloud_status = CloudStatus::from_u8( + msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?, + )?; + self.total_space = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + self.left_space = msg_parcel + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(()) } +} + +/************** All Subscribe struct-related structures and methods **************/ - pub fn get_record_data(&self) -> &HashMap { - &self.record_data +#[derive(Default)] +pub(crate) struct Subscription; + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct SubscriptionResultValue(pub(crate) Vec<(String, u32)>); + +impl Deserialize for SubscriptionResultValue { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let length = parcel.read::()? as usize; + let mut vector = Vec::with_capacity(length); + for _ in 0..length { + let alias = parcel.read::()?; + let subscription_id = parcel.read::()?; + vector.push((alias, subscription_id)); + } + Ok(SubscriptionResultValue(vector)) } +} - pub fn get_is_delete(&self) -> bool { - self.is_delete +#[derive(Default)] +pub(crate) struct SubscriptionResult(pub(crate) (u64, HashMap)); + +impl Deserialize for SubscriptionResult { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let interval = parcel.read::()?; + let result = SubscriptionResult(( + interval, + hash_map_raw_read::(parcel)?, + )); + Ok(result) } +} - pub fn get_version(&self) -> u32 { - self.version +#[derive(Default)] +pub(crate) struct UnsubscriptionInfo(pub(crate) HashMap>); + +impl Serialize for UnsubscriptionInfo { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + hash_map_raw_write(parcel, &self.0) } +} + +impl Subscription { + // For subsequent extensibility, use `&self` here. + pub(crate) fn subscribe( + &self, + remote: &Option, + expiration: u64, + bundle_name: &str, + databases: &Databases, + ) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?; + msg_parcel + .write(&expiration) + .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?; + msg_parcel + .write(bundle_name) + .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?; + msg_parcel + .write(databases) + .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?; + + let function_number = Subscribe as u32; + let remote_obj = remote + .as_ref() + .ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Errors(vec![Error::SendRequestFailed]))?; - pub fn get_is_new_create(&self) -> bool { - self.is_new_create + if let Ok(cloud_subscription_info) = receive.read::() { + return Ok(cloud_subscription_info); + } + + if let Ok(errs) = receive.read::() { + return Err(errs); + } + + Err(Errors::default()) } - // And more... + pub(crate) fn unsubscribe( + &self, + remote: &Option, + info: &UnsubscriptionInfo, + ) -> Result<(), Errors> { + let mut msg_parcel = MsgParcel::new().ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?; + msg_parcel + .write(info) + .map_err(|_| Errors(vec![Error::WriteMsgParcelFailed]))?; + + let function_number = Unsubscribe as u32; + let remote_obj = remote + .as_ref() + .ok_or(Errors(vec![Error::CreateMsgParcelFailed]))?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Errors(vec![Error::SendRequestFailed]))?; + + if let Ok(errs) = receive.read::() { + return Err(errs); + } + + Ok(()) + } } -pub struct Lock { - interval: i32, - session_id: String, +/************** All Connect struct-related structures and methods **************/ + +pub(crate) struct Connect { + pub(crate) remote_obj: Option, + infos: HashMap, } -pub struct Database { - // And more... +impl Drop for Connect { + fn drop(&mut self) { + println!("进入 Connect Drop 流程了"); + } } -impl Database { - pub fn generate_ids(number: usize) -> Vec { - todo!() +impl Connect { + // Creates a structure `Connect` for connecting to the cloud and getting various information about the cloud. + pub(crate) fn new(user_id: i32) -> Result { + let remote_obj = + unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) } + .ok_or(Error::GetProxyObjectFailed)?; + Ok(Self { + remote_obj: Some(remote_obj), + infos: HashMap::default(), + }) } - pub fn save_records(&mut self) { - todo!() + // Sends `MsgParcel` to the cloud, + // wait for the return value from the cloud, + // get the required app infos from the cloud. + pub(crate) fn get_app_brief_info( + &mut self, + user_id: i32, + ) -> Result, Error> { + self.infos.entry(user_id).or_insert_with(Infos::default); + let infos = self.infos.get_mut(&user_id).unwrap(); + + let mut lock = infos.app_infos.write().unwrap(); + + let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + + let function_number = GetAppBriefInfo as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::GetProxyObjectFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + lock.0 = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)? + .0; + + drop(lock); + + Ok(infos.app_infos.read().unwrap()) } - pub fn delete_records(&mut self) { todo!() } + // Sends `MsgParcel` to the cloud, + // wait for the return value from the cloud, + // get the required user info from the cloud. + pub(crate) fn get_service_info( + &mut self, + user_id: i32, + ) -> Result, Error> { + self.infos.entry(user_id).or_insert_with(Infos::default); + let infos = self.infos.get_mut(&user_id).unwrap(); - pub fn fetch_records(&mut self) { todo!() } + let mut lock = infos.service_info.write().unwrap(); - pub fn get_start_cursor(&mut self) { todo!() } + let msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; - pub fn fetch_database_changes(&mut self) { todo!() } + let function_number = GetServiceInfo as u32; + let remote_obj = self + .remote_obj + .clone() + .ok_or(Error::CreateMsgParcelFailed)?; + let mut receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; - pub fn get_lock(&mut self) { todo!() } + lock.read(&mut receive)?; - pub fn delete_lock(&mut self) { todo!() } + drop(lock); + + Ok(infos.service_info.read().unwrap()) + } - // And more... + pub(crate) fn get_app_schema( + &mut self, + user_id: i32, + bundle_name: &str, + ) -> Result, Error> { + self.infos.entry(user_id).or_insert_with(Infos::default); + let infos = self.infos.get_mut(&user_id).unwrap(); + + let mut lock = infos.app_schema.write().unwrap(); + + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(bundle_name) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = GetAppSchema as u32; + let remote_obj = self + .remote_obj + .clone() + .ok_or(Error::CreateMsgParcelFailed)?; + let mut receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + lock.read(&mut receive)?; + + drop(lock); + + Ok(infos.app_schema.read().unwrap()) + } + + pub(crate) fn subscribe( + &self, + expiration: u64, + bundle_name: &str, + databases: &Databases, + ) -> Result { + let subscription = Subscription::default(); + subscription.subscribe(&self.remote_obj, expiration, bundle_name, databases) + } + + pub(crate) fn unsubscribe(&self, info: &UnsubscriptionInfo) -> Result<(), Errors> { + let subscription = Subscription::default(); + subscription.unsubscribe(&self.remote_obj, info) + } +} + +#[derive(Default)] +pub(crate) struct AppInfos(pub(crate) Vec); + +impl Deserialize for AppInfos { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = AppInfos(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Default)] +pub(crate) struct Infos { + app_infos: RwLock, + service_info: RwLock, + app_schema: RwLock, +} + +#[cfg(all(test))] +mod test { + use crate::ipc_conn::connect::{FieldType, SubscriptionResultValue}; + use crate::ipc_conn::{ + AppInfo, Connect, Database, Databases, OrderTable, Schema, SchemaOrderTables, + UnsubscriptionInfo, + }; + use ipc_rust::MsgParcel; + + /// UT test for order_table_serialize. + /// + /// # Title + /// ut_order_table_serialize + /// + /// # Brief + /// 1. Create a `OrderTable` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_order_table_serialize() { + let order_table = OrderTable::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&order_table).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), String::default()); + assert_eq!(msg_parcel.read::().unwrap(), String::default()); + } + + /// UT test for order_table_deserialize. + /// + /// # Title + /// ut_order_table_deserialize + /// + /// # Brief + /// 1. Create a `OrderTable` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `OrderTable`. + /// 5. Check if it is correct. + #[test] + fn ut_order_table_deserialize() { + let order_table = OrderTable::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&order_table).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), order_table); + } + + /// UT test for schema_order_tables_serialize. + /// + /// # Title + /// ut_schema_order_tables_serialize + /// + /// # Brief + /// 1. Create a `SchemaOrderTables` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_schema_order_tables_serialize() { + let mut schema_order_tables = SchemaOrderTables::default(); + let order_table_one = OrderTable::default(); + let order_table_two = OrderTable::default(); + schema_order_tables.0.push(order_table_one); + schema_order_tables.0.push(order_table_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&schema_order_tables).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 2); + assert_eq!( + msg_parcel.read::().unwrap(), + OrderTable::default() + ); + assert_eq!( + msg_parcel.read::().unwrap(), + OrderTable::default() + ); + } + + /// UT test for schema_order_tables_deserialize. + /// + /// # Title + /// ut_schema_order_tables_deserialize + /// + /// # Brief + /// 1. Create a `SchemaOrderTables` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `SchemaOrderTables`. + /// 5. Check if it is correct. + #[test] + fn ut_schema_order_tables_deserialize() { + let mut schema_order_tables = SchemaOrderTables::default(); + let order_table_one = OrderTable::default(); + let order_table_two = OrderTable::default(); + schema_order_tables.0.push(order_table_one); + schema_order_tables.0.push(order_table_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&schema_order_tables).unwrap(); + + assert_eq!( + msg_parcel.read::().unwrap(), + schema_order_tables + ); + } + + /// UT test for databases_serialize. + /// + /// # Title + /// ut_databases_serialize + /// + /// # Brief + /// 1. Create a `Databases` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_databases_serialize() { + let mut databases = Databases::default(); + let database_one = Database::default(); + let database_two = Database::default(); + databases.0.push(database_one); + databases.0.push(database_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&databases).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 2); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for databases_deserialize. + /// + /// # Title + /// ut_databases_deserialize + /// + /// # Brief + /// 1. Create a `Databases` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Databases`. + /// 5. Check if it is correct. + #[test] + fn ut_databases_deserialize() { + let mut databases = Databases::default(); + let database_one = Database::default(); + let database_two = Database::default(); + databases.0.push(database_one); + databases.0.push(database_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&databases).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for schema_deserialize. + /// + /// # Title + /// ut_schema_deserialize + /// + /// # Brief + /// 1. Create a `Schema` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Schema`. + /// 5. Check if it is correct. + #[test] + fn ut_schema_deserialize() { + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&0_i32).unwrap(); + msg_parcel.write(&String::from("")).unwrap(); + msg_parcel.write(&Databases::default()).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for app_info_deserialize. + /// + /// # Title + /// ut_app_info_deserialize + /// + /// # Brief + /// 1. Create a `AppInfo` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `AppInfo`. + /// 5. Check if it is correct. + #[test] + fn ut_app_info_deserialize() { + let app_info = AppInfo::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&String::default()).unwrap(); + msg_parcel.write(&String::default()).unwrap(); + msg_parcel.write(&String::default()).unwrap(); + msg_parcel.write(&bool::default()).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), app_info); + } + + /// UT test for subscription_result_value_deserialize. + /// + /// # Title + /// ut_subscription_result_value_deserialize + /// + /// # Brief + /// 1. Create a `SubscriptionResultValue` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `SubscriptionResultValue`. + /// 5. Check if it is correct. + #[test] + fn ut_subscription_result_value_deserialize() { + let mut subscription = SubscriptionResultValue::default(); + subscription.0.push((String::from("test1"), 2)); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&1_u32).unwrap(); + msg_parcel.write(&String::from("test1")).unwrap(); + msg_parcel.write(&2_u32).unwrap(); + + assert_eq!( + msg_parcel.read::().unwrap(), + subscription + ); + } + + /// UT test for connect_new. + /// + /// # Title + /// ut_connect_new + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Check if it is correct. + #[test] + fn ut_connect_new() { + let user_id = 100; + let connect = Connect::new(user_id).unwrap(); + assert!(connect.remote_obj.is_some()); + } + + /// UT test for connect_get_app_brief_info. + /// + /// # Title + /// ut_connect_get_app_brief_info + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Get app infos. + /// 3. Check if it is correct. + #[test] + fn ut_connect_get_app_brief_info() { + let user_id = 100; + let mut connect = Connect::new(user_id).unwrap(); + let app_infos = &connect.get_app_brief_info(user_id).unwrap().0; + assert_eq!(app_infos.len(), 3); + assert_eq!( + app_infos[0].bundle_name, + String::from("com.huawei.hmos.calendardata") + ); + assert_eq!(app_infos[0].id, String::from("10055832")); + assert_eq!( + app_infos[1].bundle_name, + String::from("com.huawei.hmos.notepad") + ); + assert_eq!(app_infos[1].id, String::from("10055832")); + assert_eq!(app_infos[2].bundle_name, String::from("com.ohos.contacts")); + assert_eq!(app_infos[2].id, String::from("10055832")); + } + + /// UT test for connect_get_service_info. + /// + /// # Title + /// ut_connect_get_service_info + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Get service info. + /// 3. Check if it is correct. + #[test] + fn ut_connect_get_service_info() { + let user_id = 100; + let mut connect = Connect::new(user_id).unwrap(); + let service_info = &connect.get_service_info(user_id).unwrap(); + assert_eq!(service_info.account_id, String::from("2850086000356238647")); + assert_eq!(service_info.left_space, 536844326007); + assert_eq!(service_info.total_space, 536844326007); + } + + /// UT test for connect_get_app_schema. + /// + /// # Title + /// ut_connect_get_app_schema + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Get schema info. + /// 3. Check if it is correct. + #[test] + fn ut_connect_get_app_schema() { + let user_id = 100; + let bundle_name = String::from("com.huawei.hmos.notepad"); + let mut connect = Connect::new(user_id).unwrap(); + let app_schema = &connect.get_app_schema(user_id, &bundle_name).unwrap(); + assert_eq!( + app_schema.bundle_name, + String::from("com.huawei.hmos.notepad") + ); + assert_eq!(app_schema.version, 100); + assert_eq!(app_schema.databases.0.len(), 5); + let databases = &app_schema.databases.0; + + assert!(databases[0].remote_obj.is_none()); + assert_eq!(databases[0].name, String::from("notepad")); + assert_eq!(databases[0].alias, String::from("notepad")); + + assert_eq!(databases[0].tables.0.len(), 5); + assert_eq!(databases[0].tables.0[0].alias, String::from("notetag")); + assert_eq!( + databases[0].tables.0[0].table_name, + String::from("cloud_folders") + ); + assert_eq!(databases[0].tables.0[0].fields.0.len(), 4); + assert_eq!( + databases[0].tables.0[0].fields.0[0].alias, + String::from("data") + ); + assert_eq!( + databases[0].tables.0[0].fields.0[0].col_name, + String::from("data") + ); + assert!(databases[0].tables.0[0].fields.0[0].nullable); + assert!(!databases[0].tables.0[0].fields.0[0].primary); + assert_eq!(databases[0].tables.0[0].fields.0[0].typ, FieldType::Text); + assert_eq!( + databases[0].tables.0[0].fields.0[1].alias, + String::from("recycled") + ); + assert_eq!( + databases[0].tables.0[0].fields.0[1].col_name, + String::from("recycled") + ); + assert!(databases[0].tables.0[0].fields.0[1].nullable); + assert!(!databases[0].tables.0[0].fields.0[1].primary); + assert_eq!(databases[0].tables.0[0].fields.0[1].typ, FieldType::Bool); + assert_eq!( + databases[0].tables.0[0].fields.0[2].alias, + String::from("recycledTime") + ); + assert_eq!( + databases[0].tables.0[0].fields.0[2].col_name, + String::from("recycledTime") + ); + assert!(databases[0].tables.0[0].fields.0[2].nullable); + assert!(!databases[0].tables.0[0].fields.0[2].primary); + assert_eq!(databases[0].tables.0[0].fields.0[2].typ, FieldType::Number); + assert_eq!( + databases[0].tables.0[0].fields.0[3].alias, + String::from("uuid") + ); + assert_eq!( + databases[0].tables.0[0].fields.0[3].col_name, + String::from("uuid") + ); + assert!(databases[0].tables.0[0].fields.0[3].nullable); + assert!(databases[0].tables.0[0].fields.0[3].primary); + assert_eq!(databases[0].tables.0[0].fields.0[3].typ, FieldType::Text); + + assert_eq!(databases[0].tables.0[1].alias, String::from("shorthand")); + assert_eq!( + databases[0].tables.0[1].table_name, + String::from("cloud_tasks") + ); + assert_eq!(databases[0].tables.0[1].fields.0.len(), 5); + assert_eq!( + databases[0].tables.0[1].fields.0[0].alias, + String::from("attachments") + ); + assert_eq!( + databases[0].tables.0[1].fields.0[0].col_name, + String::from("attachments") + ); + assert!(databases[0].tables.0[1].fields.0[0].nullable); + assert!(!databases[0].tables.0[1].fields.0[0].primary); + assert_eq!(databases[0].tables.0[1].fields.0[0].typ, FieldType::Assets); + assert_eq!( + databases[0].tables.0[1].fields.0[1].alias, + String::from("data") + ); + assert_eq!( + databases[0].tables.0[1].fields.0[1].col_name, + String::from("data") + ); + assert!(databases[0].tables.0[1].fields.0[1].nullable); + assert!(!databases[0].tables.0[1].fields.0[1].primary); + assert_eq!(databases[0].tables.0[1].fields.0[1].typ, FieldType::Text); + assert_eq!( + databases[0].tables.0[1].fields.0[2].alias, + String::from("recycled") + ); + assert_eq!( + databases[0].tables.0[1].fields.0[2].col_name, + String::from("recycled") + ); + assert!(databases[0].tables.0[1].fields.0[2].nullable); + assert!(!databases[0].tables.0[1].fields.0[2].primary); + assert_eq!(databases[0].tables.0[1].fields.0[2].typ, FieldType::Bool); + assert_eq!( + databases[0].tables.0[1].fields.0[3].alias, + String::from("recycledTime") + ); + assert_eq!( + databases[0].tables.0[1].fields.0[3].col_name, + String::from("recycledTime") + ); + assert!(databases[0].tables.0[1].fields.0[3].nullable); + assert!(!databases[0].tables.0[1].fields.0[3].primary); + assert_eq!(databases[0].tables.0[1].fields.0[3].typ, FieldType::Number); + assert_eq!( + databases[0].tables.0[1].fields.0[4].alias, + String::from("uuid") + ); + assert_eq!( + databases[0].tables.0[1].fields.0[4].col_name, + String::from("uuid") + ); + assert!(databases[0].tables.0[1].fields.0[4].nullable); + assert!(databases[0].tables.0[1].fields.0[4].primary); + assert_eq!(databases[0].tables.0[1].fields.0[4].typ, FieldType::Text); + + assert_eq!(databases[0].tables.0[2].alias, String::from("note")); + assert_eq!( + databases[0].tables.0[2].table_name, + String::from("cloud_old_notes") + ); + assert_eq!(databases[0].tables.0[2].fields.0.len(), 5); + assert_eq!( + databases[0].tables.0[2].fields.0[0].alias, + String::from("attachments") + ); + assert_eq!( + databases[0].tables.0[2].fields.0[0].col_name, + String::from("attachments") + ); + assert!(databases[0].tables.0[2].fields.0[0].nullable); + assert!(!databases[0].tables.0[2].fields.0[0].primary); + assert_eq!(databases[0].tables.0[2].fields.0[0].typ, FieldType::Assets); + assert_eq!( + databases[0].tables.0[2].fields.0[1].alias, + String::from("data") + ); + assert_eq!( + databases[0].tables.0[2].fields.0[1].col_name, + String::from("data") + ); + assert!(databases[0].tables.0[2].fields.0[1].nullable); + assert!(!databases[0].tables.0[2].fields.0[1].primary); + assert_eq!(databases[0].tables.0[2].fields.0[1].typ, FieldType::Text); + assert_eq!( + databases[0].tables.0[2].fields.0[2].alias, + String::from("recycled") + ); + assert_eq!( + databases[0].tables.0[2].fields.0[2].col_name, + String::from("recycled") + ); + assert!(databases[0].tables.0[2].fields.0[2].nullable); + assert!(!databases[0].tables.0[2].fields.0[2].primary); + assert_eq!(databases[0].tables.0[2].fields.0[2].typ, FieldType::Bool); + assert_eq!( + databases[0].tables.0[2].fields.0[3].alias, + String::from("recycledTime") + ); + assert_eq!( + databases[0].tables.0[2].fields.0[3].col_name, + String::from("recycledTime") + ); + assert!(databases[0].tables.0[2].fields.0[3].nullable); + assert!(!databases[0].tables.0[2].fields.0[3].primary); + assert_eq!(databases[0].tables.0[2].fields.0[3].typ, FieldType::Number); + assert_eq!( + databases[0].tables.0[2].fields.0[4].alias, + String::from("uuid") + ); + assert_eq!( + databases[0].tables.0[2].fields.0[4].col_name, + String::from("uuid") + ); + assert!(databases[0].tables.0[2].fields.0[4].nullable); + assert!(databases[0].tables.0[2].fields.0[4].primary); + assert_eq!(databases[0].tables.0[2].fields.0[4].typ, FieldType::Text); + + assert_eq!( + databases[0].tables.0[3].alias, + String::from("noteattachment") + ); + assert_eq!( + databases[0].tables.0[3].table_name, + String::from("cloud_attachments") + ); + assert_eq!(databases[0].tables.0[3].fields.0.len(), 5); + assert_eq!( + databases[0].tables.0[3].fields.0[0].alias, + String::from("attachments") + ); + assert_eq!( + databases[0].tables.0[3].fields.0[0].col_name, + String::from("attachments") + ); + assert!(databases[0].tables.0[3].fields.0[0].nullable); + assert!(!databases[0].tables.0[3].fields.0[0].primary); + assert_eq!(databases[0].tables.0[3].fields.0[0].typ, FieldType::Assets); + assert_eq!( + databases[0].tables.0[3].fields.0[1].alias, + String::from("data") + ); + assert_eq!( + databases[0].tables.0[3].fields.0[1].col_name, + String::from("data") + ); + assert!(databases[0].tables.0[3].fields.0[1].nullable); + assert!(!databases[0].tables.0[3].fields.0[1].primary); + assert_eq!(databases[0].tables.0[3].fields.0[1].typ, FieldType::Text); + assert_eq!( + databases[0].tables.0[3].fields.0[2].alias, + String::from("recycled") + ); + assert_eq!( + databases[0].tables.0[3].fields.0[2].col_name, + String::from("recycled") + ); + assert!(databases[0].tables.0[3].fields.0[2].nullable); + assert!(!databases[0].tables.0[3].fields.0[2].primary); + assert_eq!(databases[0].tables.0[3].fields.0[2].typ, FieldType::Bool); + assert_eq!( + databases[0].tables.0[3].fields.0[3].alias, + String::from("recycledTime") + ); + assert_eq!( + databases[0].tables.0[3].fields.0[3].col_name, + String::from("recycledTime") + ); + assert!(databases[0].tables.0[3].fields.0[3].nullable); + assert!(!databases[0].tables.0[3].fields.0[3].primary); + assert_eq!(databases[0].tables.0[3].fields.0[3].typ, FieldType::Number); + assert_eq!( + databases[0].tables.0[3].fields.0[4].alias, + String::from("uuid") + ); + assert_eq!( + databases[0].tables.0[3].fields.0[4].col_name, + String::from("uuid") + ); + assert!(databases[0].tables.0[3].fields.0[4].nullable); + assert!(databases[0].tables.0[3].fields.0[4].primary); + assert_eq!(databases[0].tables.0[3].fields.0[4].typ, FieldType::Text); + + assert_eq!(databases[0].tables.0[4].alias, String::from("newnote")); + assert_eq!( + databases[0].tables.0[4].table_name, + String::from("cloud_notes") + ); + assert_eq!(databases[0].tables.0[4].fields.0.len(), 5); + assert_eq!( + databases[0].tables.0[4].fields.0[0].alias, + String::from("attachments") + ); + assert_eq!( + databases[0].tables.0[4].fields.0[0].col_name, + String::from("attachments") + ); + assert!(databases[0].tables.0[4].fields.0[0].nullable); + assert!(!databases[0].tables.0[4].fields.0[0].primary); + assert_eq!(databases[0].tables.0[4].fields.0[0].typ, FieldType::Assets); + assert_eq!( + databases[0].tables.0[4].fields.0[1].alias, + String::from("data") + ); + assert_eq!( + databases[0].tables.0[4].fields.0[1].col_name, + String::from("data") + ); + assert!(databases[0].tables.0[4].fields.0[1].nullable); + assert!(!databases[0].tables.0[4].fields.0[1].primary); + assert_eq!(databases[0].tables.0[4].fields.0[1].typ, FieldType::Text); + assert_eq!( + databases[0].tables.0[4].fields.0[2].alias, + String::from("recycled") + ); + assert_eq!( + databases[0].tables.0[4].fields.0[2].col_name, + String::from("recycled") + ); + assert!(databases[0].tables.0[4].fields.0[2].nullable); + assert!(!databases[0].tables.0[4].fields.0[2].primary); + assert_eq!(databases[0].tables.0[4].fields.0[2].typ, FieldType::Bool); + assert_eq!( + databases[0].tables.0[4].fields.0[3].alias, + String::from("recycledTime") + ); + assert_eq!( + databases[0].tables.0[4].fields.0[3].col_name, + String::from("recycledTime") + ); + assert!(databases[0].tables.0[4].fields.0[3].nullable); + assert!(!databases[0].tables.0[4].fields.0[3].primary); + assert_eq!(databases[0].tables.0[4].fields.0[3].typ, FieldType::Number); + assert_eq!( + databases[0].tables.0[4].fields.0[4].alias, + String::from("uuid") + ); + assert_eq!( + databases[0].tables.0[4].fields.0[4].col_name, + String::from("uuid") + ); + assert!(databases[0].tables.0[4].fields.0[4].nullable); + assert!(databases[0].tables.0[4].fields.0[4].primary); + assert_eq!(databases[0].tables.0[4].fields.0[4].typ, FieldType::Text); + } + + /// UT test for connect_subscribe. + /// + /// # Title + /// ut_connect_subscribe + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Get subscribe info. + /// 3. Check if it is correct. + #[test] + fn ut_connect_subscribe() { + let user_id = 100; + let expiration = 1693488896462; + let bundle_name = String::from("com.huawei.hmos.notepad"); + let mut connect = Connect::new(user_id).unwrap(); + let app_schema = connect.get_app_schema(user_id, &bundle_name).unwrap(); + let databases = &app_schema.databases; + let result = connect.subscribe(expiration, &bundle_name, databases); + assert!(result.is_ok()); + } + + /// UT test for connect_unsubscribe. + /// + /// # Title + /// ut_connect_unsubscribe + /// + /// # Brief + /// 1. Create a `Connect` struct. + /// 2. Get unsubscribe info. + /// 3. Check if it is correct. + #[test] + fn ut_connect_unsubscribe() { + let user_id = 100; + let expiration = 1693488896462; + let bundle_name = String::from("com.huawei.hmos.notepad"); + let mut connect = Connect::new(user_id).unwrap(); + let app_schema = connect.get_app_schema(user_id, &bundle_name).unwrap(); + let databases = &app_schema.databases; + let result = connect + .subscribe(expiration, &bundle_name, databases) + .unwrap(); + + let mut unsubscribe_info = UnsubscriptionInfo::default(); + for info in result.0 .1 { + let mut temp = vec![]; + for (_, id) in &info.1 .0 { + temp.push(id.clone()); + } + unsubscribe_info.0.insert(info.0, temp); + } + let result = connect.unsubscribe(&unsubscribe_info); + assert!(result.is_ok()); + } } diff --git a/src/cloud_extension/src/ipc_conn/database.rs b/src/cloud_extension/src/ipc_conn/database.rs new file mode 100644 index 0000000000000000000000000000000000000000..dd37a85f126eb284e9630f1370eefb8785318b72 --- /dev/null +++ b/src/cloud_extension/src/ipc_conn/database.rs @@ -0,0 +1,954 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn::asset::{Asset, Assets}; +use crate::ipc_conn::connect::SchemaOrderTables; +use crate::ipc_conn::error::Error; +use crate::ipc_conn::ffi::ConnectService; +use crate::ipc_conn::function::CloudDB::{ + Delete, GenerateIds, Heartbeat, Insert, Lock, Query, Unlock, Update, +}; +use crate::ipc_conn::function::CloudServiceFunc::ConnectDatabase; +use crate::ipc_conn::{hash_map_raw_read, hash_map_raw_write, vec_raw_read, vec_raw_write}; +use ipc_rust::{ + BorrowedMsgParcel, CRemoteObject, Deserialize, IRemoteObj, IpcStatusCode, MsgParcel, RemoteObj, + Serialize, +}; +use std::collections::HashMap; + +#[derive(Default, Debug, PartialEq, Clone)] +pub(crate) struct Response { + pub(crate) time: i64, + pub(crate) device_name: String, + pub(crate) app_id: String, +} + +impl Serialize for Response { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + parcel.write(&self.time)?; + parcel.write(&self.device_name)?; + parcel.write(&self.app_id)?; + Ok(()) + } +} + +impl Deserialize for Response { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let time = parcel.read::()?; + let device_name = parcel.read::()?; + let app_id = parcel.read::()?; + + let result = Response { + time, + device_name, + app_id, + }; + Ok(result) + } +} + +pub(crate) enum Field { + Null, + Number(i64), + Real(f64), + Text(String), + Bool(bool), + Blob(Vec), + Asset(Asset), + Assets(Assets), +} + +impl From for u8 { + fn from(value: Field) -> Self { + match value { + Field::Null => 0, + Field::Number(_) => 1, + Field::Real(_) => 2, + Field::Text(_) => 3, + Field::Bool(_) => 4, + Field::Blob(_) => 5, + Field::Asset(_) => 6, + Field::Assets(_) => 7, + } + } +} + +impl Serialize for Field { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + match self { + Field::Null => parcel.write(&0_u32), + Field::Number(number) => { + parcel.write(&1_u32)?; + parcel.write(number) + } + Field::Real(real) => { + parcel.write(&2_u32)?; + parcel.write(real) + } + Field::Text(text) => { + parcel.write(&3_u32)?; + parcel.write(text) + } + Field::Bool(boolean) => { + parcel.write(&4_u32)?; + parcel.write(boolean) + } + Field::Blob(blob) => { + parcel.write(&5_u32)?; + parcel.write(blob) + } + Field::Asset(asset) => { + parcel.write(&6_u32)?; + parcel.write(asset) + } + Field::Assets(assets) => { + parcel.write(&7_u32)?; + parcel.write(assets) + } + } + } +} + +impl Deserialize for Field { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let index = parcel.read::()?; + match index { + 0 => Ok(Field::Null), + 1 => { + let number = parcel.read::()?; + Ok(Field::Number(number)) + } + 2 => { + let real = parcel.read::()?; + Ok(Field::Real(real)) + } + 3 => { + let text = parcel.read::()?; + Ok(Field::Text(text)) + } + 4 => { + let boolean = parcel.read::()?; + Ok(Field::Bool(boolean)) + } + 5 => { + let blob = parcel.read::>()?; + Ok(Field::Blob(blob)) + } + 6 => { + let asset = parcel.read::()?; + Ok(Field::Asset(asset)) + } + 7 => { + let assets = parcel.read::()?; + Ok(Field::Assets(assets)) + } + _ => Err(IpcStatusCode::Failed), + } + } +} + +#[derive(Default)] +pub(crate) struct ValueBucketData(pub(crate) HashMap); + +impl Serialize for ValueBucketData { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + hash_map_raw_write(parcel, &self.0) + } +} + +impl Deserialize for ValueBucketData { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = ValueBucketData(hash_map_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Default)] +pub(crate) struct ValueBucket { + pub(crate) id: String, + pub(crate) typ: String, + pub(crate) create_info: Response, + pub(crate) modified_info: Response, + pub(crate) data: ValueBucketData, + pub(crate) is_delete: bool, + pub(crate) version: u32, + pub(crate) is_new_create: bool, +} + +impl Serialize for ValueBucket { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + parcel.write(&self.id)?; + parcel.write(&self.typ)?; + parcel.write(&self.create_info)?; + parcel.write(&self.modified_info)?; + parcel.write(&self.data)?; + parcel.write(&self.is_delete)?; + parcel.write(&self.version)?; + parcel.write(&self.is_new_create)?; + Ok(()) + } +} + +impl Deserialize for ValueBucket { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let id = parcel.read::()?; + let typ = parcel.read::()?; + let create_info = parcel.read::()?; + let modified_info = parcel.read::()?; + let data = parcel.read::()?; + let is_delete = parcel.read::()?; + let version = parcel.read::()?; + let is_new_create = parcel.read::()?; + + let result = ValueBucket { + id, + typ, + create_info, + modified_info, + data, + is_delete, + version, + is_new_create, + }; + Ok(result) + } +} + +#[derive(Default)] +pub(crate) struct ValueBuckets(pub(crate) Vec); + +impl Serialize for ValueBuckets { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + vec_raw_write(parcel, &self.0) + } +} + +impl Deserialize for ValueBuckets { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = ValueBuckets(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Default)] +pub(crate) struct CloudData { + pub(crate) next_cursor: String, + pub(crate) has_more: bool, + pub(crate) values: ValueBuckets, +} + +impl Deserialize for CloudData { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let next_cursor = parcel.read::()?; + let has_more = parcel.read::()?; + let values = parcel.read::()?; + + let cloud_data = CloudData { + next_cursor, + has_more, + values, + }; + Ok(cloud_data) + } +} + +#[derive(Default)] +pub(crate) struct Database { + pub(crate) remote_obj: Option, + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) tables: SchemaOrderTables, + pub(crate) lock_session_id: String, +} + +impl Serialize for Database { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + parcel.write(&self.name)?; + parcel.write(&self.alias)?; + parcel.write(&self.tables)?; + parcel.write(&self.lock_session_id)?; + Ok(()) + } +} + +impl Deserialize for Database { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let name = parcel.read::()?; + let cloud_name = parcel.read::()?; + let tables = parcel.read::()?; + + let result = Database { + remote_obj: None, + name, + alias: cloud_name, + tables, + lock_session_id: "".to_string(), + }; + Ok(result) + } +} + +impl Database { + pub(crate) fn new(user_id: i32, bundle_name: &str) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(&bundle_name) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = ConnectDatabase as u32; + let remote_obj = + unsafe { RemoteObj::from_raw(ConnectService(user_id) as *mut CRemoteObject) } + .ok_or(Error::GetProxyObjectFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + let mut result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + result.remote_obj = Some(remote_obj); + Ok(result) + } + + pub(crate) fn generate_ids(&mut self, number: u32) -> Result, Error> { + #[derive(Default)] + struct Ids(Vec); + + impl Deserialize for Ids { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let ids = Ids(vec_raw_read(parcel)?); + Ok(ids) + } + } + + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(&number) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = GenerateIds as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let ids = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)? + .0; + Ok(ids) + } + + pub(crate) fn insert( + &mut self, + table: &str, + values: &ValueBuckets, + extends: &ValueBuckets, + ) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self.alias) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(values) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(extends) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Insert as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(result) + } + + pub(crate) fn update( + &mut self, + table: &str, + values: &ValueBuckets, + extends: &ValueBuckets, + ) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self.alias) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(values) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(extends) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Update as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(result) + } + + pub(crate) fn delete( + &mut self, + table: &str, + extends: &ValueBuckets, + ) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self.alias) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(extends) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Delete as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(result) + } + + pub(crate) fn query_values( + &mut self, + table: &str, + columns: &Vec, + result_limit: i32, + cursor: &str, + ) -> Result { + struct Columns(Vec); + + impl Serialize for Columns { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult<()> { + vec_raw_write(parcel, &self.0) + } + } + + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(table) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&self.alias) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(columns) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(&result_limit) + .map_err(|_| Error::WriteMsgParcelFailed)?; + msg_parcel + .write(cursor) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Query as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(result) + } + + pub(crate) fn lock(&mut self, interval: i32) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(&interval) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Lock as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let interval = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + self.lock_session_id = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + + Ok(interval) + } + + pub(crate) fn unlock(&self) -> Result<(), Error> { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(&self.lock_session_id) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Unlock as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let result = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + if !result { + return Err(Error::UnlockFailed); + } + + Ok(()) + } + + pub(crate) fn heartbeat(&self) -> Result { + let mut msg_parcel = MsgParcel::new().ok_or(Error::CreateMsgParcelFailed)?; + msg_parcel + .write(&self.lock_session_id) + .map_err(|_| Error::WriteMsgParcelFailed)?; + + let function_number = Heartbeat as u32; + let remote_obj = self + .remote_obj + .as_ref() + .ok_or(Error::CreateMsgParcelFailed)?; + let receive = remote_obj + .send_request(function_number, &msg_parcel, false) + .map_err(|_| Error::SendRequestFailed)?; + + let interval = receive + .read::() + .map_err(|_| Error::ReadMsgParcelFailed)?; + Ok(interval) + } +} + +#[cfg(all(test))] +mod test { + use crate::ipc_conn::database::ValueBucketData; + use crate::ipc_conn::{ + Asset, Assets, CloudData, Database, Field, Response, SchemaOrderTables, ValueBucket, + ValueBuckets, + }; + use ipc_rust::MsgParcel; + + /// UT test for response_serialize. + /// + /// # Title + /// ut_response_serialize + /// + /// # Brief + /// 1. Create a `Response` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_response_serialize() { + let response = Response::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&response).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 0); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + } + + /// UT test for response_deserialize. + /// + /// # Title + /// ut_response_deserialize + /// + /// # Brief + /// 1. Create a `Response` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Response`. + /// 5. Check if it is correct. + #[test] + fn ut_response_deserialize() { + let response = Response::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&response).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), response); + } + + /// UT test for field_serialize. + /// + /// # Title + /// ut_field_serialize + /// + /// # Brief + /// 1. Create a `Field` enum. + /// 2. Serialize the enum. + /// 3. Check if it is correct. + #[test] + fn ut_field_serialize() { + let null = Field::Null; + let number = Field::Number(1); + let real = Field::Real(2_f64); + let text = Field::Text(String::from("text")); + let boolean = Field::Bool(true); + let blob = Field::Blob(vec![0]); + let asset = Field::Asset(Asset::default()); + let assets = Field::Assets(Assets::default()); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&null).unwrap(); + msg_parcel.write(&number).unwrap(); + msg_parcel.write(&real).unwrap(); + msg_parcel.write(&text).unwrap(); + msg_parcel.write(&boolean).unwrap(); + msg_parcel.write(&blob).unwrap(); + msg_parcel.write(&asset).unwrap(); + msg_parcel.write(&assets).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 0); + assert_eq!(msg_parcel.read::().unwrap(), 1); + assert_eq!(msg_parcel.read::().unwrap(), 2_f64); + assert_eq!(msg_parcel.read::().unwrap(), String::from("text")); + assert!(msg_parcel.read::().unwrap()); + assert_eq!(msg_parcel.read::>().unwrap(), vec![0]); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for field_deserialize. + /// + /// # Title + /// ut_field_deserialize + /// + /// # Brief + /// 1. Create a `Field` enum. + /// 2. Serialize the enum. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Field`. + /// 5. Check if it is correct. + #[test] + fn ut_field_deserialize() { + let null = Field::Null; + let number = Field::Number(1); + let real = Field::Real(2_f64); + let text = Field::Text(String::from("text")); + let boolean = Field::Bool(true); + let blob = Field::Blob(vec![0]); + let asset = Field::Asset(Asset::default()); + let assets = Field::Assets(Assets::default()); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&null).unwrap(); + msg_parcel.write(&number).unwrap(); + msg_parcel.write(&real).unwrap(); + msg_parcel.write(&text).unwrap(); + msg_parcel.write(&boolean).unwrap(); + msg_parcel.write(&blob).unwrap(); + msg_parcel.write(&asset).unwrap(); + msg_parcel.write(&assets).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for value_bucket_data_serialize. + /// + /// # Title + /// ut_value_bucket_data_serialize + /// + /// # Brief + /// 1. Create a `ValueBucketData` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_value_bucket_data_serialize() { + let mut value_bucket_data = ValueBucketData::default(); + value_bucket_data + .0 + .insert(String::from("key1"), Field::Null); + value_bucket_data + .0 + .insert(String::from("key2"), Field::Number(1)); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_bucket_data).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 2); + assert_eq!(msg_parcel.read::().unwrap(), String::from("key1")); + assert!(msg_parcel.read::().is_ok()); + assert_eq!(msg_parcel.read::().unwrap(), String::from("key2")); + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for value_bucket_data_deserialize. + /// + /// # Title + /// ut_value_bucket_data_deserialize + /// + /// # Brief + /// 1. Create a `ValueBucketData` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `ValueBucketData`. + /// 5. Check if it is correct. + #[test] + fn ut_value_bucket_data_deserialize() { + let mut value_bucket_data = ValueBucketData::default(); + value_bucket_data + .0 + .insert(String::from("key1"), Field::Null); + value_bucket_data + .0 + .insert(String::from("key2"), Field::Number(1)); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_bucket_data).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for value_bucket_serialize. + /// + /// # Title + /// ut_value_bucket_serialize + /// + /// # Brief + /// 1. Create a `ValueBucket` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_value_bucket_serialize() { + let value_bucket = ValueBucket::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_bucket).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), Response::default()); + assert_eq!(msg_parcel.read::().unwrap(), Response::default()); + assert!(msg_parcel.read::().is_ok()); + assert!(!msg_parcel.read::().unwrap()); + assert_eq!(msg_parcel.read::().unwrap(), 0_u32); + assert!(!msg_parcel.read::().unwrap()); + } + + /// UT test for value_bucket_deserialize. + /// + /// # Title + /// ut_value_bucket_deserialize + /// + /// # Brief + /// 1. Create a `ValueBucket` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `ValueBucket`. + /// 5. Check if it is correct. + #[test] + fn ut_value_bucket_deserialize() { + let value_bucket = ValueBucket::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_bucket).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for value_buckets_serialize. + /// + /// # Title + /// ut_value_buckets_serialize + /// + /// # Brief + /// 1. Create a `ValueBuckets` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_value_buckets_serialize() { + let mut value_buckets = ValueBuckets::default(); + let value_bucket_one = ValueBucket::default(); + let value_bucket_two = ValueBucket::default(); + value_buckets.0.push(value_bucket_one); + value_buckets.0.push(value_bucket_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_buckets).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), 2); + for _ in 0..2 { + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), Response::default()); + assert_eq!(msg_parcel.read::().unwrap(), Response::default()); + assert!(msg_parcel.read::().is_ok()); + assert!(!msg_parcel.read::().unwrap()); + assert_eq!(msg_parcel.read::().unwrap(), 0_u32); + assert!(!msg_parcel.read::().unwrap()); + } + } + + /// UT test for value_buckets_deserialize. + /// + /// # Title + /// ut_value_buckets_deserialize + /// + /// # Brief + /// 1. Create a `ValueBuckets` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `ValueBuckets`. + /// 5. Check if it is correct. + #[test] + fn ut_value_buckets_deserialize() { + let mut value_buckets = ValueBuckets::default(); + let value_bucket_one = ValueBucket::default(); + let value_bucket_two = ValueBucket::default(); + value_buckets.0.push(value_bucket_one); + value_buckets.0.push(value_bucket_two); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&value_buckets).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for cloud_data_deserialize. + /// + /// # Title + /// ut_cloud_data_deserialize + /// + /// # Brief + /// 1. Create a `CloudData` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `CloudData`. + /// 5. Check if it is correct. + #[test] + fn ut_cloud_data_deserialize() { + let next_cursor = String::from(""); + let has_more = false; + let values = ValueBuckets::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&next_cursor).unwrap(); + msg_parcel.write(&has_more).unwrap(); + msg_parcel.write(&values).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for database_serialize. + /// + /// # Title + /// ut_database_serialize + /// + /// # Brief + /// 1. Create a `Database` struct. + /// 2. Serialize the struct. + /// 3. Check if it is correct. + #[test] + fn ut_database_serialize() { + let database = Database::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&database).unwrap(); + + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + assert_eq!( + msg_parcel.read::().unwrap(), + SchemaOrderTables::default() + ); + assert_eq!(msg_parcel.read::().unwrap(), String::from("")); + } + + /// UT test for database_deserialize. + /// + /// # Title + /// ut_database_deserialize + /// + /// # Brief + /// 1. Create a `Database` struct. + /// 2. Serialize the struct. + /// 3. Read the data in `MsgParcel`. + /// 4. Deserialize the data to `Database`. + /// 5. Check if it is correct. + #[test] + fn ut_database_deserialize() { + let database = Database::default(); + + let mut msg_parcel = MsgParcel::new().unwrap(); + msg_parcel.write(&database).unwrap(); + + assert!(msg_parcel.read::().is_ok()); + } + + /// UT test for database_new. + /// + /// # Title + /// ut_database_new + /// + /// # Brief + /// 1. Create a `Database` struct. + /// 2. Check if it is correct. + #[test] + fn ut_database_new() { + let user_id = 100; + let bundle_name = String::from("com.huawei.hmos.notepad"); + let database = Database::new(user_id, &bundle_name); + assert!(database.is_ok()); + } +} diff --git a/src/cloud_extension/src/ipc_conn/error.rs b/src/cloud_extension/src/ipc_conn/error.rs index 8b137891791fe96927ad78e64b0aad7bded08bdc..2f642a92c84fe1a67d1aabf161a4e09c8f23be99 100644 --- a/src/cloud_extension/src/ipc_conn/error.rs +++ b/src/cloud_extension/src/ipc_conn/error.rs @@ -1 +1,97 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::ipc_conn::vec_raw_read; +use ipc_rust::{BorrowedMsgParcel, Deserialize, IpcStatusCode}; +use std::fmt::{Debug, Display, Formatter}; + +#[derive(Default, Debug)] +pub(crate) struct Errors(pub(crate) Vec); + +impl Deserialize for Errors { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let result = Errors(vec_raw_read::(parcel)?); + Ok(result) + } +} + +#[derive(Debug)] +/// Indicates IPC connection and data acquisition errors. +pub enum Error { + /// Creates MsgParcel Failed. + CreateMsgParcelFailed, + + /// Writes MsgParcel Failed. + WriteMsgParcelFailed, + + /// Reads MsgParcel Failed. + ReadMsgParcelFailed, + + /// Gets Proxy Object Failed. + GetProxyObjectFailed, + + /// Sends Request Failed. + SendRequestFailed, + + /// Unlock Failed. + UnlockFailed, + + /// Invalid Cloud Status. + InvalidCloudStatus, + + /// Invalid Space Status. + InvalidSpaceStatus, + + /// Downloads Failed. + DownloadFailed, + + /// Uploads Failed. + UploadFailed, +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Error::CreateMsgParcelFailed => write!(f, "Creates MsgParcel Failed"), + Error::WriteMsgParcelFailed => write!(f, "Writes MsgParcel Failed"), + Error::ReadMsgParcelFailed => write!(f, "Reads MsgParcel Failed"), + Error::GetProxyObjectFailed => write!(f, "Gets Proxy Object Failed"), + Error::SendRequestFailed => write!(f, "Sends Request Failed"), + Error::UnlockFailed => write!(f, "Unlock failed"), + Error::InvalidCloudStatus => write!(f, "Invalid Cloud Status"), + Error::InvalidSpaceStatus => write!(f, "Invalid Space Status"), + Error::DownloadFailed => write!(f, "Downloads Failed"), + Error::UploadFailed => write!(f, "Uploads Failed"), + } + } +} + +impl std::error::Error for Error {} + +impl Deserialize for Error { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> ipc_rust::IpcResult { + let index = parcel.read::()?; + match index { + 0 => Ok(Error::CreateMsgParcelFailed), + 1 => Ok(Error::WriteMsgParcelFailed), + 2 => Ok(Error::ReadMsgParcelFailed), + 3 => Ok(Error::GetProxyObjectFailed), + 4 => Ok(Error::SendRequestFailed), + 5 => Ok(Error::InvalidCloudStatus), + 6 => Ok(Error::InvalidSpaceStatus), + 7 => Ok(Error::DownloadFailed), + 8 => Ok(Error::UploadFailed), + _ => Err(IpcStatusCode::Failed), + } + } +} diff --git a/src/cloud_extension/src/ipc_conn/ffi.rs b/src/cloud_extension/src/ipc_conn/ffi.rs new file mode 100644 index 0000000000000000000000000000000000000000..e36766f7a35e118d00b1dd11488218af62ee48f7 --- /dev/null +++ b/src/cloud_extension/src/ipc_conn/ffi.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::ffi::{c_int, c_void}; + +#[link(name = "conn_adapter.z")] +extern "C" { + pub(crate) fn ConnectService(user_id: c_int) -> *mut c_void; +} diff --git a/src/cloud_extension/src/ipc_conn/function.rs b/src/cloud_extension/src/ipc_conn/function.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a8dff166d5c7c8774b6e198be59d808eed693c0 --- /dev/null +++ b/src/cloud_extension/src/ipc_conn/function.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Used to correspond to the cloud function code. +pub(crate) enum CloudDB { + GenerateIds, + Insert, + Update, + Delete, + Query, + Lock, + Unlock, + Heartbeat, +} + +pub(crate) enum AssetLoaderFunc { + ConnectAssetLoader, + Download, + Upload, +} + +pub(crate) enum CloudServiceFunc { + ConnectDatabase, + GetAppBriefInfo, + GetServiceInfo, + GetAppSchema, + Subscribe, + Unsubscribe, +} diff --git a/src/cloud_extension/src/ipc_conn/mod.rs b/src/cloud_extension/src/ipc_conn/mod.rs index 262d2e78664b192d3b07e26f0b6fdf1f14b72a9b..849a586290a7962756661e0e400c451e70717307 100644 --- a/src/cloud_extension/src/ipc_conn/mod.rs +++ b/src/cloud_extension/src/ipc_conn/mod.rs @@ -11,5 +11,188 @@ // See the License for the specific language governing permissions and // limitations under the License. +mod asset; mod connect; +mod database; mod error; +mod ffi; +mod function; + +use ipc_rust::{BorrowedMsgParcel, Deserialize, Serialize}; +use std::collections::HashMap; +use std::hash::Hash; + +pub(crate) use asset::{Asset, Assets, OperationType}; +pub(crate) use connect::{ + AppInfo, CloudStatus, Connect, Databases, OrderTable, Schema, SchemaOrderTables, ServiceInfo, + UnsubscriptionInfo, +}; +pub(crate) use database::{ + CloudData, Database, Field, Response, ValueBucket, ValueBucketData, ValueBuckets, +}; +pub(crate) use error::Errors; + +/// Error message at the ipc. +pub use error::Error; + +pub(crate) fn hash_map_raw_write( + msg_parcel: &mut BorrowedMsgParcel<'_>, + hash_map: &HashMap, +) -> ipc_rust::IpcResult<()> { + msg_parcel.write(&(hash_map.len() as i32))?; + for (key, value) in hash_map { + msg_parcel.write(key)?; + msg_parcel.write(value)?; + } + Ok(()) +} + +pub(crate) fn hash_map_raw_read( + msg_parcel: &BorrowedMsgParcel<'_>, +) -> ipc_rust::IpcResult> { + let length = msg_parcel.read::()?; + let mut hash_map = HashMap::new(); + for _ in 0..length { + let key = msg_parcel.read::()?; + let value = msg_parcel.read::()?; + hash_map.insert(key, value); + } + Ok(hash_map) +} + +pub(crate) fn vec_raw_write( + msg_parcel: &mut BorrowedMsgParcel<'_>, + vector: &Vec, +) -> ipc_rust::IpcResult<()> { + msg_parcel.write(&(vector.len() as u32))?; + for value in vector { + msg_parcel.write(value)?; + } + Ok(()) +} + +pub(crate) fn vec_raw_read( + msg_parcel: &BorrowedMsgParcel<'_>, +) -> ipc_rust::IpcResult> { + let length = msg_parcel.read::()? as usize; + let mut vector = Vec::with_capacity(length); + for _ in 0..length { + let value = msg_parcel.read::()?; + vector.push(value); + } + Ok(vector) +} + +#[cfg(all(test))] +mod test { + use crate::ipc_conn::{hash_map_raw_read, hash_map_raw_write, vec_raw_read, vec_raw_write}; + use ipc_rust::MsgParcel; + use std::collections::HashMap; + + /// UT test for hash_map_raw_write. + /// + /// # Title + /// ut_hash_map_raw_write + /// + /// # Brief + /// 1. Create a `HashMap` and `MsgParcel` struct. + /// 2. Write the data in the `HashMap` to the `MsgParcel`. + /// 3. Read the data and check if it is correct. + #[test] + fn ut_hash_map_raw_write() { + let mut hashmap = HashMap::new(); + hashmap.insert(String::from("key1"), String::from("value1")); + hashmap.insert(String::from("key2"), String::from("value2")); + hashmap.insert(String::from("key3"), String::from("value3")); + + let mut msg_parcel = MsgParcel::new().unwrap(); + let mut parcel = msg_parcel.borrowed(); + assert!(hash_map_raw_write(&mut parcel, &hashmap).is_ok()); + + assert_eq!(parcel.read::().unwrap(), 3); + assert_eq!(parcel.read::().unwrap(), String::from("key1")); + assert_eq!(parcel.read::().unwrap(), String::from("value1")); + assert_eq!(parcel.read::().unwrap(), String::from("key2")); + assert_eq!(parcel.read::().unwrap(), String::from("value2")); + assert_eq!(parcel.read::().unwrap(), String::from("key3")); + assert_eq!(parcel.read::().unwrap(), String::from("value3")); + } + + /// UT test for hash_map_raw_read. + /// + /// # Title + /// ut_hash_map_raw_read + /// + /// # Brief + /// 1. Create a `HashMap` and `MsgParcel` struct. + /// 2. Write the data in the `HashMap` to the `MsgParcel`. + /// 3. Read the data in the `MsgParcel` to the `HashMap`. + /// 4. Check if it is correct. + #[test] + fn ut_hash_map_raw_read() { + let mut hashmap = HashMap::new(); + hashmap.insert(String::from("key1"), String::from("value1")); + hashmap.insert(String::from("key2"), String::from("value2")); + hashmap.insert(String::from("key3"), String::from("value3")); + + let mut msg_parcel = MsgParcel::new().unwrap(); + let mut parcel = msg_parcel.borrowed(); + assert!(hash_map_raw_write(&mut parcel, &hashmap).is_ok()); + + let result = hash_map_raw_read::(&parcel).unwrap(); + assert_eq!(hashmap, result); + } + + /// UT test for vec_raw_write. + /// + /// # Title + /// ut_vec_raw_write + /// + /// # Brief + /// 1. Create a `Vec` and `MsgParcel` struct. + /// 2. Write the data in the `Vec` to the `MsgParcel`. + /// 3. Read the data and check if it is correct. + #[test] + fn ut_vec_raw_write() { + let vector = vec![ + String::from("value1"), + String::from("value2"), + String::from("value3"), + ]; + + let mut msg_parcel = MsgParcel::new().unwrap(); + let mut parcel = msg_parcel.borrowed(); + assert!(vec_raw_write(&mut parcel, &vector).is_ok()); + + assert_eq!(parcel.read::().unwrap(), 3); + assert_eq!(parcel.read::().unwrap(), String::from("value1")); + assert_eq!(parcel.read::().unwrap(), String::from("value2")); + assert_eq!(parcel.read::().unwrap(), String::from("value3")); + } + + /// UT test for vec_raw_read. + /// + /// # Title + /// ut_vec_raw_read + /// + /// # Brief + /// 1. Create a `Vec` and `MsgParcel` struct. + /// 2. Write the data in the `Vec` to the `MsgParcel`. + /// 3. Read the data in the `MsgParcel` to the `Vec`. + /// 4. Check if it is correct. + #[test] + fn ut_vec_raw_read() { + let vector = vec![ + String::from("value1"), + String::from("value2"), + String::from("value3"), + ]; + + let mut msg_parcel = MsgParcel::new().unwrap(); + let mut parcel = msg_parcel.borrowed(); + assert!(vec_raw_write(&mut parcel, &vector).is_ok()); + + let result = vec_raw_read::(&parcel).unwrap(); + assert_eq!(vector, result); + } +} diff --git a/src/cloud_extension/src/lib.rs b/src/cloud_extension/src/lib.rs index d7159e1fc09d17696da94793387b2c95611be69d..4541e8bb57a67a2a53647f6d07262757808c27b0 100644 --- a/src/cloud_extension/src/lib.rs +++ b/src/cloud_extension/src/lib.rs @@ -1,9 +1,26 @@ -#[cfg(feature = "c_adapter")] +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Cloud Service Synchronization Crate, implemented in Rust. This crate is used as bridge between +//! upper C++ apk and JS cloud service. C++ will call C-ffi of Rust, and Rust will use IPC to +//! connect to JS. + +/// Module of C FFI adapter. pub mod c_adapter; -mod ipc_conn; -mod service_impl; -pub use service_impl::*; +/// Module of IPC Conn Serialization and Deserialization. +pub mod ipc_conn; -// TODO: 可能改名 -pub use ipc_conn::IpcConfig; +/// Module of Cloud Service Synchronization, providing structs of AssetLoader, CloudDatabase, +/// CloudServer and relating structs and functions. +pub mod service_impl; diff --git a/src/cloud_extension/src/service_impl/asset_loader.rs b/src/cloud_extension/src/service_impl/asset_loader.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d358c2c0d2f59a5f0668947c7d16621765c2727 100644 --- a/src/cloud_extension/src/service_impl/asset_loader.rs +++ b/src/cloud_extension/src/service_impl/asset_loader.rs @@ -0,0 +1,306 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn; +use crate::ipc_conn::OperationType; +use crate::service_impl::error::SyncError; +use crate::service_impl::types::{Database, RawValueBucket, Table, Value}; +use std::collections::HashMap; + +/// Asset loader struct. +pub struct AssetLoader<'a> { + tables: &'a HashMap, + user_id: i32, + // Keep these two fields below for future extension. + #[allow(unused)] + bundle_name: String, +} + +impl<'a> AssetLoader<'a> { + /// Initialize an AssetLoader instance from user id, bundle name, and database. + /// Database can be obtained from CloudInfo and CloudServer. + #[inline] + pub fn new(user_id: i32, bundle_name: &str, db: &'a Database) -> Self { + AssetLoader { + tables: &db.tables, + user_id, + bundle_name: bundle_name.to_string(), + } + } + + /// Take in table name, gid, prefix, and assets, send upload request to the other side of IPC + /// connection. Necessary information will be updated in the parameter assets. The cloud storage + /// will ask for files from source paths, and this function only returns result and relating infos. + pub fn upload( + &self, + table_name: &str, + gid: &str, + prefix: &str, + assets: &mut RawValueBucket, + ) -> Result<(), SyncError> { + self.upload_download_inner(table_name, gid, prefix, assets, ipc_conn::Asset::upload) + } + + /// Take in table name, gid, prefix, and assets, send download request to the other side of IPC + /// connection. Necessary information will be updated in the parameter assets. The cloud storage + /// will send files to target paths, and this function only returns result and relating infos. + pub fn download( + &self, + table_name: &str, + gid: &str, + prefix: &str, + assets: &mut RawValueBucket, + ) -> Result<(), SyncError> { + self.upload_download_inner(table_name, gid, prefix, assets, ipc_conn::Asset::download) + } + + /// Remove local file according to the path in the asset passed in. + pub fn remove_local_assets(asset: &Asset) -> Result<(), SyncError> { + std::fs::remove_file(&asset.local_path)?; + Ok(()) + } + + fn upload_download_inner( + &self, + table_name: &str, + gid: &str, + prefix: &str, + assets: &mut RawValueBucket, + mut f: F, + ) -> Result<(), SyncError> + where + F: FnMut(&ipc_conn::Asset, &str) -> Result<(), ipc_conn::Error>, + { + let mut ret = Ok(()); + for value in assets.values_mut() { + match value { + Value::Assets(some_asset) => { + for asset in some_asset { + if asset.status == AssetStatus::Delete { + AssetLoader::remove_local_assets(asset)?; + } + let alias = self.get_table_alias(table_name); + let mut asset_ipc = asset.as_ipc_asset(self.user_id)?; + asset_ipc.prefix = prefix.to_string(); + asset_ipc.alias = alias.to_string(); + asset_ipc.gid = gid.to_string(); + match f(&asset_ipc, alias) { + Ok(_) => asset.status = AssetStatus::Normal, + Err(_) => { + asset.status = AssetStatus::Abnormal; + ret = Err(SyncError::AssetDownloadFailure); + } + } + } + } + _ => return Err(SyncError::NotAnAsset), + } + } + ret + } + + fn get_table_alias(&self, table_name: &str) -> &str { + match self.tables.get(table_name) { + None => "", + Some(t) => t.alias.as_str(), + } + } +} + +/// Asset struct storing relating information to upload and download assets. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Asset { + pub(crate) status: AssetStatus, + pub(crate) id: String, + pub(crate) name: String, + pub(crate) uri: String, + pub(crate) local_path: String, + pub(crate) create_time: String, + pub(crate) modify_time: String, + pub(crate) size: String, + pub(crate) hash: String, +} + +impl Default for Asset { + fn default() -> Self { + Asset { + status: AssetStatus::Unknown, + id: "".to_string(), + name: "".to_string(), + uri: "".to_string(), + local_path: "".to_string(), + create_time: "".to_string(), + modify_time: "".to_string(), + size: "".to_string(), + hash: "".to_string(), + } + } +} + +impl Asset { + /// Get Asset id. + pub fn id(&self) -> &str { + &self.id + } + + /// Get Asset name. + pub fn name(&self) -> &str { + &self.name + } + + /// Get Asset uri, a real path pointing to the Asset. + pub fn uri(&self) -> &str { + &self.uri + } + + /// Get Asset local path relative to the application sandbox. + pub fn local_path(&self) -> &str { + &self.local_path + } + + /// Get create time of this Asset. + pub fn create_time(&self) -> &str { + &self.create_time + } + + /// Get modify time of this Asset. + pub fn modify_time(&self) -> &str { + &self.modify_time + } + + /// Get size of this Asset in kb. + pub fn size(&self) -> &str { + &self.size + } + + /// Get hash value of this Asset. + pub fn hash(&self) -> &str { + &self.hash + } + + /// Set Asset id. + pub fn set_id(&mut self, id: String) { + self.id = id; + } + + /// Set Asset name. + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + /// Set Asset uri, a real path pointing to the Asset. + pub fn set_uri(&mut self, uri: String) { + self.uri = uri; + } + + /// Set Asset local path relative to the application sandbox. + pub fn set_local_path(&mut self, local_path: String) { + self.local_path = local_path; + } + + /// Set create time of this Asset. + pub fn set_create_time(&mut self, create_time: String) { + self.create_time = create_time; + } + + /// Set modify time of this Asset. + pub fn set_modify_time(&mut self, modify_time: String) { + self.modify_time = modify_time; + } + + /// Set size of this Asset in kb. + pub fn set_size(&mut self, size: String) { + self.size = size; + } + + /// Set hash value of this Asset. + pub fn set_hash(&mut self, hash: String) { + self.hash = hash; + } + + pub(crate) fn as_ipc_asset(&self, user_id: i32) -> Result { + let mut asset = ipc_conn::Asset::new(user_id)?; + asset.uri = self.uri.to_string(); + asset.asset_name = self.name.clone(); + asset.operation_type = self.status.as_oper_type(); + asset.hash = self.hash.clone(); + asset.asset_id = self.id.to_string(); + asset.sub_path = self.local_path.to_string(); + Ok(asset) + } +} + +impl From<&ipc_conn::Asset> for Asset { + fn from(value: &ipc_conn::Asset) -> Self { + Asset { + status: AssetStatus::from(&value.operation_type), + id: value.asset_id.clone(), + name: value.asset_name.clone(), + uri: value.uri.clone(), + local_path: value.sub_path.clone(), + hash: value.hash.clone(), + ..Default::default() + } + } +} + +/// Struct that mark status of an Asset. +#[repr(C)] +#[derive(Eq, PartialEq, Clone, Debug)] +pub enum AssetStatus { + /// Unknown status + Unknown, + + /// Normal. Everything works fine for the asset. + Normal, + + /// The asset is about to be inserted. + Insert, + + /// The asset is about to be updated. + Update, + + /// The asset is about to be deleted. + Delete, + + /// The asset is abnormal and something is wrong with it. + Abnormal, + + /// The asset is being downloading. + Downloading, + + /// The asset is butt. + Butt, +} + +impl AssetStatus { + pub(crate) fn as_oper_type(&self) -> OperationType { + match self { + AssetStatus::Delete => OperationType::Delete, + AssetStatus::Insert => OperationType::Add, + AssetStatus::Update => OperationType::Update, + _ => OperationType::None, + } + } +} + +impl From<&OperationType> for AssetStatus { + fn from(value: &OperationType) -> Self { + match value { + OperationType::Delete => AssetStatus::Delete, + OperationType::Add => AssetStatus::Insert, + OperationType::Update => AssetStatus::Update, + OperationType::None => AssetStatus::Unknown, + } + } +} diff --git a/src/cloud_extension/src/service_impl/c_ffi.rs b/src/cloud_extension/src/service_impl/c_ffi.rs new file mode 100644 index 0000000000000000000000000000000000000000..08cbfcd3851725fd8bc76778246d4a72b38ce4d0 --- /dev/null +++ b/src/cloud_extension/src/service_impl/c_ffi.rs @@ -0,0 +1,16 @@ +use std::collections::HashMap; + +#[repr(C)] +pub struct Relation { + id: String, + bundle_name: String, + pub(crate) relations: HashMap, +} + +#[repr(C)] +pub struct SubscriptionResult { + expiration_time: u64, + id: String, +} + +// impl From<&> diff --git a/src/cloud_extension/src/service_impl/cloud_db.rs b/src/cloud_extension/src/service_impl/cloud_db.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..be2681e9b4c2e3a71db8360292cc0a7f387b103e 100644 --- a/src/cloud_extension/src/service_impl/cloud_db.rs +++ b/src/cloud_extension/src/service_impl/cloud_db.rs @@ -0,0 +1,448 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn; +use crate::service_impl::error::SyncError; +use crate::service_impl::types::{ + Database, RawValueBucket, Value, CREATE_FIELD, GID_FIELD, MODIFY_FIELD, +}; +use std::collections::HashMap; + +const LOCK_EXPIRE: i32 = 60 * 60; + +/// Struct of CloudDatabase +pub struct CloudDatabase { + user_id: i32, + database_ipc: ipc_conn::Database, + database: Database, +} + +impl CloudDatabase { + /// Initialize a CloudDatabase with user id and database. This function will send IPC request + /// with user id to get relating cloud database information. + pub fn new( + user_id: i32, + bundle_name: &str, + database: Database, + ) -> Result { + let database_ipc = ipc_conn::Database::new(user_id, bundle_name)?; + Ok(CloudDatabase { + user_id, + database_ipc, + database, + }) + } + + /// Execute a sql on the cloud database. Unsupported currently. + pub fn execute( + &mut self, + _table: &str, + _sql: &str, + _extend: &RawValueBucket, + ) -> Result<(), SyncError> { + Err(SyncError::Unsupported) + } + + /// Insert a batch of value buckets into the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_insert( + &mut self, + table: &str, + values: &[RawValueBucket], + extends: &mut Vec, + ) -> Result<(), SyncError> { + let ids = self.database_ipc.generate_ids(values.len() as u32)?; + + for (i, id) in ids.iter().enumerate() { + if i < extends.len() { + extends[i].insert(GID_FIELD.to_string(), Value::String(id.to_string())); + } else { + let mut vb = HashMap::new(); + vb.insert(GID_FIELD.to_string(), Value::String(id.to_string())); + extends.push(vb); + } + } + + let ret = self.upload(table, values, extends, true, ipc_conn::Database::insert)?; + self.write_back(extends, ret); + Ok(()) + } + + /// Update a batch of value buckets in the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_update( + &mut self, + table: &str, + values: &[RawValueBucket], + extends: &mut [RawValueBucket], + ) -> Result<(), SyncError> { + let ret = self.upload(table, values, extends, false, ipc_conn::Database::update)?; + self.write_back(extends, ret); + Ok(()) + } + + /// Delete a batch of value buckets from the cloud database, with specific target table name. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_delete( + &mut self, + table: &str, + extends: &[RawValueBucket], + ) -> Result<(), SyncError> { + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut records = vec![]; + for ext in extends.iter() { + match ext.get(GID_FIELD) { + Some(Value::String(gid)) => { + let modify_field = match ext.get(MODIFY_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let modified_response = ipc_conn::Response { + time: modify_field, + ..Default::default() + }; + + let record_ipc = ipc_conn::ValueBucket { + id: gid.clone(), + typ: alias.clone(), + modified_info: modified_response, + is_delete: true, + is_new_create: false, + ..Default::default() + }; + + records.push(record_ipc); + } + _ => continue, + } + } + let extends = ipc_conn::ValueBuckets(records); + self.database_ipc.delete(alias, &extends)?; + Ok(()) + } + } + } + + /// Query value buckets from the cloud database, with specific target table name. Return with + /// the corresponding Cursor and ValueBuckets. + /// + /// Values and extends are all value buckets from the users, but for the convenience, split + /// them in parameters. Extends will also be changed to store information and results from + /// the insertion action. + pub fn batch_query(&mut self, table: &str, cursor: &str) -> Result { + const QUERY_LIMIT: usize = 10; + + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut fields = vec![]; + for field in &t.fields { + fields.push(field.alias.clone()); + } + let ret = + self.database_ipc + .query_values(alias, &fields, QUERY_LIMIT as i32, cursor)?; + Ok(ret.into()) + } + } + } + + /// Send lock request to the other end through IPC message, so that users can get exclusive + /// access to the database. + /// + /// Return err if that fails. + pub fn lock(&mut self) -> Result { + if !self.database_ipc.lock_session_id.is_empty() { + return Ok(0); + } + let int = self.database_ipc.lock(LOCK_EXPIRE)?; + Ok(int) + } + + /// Send unlock request to the other end through IPC message, so that users can release exclusive + /// access to the database, and others can use it. + /// + /// Return err if that fails. + pub fn unlock(&mut self) -> Result<(), SyncError> { + if self.database_ipc.lock_session_id.is_empty() { + return Err(SyncError::SessionUnlocked); + } + self.database_ipc.unlock()?; + Ok(()) + } + + /// Send heartbeat to the other end through IPC message, so that the users can prolong exclusive + /// access to the database. If no action is taken, this access will expire after some time (by + /// default, 60 min). + pub fn heartbeat(&mut self) -> Result<(), SyncError> { + if self.database_ipc.lock_session_id.is_empty() { + return Err(SyncError::SessionUnlocked); + } + self.database_ipc.heartbeat()?; + Ok(()) + } + + fn upload( + &mut self, + table: &str, + values: &[RawValueBucket], + extends: &mut [RawValueBucket], + is_new: bool, + mut f: F, + ) -> Result, SyncError> + where + F: FnMut( + &mut ipc_conn::Database, + &str, + &ipc_conn::ValueBuckets, + &ipc_conn::ValueBuckets, + ) -> Result, + { + let mut has_asset = false; + match self.database.tables.get(table) { + None => Err(SyncError::NoSuchTableInDb), + Some(t) => { + let alias = &t.alias; + let mut values_records = vec![]; + let mut extends_records = vec![]; + for (bucket, ext) in values.iter().zip(extends.iter_mut()) { + match ext.get(GID_FIELD) { + Some(Value::String(gid)) => { + let mut value_ipc = ipc_conn::ValueBucket { + id: gid.clone(), + typ: alias.clone(), + is_delete: false, + is_new_create: is_new, + ..Default::default() + }; + + let create_field = match ext.get(CREATE_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let create_response = ipc_conn::Response { + time: create_field, + ..Default::default() + }; + let modify_field = match ext.get(MODIFY_FIELD) { + Some(Value::Int(i)) => *i, + _ => 0, + }; + let modified_response = ipc_conn::Response { + time: modify_field, + ..Default::default() + }; + let ext_ipc = ipc_conn::ValueBucket { + create_info: create_response, + modified_info: modified_response, + ..Default::default() + }; + + let mut record_data = HashMap::default(); + for (key, value) in bucket { + if value.has_asset() { + has_asset = true; + ext.insert(key.to_string(), Value::Empty); + } + let field = value.as_record_field_ipc(self.user_id)?; + record_data.insert(key.clone(), field); + } + value_ipc.data = ipc_conn::ValueBucketData(record_data); + values_records.push(value_ipc); + extends_records.push(ext_ipc); + } + _ => continue, + } + } + let value_raw_vb = ipc_conn::ValueBuckets(values_records); + let extend_raw_vb = ipc_conn::ValueBuckets(extends_records); + let ret = f(&mut self.database_ipc, alias, &value_raw_vb, &extend_raw_vb)?; + if has_asset { + Ok(Some(ret)) + } else { + Ok(None) + } + } + } + } + + fn write_back( + &self, + extends: &mut [RawValueBucket], + upload_ret: Option, + ) { + if let Some(ret) = upload_ret { + for (extend, vbs) in extends.iter_mut().zip(ret.0.iter()) { + let fields = &vbs.data.0; + for (key, value) in extend { + if let Some(v) = fields.get(key) { + *value = Value::from(v); + } + } + } + } + } +} + +/// Struct of CloudData. +#[derive(Clone)] +pub struct CloudData { + pub(crate) next_cursor: String, + pub(crate) has_more: bool, + pub(crate) values: Vec, +} + +impl From for CloudData { + fn from(value: ipc_conn::CloudData) -> Self { + let mut vec = vec![]; + for v in value.values.0 { + vec.push(v.into()); + } + CloudData { + next_cursor: value.next_cursor, + has_more: value.has_more, + values: vec, + } + } +} + +impl CloudData { + /// Get next cursor from CloudData instance. + pub fn next_cursor(&self) -> &str { + &self.next_cursor + } + + /// Check whether CloudData instance has more value buckets. + pub fn has_more(&self) -> bool { + self.has_more + } + + /// Get values from CloudData instance. + pub fn values(&self) -> &[ValueBucket] { + &self.values + } +} + +/// Struct of ValueBucket. +#[derive(Clone)] +pub struct ValueBucket { + pub(crate) id: String, + pub(crate) typ: String, + pub(crate) create_info: VBInfo, + pub(crate) modified_info: VBInfo, + pub(crate) data: RawValueBucket, + pub(crate) is_delete: bool, + pub(crate) version: u32, + pub(crate) is_new_create: bool, +} + +impl From for ValueBucket { + fn from(value: ipc_conn::ValueBucket) -> Self { + let mut map = HashMap::new(); + for (key, field) in value.data.0 { + map.insert(key, Value::from(&field)); + } + ValueBucket { + id: value.id, + typ: value.typ, + create_info: value.create_info.into(), + modified_info: value.modified_info.into(), + data: map, + is_delete: value.is_delete, + version: value.version, + is_new_create: value.is_new_create, + } + } +} + +impl ValueBucket { + /// Get id of ValueBucket instance. + pub fn id(&self) -> &str { + &self.id + } + + /// Get type of ValueBucket instance. + pub fn typ(&self) -> &str { + &self.typ + } + + /// Get create info of ValueBucket instance. + pub fn create_info(&self) -> &VBInfo { + &self.create_info + } + + /// Get modified info of ValueBucket instance. + pub fn modified_info(&self) -> &VBInfo { + &self.modified_info + } + + /// Get data of ValueBucket instance. + pub fn data(&self) -> &RawValueBucket { + &self.data + } + + /// Check whether the ValueBucket instance is deleted. + pub fn is_delete(&self) -> bool { + self.is_delete + } + + /// Get version of ValueBucket instance. + pub fn version(&self) -> u32 { + self.version + } + + /// Check whether the ValueBucket instance is newly created. + pub fn is_new_create(&self) -> bool { + self.is_new_create + } +} + +/// Struct of Value Bucket infos. +#[derive(Clone)] +pub struct VBInfo(ipc_conn::Response); + +impl From for VBInfo { + fn from(value: ipc_conn::Response) -> Self { + VBInfo(value) + } +} + +impl VBInfo { + /// Get time of VBInfo instance. + pub fn time(&self) -> i64 { + self.0.time + } + + /// Get device name of VBInfo instance. + pub fn device_name(&self) -> &str { + &self.0.device_name + } + + /// Get app id of VBInfo instance. + pub fn app_id(&self) -> &str { + &self.0.app_id + } +} diff --git a/src/cloud_extension/src/service_impl/cloud_service.rs b/src/cloud_extension/src/service_impl/cloud_service.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f9961c48b96fdf37cefd91ec8e94bab862253f3 100644 --- a/src/cloud_extension/src/service_impl/cloud_service.rs +++ b/src/cloud_extension/src/service_impl/cloud_service.rs @@ -0,0 +1,312 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn; +use crate::service_impl::error::SyncError; +use crate::service_impl::types::Database; +use std::collections::HashMap; +use std::ops::Deref; + +/// Struct of CloudServer +pub struct CloudServer { + user_id: i32, + connect: ipc_conn::Connect, +} + +impl CloudServer { + /// Get a server instance through IPC, and initialize a CloudServer instance + pub fn new(user_id: i32) -> Result { + Ok(CloudServer { + user_id, + connect: ipc_conn::Connect::new(user_id)?, + }) + } + + #[inline] + fn get_cloud_info(&mut self) -> Result { + let user_info_ipc = self.connect.get_service_info(self.user_id)?; + Ok(CloudInfo::from(user_info_ipc.deref())) + } + + /// Get a cloud server info from the other end through the IPC connection + pub fn get_service_info(&mut self) -> Result { + let mut cloud_info = self.get_cloud_info()?; + cloud_info.user = self.user_id; + let app_infos = self.connect.get_app_brief_info(self.user_id)?; + for app in &app_infos.0 { + let cloud_app_info = AppInfo::from(app); + cloud_info + .apps + .insert(cloud_app_info.bundle_name.clone(), cloud_app_info); + } + Ok(cloud_info) + } + + /// Get a application schema by bundle name from the other end through the IPC connection + pub fn get_app_schema(&mut self, bundle_name: &str) -> Result { + let schema_ipc = self.connect.get_app_schema(self.user_id, bundle_name)?; + Ok(SchemaMeta::from(schema_ipc.deref())) + } + + /// Passing in relations and a callback function, if possible, send the subscription relation + /// to the cloud server and the specific database. + pub fn subscribe( + &mut self, + dbs: &HashMap>, + expire: u32, + ) -> Result, SyncError> { + let mut result = HashMap::new(); + for (bundle_name, dbmetas) in dbs { + let mut vec = vec![]; + for dbmeta in dbmetas { + vec.push(dbmeta.into()) + } + let dbs_ipc = ipc_conn::Databases(vec); + + let ret = self.connect.subscribe(expire, bundle_name, &dbs_ipc)?; + let (ret_expire, map) = ret.0; + for (bundle_name_ret, sub_result) in map { + let mut r = RelationSet { + bundle_name: bundle_name.clone(), + expire_time: ret_expire, + relations: HashMap::new(), + }; + for (alias, id) in sub_result.0 { + r.relations.insert(alias, id); + } + result.insert(bundle_name_ret, r); + } + } + Ok(result) + } + + /// Passing in relations and a callback function, if possible, send the unsubscription relation + /// to the cloud server and the specific database. + pub fn unsubscribe(&mut self, relations: &HashMap>) -> Result<(), SyncError> { + if relations.is_empty() { + return Ok(()); + } + let unsub = ipc_conn::UnsubscriptionInfo(relations.clone()); + self.connect.unsubscribe(&unsub)?; + Ok(()) + } +} + +/// Struct of SchemaMeta, containing schema information on a cloud server. +pub struct SchemaMeta { + version: i32, + bundle_name: String, + databases: Vec, +} + +impl Default for SchemaMeta { + fn default() -> Self { + SchemaMeta { + version: 0, + bundle_name: "".to_string(), + databases: vec![], + } + } +} + +impl SchemaMeta { + /// Get version of SchemaMeta instance. + pub fn version(&self) -> i32 { + self.version + } + + /// Get bundle name of SchemaMeta instance. + pub fn bundle_name(&self) -> &str { + &self.bundle_name + } + + /// Get database from a SchemaMeta instance by store id. + pub fn databases(&self) -> &[Database] { + &self.databases + } +} + +impl From<&ipc_conn::Schema> for SchemaMeta { + fn from(value: &ipc_conn::Schema) -> Self { + let mut dbs = vec![]; + for db in &value.databases.0 { + dbs.push(Database::from(db)); + } + SchemaMeta { + version: value.version, + bundle_name: value.bundle_name.clone(), + databases: dbs, + } + } +} + +/// Struct of CloudInfo +pub struct CloudInfo { + pub(crate) user: i32, + pub(crate) id: String, + pub(crate) total_space: u64, + pub(crate) remain_space: u64, + pub(crate) enable_cloud: bool, + pub(crate) apps: HashMap, +} + +impl From<&ipc_conn::ServiceInfo> for CloudInfo { + fn from(value: &ipc_conn::ServiceInfo) -> Self { + CloudInfo { + user: 0, + id: value.account_id.clone(), + total_space: value.total_space, + remain_space: value.left_space, + enable_cloud: value.cloud_status == ipc_conn::CloudStatus::Login, + apps: Default::default(), + } + } +} + +impl CloudInfo { + /// Get user of CloudInfo instance. + pub fn get_user(&self) -> i32 { + self.user + } + + /// Get id of CloudInfo instance. + pub fn get_id(&self) -> &str { + &self.id + } + + /// Get total space of CloudInfo instance. + pub fn get_total_space(&self) -> u64 { + self.total_space + } + + /// Get remain space of CloudInfo instance. + pub fn get_remain_space(&self) -> u64 { + self.remain_space + } + + /// Check whether the CloudInfo instance enables cloud synchronization. + pub fn get_enable_cloud(&self) -> bool { + self.enable_cloud + } + + /// Get app info from CloudInfo instance. + pub fn get_app_info(&self) -> &HashMap { + &self.apps + } +} + +/// Struct of App Info. +#[derive(Clone)] +pub struct AppInfo { + pub(crate) bundle_name: String, + pub(crate) app_id: String, + pub(crate) instance_id: i32, + pub(crate) cloud_switch: bool, +} + +impl From<&ipc_conn::AppInfo> for AppInfo { + fn from(value: &ipc_conn::AppInfo) -> Self { + AppInfo { + bundle_name: value.bundle_name.clone(), + app_id: value.id.clone(), + instance_id: 0, + cloud_switch: value.enable_cloud, + } + } +} + +impl AppInfo { + /// Get bundle name of AppInfo instance. + pub fn bundle_name(&self) -> &str { + &self.bundle_name + } + + /// Get app id of AppInfo instance. + pub fn app_id(&self) -> &str { + &self.app_id + } + + /// Check whether the AppInfo instance allows cloud switch. + pub fn cloud_switch(&self) -> bool { + self.cloud_switch + } + + /// Get instance id of AppInfo instance. + pub fn instance_id(&self) -> i32 { + self.instance_id + } +} + +/// Struct of Relation to represent subscription relations. +#[derive(Default, Clone)] +pub struct RelationSet { + pub(crate) bundle_name: String, + pub(crate) expire_time: u32, + // Database alias as key, subscription id and time generated by the cloud as value + pub(crate) relations: HashMap, +} + +impl RelationSet { + /// New a RelationSet. + pub fn new( + bundle_name: String, + expire_time: u32, + relations: HashMap, + ) -> RelationSet { + RelationSet { + bundle_name, + expire_time, + relations, + } + } + + /// Get the relating bundle name of this RelationSet instance. + pub fn bundle_name(&self) -> &str { + self.bundle_name.as_str() + } + + /// Get expire time of this RelationSet instance. + pub fn expire_time(&self) -> u32 { + self.expire_time + } + + /// Get the inner hashmap describing relations, with database alias as key, subscription id and + /// time as value. + pub fn relations(&self) -> &HashMap { + &self.relations + } +} + +#[cfg(tests)] +mod cloud_server_test { + use crate::service_impl::cloud_service::CloudServer; + + #[test] + fn get_cloud_info() { + let mut server = CloudServer::new(100).unwrap(); + let cloud_info = server.get_cloud_info().unwrap(); + dbg!(&cloud_info.id); + dbg!(&cloud_info.enable_cloud); + dbg!(&cloud_info.remain_space); + dbg!(&cloud_info.total_space); + dbg!(&cloud_info.user); + println!("App infos:"); + for (bundle_name, app) in &cloud_info.apps { + dbg!(format!( + "{:?}: {:?}, {:?}, {:?}, {:?}", + bundle_name, app.app_id, app.bundle_name, app.cloud_switch, app.instance_id + )); + } + panic!(); + } +} diff --git a/src/cloud_extension/src/service_impl/db_types.rs b/src/cloud_extension/src/service_impl/db_types.rs new file mode 100644 index 0000000000000000000000000000000000000000..a88b868e99def826caf91ab7a6397007c1d05869 --- /dev/null +++ b/src/cloud_extension/src/service_impl/db_types.rs @@ -0,0 +1,183 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +pub(crate) const DELETE_FIELD: &str = "#_deleted"; +pub(crate) const GID_FIELD: &str = "#_gid"; +pub(crate) const CREATE_FIELD: &str = "#_createTime"; +pub(crate) const MODIFY_FIELD: &str = "#_modifyTime"; +pub(crate) const CURSOR_FIELD: &str = "#_cursor"; +pub(crate) const ERROR_FIELD: &str = "#_error"; + +pub(crate) struct SchemaMeta { + version: i32, + bundle_name: String, + databases: Vec, +} + +impl Default for SchemaMeta { + fn default() -> Self { + SchemaMeta { + version: 0, + bundle_name: "".to_string(), + databases: vec![], + } + } +} + +impl SchemaMeta { + pub(crate) fn set_version(&mut self, version: i32) { + self.version = version; + } + + pub(crate) fn set_bundle_name(&mut self, bundle_name: String) { + self.bundle_name = bundle_name; + } + + pub(crate) fn push_database(&mut self, database: Database) { + self.databases.push(database) + } + + pub fn get_database(&mut self, store_id: &str) -> &Database { + todo!(); + } + + pub fn is_valid(&self) -> bool { + todo!(); + } +} + +pub(crate) struct Database { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) tables: HashMap, +} + +impl Default for Database { + fn default() -> Self { + Database { + name: "".to_string(), + alias: "".to_string(), + tables: Default::default(), + } + } +} + +impl Database { + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + pub fn set_alias(&mut self, alias: String) { + self.alias = alias; + } + + pub fn push_table(&mut self, key: String, table: Table) { + self.tables.insert(key, table); + } +} + +pub(crate) struct Table { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) fields: Vec, +} + +impl Default for Table { + fn default() -> Self { + Table { + name: "".to_string(), + alias: "".to_string(), + fields: vec![], + } + } +} + +impl Table { + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + pub fn set_alias(&mut self, alias: String) { + self.alias = alias; + } + + pub fn push_field(&mut self, field: Field) { + self.fields.push(field); + } +} + +pub(crate) struct Cursor { + pub(crate) cursor: String, + pub(crate) offset: String, + data: Vec, + consumed: bool, + finished: bool, + names: Vec, +} + +impl Default for Cursor { + fn default() -> Self { + Cursor { + cursor: "".to_string(), + offset: "".to_string(), + data: vec![], + consumed: false, + finished: false, + names: vec![], + } + } +} + +pub(crate) struct Field { + pub(crate) colName: String, + pub(crate) alias: String, + pub(crate) typ: i32, + // By default is false + pub(crate) primary: bool, + // By default is true + pub(crate) nullable: bool, +} + +impl Default for Field { + fn default() -> Self { + Field { + colName: String::default(), + alias: String::default(), + typ: i32::default(), + primary: false, + nullable: true, + } + } +} + +impl Field { + pub(crate) fn get_type(typ: i32, subtyp: i32) -> i32 { + // Corresponds to the order of types in Enum Value + const TYPES_IDX: &[usize] = &[0, 1, 2, 3, 4, 5, 0xFFFF0000, 0, 6, 0]; + if let Ok(idx) = usize::try_from(typ) { + if idx < TYPES_IDX.len() && TYPES_IDX[idx] != 0xFFFF0000 { + return TYPES_IDX[idx] as i32; + } + } + + if let Ok(subidx) = usize::try_from(subtyp) { + // If idx is the idx of Asset + if subidx < TYPES_IDX.len() && TYPES_IDX[subidx] == 6 { + return 6; + } + } + 0 + } +} diff --git a/src/cloud_extension/src/service_impl/error.rs b/src/cloud_extension/src/service_impl/error.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6d6eff7400c29d4b245b149ea245818c4bb8ffce 100644 --- a/src/cloud_extension/src/service_impl/error.rs +++ b/src/cloud_extension/src/service_impl/error.rs @@ -0,0 +1,98 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn; +use std::fmt::{Debug, Display, Formatter}; + +/// Struct of Synchronization Error. +pub enum SyncError { + /// Unknown error + Unknown, + + /// Target table is not in the source database + NoSuchTableInDb, + + /// Error because the input is not an asset value and can't call asset relating functions + NotAnAsset, + + /// General asset download failure + AssetDownloadFailure, + + /// General asset upload failure + AssetUploadFailure, + + /// Session has been unlocked and can't be unclocked again + SessionUnlocked, + + /// Unsupported functions or features + Unsupported, + + /// IPCError + IPCError(ipc_conn::Error), + + /// A bunch of IPC Errors + IPCErrors(Vec), + + /// IO Error + IO(std::io::Error), +} + +impl From for SyncError { + fn from(value: std::io::Error) -> Self { + SyncError::IO(value) + } +} + +impl From for SyncError { + fn from(value: ipc_conn::Error) -> Self { + SyncError::IPCError(value) + } +} + +impl From for SyncError { + fn from(value: ipc_conn::Errors) -> Self { + SyncError::IPCErrors(value.0) + } +} + +impl Debug for SyncError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + SyncError::Unknown => "unknown".to_string(), + SyncError::NotAnAsset => "NotAnAsset".to_string(), + SyncError::AssetDownloadFailure => "AssetDownloadFailure".to_string(), + SyncError::AssetUploadFailure => "AssetUploadFailure".to_string(), + SyncError::NoSuchTableInDb => "NoSuchTableInDb".to_string(), + SyncError::SessionUnlocked => "SessionUnlocked".to_string(), + SyncError::Unsupported => "Unsupported".to_string(), + SyncError::IPCError(e) => e.to_string(), + SyncError::IPCErrors(es) => { + let mut ret = String::new(); + for (idx, e) in es.iter().enumerate() { + ret.push_str(&format!("\n\tidx {}: {}", idx, e)); + } + ret + } + SyncError::IO(e) => e.to_string(), + }; + write!(f, "{}", str) + } +} + +impl Display for SyncError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl std::error::Error for SyncError {} diff --git a/src/cloud_extension/src/service_impl/mod.rs b/src/cloud_extension/src/service_impl/mod.rs index 2c01c573f366b5c29a11122cc4917b7f18271367..427b5d6604ad08a9fa12e40911eab4c99d310bd8 100644 --- a/src/cloud_extension/src/service_impl/mod.rs +++ b/src/cloud_extension/src/service_impl/mod.rs @@ -1,4 +1,27 @@ -mod error; -mod asset_loader; -mod cloud_db; -mod cloud_service; \ No newline at end of file +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Module of AssetLoader. +pub mod asset_loader; + +/// Module of CloudDatabase. +pub mod cloud_db; + +/// Module of CloudService. +pub mod cloud_service; + +/// Module of Errors. +pub mod error; + +/// Module of Cloud Service Synchronization basic types. +pub mod types; diff --git a/src/cloud_extension/src/service_impl/types.rs b/src/cloud_extension/src/service_impl/types.rs new file mode 100644 index 0000000000000000000000000000000000000000..6295172fbc1132abf9e30afa1e3f9e309d2c26d8 --- /dev/null +++ b/src/cloud_extension/src/service_impl/types.rs @@ -0,0 +1,268 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ipc_conn; +use crate::service_impl::asset_loader::Asset; +use crate::service_impl::error::SyncError; +use std::collections::HashMap; + +pub(crate) const GID_FIELD: &str = "#_gid"; +pub(crate) const CREATE_FIELD: &str = "#_createTime"; +pub(crate) const MODIFY_FIELD: &str = "#_modifyTime"; + +/// Struct of single value. +#[derive(Clone, Debug, PartialEq)] +pub enum Value { + /// Empty Value + Empty, + + /// Value containing an int + Int(i64), + + /// Value containing a double + Double(f64), + + /// Value containing a string + String(String), + + /// Value containing a bool + Bool(bool), + + /// Value containing a byte array + Bytes(Vec), + + /// Value containing an asset + Asset(Asset), + + /// Value containing an asset array + Assets(Vec), +} + +impl Value { + pub(crate) fn has_asset(&self) -> bool { + matches!(self, Value::Asset(_) | Value::Assets(_)) + } + + pub(crate) fn as_record_field_ipc(&self, user_id: i32) -> Result { + Ok(match self { + Value::Empty => ipc_conn::Field::Null, + Value::Int(i) => ipc_conn::Field::Number(*i), + Value::Double(f) => ipc_conn::Field::Real(*f), + Value::String(s) => ipc_conn::Field::Text(s.clone()), + Value::Bool(b) => ipc_conn::Field::Bool(*b), + Value::Bytes(b) => ipc_conn::Field::Blob(b.clone()), + Value::Asset(a) => ipc_conn::Field::Asset(a.as_ipc_asset(user_id)?), + Value::Assets(a) => { + let mut ret = vec![]; + for asset in a { + ret.push(asset.as_ipc_asset(user_id)?); + } + let assets = ipc_conn::Assets(ret); + ipc_conn::Field::Assets(assets) + } + }) + } +} + +impl From<&ipc_conn::Field> for Value { + fn from(value: &ipc_conn::Field) -> Self { + match value { + ipc_conn::Field::Null => Value::Empty, + ipc_conn::Field::Number(i) => Value::Int(*i), + ipc_conn::Field::Real(f) => Value::Double(*f), + ipc_conn::Field::Text(s) => Value::String(s.clone()), + ipc_conn::Field::Bool(b) => Value::Bool(*b), + ipc_conn::Field::Blob(b) => Value::Bytes(b.clone()), + ipc_conn::Field::Asset(a) => Value::Asset(Asset::from(a)), + ipc_conn::Field::Assets(a) => { + let mut vec = vec![]; + for single_asset in &a.0 { + let asset = Asset::from(single_asset); + vec.push(asset); + } + Value::Assets(vec) + } + } + } +} + +/// Hashmap of Value name as keys, Value as values. +pub type RawValueBucket = HashMap; + +/// Struct of Database stored in the cloud server. Database can be obtained from schema meta. +#[derive(Clone)] +pub struct Database { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) tables: HashMap, +} + +impl From<&ipc_conn::Database> for Database { + fn from(value: &ipc_conn::Database) -> Self { + let mut tables = HashMap::new(); + for t in &value.tables.0 { + tables.insert(t.table_name.clone(), Table::from(t)); + } + Database { + name: value.name.clone(), + alias: value.alias.clone(), + tables, + } + } +} + +impl From<&Database> for ipc_conn::Database { + fn from(value: &Database) -> Self { + let mut tables = vec![]; + for table in value.tables.values() { + tables.push(table.into()); + } + + ipc_conn::Database { + name: value.name.clone(), + alias: value.alias.to_string(), + tables: ipc_conn::SchemaOrderTables(tables), + remote_obj: None, + lock_session_id: String::default(), + } + } +} + +impl Database { + /// New a database instance + pub fn new(name: String, alias: String, tables: HashMap) -> Database { + Database { + name, + alias, + tables, + } + } + + /// Get local name of Database instance. + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// Get alias, or say, cloud name, of Database instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get tables of Database instance. + pub fn tables(&self) -> &HashMap { + &self.tables + } +} + +/// Struct of Table in Database. +#[derive(Clone)] +pub struct Table { + pub(crate) name: String, + pub(crate) alias: String, + pub(crate) fields: Vec, +} + +impl Table { + /// New a table instance + pub fn new(name: String, alias: String, fields: Vec) -> Table { + Table { + name, + alias, + fields, + } + } + + /// Get name of Table instance. + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// Get name of Table instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get fields of Table instance. + pub fn fields(&self) -> &[Field] { + self.fields.as_slice() + } +} + +impl From<&ipc_conn::OrderTable> for Table { + fn from(value: &ipc_conn::OrderTable) -> Self { + Table { + name: value.table_name.clone(), + alias: value.alias.clone(), + fields: vec![], + } + } +} + +impl From<&Table> for ipc_conn::OrderTable { + fn from(value: &Table) -> Self { + ipc_conn::OrderTable { + alias: value.alias.clone(), + table_name: value.name.clone(), + } + } +} + +/// Struct of Field in Database Table. +#[derive(Clone)] +pub struct Field { + pub(crate) col_name: String, + pub(crate) alias: String, + pub(crate) typ: u8, + // By default is false + pub(crate) primary: bool, + // By default is true + pub(crate) nullable: bool, +} + +impl Field { + /// New a field instance + pub fn new(col_name: String, alias: String, typ: u8, primary: bool, nullable: bool) -> Field { + Field { + col_name, + alias, + typ, + primary, + nullable, + } + } + + /// Get column name of Field instance. + pub fn col_name(&self) -> &str { + self.col_name.as_str() + } + + /// Get alias of Field instance. + pub fn alias(&self) -> &str { + self.alias.as_str() + } + + /// Get type of Field instance. + pub fn typ(&self) -> u8 { + self.typ + } + + /// Check whether this Field is primary. + pub fn primary(&self) -> bool { + self.primary + } + + /// Check whether this Field is nullable. + pub fn nullable(&self) -> bool { + self.nullable + } +} diff --git a/src/connect_adapter/connect.cpp b/src/connect_adapter/connect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d975511bb131a398011a4c55ac6253bcee96feb --- /dev/null +++ b/src/connect_adapter/connect.cpp @@ -0,0 +1,60 @@ +#include "connect.h" +#include "hilog/log_cpp.h" +#include + +#define RESULT_OK 0 + +using namespace OHOS; + +const std::string bundleName = "com.example.cloudsync"; +const std::string abilityName = "ServiceExtAbility"; + +static const OHOS::HiviewDFX::HiLogLabel g_CONNECT_LABEL = { LOG_CORE, 0xD001610, "Connect"}; + +void Connect::OnAbilityConnectDone(const OHOS::AppExecFwk::ElementName &element, + const OHOS::sptr &remoteObject, + int resultCode) { + if (resultCode != RESULT_OK) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "ability connect failed, error code:%{public}d", resultCode); + } + + OHOS::HiviewDFX::HiLog::Debug(g_CONNECT_LABEL, "ability connect success, ability name:%{public}s", element.GetAbilityName().c_str()); + remoteObject_ = remoteObject; +} + +void Connect::OnAbilityDisconnectDone(const OHOS::AppExecFwk::ElementName &element, int resultCode) { + if (resultCode != RESULT_OK) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "ability disconnect failed, error code:%{public}d", resultCode); + } + remoteObject_ = nullptr; +} + +CRemoteObject *ConnectServiceInner(int userId) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "Connect Service Inner access"); + OHOS::sptr ipc_connect = new Connect(); + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "Connect new"); + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "new Want"); + OHOS::AAFwk::Want want; + want.SetAction(""); + want.SetElementName(bundleName, abilityName); + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "set Want"); + int ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbility(want, + iface_cast(ipc_connect), nullptr, userId); + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "ConnectAbility"); + if (ret != RESULT_OK) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "connect ability fail, error code:%{pubic}d", ret); + } + if (ipc_connect == nullptr) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "ipc_connect is null"); + return nullptr; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + CRemoteObject *holder = new (std::nothrow) CRemoteObjectHolder(); + if (holder == nullptr) { + OHOS::HiviewDFX::HiLog::Error(g_CONNECT_LABEL, "%{public}s: create samgr proxy holder failed\n", __func__); + return nullptr; + } + holder->IncStrongRef(nullptr); + holder->remote_ = ipc_connect->remoteObject_; + return holder; +} \ No newline at end of file diff --git a/src/connect_adapter/connect.h b/src/connect_adapter/connect.h new file mode 100644 index 0000000000000000000000000000000000000000..a193f99c0a85bbefe786589a87ea791edec7e360 --- /dev/null +++ b/src/connect_adapter/connect.h @@ -0,0 +1,26 @@ +#ifndef RUST_PROJ_MOCK_CONNECT_H +#define RUST_PROJ_MOCK_CONNECT_H + +#include "ability_connect_callback_stub.h" +#include "ability_manager_interface.h" +#include "ability_manager_client.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "c_remote_object_internal.h" +#include "c_parcel.h" +#include "iremote_broker.h" + +class Connect : public OHOS::AAFwk::AbilityConnectionStub { +public: + void OnAbilityConnectDone(const OHOS::AppExecFwk::ElementName &element, + const OHOS::sptr &remoteObject, + int resultCode) override; + + void OnAbilityDisconnectDone(const OHOS::AppExecFwk::ElementName &element, + int resultCode) override; +public: + OHOS::sptr remoteObject_ = nullptr; +}; + +CRemoteObject *ConnectServiceInner(int userId); +#endif //RUST_PROJ_MOCK_CONNECT_H diff --git a/src/connect_adapter/connect_wrapper.cpp b/src/connect_adapter/connect_wrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb7a01e8a1254b9c359ba83cd25a09e0a247ff92 --- /dev/null +++ b/src/connect_adapter/connect_wrapper.cpp @@ -0,0 +1,13 @@ +#include "connect.h" +#include "connect_wrapper.h" + +#ifdef __cplusplus +extern "C" { +#endif +void *ConnectService(int userId) { + CRemoteObject *c_remote_object = ConnectServiceInner(userId); + return (void *)c_remote_object; +} +#ifdef __cplusplus +}; +#endif \ No newline at end of file diff --git a/src/connect_adapter/connect_wrapper.h b/src/connect_adapter/connect_wrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..5835a4631d6836d31b9a0deb9de36a37ff3e78a5 --- /dev/null +++ b/src/connect_adapter/connect_wrapper.h @@ -0,0 +1,10 @@ +#ifndef RUST_PROJ_MOCK_CONNECT_WRAPPER_H +#define RUST_PROJ_MOCK_CONNECT_WRAPPER_H +#ifdef __cplusplus +extern "C" { +#endif +void *ConnectService(int userId); +#ifdef __cplusplus +}; +#endif +#endif //RUST_PROJ_MOCK_CONNECT_WRAPPER_H diff --git a/src/hilog_rust/src/lib.rs b/src/hilog_rust/src/lib.rs index ecc3f500ace8e3b1ed1cb15357127d689ccad067..94b51006d201094dfa5dbfc47d242c7498d4f5fe 100644 --- a/src/hilog_rust/src/lib.rs +++ b/src/hilog_rust/src/lib.rs @@ -14,7 +14,7 @@ */ //! hilog dylib_crate for Rust. -use std::ffi::{c_char}; +use std::ffi::c_char; #[macro_use] mod macros; @@ -24,28 +24,28 @@ mod macros; pub enum LogLevel { /// min log level LogLevelMin = 0, - /// The "debug" level. - /// + /// The "debug" level. + /// /// Designates lower priority log. Debug = 3, - /// The "info" level. - /// + /// The "info" level. + /// /// Designates useful information. Info = 4, - /// The "warn" level. - /// - /// Designates hazardous situations. + /// The "warn" level. + /// + /// Designates hazardous situations. Warn = 5, - /// The "error" level. - /// - /// Designates very serious errors. + /// The "error" level. + /// + /// Designates very serious errors. Error = 6, - /// The "fatal" level. - /// - /// Designates major fatal anomaly. + /// The "fatal" level. + /// + /// Designates major fatal anomaly. Fatal = 7, /// max log level - LogLevelMax + LogLevelMax, } /// log type @@ -60,7 +60,7 @@ pub enum LogType { /// log type for kernel log LogKmsg = 4, /// max log type - LogTypeMax + LogTypeMax, } /// hilog label @@ -74,15 +74,21 @@ pub struct HiLogLabel { pub tag: &'static str, } - // hilog ffi interface -extern "C"{ +extern "C" { /// hilog ffi interface HiLogIsLoggabel - pub fn HiLogIsLoggable(domain: u32, tag: *const c_char, level:u32) -> bool; + pub fn HiLogIsLoggable(domain: u32, tag: *const c_char, level: u32) -> bool; /// hilog ffi interface HiLogPrint - pub fn HiLogPrint(logType: u8, level: u8, domain: u32, tag: *const c_char, fmt: *const c_char, ...) -> u32; + pub fn HiLogPrint( + logType: u8, + level: u8, + domain: u32, + tag: *const c_char, + fmt: *const c_char, + ... + ) -> u32; /// hilog ffi interface IsPrivateSwitchOn pub fn IsPrivateSwitchOn() -> bool; /// hilog ffi interface IsDebugOn pub fn IsDebugOn() -> bool; -} \ No newline at end of file +} diff --git a/src/hilog_rust/src/macros.rs b/src/hilog_rust/src/macros.rs index 96d705ccd0b9ddcaa49f7b68e53c982ee0d3b5c7..4b3d201b06045dd98fc1f2b89efac7009c3efe3c 100644 --- a/src/hilog_rust/src/macros.rs +++ b/src/hilog_rust/src/macros.rs @@ -21,7 +21,7 @@ macro_rules! hilog { (@call $log_label:ident, $level:expr, $fmt:literal, $(,)? $($processed_args:expr),* ) => ( let log_str = format!($fmt, $($processed_args),*); - let res = unsafe { + let res = unsafe { $crate::HiLogPrint($log_label.log_type as u8, $level as u8, $log_label.domain as u32, CString::new($log_label.tag).expect("default tag").as_ptr() as *const c_char, CString::new(log_str).expect("default log").as_ptr() as *const c_char) @@ -81,12 +81,12 @@ macro_rules! hilog { } /// printf log at the debug level. -/// +/// /// #Examples -/// +/// /// ``` /// use hilog_rust::{debug, hilog, HiLogLabel, LogType}; -/// +/// /// # fn main() { /// let log_label: HiLogLabel = HiLogLabel { /// log_type: LogType::LogCore, @@ -104,12 +104,12 @@ macro_rules! debug{ } /// printf log at the info level. -/// +/// /// #Examples -/// +/// /// ``` /// use hilog_rust::{info, hilog, HiLogLabel, LogType}; -/// +/// /// # fn main() { /// let log_label: HiLogLabel = HiLogLabel { /// log_type: LogType::LogCore, @@ -127,12 +127,12 @@ macro_rules! info{ } /// printf log at the warn level. -/// +/// /// #Examples -/// +/// /// ``` /// use hilog_rust::{warn, hilog, HiLogLabel, LogType}; -/// +/// /// # fn main() { /// let log_label: HiLogLabel = HiLogLabel { /// log_type: LogType::LogCore, @@ -150,12 +150,12 @@ macro_rules! warn{ } /// printf log at the error level. -/// +/// /// #Examples -/// +/// /// ``` /// use hilog_rust::{error, hilog, HiLogLabel, LogType}; -/// +/// /// # fn main() { /// let log_label: HiLogLabel = HiLogLabel { /// log_type: LogType::LogCore, @@ -173,12 +173,12 @@ macro_rules! error{ } /// printf log at the fatal level. -/// +/// /// #Examples -/// +/// /// ``` /// use hilog_rust::{fatal, hilog, HiLogLabel, LogType}; -/// +/// /// # fn main() { /// let log_label: HiLogLabel = HiLogLabel { /// log_type: LogType::LogCore, @@ -193,4 +193,4 @@ macro_rules! fatal{ ($log_label:ident, $($arg:tt)*) => ( hilog!($log_label, $crate::LogLevel::Fatal, $($arg)*) ); -} \ No newline at end of file +} diff --git a/src/ipc_rust/BUILD.gn b/src/ipc_rust/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2fe9501f1545e6749a34fe213bb7c4262cc70712 --- /dev/null +++ b/src/ipc_rust/BUILD.gn @@ -0,0 +1,81 @@ +# Copyright (C) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") + +SUBSYSTEM_DIR = "//foundation/communication/ipc" +IPC_CORE_ROOT = "$SUBSYSTEM_DIR/ipc/native" + +ohos_rust_shared_library("ipc_rust") { + sources = [ + "src/errors.rs", + "src/ipc/macros.rs", + "src/ipc/mod.rs", + "src/ipc/remote_obj.rs", + "src/ipc/remote_stub.rs", + "src/ipc_binding.rs", + "src/lib.rs", + "src/parcel/mod.rs", + "src/parcel/parcelable.rs", + "src/parcel/types.rs", + ] + + deps = [ ":ipc_c" ] + + external_deps = [ "hilog:hilog_rust" ] + + crate_name = "ipc_rust" + crate_type = "dylib" + + install_images = [ system_base_dir ] + subsystem_name = "communication" + part_name = "ipc" +} + +config("libipc_c_private_config") { + cflags_cc = [ "-O2" ] +} + +ohos_shared_library("ipc_c") { + sanitize = { + integer_overflow = true + } + include_dirs = [ + "$IPC_CORE_ROOT/src/c_wrapper/include", + "$SUBSYSTEM_DIR/utils/include", + ] + sources = [ + "$IPC_CORE_ROOT/src/c_wrapper/source/c_ashmem.cpp", + "$IPC_CORE_ROOT/src/c_wrapper/source/c_parcel.cpp", + "$IPC_CORE_ROOT/src/c_wrapper/source/c_process.cpp", + "$IPC_CORE_ROOT/src/c_wrapper/source/c_remote_object.cpp", + ] + + configs = [ ":libipc_c_private_config" ] + + deps = [ "$SUBSYSTEM_DIR/interfaces/innerkits/ipc_single:ipc_single" ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + ] + + output_extension = "so" + install_images = [ system_base_dir ] + subsystem_name = "communication" + part_name = "ipc" +} + +group("rust_ipc_component") { + deps = [ ":ipc_rust" ] +} diff --git a/src/ipc_rust/src/ashmem.rs b/src/ipc_rust/src/ashmem.rs index b7f1e86c2a78c0d1a7b19457e0dcfcfbe69d879b..6690f52c79ad2d48953255b0a52a1d8d6fca0008 100644 --- a/src/ipc_rust/src/ashmem.rs +++ b/src/ipc_rust/src/ashmem.rs @@ -13,19 +13,18 @@ * limitations under the License. */ +use crate::ipc_binding::CAshmem; +use crate::parcel::parcelable::{Deserialize, Serialize}; use crate::{ - ipc_binding, RawData, Result, BorrowedMsgParcel, result_status, - AsRawPtr + ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode, RawData, }; -use crate::ipc_binding::CAshmem; -use std::ffi::{CString, c_char}; -use crate::parcel::parcelable::{Serialize, Deserialize}; use hilog_rust::{error, hilog, HiLogLabel, LogType}; +use std::ffi::{c_char, CString}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustAshmem" + tag: "RustAshmem", }; /// Ashmem packed the native CAshmem @@ -40,9 +39,7 @@ impl Ashmem { } let c_name = CString::new(name).expect("ashmem name is invalid!"); // SAFETY: - let native = unsafe { - ipc_binding::CreateCAshmem(c_name.as_ptr(), size) - }; + let native = unsafe { ipc_binding::CreateCAshmem(c_name.as_ptr(), size) }; if native.is_null() { None } else { @@ -79,66 +76,62 @@ pub const PROT_EXEC: i32 = 4; impl Ashmem { /// Close Ashmem, the ashmem becomes invalid after closing. pub fn close(&self) { + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. ipc_binding::CloseCAshmem(self.as_inner()); } } /// Set ashmem map type with above PROT_XXX by mmap() pub fn map(&self, map_type: i32) -> bool { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::MapCAshmem(self.as_inner(), map_type) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::MapCAshmem(self.as_inner(), map_type) } } /// Set ashmem map type with `PROT_READ | PROT_WRITE` by mmap() pub fn map_read_write(&self) -> bool { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::MapReadAndWriteCAshmem(self.as_inner()) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::MapReadAndWriteCAshmem(self.as_inner()) } } /// Set ashmem map type with `PROT_READ` by mmap() pub fn map_readonly(&self) -> bool { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::MapReadOnlyCAshmem(self.as_inner()) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::MapReadOnlyCAshmem(self.as_inner()) } } /// unmap ashmem pub fn unmap(&self) { + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. ipc_binding::UnmapCAshmem(self.as_inner()); } } /// Set ashmem map type with above PROT_XXX by ioctl() pub fn set_protection(&self, protection: i32) -> bool { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::SetCAshmemProtection(self.as_inner(), protection) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::SetCAshmemProtection(self.as_inner(), protection) } } /// Get ashmem map type pub fn get_protection(&self) -> i32 { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::GetCAshmemProtection(self.as_inner()) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::GetCAshmemProtection(self.as_inner()) } } /// Get ashmem size pub fn get_size(&self) -> i32 { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::GetCAshmemSize(self.as_inner()) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::GetCAshmemSize(self.as_inner()) } } /// Write data to ashmem @@ -148,21 +141,18 @@ impl Ashmem { error!(LOG_LABEL, "invalid offset: {}, len: {}", offset, len); return false; } - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::WriteToCAshmem(self.as_inner(), - data.as_ptr(), len, offset) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::WriteToCAshmem(self.as_inner(), data.as_ptr(), len, offset) } } /// Read ashmem - pub fn read(&self, size: i32, offset: i32) -> Result { - let raw_ptr = unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::ReadFromCAshmem(self.as_inner(), size, offset) - }; - if raw_ptr.is_null() { - Err(-1) + pub fn read(&self, size: i32, offset: i32) -> IpcResult { + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + let raw_ptr = unsafe { ipc_binding::ReadFromCAshmem(self.as_inner(), size, offset) }; + if raw_ptr.is_null() { + Err(IpcStatusCode::Failed) } else { Ok(RawData::new(raw_ptr, size as u32)) } @@ -170,10 +160,9 @@ impl Ashmem { /// Get ashmem inner file descriptor pub fn get_fd(&self) -> i32 { - unsafe { - // SAFETY: Rust Ashmem always hold a valid native CAshmem. - ipc_binding::GetCAshmemFd(self.as_inner()) - } + // SAFETY: + // Rust Ashmem always hold a valid native CAshmem. + unsafe { ipc_binding::GetCAshmemFd(self.as_inner()) } } } @@ -183,7 +172,7 @@ impl Clone for Ashmem { unsafe { ipc_binding::CAshmemIncStrongRef(self.as_inner()); } - // SAFETY: no `None` here, cause `self` is valid + // SAFETY: no `None` here, cause `self` is valid Self(self.0) } } @@ -199,23 +188,20 @@ impl Drop for Ashmem { /// Write a ashmem impl Serialize for Ashmem { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { // SAFETY: - let ret = unsafe { - ipc_binding::CParcelWriteAshmem(parcel.as_mut_raw(), self.as_inner()) - }; - result_status::<()>(ret, ()) + let ret = unsafe { ipc_binding::CParcelWriteAshmem(parcel.as_mut_raw(), self.as_inner()) }; + status_result::<()>(ret as i32, ()) } } /// read a ashmem impl Deserialize for Ashmem { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let ptr = unsafe { - ipc_binding::CParcelReadAshmem(parcel.as_raw()) - }; + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + // SAFETY: + let ptr = unsafe { ipc_binding::CParcelReadAshmem(parcel.as_raw()) }; if ptr.is_null() { - Err(-1) + Err(IpcStatusCode::Failed) } else { // SAFETY: unsafe { @@ -224,4 +210,4 @@ impl Deserialize for Ashmem { } } } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/errors.rs b/src/ipc_rust/src/errors.rs index 48d739348c6714cb4620c04a448eb5e603f203ea..580118f69cda7eecde1ca72218638f64f58a01b5 100644 --- a/src/ipc_rust/src/errors.rs +++ b/src/ipc_rust/src/errors.rs @@ -13,14 +13,77 @@ * limitations under the License. */ +use hilog_rust::{debug, hilog, HiLogLabel, LogType}; +use std::error::Error; +use std::ffi::{c_char, CString}; +use std::fmt; + +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xd001510, + tag: "RustStatus", +}; + /// IPC specific Result, error is i32 type -pub type Result = std::result::Result; - -/// Generate a rust ipc Result by ret and val arguments. -pub fn result_status(ret: bool, val: T) -> Result { - if ret { - Ok(val) - } else { - Err(-1) +pub type IpcResult = std::result::Result; + +/// usage: +/// status_result::<()>(result, ()) +/// or +/// status_result::(result, reply) +pub fn status_result(code: i32, val: T) -> IpcResult { + debug!(LOG_LABEL, "rust status code: {}", code); + match parse_status_code(code) { + IpcStatusCode::Ok => Ok(val), + e => Err(e), + } +} + +/// Parse status code +pub fn parse_status_code(code: i32) -> IpcStatusCode { + match code { + e if e == IpcStatusCode::Ok as i32 => IpcStatusCode::Ok, + e if e == IpcStatusCode::Failed as i32 => IpcStatusCode::Failed, + e if e == IpcStatusCode::Einval as i32 => IpcStatusCode::Einval, + e if e == IpcStatusCode::ErrNullObject as i32 => IpcStatusCode::ErrNullObject, + e if e == IpcStatusCode::ErrDeadObject as i32 => IpcStatusCode::ErrDeadObject, + e if e == IpcStatusCode::InvalidValue as i32 => IpcStatusCode::InvalidValue, + _ => IpcStatusCode::Unknow, + } +} + +/// IPC unified status code +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum IpcStatusCode { + /// success + Ok = 1, + /// failed + Failed = -1, + /// RemoteObj Err Code + /// Invalide Params + Einval = 22, + /// Object is null + ErrNullObject = 7, + /// The object has died + ErrDeadObject = -32, + /// invail value + InvalidValue = 0, + /// unknow value + Unknow = 99999, +} + +impl Error for IpcStatusCode {} + +impl fmt::Display for IpcStatusCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IpcStatusCode::Ok => write!(f, "Call Ok"), + IpcStatusCode::Failed => write!(f, "Call Failed"), + IpcStatusCode::Einval => write!(f, "Invalid Params"), + IpcStatusCode::ErrNullObject => write!(f, "Null Obj"), + IpcStatusCode::ErrDeadObject => write!(f, "Dead Obj"), + IpcStatusCode::InvalidValue => write!(f, "Invalid Value"), + _ => write!(f, "Unknow Error"), + } } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/ipc/macros.rs b/src/ipc_rust/src/ipc/macros.rs index f26133fcd5f3c02b9fb3c824774f34d7dca6763c..997bf6495585d32064fba3ad79ea987c6b3cb80c 100644 --- a/src/ipc_rust/src/ipc/macros.rs +++ b/src/ipc_rust/src/ipc/macros.rs @@ -46,7 +46,7 @@ macro_rules! define_remote_object { impl $proxy { /// Create proxy object by RemoteObj - fn from_remote_object(remote: &RemoteObj) -> $crate::Result { + fn from_remote_object(remote: &RemoteObj) -> $crate::IpcResult { Ok(Self { remote: remote.clone(), $($item_name: $item_init),* @@ -89,21 +89,25 @@ macro_rules! define_remote_object { reply: &mut $crate::BorrowedMsgParcel) -> i32 { // For example, "self.0" is "Box", "*self.0" is "dyn ITest" let result = $on_remote_request(&*self.0, code, data, reply); + match result { Ok(_) => 0, Err(error) => { - error + error as i32 } } } + fn on_dump(&self, file: &$crate::FileDesc, args: &mut Vec<$crate::String16>) -> i32 { + self.0.dump(file, args) + } } impl $crate::FromRemoteObj for dyn $remote_broker { /// For example, convert RemoteObj to RemoteObjRef - fn from(object: $crate::RemoteObj) - -> $crate::Result<$crate::RemoteObjRef> { + fn try_from(object: $crate::RemoteObj) + -> $crate::IpcResult<$crate::RemoteObjRef> { Ok($crate::RemoteObjRef::new(Box::new($proxy::from_remote_object(&object)?))) } } }; -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/ipc/mod.rs b/src/ipc_rust/src/ipc/mod.rs index d56396f0b076d8b0c3e3706c647ae208da7eff83..61fb3ceb52cb892b58636aa4f5017cca10835ce7 100644 --- a/src/ipc_rust/src/ipc/mod.rs +++ b/src/ipc_rust/src/ipc/mod.rs @@ -13,14 +13,14 @@ * limitations under the License. */ +pub mod macros; pub mod remote_obj; pub mod remote_stub; -pub mod macros; -use crate::{BorrowedMsgParcel, MsgParcel, Result, DeathRecipient,}; -use std::ops::{Deref}; -use std::cmp::Ordering; use crate::String16; +use crate::{BorrowedMsgParcel, DeathRecipient, FileDesc, IpcResult, IpcStatusCode, MsgParcel}; +use std::cmp::Ordering; +use std::ops::Deref; // Export types of this module pub use crate::RemoteObj; @@ -28,7 +28,7 @@ pub use crate::RemoteObj; /// Like C++ IRemoteObject class, define function for both proxy and stub object pub trait IRemoteObj { /// Send a IPC request to remote service - fn send_request(&self, code: u32, data: &MsgParcel, is_async: bool) -> Result; + fn send_request(&self, code: u32, data: &MsgParcel, is_async: bool) -> IpcResult; /// Add a death recipient fn add_death_recipient(&self, recipient: &mut DeathRecipient) -> bool; @@ -39,14 +39,14 @@ pub trait IRemoteObj { /// Determine whether it is a proxy object fn is_proxy(&self) -> bool; - /// Dump a service through a string + /// Dump a service through a String16 fn dump(&self, fd: i32, args: &mut Vec) -> i32; /// Judge whether the object is dead fn is_dead(&self) -> bool; /// get interface descriptor - fn interface_descriptor(&self) -> Result; + fn interface_descriptor(&self) -> IpcResult; } /// Like C++ IPCObjectStub class, define function for stub object only, like on_remote_request(). @@ -55,7 +55,15 @@ pub trait IRemoteStub: Send + Sync { fn get_descriptor() -> &'static str; /// Callback for deal IPC request - fn on_remote_request(&self, code: u32, data: &BorrowedMsgParcel, reply: &mut BorrowedMsgParcel) -> i32; + fn on_remote_request( + &self, + code: u32, + data: &BorrowedMsgParcel, + reply: &mut BorrowedMsgParcel, + ) -> i32; + + /// Callback for IPC dump + fn on_dump(&self, file: &FileDesc, args: &mut Vec) -> i32; } /// Like C++ IRemoteBroker class @@ -64,13 +72,18 @@ pub trait IRemoteBroker: Send + Sync { fn as_object(&self) -> Option { panic!("This is not a RemoteObject.") } + /// Default dump + fn dump(&self, _file: &FileDesc, _args: &mut Vec) -> i32 { + println!("This is the default dump function, and you can override it to implement your own dump function"); + IpcStatusCode::Ok as i32 + } } /// Define function which how to convert a RemoteObj to RemoteObjRef, the later contains a /// dynamic trait object: IRemoteObject. For example, "dyn ITest" should implements this trait pub trait FromRemoteObj: IRemoteBroker { /// Convert a RemoteObj to RemoteObjeRef - fn from(object: RemoteObj) -> Result>; + fn try_from(object: RemoteObj) -> IpcResult>; } /// Strong reference for "dyn IRemoteBroker" object, for example T is "dyn ITest" @@ -93,8 +106,10 @@ impl Deref for RemoteObjRef { impl Clone for RemoteObjRef { fn clone(&self) -> Self { - // non None - FromRemoteObj::from(self.0.as_object().unwrap()).unwrap() + // Clone is a method in the RemoteObjRef structure. + // T in RemoteObjRefimplements the trait FromRemoteObj, + // so self.0.as_ Object(). unwrap() must be a RemoteObj object that exists + FromRemoteObj::try_from(self.0.as_object().unwrap()).unwrap() } } diff --git a/src/ipc_rust/src/ipc/remote_obj.rs b/src/ipc_rust/src/ipc/remote_obj.rs index 525a4009a2ec5c4d2259c25d191b102ee70f214f..8ac74262cd913e0a2ae64e1b8dd72a27a3c6dc74 100644 --- a/src/ipc_rust/src/ipc/remote_obj.rs +++ b/src/ipc_rust/src/ipc/remote_obj.rs @@ -15,19 +15,25 @@ //! Implement of RemoteObj type, which represent a C++ IRemoteObject. -use std::ptr; +use crate::ipc_binding::{CDeathRecipient, CRemoteObject}; +use crate::parcel::parcelable::{allocate_vec_with_buffer, Deserialize, Serialize}; +use crate::String16; use crate::{ - ipc_binding, IRemoteObj, DeathRecipient, Result, - MsgParcel, BorrowedMsgParcel, AsRawPtr, parcel::on_string16_writer, - parcel::vec_u16_to_string, + ipc_binding, parcel::vec_u16_to_string, parse_status_code, AsRawPtr, BorrowedMsgParcel, + DeathRecipient, IRemoteObj, IpcResult, IpcStatusCode, MsgParcel, +}; +use hilog_rust::{error, hilog, HiLogLabel, LogType}; +use std::ffi::{c_char, c_void, CString}; +use std::ptr; + +const LOG_LABEL: HiLogLabel = HiLogLabel { + log_type: LogType::LogCore, + domain: 0xd001510, + tag: "RustRemoteObj", }; -use crate::ipc_binding::{CRemoteObject, CDeathRecipient}; -use crate::parcel::parcelable::{Serialize, Deserialize, allocate_vec_with_buffer}; -use std::ffi::{c_void}; -use crate::String16; -pub mod death_recipient; pub mod cmp; +pub mod death_recipient; /// RemoteObject can be used as proxy or stub object. /// It always contained a native CRemoteObject pointer. @@ -43,7 +49,7 @@ impl RemoteObj { if obj.is_null() { None } else { - Some(RemoteObj(unsafe{ptr::NonNull::new_unchecked(obj)})) + Some(RemoteObj(unsafe { ptr::NonNull::new_unchecked(obj) })) } } @@ -55,16 +61,21 @@ impl RemoteObj { } impl IRemoteObj for RemoteObj { - fn send_request(&self, code: u32, data: &MsgParcel, is_async: bool) -> Result { + fn send_request(&self, code: u32, data: &MsgParcel, is_async: bool) -> IpcResult { // SAFETY: unsafe { let mut reply = MsgParcel::new().expect("create reply MsgParcel not success"); - let result = ipc_binding::RemoteObjectSendRequest(self.as_inner(), code, data.as_raw(), - reply.as_mut_raw(), is_async); + let result = ipc_binding::RemoteObjectSendRequest( + self.as_inner(), + code, + data.as_raw(), + reply.as_mut_raw(), + is_async, + ); if result == 0 { Ok(reply) } else { - Err(result) + Err(parse_status_code(result)) } } } @@ -72,75 +83,77 @@ impl IRemoteObj for RemoteObj { // Add death Recipient fn add_death_recipient(&self, recipient: &mut DeathRecipient) -> bool { // SAFETY: - unsafe { - ipc_binding::AddDeathRecipient(self.as_inner(), recipient.as_mut_raw()) - } + unsafe { ipc_binding::AddDeathRecipient(self.as_inner(), recipient.as_mut_raw()) } } // remove death Recipients fn remove_death_recipient(&self, recipient: &mut DeathRecipient) -> bool { // SAFETY: - unsafe { - ipc_binding::RemoveDeathRecipient(self.as_inner(), recipient.as_mut_raw()) - } + unsafe { ipc_binding::RemoveDeathRecipient(self.as_inner(), recipient.as_mut_raw()) } } fn is_proxy(&self) -> bool { // SAFETY: - unsafe { - ipc_binding::IsProxyObject(self.as_inner()) - } + unsafe { ipc_binding::IsProxyObject(self.as_inner()) } } fn dump(&self, fd: i32, args: &mut Vec) -> i32 { - let slice = &args[..]; - // SAFETY: - unsafe { - ipc_binding::Dump(self.as_inner(), fd, slice.as_ptr() as *const c_void, - slice.len().try_into().unwrap(), on_string16_writer) + let mut parcel = match MsgParcel::new() { + Some(parcel) => parcel, + None => { + error!(LOG_LABEL, "create MsgParcel failed"); + return IpcStatusCode::Failed as i32; + } + }; + match parcel.write::>(args) { + Ok(_) => { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + unsafe { ipc_binding::Dump(self.as_inner(), fd, parcel.into_raw()) } + } + _ => { + error!(LOG_LABEL, "create MsgParcel failed"); + IpcStatusCode::Failed as i32 + } } } fn is_dead(&self) -> bool { // SAFETY: - unsafe { - ipc_binding::IsObjectDead(self.as_inner()) - } + unsafe { ipc_binding::IsObjectDead(self.as_inner()) } } - fn interface_descriptor(&self) -> Result { + fn interface_descriptor(&self) -> IpcResult { let mut vec: Option> = None; + // SAFETY: let ok_status = unsafe { - // SAFETY: ipc_binding::GetInterfaceDescriptor( self.as_inner(), &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { vec_u16_to_string(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } impl Serialize for RemoteObj { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - ipc_binding::CParcelWriteRemoteObject(parcel.as_mut_raw(), self.as_inner()) - }; + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + let ret = + unsafe { ipc_binding::CParcelWriteRemoteObject(parcel.as_mut_raw(), self.as_inner()) }; if ret { Ok(()) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } impl Deserialize for RemoteObj { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. We pass a valid, mutable pointer to `val`, a // literal of type `$ty`, and `$read_fn` will write the @@ -151,7 +164,7 @@ impl Deserialize for RemoteObj { if let Some(x) = object { Ok(x) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } diff --git a/src/ipc_rust/src/ipc/remote_obj/cmp.rs b/src/ipc_rust/src/ipc/remote_obj/cmp.rs index 49eb8eae57ee0d0aab0e79500c510f7da77ed880..7c137239302a1301065d484375bc5c0538ba54f1 100644 --- a/src/ipc_rust/src/ipc/remote_obj/cmp.rs +++ b/src/ipc_rust/src/ipc/remote_obj/cmp.rs @@ -18,16 +18,14 @@ use std::cmp::Ordering; impl Ord for RemoteObj { fn cmp(&self, other: &Self) -> Ordering { - let less_than = unsafe { - // SAFETY: RemoteObj always holds a valid `CRemoteObject` pointer\ - // (null is also safe to pass to this function, but we should never do that). - ipc_binding::RemoteObjectLessThan(self.0.as_ptr(), other.0.as_ptr()) - }; - let greater_than = unsafe { - // SAFETY: RemoteObj always holds a valid `CRemoteObject` pointer\ - // (null is also safe to pass to this function, but we should never do that). - ipc_binding::RemoteObjectLessThan(other.0.as_ptr(), self.0.as_ptr()) - }; + // SAFETY: RemoteObj always holds a valid `CRemoteObject` pointer + // (null is also safe to pass to this function, but we should never do that). + let less_than = + unsafe { ipc_binding::RemoteObjectLessThan(self.0.as_ptr(), other.0.as_ptr()) }; + // SAFETY: RemoteObj always holds a valid `CRemoteObject` pointer + // (null is also safe to pass to this function, but we should never do that). + let greater_than = + unsafe { ipc_binding::RemoteObjectLessThan(other.0.as_ptr(), self.0.as_ptr()) }; if !less_than && !greater_than { Ordering::Equal } else if less_than { diff --git a/src/ipc_rust/src/ipc/remote_obj/death_recipient.rs b/src/ipc_rust/src/ipc/remote_obj/death_recipient.rs index 446d27a600f22707ef4e12960732792d5cd17cd7..120480bbdd983f1376f24a0db080f52d47ea4dc7 100644 --- a/src/ipc_rust/src/ipc/remote_obj/death_recipient.rs +++ b/src/ipc_rust/src/ipc/remote_obj/death_recipient.rs @@ -14,16 +14,16 @@ */ use super::*; -use std::ffi::{CString, c_char}; -use hilog_rust::{info, hilog, HiLogLabel, LogType}; +use hilog_rust::{hilog, info, HiLogLabel, LogType}; +use std::ffi::{c_char, CString}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustDeathRecipient" + tag: "RustDeathRecipient", }; -/// This type represent a rust DeathRecipient which like C++ DethRecipient. +/// This type represent a rust DeathRecipient which like C++ DethRecipient. #[repr(C)] pub struct DeathRecipient { native: *mut CDeathRecipient, @@ -37,11 +37,14 @@ impl DeathRecipient { F: Fn() + Send + Sync + 'static, { let callback = Box::into_raw(Box::new(callback)); + // SAFETY: set callback pointer to native, so we can find call which fuction + // when remote service died. let native = unsafe { - // set callback pointer to native, so we can find call which fuction - // when remote service died. - ipc_binding::CreateDeathRecipient(Self::on_remote_died::, - Self::on_destroy::, callback as *mut c_void) + ipc_binding::CreateDeathRecipient( + Self::on_remote_died::, + Self::on_destroy::, + callback as *mut c_void, + ) }; if native.is_null() { None @@ -100,10 +103,10 @@ unsafe impl AsRawPtr for DeathRecipient { impl Drop for DeathRecipient { fn drop(&mut self) { + // Safety: DeathRecipient will always hold a reference for + // native CDeathRecipient. unsafe { - // Safety: DeathRecipient will always hold a reference for - // native CDeathRecipient. ipc_binding::DeathRecipientDecStrongRef(self.native); } } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/ipc/remote_stub.rs b/src/ipc_rust/src/ipc/remote_stub.rs index 96a1587a1146ba089d28efa22c08c02bf05f12c6..485d2fca82b1d91164923cab9c38e7faf04f4544 100644 --- a/src/ipc_rust/src/ipc/remote_stub.rs +++ b/src/ipc_rust/src/ipc/remote_stub.rs @@ -13,20 +13,26 @@ * limitations under the License. */ -use crate::{ipc_binding, IRemoteStub, IRemoteBroker, RemoteObj, BorrowedMsgParcel, }; -use crate::ipc_binding::{CRemoteObject, CParcel}; -use std::ffi::{c_void, CString, c_char}; -use std::ops::{Deref}; -use hilog_rust::{info, hilog, HiLogLabel, LogType}; +use crate::ipc_binding::{CParcel, CRemoteObject}; +use crate::{ + ipc_binding, BorrowedMsgParcel, FileDesc, IRemoteBroker, IRemoteStub, IpcStatusCode, RemoteObj, + String16, +}; +use hilog_rust::{error, hilog, info, HiLogLabel, LogType}; +use std::ffi::{c_char, c_void, CString}; +use std::ops::Deref; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustRemoteStub" + tag: "RustRemoteStub", }; /// RemoteStub packed the native CRemoteObject and the rust stub object T /// which must implement IRemoteStub trait. +/// Safety Invariant: The native pointer must be a valid pointer and cannot be null. +/// The native is guaranteed by the c interface +/// FFI Safety : Ensure stable memory layout C-ABI compatibility #[repr(C)] pub struct RemoteStub { native: *mut CRemoteObject, @@ -38,17 +44,26 @@ impl RemoteStub { pub fn new(rust: T) -> Option { let rust = Box::into_raw(Box::new(rust)); let descripor = CString::new(T::get_descriptor()).expect("descripor must be valid!"); - // SAFETY: + // SAFETY: The incoming parameters are FFI safety + // Descripor is converted to a string type compatible with the c interface through CString. + // on_remote_request and on_destroy callback function has been checked for security, + // and the parameter type is FFI safety let native = unsafe { // set rust object pointer to native, so we can figure out who deal // the request during on_remote_request(). - ipc_binding::CreateRemoteStub(descripor.as_ptr(), Self::on_remote_request, - Self::on_destroy, rust as *mut c_void) + ipc_binding::CreateRemoteStub( + descripor.as_ptr(), + Self::on_remote_request, + Self::on_destroy, + rust as *mut c_void, + Self::on_dump, + ) }; + if native.is_null() { None } else { - Some( RemoteStub { native, rust } ) + Some(RemoteStub { native, rust }) } } } @@ -66,21 +81,25 @@ impl IRemoteBroker for RemoteStub { } unsafe impl Send for RemoteStub {} +/// # Safety +/// +/// RemoteSub thread safety. Multi-thread access and sharing have been considered inside the C-side code unsafe impl Sync for RemoteStub {} impl Deref for RemoteStub { type Target = T; fn deref(&self) -> &Self::Target { - // SAFETY: Rust `Box::into_raw` poiter, so is valid - unsafe { - &*self.rust - } + // SAFETY: + // Rust `Box::into_raw` poiter, so is valid + unsafe { &*self.rust } } } impl Drop for RemoteStub { fn drop(&mut self) { + // SAFETY: + // Because self is valid, its internal native pointer is valid. unsafe { ipc_binding::RemoteObjectDecStrongRef(self.native); } @@ -89,9 +108,19 @@ impl Drop for RemoteStub { /// C call Rust impl RemoteStub { - unsafe extern "C" fn on_remote_request(user_data: *mut c_void, code: u32, - data: *const CParcel, reply: *mut CParcel) -> i32 { + /// # Safety + /// + /// The parameters passed in should ensure FFI safety + /// user_data pointer, data pointer and reply pointer on the c side must be guaranteed not to be null + unsafe extern "C" fn on_remote_request( + user_data: *mut c_void, + code: u32, + data: *const CParcel, + reply: *mut CParcel, + ) -> i32 { let res = { + // BorrowedMsgParcel calls the correlation function from_raw must return as Some, + // direct deconstruction will not crash. let mut reply = BorrowedMsgParcel::from_raw(reply).unwrap(); let data = BorrowedMsgParcel::from_raw(data as *mut CParcel).unwrap(); let rust_object: &T = &*(user_data as *mut T); @@ -99,10 +128,41 @@ impl RemoteStub { }; res } - + /// # Safety + /// + /// The parameters passed in should ensure FFI safety + /// user_data pointer, data pointer and reply pointer on the c side must be guaranteed not to be null + unsafe extern "C" fn on_dump(user_data: *mut c_void, data: *const CParcel) -> i32 { + let res = { + let rust_object: &T = &*(user_data as *mut T); + // BorrowedMsgParcel calls the correlation functio from_raw must return as Some, + // direct deconstruction will not crash. + let data = BorrowedMsgParcel::from_raw(data as *mut CParcel).unwrap(); + let file: FileDesc = match data.read::() { + Ok(file) => file, + _ => { + error!(LOG_LABEL, "read FileDesc failed"); + return IpcStatusCode::Failed as i32; + } + }; + let mut args: Vec = match data.read::>() { + Ok(args) => args, + _ => { + error!(LOG_LABEL, "read String16 array failed"); + return IpcStatusCode::Failed as i32; + } + }; + rust_object.on_dump(&file, &mut args) + }; + res + } + /// # Safety + /// + /// The parameters passed in should ensure FFI safety + /// user_data pointer, data pointer and reply pointer on the c side must be guaranteed not to be null unsafe extern "C" fn on_destroy(user_data: *mut c_void) { info!(LOG_LABEL, "RemoteStub on_destroy in Rust"); // T will be freed by Box after this function end. drop(Box::from_raw(user_data as *mut T)); } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/ipc_binding.rs b/src/ipc_rust/src/ipc_binding.rs index 93ab5418438d8707e585d89a9054e82157491dd2..e8b9003f48979d38b8248c1b45fc62d11d1c3baa 100644 --- a/src/ipc_rust/src/ipc_binding.rs +++ b/src/ipc_rust/src/ipc_binding.rs @@ -15,7 +15,7 @@ #![allow(dead_code)] -use std::ffi::{c_char, c_void, c_ulong}; +use std::ffi::{c_char, c_ulong, c_void}; #[repr(C)] pub struct CRemoteObject { @@ -39,52 +39,53 @@ pub struct CAshmem { // Callback function type for OnRemoteRequest() from native, this // callback will be called when native recive client IPC request. -pub type OnRemoteRequest = unsafe extern "C" fn ( +pub type OnRemoteRequest = unsafe extern "C" fn( user_data: *mut c_void, code: u32, data: *const CParcel, - reply: *mut CParcel + reply: *mut CParcel, ) -> i32; +// Callback function type for OnRemoteDump() from native, this +// callback will be called when native recive client IPC dump. +pub type OnRemoteDump = unsafe extern "C" fn(user_data: *mut c_void, data: *const CParcel) -> i32; + // Callback function type for OnRemoteObjectDestroy() from native, // this callback will be called when native remote object destroyed. -pub type OnRemoteObjectDestroy = unsafe extern "C" fn ( - user_data: *mut c_void -); +pub type OnRemoteObjectDestroy = unsafe extern "C" fn(user_data: *mut c_void); // Callback function type for OnDeathRecipientCb() from native, // this callback will be called when remote stub is destroyed. -pub type OnDeathRecipientCb = unsafe extern "C" fn ( - callback: *mut c_void -); +pub type OnDeathRecipientCb = unsafe extern "C" fn(callback: *mut c_void); // Callback function type for OnDeathRecipientDestroyCb() from native, // this callback will be called when native CDeathRecipient destroyed. -pub type OnDeathRecipientDestroyCb = unsafe extern "C" fn ( - callback: *mut c_void -); +pub type OnDeathRecipientDestroyCb = unsafe extern "C" fn(callback: *mut c_void); // Callback function type for OnCParcelBytesAllocator() from native, // this callback will be called when native parcel need allocate buffer // for string or bytes buffer by rust. -pub type OnCParcelBytesAllocator = unsafe extern "C" fn ( - value: *mut c_void, - buffer: *mut *mut T, - len: i32 -) -> bool; +pub type OnCParcelBytesAllocator = + unsafe extern "C" fn(value: *mut c_void, buffer: *mut *mut T, len: i32) -> bool; -pub type OnCParcelAllocator = unsafe extern "C" fn ( - value: *mut c_void, - len: i32 -) -> bool; +pub type OnCParcelAllocator = unsafe extern "C" fn(value: *mut c_void, len: i32) -> bool; // Callback function type for CParcelReadStringArray() from native. // Rust side need read string one by one from native according calling // CParcelReadStringElement(). pub type OnStringArrayRead = unsafe extern "C" fn( data: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust vector pointer - len: u32 // C++ vector length + value: *mut c_void, // Rust vector pointer + len: u32, // C++ vector length +) -> bool; + +// Callback function type for CParcelReadString16Array() from native. +// Rust side need read string one by one from native according calling +// CParcelReadString16Element(). +pub type OnString16ArrayRead = unsafe extern "C" fn( + data: *const c_void, // C++ vector pointer + value: *mut c_void, // Rust vector pointer + len: u32, // C++ vector length ) -> bool; // Callback function type for CParcelWriteStringArray() from native. @@ -92,51 +93,67 @@ pub type OnStringArrayRead = unsafe extern "C" fn( // CParcelWriteStringElement(). pub type OnStringArrayWrite = unsafe extern "C" fn( array: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust vector pointer - len: u32, // Rust vector length + value: *mut c_void, // Rust vector pointer + len: u32, // Rust vector length ) -> bool; -pub type OnCParcelWriteElement = unsafe extern "C" fn ( - value: *mut CParcel, - arr: *const c_void, - index: c_ulong, +// Callback function type for CParcelWriteString16Array() from native. +// Rust side need write string one by one to native according calling +// CParcelWriteString16Element(). +pub type OnString16ArrayWrite = unsafe extern "C" fn( + array: *const c_void, // C++ vector pointer + value: *mut c_void, // Rust vector pointer + len: u32, // Rust vector length ) -> bool; -pub type OnCParcelReadElement = unsafe extern "C" fn ( - value: *const CParcel, - arr: *mut c_void, - index: c_ulong, -) -> bool; +pub type OnCParcelWriteElement = + unsafe extern "C" fn(value: *mut CParcel, arr: *const c_void, index: c_ulong) -> bool; + +pub type OnCParcelReadElement = + unsafe extern "C" fn(value: *const CParcel, arr: *mut c_void, index: c_ulong) -> bool; // C interface for IPC core object extern "C" { - pub fn CreateRemoteStub(descripor: *const c_char, on_remote_request: OnRemoteRequest, + pub fn CreateRemoteStub( + descripor: *const c_char, + on_remote_request: OnRemoteRequest, on_remote_object_destroy: OnRemoteObjectDestroy, - user_data: *const c_void) -> *mut CRemoteObject; + user_data: *const c_void, + on_remote_dump: OnRemoteDump, + ) -> *mut CRemoteObject; pub fn RemoteObjectIncStrongRef(object: *mut CRemoteObject); pub fn RemoteObjectDecStrongRef(object: *mut CRemoteObject); - pub fn RemoteObjectSendRequest(object: *mut CRemoteObject, code: u32, - data: *const CParcel, reply: *mut CParcel, is_async: bool) -> i32; - pub fn RemoteObjectLessThan(object: *const CRemoteObject, - other: *const CRemoteObject) -> bool; + pub fn RemoteObjectSendRequest( + object: *mut CRemoteObject, + code: u32, + data: *const CParcel, + reply: *mut CParcel, + is_async: bool, + ) -> i32; + pub fn RemoteObjectLessThan(object: *const CRemoteObject, other: *const CRemoteObject) -> bool; - pub fn CreateDeathRecipient(onDeathRecipient: OnDeathRecipientCb, + pub fn CreateDeathRecipient( + onDeathRecipient: OnDeathRecipientCb, onDestroy: OnDeathRecipientDestroyCb, - userData: *const c_void) -> *mut CDeathRecipient; + userData: *const c_void, + ) -> *mut CDeathRecipient; pub fn DeathRecipientDecStrongRef(recipient: *mut CDeathRecipient); - pub fn AddDeathRecipient(object: *mut CRemoteObject, - recipient: *mut CDeathRecipient) -> bool; - pub fn RemoveDeathRecipient(object: *mut CRemoteObject, - recipient: *mut CDeathRecipient) -> bool; + pub fn AddDeathRecipient(object: *mut CRemoteObject, recipient: *mut CDeathRecipient) -> bool; + pub fn RemoveDeathRecipient( + object: *mut CRemoteObject, + recipient: *mut CDeathRecipient, + ) -> bool; pub fn IsProxyObject(object: *mut CRemoteObject) -> bool; - pub fn Dump(object: *mut CRemoteObject, fd: i32, value: *const c_void, len: i32, - writer: OnStringArrayWrite) -> i32; + pub fn Dump(object: *mut CRemoteObject, fd: i32, parcel: *mut CParcel) -> i32; pub fn IsObjectDead(object: *mut CRemoteObject) -> bool; - pub fn GetInterfaceDescriptor(object: *mut CRemoteObject, - value: *mut c_void, allocator: OnCParcelBytesAllocator::) -> bool; + pub fn GetInterfaceDescriptor( + object: *mut CRemoteObject, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; } // C interface for Parcel @@ -159,59 +176,124 @@ extern "C" { pub fn CParcelWriteDouble(parcel: *mut CParcel, value: f64) -> bool; pub fn CParcelReadDouble(parcel: *const CParcel, value: *mut f64) -> bool; pub fn CParcelWriteString(parcel: *mut CParcel, value: *const c_char, len: i32) -> bool; - pub fn CParcelReadString(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadString( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteString16(parcel: *mut CParcel, value: *const c_char, len: i32) -> bool; - pub fn CParcelReadString16(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; - pub fn CParcelWriteInterfaceToken(parcel: *mut CParcel, - token: *const c_char, len: i32) -> bool; - pub fn CParcelReadInterfaceToken(parcel: *const CParcel, token: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadString16( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; + pub fn CParcelWriteInterfaceToken(parcel: *mut CParcel, token: *const c_char, len: i32) + -> bool; + pub fn CParcelReadInterfaceToken( + parcel: *const CParcel, + token: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteRemoteObject(parcel: *mut CParcel, object: *mut CRemoteObject) -> bool; pub fn CParcelReadRemoteObject(parcel: *const CParcel) -> *mut CRemoteObject; pub fn CParcelWriteBuffer(parcel: *mut CParcel, value: *const u8, len: u32) -> bool; pub fn CParcelReadBuffer(parcel: *const CParcel, value: *mut u8, len: u32) -> bool; pub fn CParcelWriteRawData(parcel: *mut CParcel, value: *const u8, len: u32) -> bool; - pub fn CParcelReadRawData(parcel: *const CParcel, len: u32) -> *mut u8; + pub fn CParcelReadRawData(parcel: *const CParcel, len: u32) -> *mut u8; pub fn CParcelWriteFileDescriptor(parcel: *mut CParcel, fd: i32) -> bool; pub fn CParcelReadFileDescriptor(parcel: *const CParcel, fd: *mut i32) -> bool; pub fn CParcelWriteBoolArray(parcel: *mut CParcel, value: *const bool, len: i32) -> bool; - pub fn CParcelReadBoolArray(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadBoolArray( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteInt8Array(parcel: *mut CParcel, value: *const i8, len: i32) -> bool; - pub fn CParcelReadInt8Array(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadInt8Array( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteInt16Array(parcel: *mut CParcel, value: *const i16, len: i32) -> bool; - pub fn CParcelReadInt16Array(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadInt16Array( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteInt32Array(parcel: *mut CParcel, value: *const i32, len: i32) -> bool; - pub fn CParcelReadInt32Array(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadInt32Array( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteInt64Array(parcel: *mut CParcel, value: *const i64, len: i32) -> bool; - pub fn CParcelReadInt64Array(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadInt64Array( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteFloatArray(parcel: *mut CParcel, value: *const f32, len: i32) -> bool; - pub fn CParcelReadFloatArray(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; + pub fn CParcelReadFloatArray( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; pub fn CParcelWriteDoubleArray(parcel: *mut CParcel, value: *const f64, len: i32) -> bool; - pub fn CParcelReadDoubleArray(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; - pub fn CParcelWriteStringArray(parcel: *mut CParcel, value: *const c_void, len: i32, - writer: OnStringArrayWrite) -> bool; - pub fn CParcelWriteStringElement(data: *const c_void, value: *const c_char, - len: i32) -> bool; - pub fn CParcelWritU16stringElement(data: *const c_void, value: *const c_char, - len: i32) -> bool; - pub fn CParcelReadStringArray(parcel: *const CParcel, value: *mut c_void, - reader: OnStringArrayRead) -> bool; - pub fn CParcelReadStringElement(index: u32, data: *const c_void, value: *mut c_void, - allocator: OnCParcelBytesAllocator::) -> bool; - pub fn CParcelWriteParcelableArray(parcel: *mut CParcel, value: *const c_void, len: i32, - element_writer: OnCParcelWriteElement) -> bool; - pub fn CParcelReadParcelableArray(parcel: *const CParcel, value: *mut c_void, - allocator: OnCParcelAllocator, element_reader: OnCParcelReadElement ) -> bool; + pub fn CParcelReadDoubleArray( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; + pub fn CParcelWriteStringArray( + parcel: *mut CParcel, + value: *const c_void, + len: i32, + writer: OnStringArrayWrite, + ) -> bool; + pub fn CParcelWriteString16Array( + parcel: *mut CParcel, + value: *const c_void, + len: i32, + writer: OnString16ArrayWrite, + ) -> bool; + pub fn CParcelWriteStringElement(data: *const c_void, value: *const c_char, len: i32) -> bool; + pub fn CParcelWritU16stringElement(data: *const c_void, value: *const c_char, len: i32) + -> bool; + pub fn CParcelReadStringArray( + parcel: *const CParcel, + value: *mut c_void, + reader: OnStringArrayRead, + ) -> bool; + pub fn CParcelReadString16Array( + parcel: *const CParcel, + value: *mut c_void, + reader: OnString16ArrayRead, + ) -> bool; + pub fn CParcelReadStringElement( + index: u32, + data: *const c_void, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; + pub fn CParcelReadString16Element( + index: u32, + data: *const c_void, + value: *mut c_void, + allocator: OnCParcelBytesAllocator, + ) -> bool; + pub fn CParcelWriteParcelableArray( + parcel: *mut CParcel, + value: *const c_void, + len: i32, + element_writer: OnCParcelWriteElement, + ) -> bool; + pub fn CParcelReadParcelableArray( + parcel: *const CParcel, + value: *mut c_void, + allocator: OnCParcelAllocator, + element_reader: OnCParcelReadElement, + ) -> bool; pub fn CParcelGetDataSize(parcel: *const CParcel) -> u32; pub fn CParcelSetDataSize(parcel: *mut CParcel, new_size: u32) -> bool; @@ -250,8 +332,7 @@ extern "C" { pub fn SetCAshmemProtection(ashmem: *mut CAshmem, protectionType: i32) -> bool; pub fn GetCAshmemProtection(ashmem: *const CAshmem) -> i32; pub fn GetCAshmemSize(ashmem: *const CAshmem) -> i32; - pub fn WriteToCAshmem(ashmem: *mut CAshmem, data: *const u8, - size: i32, offset: i32) -> bool; + pub fn WriteToCAshmem(ashmem: *mut CAshmem, data: *const u8, size: i32, offset: i32) -> bool; pub fn ReadFromCAshmem(ashmem: *const CAshmem, size: i32, offset: i32) -> *const u8; pub fn GetCAshmemFd(ashmem: *const CAshmem) -> i32; } @@ -270,7 +351,8 @@ extern "C" { pub fn SetMaxWorkThreadNum(maxThreadNum: i32) -> bool; pub fn IsLocalCalling() -> bool; pub fn SetCallingIdentity(identity: *const c_char) -> bool; - pub fn GetLocalDeviceID(value: *mut c_void, allocator: OnCParcelBytesAllocator::) -> bool; - pub fn GetCallingDeviceID(value: *mut c_void, allocator: OnCParcelBytesAllocator::) -> bool; - pub fn ResetCallingIdentity(value: *mut c_void, allocator: OnCParcelBytesAllocator::) -> bool; -} \ No newline at end of file + pub fn GetLocalDeviceID(value: *mut c_void, allocator: OnCParcelBytesAllocator) -> bool; + pub fn GetCallingDeviceID(value: *mut c_void, allocator: OnCParcelBytesAllocator) -> bool; + pub fn ResetCallingIdentity(value: *mut c_void, allocator: OnCParcelBytesAllocator) + -> bool; +} diff --git a/src/ipc_rust/src/lib.rs b/src/ipc_rust/src/lib.rs index acb3e00cf9d6fe23db65f570818e867c6f176407..9e8ce3e79266c4502efc4326aa9d2566db00933a 100644 --- a/src/ipc_rust/src/lib.rs +++ b/src/ipc_rust/src/lib.rs @@ -14,37 +14,37 @@ */ //! Safe Rust interface to OHOS IPC/RPC -mod ipc_binding; + +mod ashmem; mod errors; mod ipc; +mod ipc_binding; mod parcel; mod process; -mod ashmem; + +// TODO +pub use ipc_binding::CRemoteObject; // Export types of this crate -pub use crate::errors::{Result, result_status}; +pub use crate::ashmem::{Ashmem, PROT_EXEC, PROT_NONE, PROT_READ, PROT_WRITE}; +pub use crate::errors::{parse_status_code, status_result, IpcResult, IpcStatusCode}; pub use crate::ipc::{ - IRemoteBroker, IRemoteObj, IRemoteStub, FromRemoteObj, RemoteObjRef, - remote_obj::RemoteObj, remote_obj::death_recipient::DeathRecipient, - remote_stub::RemoteStub, -}; -pub use crate::parcel::{ - MsgParcel, BorrowedMsgParcel, IMsgParcel, RawData, - parcelable::{Serialize, Deserialize, SerOption, DeOption}, + remote_obj::death_recipient::DeathRecipient, remote_obj::RemoteObj, remote_stub::RemoteStub, + FromRemoteObj, IRemoteBroker, IRemoteObj, IRemoteStub, RemoteObjRef, }; -pub use crate::parcel::parcelable::{SerArray, DeArray}; +pub use crate::parcel::parcelable::{DeArray, SerArray}; pub use crate::parcel::types::{ - interface_token::InterfaceToken, file_desc::FileDesc, - string16::String16 + file_desc::FileDesc, interface_token::InterfaceToken, string16::String16, }; -pub use crate::ashmem::{ - Ashmem, PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC, +pub use crate::parcel::{ + parcelable::{DeOption, Deserialize, SerOption, Serialize}, + BorrowedMsgParcel, IMsgParcel, MsgParcel, RawData, }; pub use crate::process::{ - get_context_object, add_service, get_service, join_work_thread, stop_work_thread, - get_calling_uid, get_calling_token_id, get_first_token_id, get_self_token_id, - get_calling_pid, set_max_work_thread, is_local_calling, set_calling_identity, - get_local_device_id, get_calling_device_id, reset_calling_identity, + add_service, get_calling_device_id, get_calling_pid, get_calling_token_id, get_calling_uid, + get_context_object, get_first_token_id, get_local_device_id, get_self_token_id, get_service, + is_local_calling, join_work_thread, reset_calling_identity, set_calling_identity, + set_max_work_thread, stop_work_thread, }; /// First request code available for user IPC request(inclusive) @@ -70,4 +70,4 @@ pub unsafe trait AsRawPtr { /// Return a mutable pointer to the native version of `self` fn as_mut_raw(&mut self) -> *mut T; -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/mod.rs b/src/ipc_rust/src/parcel/mod.rs index f89edebc371a6db26f8b0eea13cd7c0e19eef32a..ad8144ae08214202560dd5c71d447137fbe6e56a 100644 --- a/src/ipc_rust/src/parcel/mod.rs +++ b/src/ipc_rust/src/parcel/mod.rs @@ -16,171 +16,157 @@ pub mod parcelable; pub mod types; +pub use parcelable::allocate_vec_with_buffer; pub use types::on_string16_writer; -pub use types::vec_u16_to_string; pub use types::vec_to_string; -pub use parcelable::allocate_vec_with_buffer; +pub use types::vec_u16_to_string; -use crate::{ipc_binding, Result}; -use crate::ipc_binding::{CParcel}; +use crate::ipc_binding::CParcel; +use crate::parcel::parcelable::{Deserialize, Serialize}; +use crate::AsRawPtr; +use crate::{ipc_binding, IpcResult, IpcStatusCode}; use std::marker::PhantomData; -use std::mem::{ManuallyDrop,MaybeUninit}; -use std::ops::{Drop}; -use std::ptr::{NonNull}; +use std::mem::{ManuallyDrop, MaybeUninit}; +use std::ops::Drop; +use std::ptr::NonNull; use std::slice; -use crate::AsRawPtr; -use crate::parcel::parcelable::{Serialize, Deserialize}; /// This trait implements the common function for MsgParcel /// and BorrowedMsgParcel pub trait IMsgParcel: AsRawPtr { /// Get current data size in parcel fn get_data_size(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetDataSize(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetDataSize(self.as_raw()) } } /// Set current data size in parcel fn set_data_size(&mut self, new_size: u32) -> bool { - unsafe { - ipc_binding::CParcelSetDataSize(self.as_mut_raw(), new_size) - } + // SAFETY: + unsafe { ipc_binding::CParcelSetDataSize(self.as_mut_raw(), new_size) } } /// Get current data capacity in parcel fn get_data_capacity(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetDataCapacity(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetDataCapacity(self.as_raw()) } } /// Set current data capacity in parcel fn set_data_capacity(&mut self, new_size: u32) -> bool { - unsafe { - ipc_binding::CParcelSetDataCapacity(self.as_mut_raw(), new_size) - } + // SAFETY: + unsafe { ipc_binding::CParcelSetDataCapacity(self.as_mut_raw(), new_size) } } /// Get maximum capacity in parcel fn get_max_capacity(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetMaxCapacity(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetMaxCapacity(self.as_raw()) } } /// Set maximum capacity in parcel fn set_max_capacity(&mut self, new_size: u32) -> bool { - unsafe { - ipc_binding::CParcelSetMaxCapacity(self.as_mut_raw(), new_size) - } + // SAFETY: + unsafe { ipc_binding::CParcelSetMaxCapacity(self.as_mut_raw(), new_size) } } /// Get current writalbe bytes in parcel fn get_writable_bytes(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetWritableBytes(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetWritableBytes(self.as_raw()) } } /// Get current readable bytes in parcel fn get_readable_bytes(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetReadableBytes(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetReadableBytes(self.as_raw()) } } /// Get current read position of parcel fn get_read_position(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetReadPosition(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetReadPosition(self.as_raw()) } } /// Get current write position of parcel fn get_write_position(&self) -> u32 { - unsafe { - ipc_binding::CParcelGetWritePosition(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetWritePosition(self.as_raw()) } } /// Rewind the read position to a new position of parcel fn rewind_read(&mut self, new_pos: u32) -> bool { - unsafe { - ipc_binding::CParcelRewindRead(self.as_mut_raw(), new_pos) - } + // SAFETY: + unsafe { ipc_binding::CParcelRewindRead(self.as_mut_raw(), new_pos) } } /// Rewind the write position to a new position of parcel fn rewind_write(&mut self, new_pos: u32) -> bool { - unsafe { - ipc_binding::CParcelRewindWrite(self.as_mut_raw(), new_pos) - } + // SAFETY: + unsafe { ipc_binding::CParcelRewindWrite(self.as_mut_raw(), new_pos) } } /// Write a bytes stream into parcel fn write_buffer(&mut self, data: &[u8]) -> bool { // SAFETY: unsafe { - ipc_binding::CParcelWriteBuffer(self.as_mut_raw(), - data.as_ptr(), data.len() as u32) + ipc_binding::CParcelWriteBuffer(self.as_mut_raw(), data.as_ptr(), data.len() as u32) } } /// Read a sized bytes stream from parcel - fn read_buffer(&self, len: u32) -> Result> { + fn read_buffer(&self, len: u32) -> IpcResult> { let mut buffer: Vec> = Vec::with_capacity(len as usize); // SAFETY: this is safe because the vector contains MaybeUninit elements which can be uninitialized - unsafe{ + unsafe { buffer.set_len(len as usize); } - + // SAFETY: let ok_status = unsafe { - ipc_binding::CParcelReadBuffer( - self.as_raw(), - buffer.as_mut_ptr() as *mut u8, - len - ) + ipc_binding::CParcelReadBuffer(self.as_raw(), buffer.as_mut_ptr() as *mut u8, len) }; // SAFETY: MaybeUninit has been initialized, this should be safe // since MaybeUninit should have same layout as inner type unsafe fn transmute_vec(v: Vec>) -> Vec { std::mem::transmute(v) } + // SAFETY: let buffer = unsafe { transmute_vec(buffer) }; - if ok_status { Ok(buffer) } else { Err(-1) } + if ok_status { + Ok(buffer) + } else { + Err(IpcStatusCode::Failed) + } } /// Write a large bytes stream into parcel fn write_raw_data(&mut self, data: &[u8]) -> bool { // SAFETY: unsafe { - ipc_binding::CParcelWriteRawData(self.as_mut_raw(), - data.as_ptr(), data.len() as u32) + ipc_binding::CParcelWriteRawData(self.as_mut_raw(), data.as_ptr(), data.len() as u32) } } /// Read a big bytes stream from parcel - fn read_raw_data(&self, len: u32) -> Result { - let raw_data_ptr = unsafe { - ipc_binding::CParcelReadRawData(self.as_raw(), len) - }; + fn read_raw_data(&self, len: u32) -> IpcResult { + // SAFETY: + let raw_data_ptr = unsafe { ipc_binding::CParcelReadRawData(self.as_raw(), len) }; if raw_data_ptr.is_null() { - Err(-1) + Err(IpcStatusCode::Failed) } else { Ok(RawData::new(raw_data_ptr, len)) - } + } } /// contain file descriptors fn has_fd(&self) -> bool { - unsafe { - ipc_binding::CParcelContainFileDescriptors(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelContainFileDescriptors(self.as_raw()) } } /// clear file descriptor fn clear_fd(&mut self) { + // SAFETY: unsafe { ipc_binding::CParcelClearFileDescriptor(self.as_mut_raw()); } @@ -188,20 +174,19 @@ pub trait IMsgParcel: AsRawPtr { /// get raw data size fn get_raw_data_size(&self) -> usize { - unsafe { - ipc_binding::CParcelGetRawDataSize(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetRawDataSize(self.as_raw()) } } /// get raw data capacity fn get_raw_data_capacity(&self) -> usize { - unsafe { - ipc_binding::CParcelGetRawDataCapacity(self.as_raw()) - } + // SAFETY: + unsafe { ipc_binding::CParcelGetRawDataCapacity(self.as_raw()) } } /// set clear fd flag fn set_clear_fd_flag(&mut self) { + // SAFETY: unsafe { ipc_binding::CParcelSetClearFdFlag(self.as_mut_raw()); } @@ -210,55 +195,46 @@ pub trait IMsgParcel: AsRawPtr { /// append a MsgParcel fn append(&mut self, data: &mut MsgParcel) -> bool { let data_parcel = data.as_mut_raw(); - unsafe { - ipc_binding::CParcelAppend(self.as_mut_raw(), data_parcel) - } + // SAFETY: + unsafe { ipc_binding::CParcelAppend(self.as_mut_raw(), data_parcel) } } } /// Rust RawData type which just for fetch data from C++ MssageParcel::ReadRawData() #[repr(C)] -pub struct RawData{ +pub struct RawData { raw_ptr: *const u8, len: u32, } -impl RawData{ +impl RawData { /// Create RawData object pub fn new(raw_ptr: *const u8, len: u32) -> Self { - RawData { - raw_ptr, - len, - } + RawData { raw_ptr, len } } /// The caller should ensure that the u8 slice can be /// correctly converted to other rust types - pub fn read(&self, start: u32, len: u32) -> Result<&[u8]> { + pub fn read(&self, start: u32, len: u32) -> IpcResult<&[u8]> { if len == 0 || len > self.len || start >= self.len || (start + len) > self.len { - return Err(-1); + return Err(IpcStatusCode::Failed); } - - let data_ptr = unsafe { - // SAFETY: raw_ptr is valid in [0..len], the memory is matained by C++ Parcel. - self.raw_ptr.add(start as usize) - }; + // SAFETY: + // raw_ptr is valid in [0..len], the memory is matained by C++ Parcel. + let data_ptr = unsafe { self.raw_ptr.add(start as usize) }; if !data_ptr.is_null() { // SAFETY: // 1. data is valid for reads for `len * mem::size_of::() ` // 2. The entire memory range of this slice be contained within a single allocated object (From Cpp) // 3. data_ptr point to len consecutive properly initialized values of `u8` // 4. The total size `len * mem::size_of::()` of the slice is no larger than `isize::MAX` - unsafe { - Ok(slice::from_raw_parts::(data_ptr, len as usize)) - } + unsafe { Ok(slice::from_raw_parts::(data_ptr, len as usize)) } } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } - /// Container for a message (data and object references) that can be sent /// through Binder. /// @@ -267,7 +243,9 @@ impl RawData{ pub struct MsgParcel { ptr: NonNull, } - +/// # Safety +/// +/// An `MsgParcel` is an immutable handle to CParcel, which is thread-safe unsafe impl Send for MsgParcel {} impl IMsgParcel for MsgParcel {} @@ -275,11 +253,10 @@ impl IMsgParcel for MsgParcel {} impl MsgParcel { /// Create a MsgParcel object pub fn new() -> Option { - let cparcel: *mut CParcel = unsafe { - ipc_binding::CParcelObtain() - }; + // SAFETY: + let cparcel: *mut CParcel = unsafe { ipc_binding::CParcelObtain() }; - NonNull::new(cparcel).map(|x| MsgParcel{ptr: x}) + NonNull::new(cparcel).map(|x| MsgParcel { ptr: x }) } /// # Safety @@ -308,9 +285,7 @@ impl MsgParcel { // Safety: MsgParcel and BorrowedParcel are both represented in the same // way as a NonNull due to their use of repr(transparent), // so casting references as done here is valid. - unsafe { - &*(self as *const MsgParcel as *const BorrowedMsgParcel<'_>) - } + unsafe { &*(self as *const MsgParcel as *const BorrowedMsgParcel<'_>) } } } @@ -330,9 +305,8 @@ unsafe impl AsRawPtr for MsgParcel { impl Drop for MsgParcel { fn drop(&mut self) { - unsafe { - ipc_binding::CParcelDecStrongRef(self.as_mut_raw()) - } + // Safety: + unsafe { ipc_binding::CParcelDecStrongRef(self.as_mut_raw()) } } } @@ -349,7 +323,6 @@ pub struct BorrowedMsgParcel<'a> { impl<'a> IMsgParcel for BorrowedMsgParcel<'a> {} impl<'a> BorrowedMsgParcel<'a> { - /// # Safety /// /// `*mut CParcel` must be a valid pointer @@ -385,24 +358,24 @@ unsafe impl<'a> AsRawPtr for BorrowedMsgParcel<'a> { impl MsgParcel { /// Read a data object which implements the Deserialize trait from MsgParcel - pub fn read(&self) -> Result { + pub fn read(&self) -> IpcResult { self.borrowed_ref().read() } /// Write a data object which implements the Serialize trait to MsgParcel - pub fn write(&mut self, parcelable: &S) -> Result<()> { + pub fn write(&mut self, parcelable: &S) -> IpcResult<()> { self.borrowed().write(parcelable) } } impl<'a> BorrowedMsgParcel<'a> { /// Read a data object which implements the Deserialize trait from BorrowedMsgParcel - pub fn read(&self) -> Result { + pub fn read(&self) -> IpcResult { D::deserialize(self) } /// Write a data object which implements the Serialize trait to BorrowedMsgParcel - pub fn write(&mut self, parcelable: &S) -> Result<()> { + pub fn write(&mut self, parcelable: &S) -> IpcResult<()> { parcelable.serialize(self) } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/parcelable.rs b/src/ipc_rust/src/parcel/parcelable.rs index cfc69ace9b11d87bf1291db32cbc64fc32173418..4806bfaceb164ea5d93f637497e574c0dd81de66 100644 --- a/src/ipc_rust/src/parcel/parcelable.rs +++ b/src/ipc_rust/src/parcel/parcelable.rs @@ -13,9 +13,9 @@ * limitations under the License. */ -use crate::{Result, result_status, BorrowedMsgParcel, ipc_binding, AsRawPtr}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode}; +use std::ffi::{c_ulong, c_void}; use std::mem::MaybeUninit; -use std::ffi::{c_void, c_ulong}; use std::ptr; /// Implement `Serialize` trait to serialize a custom MsgParcel. @@ -26,14 +26,14 @@ use std::ptr; /// struct Year(i64); /// /// impl Serialize for Year { -/// fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { +/// fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { /// parcel::write(self.0); /// } /// } /// ``` pub trait Serialize { /// Serialize Self to BorrowedMsgParcel - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()>; + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()>; } /// Implement `Deserialize` trait to deserialize a custom MsgParcel. @@ -44,7 +44,7 @@ pub trait Serialize { /// struct Year(i64); /// /// impl Deserialize for Year { -/// fn deserialize(parcel: &mut BorrowedMsgParcel<'_>) -> Result { +/// fn deserialize(parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult { /// let i = parcel::read::(parcel); /// Ok(Year(i)) /// } @@ -52,16 +52,16 @@ pub trait Serialize { /// ``` pub trait Deserialize: Sized { /// Deserialize an instance from the given [`Parcel`]. - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result; + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult; } -pub const NULL_FLAG : i32 = 0; -pub const NON_NULL_FLAG : i32 = 1; +pub const NULL_FLAG: i32 = 0; +pub const NON_NULL_FLAG: i32 = 1; /// Define trait function for Option which T must implements the trait Serialize. pub trait SerOption: Serialize { /// Serialize the Option - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<(), > { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { if let Some(inner) = this { parcel.write(&NON_NULL_FLAG)?; parcel.write(inner) @@ -74,7 +74,7 @@ pub trait SerOption: Serialize { /// Define trait function for Option which T must implements the trait Deserialize. pub trait DeOption: Deserialize { /// Deserialize the Option - fn de_option(parcel: &BorrowedMsgParcel<'_>) -> Result> { + fn de_option(parcel: &BorrowedMsgParcel<'_>) -> IpcResult> { let null: i32 = parcel.read()?; if null == NULL_FLAG { Ok(None) @@ -93,7 +93,7 @@ pub trait DeOption: Deserialize { pub unsafe extern "C" fn allocate_vec_with_buffer( value: *mut c_void, buffer: *mut *mut T, - len: i32 + len: i32, ) -> bool { let res = allocate_vec::(value, len); // `buffer` will be assigned a mutable pointer to the allocated vector data @@ -111,10 +111,7 @@ pub unsafe extern "C" fn allocate_vec_with_buffer( /// /// The opaque data pointer passed to the array read function must be a mutable /// pointer to an `Option>>`. -unsafe extern "C" fn allocate_vec( - value: *mut c_void, - len: i32, -) -> bool { +unsafe extern "C" fn allocate_vec(value: *mut c_void, len: i32) -> bool { if len < 0 { return true; } @@ -131,9 +128,9 @@ unsafe extern "C" fn allocate_vec( // a few special ones like `readByteArray` for `u8`. pub trait SerArray: Serialize + Sized { /// Default array serialize implement. - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: Safe FFI, slice will always be a safe pointer to pass. let ret = unsafe { - // SAFETY: Safe FFI, slice will always be a safe pointer to pass. ipc_binding::CParcelWriteParcelableArray( parcel.as_mut_raw(), slice.as_ptr() as *const c_void, @@ -141,13 +138,15 @@ pub trait SerArray: Serialize + Sized { ser_element::, ) }; - result_status::<()>(ret, ()) + status_result::<()>(ret as i32, ()) } } /// Callback to serialize an element of a generic parcelable array. /// -/// Safety: We are relying on c interface to not overrun our slice. As long +/// # Safety +/// +/// We are relying on c interface to not overrun our slice. As long /// as it doesn't provide an index larger than the length of the original /// slice in serialize_array, this operation is safe. The index provided /// is zero-based. @@ -161,7 +160,7 @@ pub(crate) unsafe extern "C" fn ser_element( // so the function signature matches what bindgen generates. let index = index as usize; - let slice: &[T] = std::slice::from_raw_parts(array.cast(), index+1); + let slice: &[T] = std::slice::from_raw_parts(array.cast(), index + 1); let mut parcel = match BorrowedMsgParcel::from_raw(parcel) { None => return false, @@ -175,11 +174,12 @@ pub(crate) unsafe extern "C" fn ser_element( /// but can be overridden for custom implementations like `readByteArray`. pub trait DeArray: Deserialize { /// Deserialize an array of type from the given parcel. - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { let mut vec: Option>> = None; + // SAFETY: + // Safe FFI, vec is the correct opaque type expected by + // allocate_vec and de_element. let ok_status = unsafe { - // SAFETY: Safe FFI, vec is the correct opaque type expected by - // allocate_vec and de_element. ipc_binding::CParcelReadParcelableArray( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, @@ -188,25 +188,26 @@ pub trait DeArray: Deserialize { ) }; - if ok_status{ - let vec: Option> = unsafe { - // SAFETY: We are assuming that the C-API correctly initialized every - // element of the vector by now, so we know that all the - // MaybeUninits are now properly initialized. We can transmute from - // Vec> to Vec because MaybeUninit has the same - // alignment and size as T, so the pointer to the vector allocation - // will be compatible. - std::mem::transmute(vec) - }; + if ok_status { + // SAFETY: + // We are assuming that the C-API correctly initialized every + // element of the vector by now, so we know that all the + // MaybeUninits are now properly initialized. We can transmute from + // Vec> to Vec because MaybeUninit has the same + // alignment and size as T, so the pointer to the vector allocation + // will be compatible. + let vec: Option> = unsafe { std::mem::transmute(vec) }; Ok(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } /// Callback to deserialize a parcelable element. /// +/// # Safety +/// /// The opaque array data pointer must be a mutable pointer to an /// `Option>>` with at least enough elements for `index` to be valid /// (zero-based). @@ -236,28 +237,25 @@ unsafe extern "C" fn de_element( ptr::write(vec[index].as_mut_ptr(), element); true } - -/// Safety: All elements in the vector must be properly initialized. +/// # Safety +/// +/// All elements in the vector must be properly initialized. pub unsafe fn vec_assume_init(vec: Vec>) -> Vec { // We can convert from Vec> to Vec because MaybeUninit // has the same alignment and size as T, so the pointer to the vector // allocation will be compatible. let mut vec = std::mem::ManuallyDrop::new(vec); - Vec::from_raw_parts( - vec.as_mut_ptr().cast(), - vec.len(), - vec.capacity(), - ) + Vec::from_raw_parts(vec.as_mut_ptr().cast(), vec.len(), vec.capacity()) } -pub(crate) unsafe fn allocate_vec_maybeuninit( - value: *mut c_void, - len: u32, -) { +/// # Safety +/// +/// Ensure that the value pointer is not null +pub(crate) unsafe fn allocate_vec_maybeuninit(value: *mut c_void, len: u32) { let vec = &mut *(value as *mut Option>>); let mut new_vec: Vec> = Vec::with_capacity(len as usize); // SAFETY: this is safe because the vector contains MaybeUninit elements which can be uninitialized new_vec.set_len(len as usize); ptr::write(vec, Some(new_vec)); -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/types.rs b/src/ipc_rust/src/parcel/types.rs index 05854a400dd37eced04206528c4aeab3ff476cd9..00a540204c7cc89c2cb4eaff4732fe2b8e46cc6a 100644 --- a/src/ipc_rust/src/parcel/types.rs +++ b/src/ipc_rust/src/parcel/types.rs @@ -29,22 +29,25 @@ macro_rules! impl_serde_option_for_parcelable { } pub mod bool; +pub mod boxt; +pub mod const_array; +pub mod file_desc; pub mod integer; +pub mod interface_token; pub mod option; pub mod reference; -pub mod strings; -pub mod interface_token; -pub mod string16; -pub mod file_desc; -pub mod boxt; -pub mod const_array; pub mod slices; +pub mod string16; +pub mod strings; pub mod vector; pub use self::string16::on_string16_writer; -pub use self::strings::vec_u16_to_string; pub use self::strings::vec_to_string; +pub use self::strings::vec_u16_to_string; use crate::parcel::parcelable::*; +use crate::{ + ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, DeOption, IpcResult, IpcStatusCode, + SerOption, +}; use std::ffi::{c_char, c_void}; -use crate::{ipc_binding, BorrowedMsgParcel, AsRawPtr, result_status, Result, SerOption, DeOption}; diff --git a/src/ipc_rust/src/parcel/types/bool.rs b/src/ipc_rust/src/parcel/types/bool.rs index 63f77544648fc875bb8f36adbd4263a40143c650..22835c7f7422175b0802b5750123d40f0b9ffed5 100644 --- a/src/ipc_rust/src/parcel/types/bool.rs +++ b/src/ipc_rust/src/parcel/types/bool.rs @@ -19,71 +19,66 @@ use std::mem::MaybeUninit; impl_serde_option_for_parcelable!(bool); impl Serialize for bool { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = ipc_binding::CParcelWriteBool(parcel.as_mut_raw(), *self); - result_status::<()>(ret, ()) - } + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + let ret = unsafe { ipc_binding::CParcelWriteBool(parcel.as_mut_raw(), *self) }; + status_result::<()>(ret as i32, ()) } } impl Deserialize for bool { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { let mut val = Self::default(); // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadBool(parcel.as_raw(), &mut val) - }; - - result_status::(ret, val) + let ret = unsafe { ipc_binding::CParcelReadBool(parcel.as_raw(), &mut val) }; + status_result::(ret as i32, val) } } impl SerArray for bool { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: + // `parcel` always contains a valid pointer to a `CParcel` + // If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is 0. let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. ipc_binding::CParcelWriteBoolArray( parcel.as_mut_raw(), slice.as_ptr(), slice.len().try_into().unwrap(), ) }; - result_status::<()>(ret, ()) + status_result::<()>(ret as i32, ()) } } impl DeArray for bool { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { let mut vec: Option>> = None; + // SAFETY: + // `parcel` always contains a valid pointer to a `CParcel` + // `allocate_vec` expects the opaque pointer to + // be of type `*mut Option>>`, so `&mut vec` is + // correct for it. let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. ipc_binding::CParcelReadBoolArray( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, allocate_vec_with_buffer, ) }; - if ok_status{ - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; + if ok_status { + // SAFETY: + // We are assuming that the NDK correctly + // initialized every element of the vector by now, so we + // know that all the MaybeUninits are now properly + // initialized. + let vec: Option> = unsafe { vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } diff --git a/src/ipc_rust/src/parcel/types/boxt.rs b/src/ipc_rust/src/parcel/types/boxt.rs index 689210a949d15f7fefe7a0dd2a18b778c032799f..ef3b51d8e063563c66797c5d4b88c0c78a77f3be 100644 --- a/src/ipc_rust/src/parcel/types/boxt.rs +++ b/src/ipc_rust/src/parcel/types/boxt.rs @@ -16,25 +16,25 @@ use super::*; impl Serialize for Box { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { Serialize::serialize(&**self, parcel) } } impl Deserialize for Box { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { Deserialize::deserialize(parcel).map(Box::new) } } impl SerOption for Box { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerOption::ser_option(this.map(|inner| &**inner), parcel) } } impl DeOption for Box { - fn de_option(parcel: &BorrowedMsgParcel<'_>) -> Result> { + fn de_option(parcel: &BorrowedMsgParcel<'_>) -> IpcResult> { DeOption::de_option(parcel).map(|t| t.map(Box::new)) } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/types/const_array.rs b/src/ipc_rust/src/parcel/types/const_array.rs index c25225ccc0a4342b01eb6ffbff2096fa84158b3a..f1f4bcb2640d0b4336013c27279ff911570b8926 100644 --- a/src/ipc_rust/src/parcel/types/const_array.rs +++ b/src/ipc_rust/src/parcel/types/const_array.rs @@ -16,14 +16,14 @@ use super::*; impl Serialize for [T; N] { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { // forwards to T::serialize_array. SerArray::ser_array(self, parcel) } } impl SerOption for [T; N] { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerOption::ser_option(this.map(|arr| &arr[..]), parcel) } } @@ -31,19 +31,20 @@ impl SerOption for [T; N] { impl SerArray for [T; N] {} impl Deserialize for [T; N] { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { let vec = DeArray::de_array(parcel) .transpose() - .unwrap_or(Err(-1))?; - vec.try_into().or(Err(-1)) + .unwrap_or(Err(IpcStatusCode::Failed))?; + vec.try_into().or(Err(IpcStatusCode::Failed)) } } impl DeOption for [T; N] { - fn de_option(parcel: &BorrowedMsgParcel<'_>) -> Result> { + fn de_option(parcel: &BorrowedMsgParcel<'_>) -> IpcResult> { let vec = DeArray::de_array(parcel)?; - vec.map(|v| v.try_into().or(Err(-1))).transpose() + vec.map(|v| v.try_into().or(Err(IpcStatusCode::Failed))) + .transpose() } } -impl DeArray for [T; N] {} \ No newline at end of file +impl DeArray for [T; N] {} diff --git a/src/ipc_rust/src/parcel/types/file_desc.rs b/src/ipc_rust/src/parcel/types/file_desc.rs index 49e3c977d9b4a5d8401de6a8c95af4b833fa3216..a31702783b2ced14ecf3579c3bfc91b5ad6ace5d 100644 --- a/src/ipc_rust/src/parcel/types/file_desc.rs +++ b/src/ipc_rust/src/parcel/types/file_desc.rs @@ -14,17 +14,17 @@ */ use super::*; -use crate::{ipc_binding, BorrowedMsgParcel, AsRawPtr, result_status, Result}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode}; +use hilog_rust::{error, hilog, HiLogLabel, LogType}; +use std::ffi::CString; use std::fs::File; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use std::ffi::{CString}; -use hilog_rust::{error, hilog, HiLogLabel, LogType}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustFileDesc" + tag: "RustFileDesc", }; /// Rust version of the Java class android.os.ParcelFileDescriptor @@ -73,71 +73,87 @@ impl PartialEq for FileDesc { impl Eq for FileDesc {} impl Serialize for FileDesc { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { let fd = self.0.as_raw_fd(); - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to an `CParcel`. - ipc_binding::CParcelWriteFileDescriptor(parcel.as_mut_raw(), fd) - }; - result_status::<()>(ret, ()) + // SAFETY: + // `parcel` always contains a valid pointer to an `CParcel`. + let ret = unsafe { ipc_binding::CParcelWriteFileDescriptor(parcel.as_mut_raw(), fd) }; + status_result::<()>(ret as i32, ()) } } impl SerOption for FileDesc { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { if let Some(f) = this { f.serialize(parcel) } else { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to an `CParcel`. - // `CParcelWriteFileDescriptor` accepts the value `-1` as the file - // descriptor to signify serializing a null file descriptor. - ipc_binding::CParcelWriteFileDescriptor(parcel.as_mut_raw(), -1i32) - }; - result_status::<()>(ret, ()) + // SAFETY: + // `parcel` always contains a valid pointer to an `CParcel`. + // `CParcelWriteFileDescriptor` accepts the value `-1` as the file + // descriptor to signify serializing a null file descriptor. + let ret = + unsafe { ipc_binding::CParcelWriteFileDescriptor(parcel.as_mut_raw(), -1i32) }; + status_result::<()>(ret as i32, ()) } } } impl DeOption for FileDesc { - fn de_option(parcel: &BorrowedMsgParcel<'_>) -> Result> { + fn de_option(parcel: &BorrowedMsgParcel<'_>) -> IpcResult> { let mut fd = -1i32; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to an `CParcel`. - // `CParcelWriteFileDescriptor` accepts the value `-1` as the file - // descriptor to signify serializing a null file descriptor. - // The read function passes ownership of the file - // descriptor to its caller if it was non-null, so we must take - // ownership of the file and ensure that it is eventually closed. - ipc_binding::CParcelReadFileDescriptor( - parcel.as_raw(), - &mut fd, - ) - }; - if ok_status{ + // SAFETY: + // `parcel` always contains a valid pointer to an `CParcel`. + // `CParcelWriteFileDescriptor` accepts the value `-1` as the file + // descriptor to signify serializing a null file descriptor. + // The read function passes ownership of the file + // descriptor to its caller if it was non-null, so we must take + // ownership of the file and ensure that it is eventually closed. + let ok_status = unsafe { ipc_binding::CParcelReadFileDescriptor(parcel.as_raw(), &mut fd) }; + if ok_status { if fd < 0 { error!(LOG_LABEL, "file descriptor is invalid from native"); - Err(-1) + Err(IpcStatusCode::Failed) } else { - let file = unsafe { - // SAFETY: At this point, we know that the file descriptor was - // not -1, so must be a valid, owned file descriptor which we - // can safely turn into a `File`. - File::from_raw_fd(fd) - }; + // SAFETY: + // At this point, we know that the file descriptor was + // not -1, so must be a valid, owned file descriptor which we + // can safely turn into a `File`. + let file = unsafe { File::from_raw_fd(fd) }; Ok(Some(FileDesc::new(file))) } } else { error!(LOG_LABEL, "read file descriptor failed from native"); - Err(-1) + Err(IpcStatusCode::Failed) } } } impl Deserialize for FileDesc { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - Deserialize::deserialize(parcel) - .transpose() - .unwrap_or(Err(-1)) + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + let mut fd = -1i32; + // SAFETY: + // `parcel` always contains a valid pointer to an `CParcel`. + // `CParcelWriteFileDescriptor` accepts the value `-1` as the file + // descriptor to signify serializing a null file descriptor. + // The read function passes ownership of the file + // descriptor to its caller if it was non-null, so we must take + // ownership of the file and ensure that it is eventually closed. + let ok_status = unsafe { ipc_binding::CParcelReadFileDescriptor(parcel.as_raw(), &mut fd) }; + if ok_status { + if fd < 0 { + error!(LOG_LABEL, "file descriptor is invalid from native"); + Err(IpcStatusCode::Failed) + } else { + // SAFETY: + // At this point, we know that the file descriptor was + // not -1, so must be a valid, owned file descriptor which we + // can safely turn into a `File`. + let file = unsafe { File::from_raw_fd(fd) }; + Ok(FileDesc::new(file)) + } + } else { + error!(LOG_LABEL, "read file descriptor failed from native"); + Err(IpcStatusCode::Failed) + } } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/types/integer.rs b/src/ipc_rust/src/parcel/types/integer.rs index 8b9df637975d52d53ee69e961a68aaafa1080099..bd1cc27c187427d959ec3e48d9606a562b365c84 100644 --- a/src/ipc_rust/src/parcel/types/integer.rs +++ b/src/ipc_rust/src/parcel/types/integer.rs @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Huawei Device Co., Ltd. + * Copyright (C) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -14,542 +14,329 @@ */ use super::*; -use crate::{ipc_binding, BorrowedMsgParcel, Result, result_status, AsRawPtr}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult}; use std::mem::MaybeUninit; -impl_serde_option_for_parcelable!(i8, u8, i16, u16, i32, u32, i64, u64, f32,f64); - -// Using macros Eliminating duplicate code in the future - -/// i8 && u8 -impl Serialize for i8 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteInt8(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for i8 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadInt8(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -// u8 -> i8 -impl Serialize for u8 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - (*self as i8).serialize(parcel) - } -} -// i8 -> u8 -impl Deserialize for u8 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - i8::deserialize(parcel).map(|v| v as u8) - } -} - -/// i16 && u16 -impl Serialize for i16 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteInt16(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for i16 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadInt16(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -impl SerArray for i8 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteInt8Array( - parcel.as_mut_raw(), - slice.as_ptr(), - slice.len().try_into().unwrap(), - ) - }; - result_status::<()>(ret, ()) - } -} - -impl DeArray for i8 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadInt8Array( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) +impl_serde_option_for_parcelable!(i8, u8, i16, u16, i32, u32, i64, u64, f32, f64); + +/// Macros expand signed numbers and floating point numbers +#[macro_export] +macro_rules! parcelable_number { + { + $( + impl $trait:ident for $num_ty:ty = $fn:path; + )* + } => { + $(define_impl_serde!{$trait, $num_ty, $fn})* + }; +} + +/// # Macro expand example: +/// +/// ```ignore +/// impl Serialize for i8 { +/// fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { +/// // SAFETY: `parcel` always contains a valid pointer to a `CParcel` +/// let ret = unsafe { +/// ipc_binding::CParcelWriteInt8(parcel.as_mut_raw(), *self) +/// }; +/// status_result::<()>(i32::from(ret), ()) +/// } +/// } +/// +/// impl Deserialize for i8 { +/// fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { +/// let mut val = Self::default(); +/// // SAFETY: `parcel` always contains a valid pointer to a `CParcel` +/// let ret = unsafe { +/// ipc_binding::CParcelReadInt8(parcel.as_raw(), &mut val) +/// }; +/// status_result::(ret, val) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! define_impl_serde { + {Serialize, $num_ty:ty, $cparcel_write_fn:path} => { + impl Serialize for $num_ty { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel`, + // and any `$num_ty` literal value is safe to pass to `$cparcel_write_fn`. + let ret = unsafe { + $cparcel_write_fn(parcel.as_mut_raw(), *self) + }; + status_result::<()>(i32::from(ret), ()) + } } - } -} - -impl SerArray for u8 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: - let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const i8, slice.len()) }; - i8::ser_array(slice, parcel) - } -} - -impl DeArray for u8 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - i8::de_array(parcel).map(|v| - v.map(|mut v| v.iter_mut().map(|i| *i as u8).collect()) - ) - } -} - -impl SerArray for i16 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteInt16Array( - parcel.as_mut_raw(), - slice.as_ptr(), - slice.len().try_into().unwrap() - ) - }; - result_status::<()>(ret, ()) - } -} - -impl DeArray for i16 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadInt16Array( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) + }; + + {Deserialize, $num_ty:ty, $cparcel_read_fn:path} => { + impl Deserialize for $num_ty { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + let mut val = Self::default(); + // SAFETY: `parcel` always contains a valid pointer to a `CParcel`. + // We pass a valid, mutable pointer to `val`, a literal of type `$num_ty`, + // and `$cparcel_read_fn` will write the value read into `val` if successful + let ret = unsafe { + $cparcel_read_fn(parcel.as_raw(), &mut val) + }; + status_result::<$num_ty>(ret as i32, val) + } } } } -impl SerArray for u16 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: - let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const i16, slice.len()) }; - i16::ser_array(slice, parcel) - } -} - -impl DeArray for u16 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - i16::de_array(parcel).map(|v| - v.map(|mut v| v.iter_mut().map(|i| *i as u16).collect()) - ) - } -} - -impl Serialize for u16 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - (*self as i16).serialize(parcel) - } -} - -impl Deserialize for u16 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - i16::deserialize(parcel).map(|v| v as u16) - } -} - -/// i32 && u32 -impl Serialize for i32 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteInt32(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for i32 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadInt32(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -impl SerArray for i32 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteInt32Array( - parcel.as_mut_raw(), - slice.as_ptr(), - slice.len().try_into().unwrap(), - ) - }; - result_status::<()>(ret, ()) - } -} - -impl DeArray for i32 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadInt32Array( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) +parcelable_number! { + impl Serialize for i8 = ipc_binding::CParcelWriteInt8; + impl Deserialize for i8 = ipc_binding::CParcelReadInt8; + impl Serialize for i16 = ipc_binding::CParcelWriteInt16; + impl Deserialize for i16 = ipc_binding::CParcelReadInt16; + impl Serialize for i32 = ipc_binding::CParcelWriteInt32; + impl Deserialize for i32 = ipc_binding::CParcelReadInt32; + impl Serialize for i64 = ipc_binding::CParcelWriteInt64; + impl Deserialize for i64 = ipc_binding::CParcelReadInt64; + impl Serialize for f32 = ipc_binding::CParcelWriteFloat; + impl Deserialize for f32 = ipc_binding::CParcelReadFloat; + impl Serialize for f64 = ipc_binding::CParcelWriteDouble; + impl Deserialize for f64 = ipc_binding::CParcelReadDouble; +} + +/// Unsigned number of macro expansion +#[macro_export] +macro_rules! parcelable_for_unsign_number { + { + $( + impl $trait:ident for $unum_ty:ty as $inum_ty:ty; + )* + } => { + $( + define_impl_serde_for_unsign!{ $trait, $unum_ty, $inum_ty} + )* + }; +} + +/// # Example: +/// +/// ```ignore +/// // u8 -> i8 +/// impl Serialize for u8 { +/// fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { +/// (*self as i8).serialize(parcel) +/// } +/// } +/// // i8 -> u8 +/// impl Deserialize for u8 { +/// fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { +/// i8::deserialize(parcel).map(|v| v as u8) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! define_impl_serde_for_unsign { + {Serialize, $unum_ty:ty, $inum_ty:ty} => { + impl Serialize for $unum_ty { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + (*self as $inum_ty).serialize(parcel) + } } - } -} - -impl SerArray for u32 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: - let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const i32, slice.len()) }; - i32::ser_array(slice, parcel) - } -} - -impl DeArray for u32 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - i32::de_array(parcel).map(|v| - v.map(|mut v| v.iter_mut().map(|i| *i as u32).collect()) - ) - } -} - -impl Serialize for u32 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - (*self as i32).serialize(parcel) - } -} - -impl Deserialize for u32 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - i32::deserialize(parcel).map(|v| v as u32) - } -} + }; -/// i64 && u64 -impl Serialize for i64 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteInt64(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for i64 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadInt64(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -impl SerArray for i64 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteInt64Array( - parcel.as_mut_raw(), - slice.as_ptr() as *const i64, - slice.len().try_into().unwrap() - ) - }; - result_status::<()>(ret, ()) - } -} - -impl DeArray for i64 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadInt64Array( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) + {Deserialize, $unum_ty:ty, $inum_ty:ty} => { + impl Deserialize for $unum_ty { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { + <$inum_ty>::deserialize(parcel).map(|v| v as $unum_ty) + } } } } -impl SerArray for u64 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: - let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const i64, slice.len()) }; - i64::ser_array(slice, parcel) - } -} - -impl DeArray for u64 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - i64::de_array(parcel).map(|v| - v.map(|mut v| v.iter_mut().map(|i| *i as u64).collect()) - ) - } -} - -impl Serialize for u64 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - (*self as i64).serialize(parcel) - } -} - -impl Deserialize for u64 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - i64::deserialize(parcel).map(|v| v as u64) - } -} - -/// f32 -impl Serialize for f32 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteFloat(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for f32 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadFloat(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -impl SerArray for f32 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteFloatArray( - parcel.as_mut_raw(), - slice.as_ptr(), - slice.len().try_into().or(Err(-1))?, - ) - }; - result_status::<()>(ret, ()) - } -} - -impl DeArray for f32 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadFloatArray( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) +parcelable_for_unsign_number! { + impl Serialize for u8 as i8; + impl Deserialize for u8 as i8; + impl Serialize for u16 as i16; + impl Deserialize for u16 as i16; + impl Serialize for u32 as i32; + impl Deserialize for u32 as i32; + impl Serialize for u64 as i64; + impl Deserialize for u64 as i64; +} + +/// Macros expand signed numbers and floating point arrays +#[macro_export] +macro_rules! parcelable_array { + { + $( + impl $trait:ident for $num_ty:ty = $fn:path; + )* + } => { + $(define_impl_array!{$trait, $num_ty, $fn})* + }; +} + +/// # Example: +/// +/// ```ignore +/// impl DeArray for i8 { +/// fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { +/// let mut vec: Option>> = None; +/// // SAFETY: `parcel` always contains a valid pointer to a `CParcel` +/// // `allocate_vec` expects the opaque pointer to +/// // be of type `*mut Option>>`, so `&mut vec` is +/// // correct for it. +/// let ok_status = unsafe { +/// ipc_binding::CParcelReadInt8Array( +/// parcel.as_raw(), +/// &mut vec as *mut _ as *mut c_void, +/// allocate_vec_with_buffer, +/// ) +/// }; +/// if ok_status { +/// // SAFETY: We are assuming that the NDK correctly +/// // initialized every element of the vector by now, so we +/// // know that all the MaybeUninits are now properly +/// // initialized. +/// let vec: Option> = unsafe { +/// vec.map(|vec| vec_assume_init(vec)) +/// }; +/// Ok(vec) +/// } else { +/// Err(IpcStatusCode::Failed) +/// } +/// } +// } +/// ``` +#[macro_export] +macro_rules! define_impl_array { + {SerArray, $num_ty:ty, $cparcel_write_fn:path} => { + impl SerArray for $num_ty { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + // If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is 0. + let ret = unsafe { + $cparcel_write_fn( + parcel.as_mut_raw(), + slice.as_ptr(), + slice.len().try_into().unwrap(), + ) + }; + status_result::<()>(ret as i32, ()) + } + } + }; + + {DeArray, $num_ty:ty, $cparcel_read_fn:path} => { + impl DeArray for $num_ty { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { + let mut vec: Option>> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + // `allocate_vec` expects the opaque pointer to + // be of type `*mut Option>>`, so `&mut vec` is + // correct for it. + let ok_status = unsafe { + $cparcel_read_fn( + parcel.as_raw(), + &mut vec as *mut _ as *mut c_void, + allocate_vec_with_buffer, + ) + }; + if ok_status { + // SAFETY: We are assuming that the NDK correctly + // initialized every element of the vector by now, so we + // know that all the MaybeUninits are now properly + // initialized. + let vec: Option> = unsafe { + vec.map(|vec| vec_assume_init(vec)) + }; + Ok(vec) + } else { + Err(IpcStatusCode::Failed) + } + } } } } -/// f64 -impl Serialize for f64 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelWriteDouble(parcel.as_mut_raw(), *self) - }; - result_status::<()>(ret, ()) - } -} - -impl Deserialize for f64 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { - let mut val = Self::default(); - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - let ret = unsafe { - ipc_binding::CParcelReadDouble(parcel.as_raw(), &mut val) - }; - result_status::(ret, val) - } -} - -impl SerArray for f64 { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { - let ret = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // If the slice is > 0 length, `slice.as_ptr()` will be a - // valid pointer to an array of elements of type `$ty`. If the slice - // length is 0, `slice.as_ptr()` may be dangling, but this is safe - // since the pointer is not dereferenced if the length parameter is - // 0. - ipc_binding::CParcelWriteDoubleArray( - parcel.as_mut_raw(), - slice.as_ptr(), - slice.len().try_into().unwrap() - ) - }; - result_status::<()>(ret, ()) +parcelable_array! { + impl SerArray for i8 = ipc_binding::CParcelWriteInt8Array; + impl DeArray for i8 = ipc_binding::CParcelReadInt8Array; + impl SerArray for i16 = ipc_binding::CParcelWriteInt16Array; + impl DeArray for i16 = ipc_binding::CParcelReadInt16Array; + impl SerArray for i32 = ipc_binding::CParcelWriteInt32Array; + impl DeArray for i32 = ipc_binding::CParcelReadInt32Array; + impl SerArray for i64 = ipc_binding::CParcelWriteInt64Array; + impl DeArray for i64 = ipc_binding::CParcelReadInt64Array; + impl SerArray for f32 = ipc_binding::CParcelWriteFloatArray; + impl DeArray for f32 = ipc_binding::CParcelReadFloatArray; + impl SerArray for f64 = ipc_binding::CParcelWriteDoubleArray; + impl DeArray for f64 = ipc_binding::CParcelReadDoubleArray; +} + +/// Macro Expand Unsigned Count Group +#[macro_export] +macro_rules! parcelable_for_array_unsign_number { + { + $( + impl $trait:ident for $unum_ty:ty as $inum_ty:ty; + )* + } => { + $( + define_impl_array_for_unsign!{ $trait, $unum_ty, $inum_ty} + )* + }; +} + +/// # Example: +/// +/// ```ignore +/// impl SerArray for u8 { +/// fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { +/// // SAFETY: +/// let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const i8, slice.len()) }; +/// i8::ser_array(slice, parcel) +/// } +/// } +/// +/// impl DeArray for u8 { +/// fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { +// i8::de_array(parcel).map(|v| +/// v.map(|mut v| v.iter_mut().map(|i| *i as u8).collect()) +/// ) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! define_impl_array_for_unsign { + {SerArray, $unum_ty:ty, $inum_ty:ty} => { + impl SerArray for $unum_ty { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: + let slice = unsafe {std::slice::from_raw_parts(slice.as_ptr() as *const $inum_ty, slice.len()) }; + <$inum_ty>::ser_array(slice, parcel) + } + } + }; + + {DeArray, $unum_ty:ty, $inum_ty:ty} => { + impl DeArray for $unum_ty { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { + <$inum_ty>::de_array(parcel).map(|v| + v.map(|mut v| v.iter_mut().map(|i| *i as $unum_ty).collect()) + ) + } + } } } -impl DeArray for f64 { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { - let mut vec: Option>> = None; - let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. - ipc_binding::CParcelReadDoubleArray( - parcel.as_raw(), - &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer, - ) - }; - if ok_status { - let vec: Option> = unsafe { - // SAFETY: We are assuming that the NDK correctly - // initialized every element of the vector by now, so we - // know that all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; - Ok(vec) - } else { - Err(-1) - } - } +parcelable_for_array_unsign_number! { + impl SerArray for u8 as i8; + impl DeArray for u8 as i8; + impl SerArray for u16 as i16; + impl DeArray for u16 as i16; + impl SerArray for u32 as i32; + impl DeArray for u32 as i32; + impl SerArray for u64 as i64; + impl DeArray for u64 as i64; } diff --git a/src/ipc_rust/src/parcel/types/interface_token.rs b/src/ipc_rust/src/parcel/types/interface_token.rs index b44c1ba420e7f87b054dabbfd88688cbf764f308..459eeb427094f19c460c1eb361dcc41aa254a44c 100644 --- a/src/ipc_rust/src/parcel/types/interface_token.rs +++ b/src/ipc_rust/src/parcel/types/interface_token.rs @@ -14,15 +14,15 @@ */ use super::*; -use crate::{ipc_binding, BorrowedMsgParcel, Result, AsRawPtr, result_status}; -use std::convert::TryInto; -use std::ffi::{CString, c_char}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode}; use hilog_rust::{error, hilog, HiLogLabel, LogType}; +use std::convert::TryInto; +use std::ffi::{c_char, CString}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustInterfaceToken" + tag: "RustInterfaceToken", }; /// InterfaceToken packed a String type which transfered with C++ std::u16string. @@ -41,47 +41,46 @@ impl InterfaceToken { } impl Serialize for InterfaceToken { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { let token = &self.0; // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ret = unsafe { ipc_binding::CParcelWriteInterfaceToken( parcel.as_mut_raw(), token.as_ptr() as *const c_char, - token.as_bytes().len().try_into().unwrap() - )}; - result_status::<()>(ret, ()) + token.as_bytes().len().try_into().unwrap(), + ) + }; + status_result::<()>(ret as i32, ()) } } impl Deserialize for InterfaceToken { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { let mut vec: Option> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` ipc_binding::CParcelReadInterfaceToken( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { - let result = vec.map(|s| { - match String::from_utf8(s) { - Ok(val) => val, - Err(_) => String::from("") - } + let result = vec.map(|s| match String::from_utf8(s) { + Ok(val) => val, + Err(_) => String::from(""), }); if let Some(val) = result { Ok(Self(val)) } else { error!(LOG_LABEL, "convert interface token to String fail"); - Err(-1) + Err(IpcStatusCode::Failed) } - }else{ + } else { error!(LOG_LABEL, "read interface token from native fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } } diff --git a/src/ipc_rust/src/parcel/types/option.rs b/src/ipc_rust/src/parcel/types/option.rs index acb36822de1f08c57d33219b1c8223585a513ff8..b78f5a07582079f5a4999a71f89bb0e4518d98cd 100644 --- a/src/ipc_rust/src/parcel/types/option.rs +++ b/src/ipc_rust/src/parcel/types/option.rs @@ -16,16 +16,16 @@ use super::*; impl Serialize for Option { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerOption::ser_option(self.as_ref(), parcel) } } impl Deserialize for Option { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { DeOption::de_option(parcel) } } impl DeArray for Option {} -impl SerArray for Option {} \ No newline at end of file +impl SerArray for Option {} diff --git a/src/ipc_rust/src/parcel/types/reference.rs b/src/ipc_rust/src/parcel/types/reference.rs index 67cf75e65a3f6bb9e549d6f62265933697bb61c8..ac44b46b1aa5aaa5de99ebf5c5c2dbc4a7bd07e6 100644 --- a/src/ipc_rust/src/parcel/types/reference.rs +++ b/src/ipc_rust/src/parcel/types/reference.rs @@ -14,17 +14,17 @@ */ use super::*; -use crate::{BorrowedMsgParcel, Result}; +use crate::{BorrowedMsgParcel, IpcResult}; // We need these to support Option<&T> for all T impl Serialize for &T { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { Serialize::serialize(*self, parcel) } } impl SerOption for &T { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<(), > { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerOption::ser_option(this.copied(), parcel) } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/types/slices.rs b/src/ipc_rust/src/parcel/types/slices.rs index 794b96220908e020d44f5c530fca8ad5a4772da5..686674d819ec4443cc260249fe93befe7d9aa62f 100644 --- a/src/ipc_rust/src/parcel/types/slices.rs +++ b/src/ipc_rust/src/parcel/types/slices.rs @@ -14,20 +14,20 @@ */ use super::*; -use crate::{BorrowedMsgParcel, Result}; +use crate::{BorrowedMsgParcel, IpcResult}; impl Serialize for [T] { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerArray::ser_array(self, parcel) } } impl SerOption for [T] { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { if let Some(v) = this { SerArray::ser_array(v, parcel) } else { parcel.write(&-1i32) } } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/parcel/types/string16.rs b/src/ipc_rust/src/parcel/types/string16.rs index 66c152b729fa488d7d3d1a9a7cde20ab40232daa..6fe648df7b5c5f60d4fcaf8702feedd1416c72e7 100644 --- a/src/ipc_rust/src/parcel/types/string16.rs +++ b/src/ipc_rust/src/parcel/types/string16.rs @@ -14,15 +14,16 @@ */ use super::*; -use crate::{ipc_binding, BorrowedMsgParcel, Result, result_status, AsRawPtr}; -use std::convert::TryInto; -use std::ffi::{CString}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode}; use hilog_rust::{error, hilog, HiLogLabel, LogType}; +use std::convert::TryInto; +use std::ffi::CString; +use std::mem::MaybeUninit; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustString16" + tag: "RustString16", }; /// String16 packed a String type which transfered with C++ std::u16string. @@ -41,61 +42,101 @@ impl String16 { } impl Serialize for String16 { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { let string = &self.0; // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ret = unsafe { ipc_binding::CParcelWriteString16( parcel.as_mut_raw(), string.as_ptr() as *const c_char, - string.as_bytes().len().try_into().unwrap() - )}; - result_status::<()>(ret, ()) + string.as_bytes().len().try_into().unwrap(), + ) + }; + status_result::<()>(ret as i32, ()) } } impl Deserialize for String16 { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { let mut vec: Option> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` ipc_binding::CParcelReadString16( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { - let result = vec.map(|s| { - match String::from_utf8(s) { - Ok(val) => val, - Err(_) => String::from("") - } + let result = vec.map(|s| match String::from_utf8(s) { + Ok(val) => val, + Err(_) => String::from(""), }); if let Some(val) = result { Ok(Self(val)) } else { error!(LOG_LABEL, "convert native string16 to String fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } else { error!(LOG_LABEL, "read string16 from native fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } } +impl SerArray for String16 { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + let ret = unsafe { + ipc_binding::CParcelWriteString16Array( + parcel.as_mut_raw(), + slice.as_ptr() as *const c_void, + slice.len().try_into().unwrap(), + on_string16_writer, + ) + }; + status_result::<()>(ret as i32, ()) + } +} + +impl DeArray for String16 { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { + let mut vec: Option>> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + // `allocate_vec` expects the opaque pointer to + // be of type `*mut Option>>`, so `&mut vec` is + // correct for it. + let ok_status = unsafe { + ipc_binding::CParcelReadString16Array( + parcel.as_raw(), + &mut vec as *mut _ as *mut c_void, + on_string16_reader, + ) + }; + if ok_status { + // SAFETY: all the MaybeUninits are now properly initialized. + let vec: Option> = unsafe { vec.map(|vec| vec_assume_init(vec)) }; + Ok(vec) + } else { + error!(LOG_LABEL, "read string16 from native fail"); + Err(IpcStatusCode::Failed) + } + } +} /// Callback to serialize a String16 array to c++ std::vector. /// -/// Safety: We are relying on c interface to not overrun our slice. As long +/// # Safety: +/// +/// We are relying on c interface to not overrun our slice. As long /// as it doesn't provide an index larger than the length of the original /// slice in ser_array, this operation is safe. The index provided /// is zero-based. #[allow(dead_code)] pub unsafe extern "C" fn on_string16_writer( array: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust slice pointer + value: *mut c_void, // Rust slice pointer len: u32, ) -> bool { if len == 0 { @@ -110,7 +151,8 @@ pub unsafe extern "C" fn on_string16_writer( ipc_binding::CParcelWritU16stringElement( array, item.0.as_ptr() as *const c_char, - item.0.as_bytes().len().try_into().unwrap()) + item.0.as_bytes().len().try_into().unwrap(), + ) }; if !ret { return false; @@ -118,3 +160,51 @@ pub unsafe extern "C" fn on_string16_writer( } true } + +/// Callback to deserialize a String element in Vector. +/// +/// # Safety: +/// +/// The opaque array data pointer must be a mutable pointer to an +/// `Option>>` with at least enough elements for `index` to be valid +/// (zero-based). +#[allow(dead_code)] +unsafe extern "C" fn on_string16_reader( + data: *const c_void, // C++ vector pointer + value: *mut c_void, // Rust vector pointer + len: u32, // C++ vector length +) -> bool { + // SAFETY: + // Allocate Vec capacity, data_len will set correctly by vec.push(). + unsafe { allocate_vec_maybeuninit::(value, 0) }; + let vec = &mut *(value as *mut Option>>); + for index in 0..len { + let mut vec_u16: Option> = None; + // SAFETY: The length of the index will not exceed the range, + // as the traversal range is the pointer length of the data passed from the C++side + let ok_status = unsafe { + ipc_binding::CParcelReadString16Element( + index, + data, + &mut vec_u16 as *mut _ as *mut c_void, + allocate_vec_with_buffer::, + ) + }; + if ok_status { + if let Ok(string) = vec_u16_to_string(vec_u16) { + if let Some(new_vec) = vec { + new_vec.push(MaybeUninit::new(String16::new(string.as_str()))); + } else { + error!(LOG_LABEL, "on_string_reader allocate vec failed"); + return false; + } + } else { + error!(LOG_LABEL, "on_string_reader vec_to_string failed"); + return false; + } + } else { + return false; + } + } + true +} diff --git a/src/ipc_rust/src/parcel/types/strings.rs b/src/ipc_rust/src/parcel/types/strings.rs index 9536ccb6a2946b459147b6d9a0e627947981236d..57a4de59bef7b0800484dd41ba3eaf7b1d7a2d70 100644 --- a/src/ipc_rust/src/parcel/types/strings.rs +++ b/src/ipc_rust/src/parcel/types/strings.rs @@ -14,19 +14,16 @@ */ use super::*; -use crate::{ - ipc_binding, BorrowedMsgParcel, Result, result_status, - AsRawPtr -}; +use crate::{ipc_binding, status_result, AsRawPtr, BorrowedMsgParcel, IpcResult, IpcStatusCode}; +use hilog_rust::{error, hilog, HiLogLabel, LogType}; use std::convert::TryInto; +use std::ffi::CString; use std::mem::MaybeUninit; -use std::ffi::{CString}; -use hilog_rust::{error, hilog, HiLogLabel, LogType}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustString" + tag: "RustString", }; impl SerOption for str {} @@ -34,47 +31,49 @@ impl SerOption for String {} impl DeOption for String {} impl Serialize for str { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ret = unsafe { ipc_binding::CParcelWriteString( parcel.as_mut_raw(), self.as_ptr() as *const c_char, - self.as_bytes().len().try_into().unwrap() - )}; - result_status::<()>(ret, ()) + self.as_bytes().len().try_into().unwrap(), + ) + }; + status_result::<()>(ret as i32, ()) } } impl Serialize for String { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { self.as_str().serialize(parcel) } } impl Deserialize for String { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { let mut vec: Option> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` ipc_binding::CParcelReadString( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { vec_to_string(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } } impl SerArray for &str { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ret = unsafe { - // SAFETY: ipc_binding::CParcelWriteStringArray( parcel.as_mut_raw(), slice.as_ptr() as *const c_void, @@ -82,14 +81,14 @@ impl SerArray for &str { on_str_writer, ) }; - result_status::<()>(ret, ()) + status_result::<()>(ret as i32, ()) } } impl SerArray for String { - fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_array(slice: &[Self], parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` let ret = unsafe { - // SAFETY: ipc_binding::CParcelWriteStringArray( parcel.as_mut_raw(), slice.as_ptr() as *const c_void, @@ -97,18 +96,18 @@ impl SerArray for String { on_string_writer, ) }; - result_status::<()>(ret, ()) + status_result::<()>(ret as i32, ()) } } impl DeArray for String { - fn de_array(parcel: &BorrowedMsgParcel<'_>) -> Result>> { + fn de_array(parcel: &BorrowedMsgParcel<'_>) -> IpcResult>> { let mut vec: Option>> = None; + // SAFETY: `parcel` always contains a valid pointer to a `CParcel` + // `allocate_vec` expects the opaque pointer to + // be of type `*mut Option>>`, so `&mut vec` is + // correct for it. let ok_status = unsafe { - // SAFETY: `parcel` always contains a valid pointer to a `CParcel` - // `allocate_vec` expects the opaque pointer to - // be of type `*mut Option>>`, so `&mut vec` is - // correct for it. ipc_binding::CParcelReadStringArray( parcel.as_raw(), &mut vec as *mut _ as *mut c_void, @@ -116,29 +115,28 @@ impl DeArray for String { ) }; if ok_status { - let vec: Option> = unsafe { - // SAFETY: all the MaybeUninits are now properly - // initialized. - vec.map(|vec| vec_assume_init(vec)) - }; + // SAFETY: all the MaybeUninits are now properly initialized. + let vec: Option> = unsafe { vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) } else { error!(LOG_LABEL, "read string from native fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } } /// Callback to serialize a String array to c++ std::vector. /// -/// Safety: We are relying on c interface to not overrun our slice. As long +/// # Safety: +/// +/// We are relying on c interface to not overrun our slice. As long /// as it doesn't provide an index larger than the length of the original /// slice in ser_array, this operation is safe. The index provided /// is zero-based. #[allow(dead_code)] unsafe extern "C" fn on_str_writer( array: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust slice pointer + value: *mut c_void, // Rust slice pointer len: u32, ) -> bool { if len == 0 { @@ -148,11 +146,13 @@ unsafe extern "C" fn on_str_writer( let slice: &[&str] = std::slice::from_raw_parts(value.cast(), len); for item in slice.iter().take(len) { + // SAFETY: let ret = unsafe { ipc_binding::CParcelWriteStringElement( array, item.as_ptr() as *const c_char, - item.as_bytes().len().try_into().unwrap()) + item.as_bytes().len().try_into().unwrap(), + ) }; if !ret { return false; @@ -163,14 +163,16 @@ unsafe extern "C" fn on_str_writer( /// Callback to serialize a String array to c++ std::vector. /// -/// Safety: We are relying on c interface to not overrun our slice. As long +/// # Safety: +/// +/// We are relying on c interface to not overrun our slice. As long /// as it doesn't provide an index larger than the length of the original /// slice in ser_array, this operation is safe. The index provided /// is zero-based. #[allow(dead_code)] unsafe extern "C" fn on_string_writer( array: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust slice pointer + value: *mut c_void, // Rust slice pointer len: u32, ) -> bool { if len == 0 { @@ -180,11 +182,13 @@ unsafe extern "C" fn on_string_writer( let slice: &[String] = std::slice::from_raw_parts(value.cast(), len); for item in slice.iter().take(len) { + // SAFETY: let ret = unsafe { ipc_binding::CParcelWriteStringElement( array, item.as_ptr() as *const c_char, - item.as_bytes().len().try_into().unwrap()) + item.as_bytes().len().try_into().unwrap(), + ) }; if !ret { return false; @@ -195,26 +199,30 @@ unsafe extern "C" fn on_string_writer( /// Callback to deserialize a String element in Vector. /// +/// # Safety: +/// /// The opaque array data pointer must be a mutable pointer to an /// `Option>>` with at least enough elements for `index` to be valid /// (zero-based). #[allow(dead_code)] unsafe extern "C" fn on_string_reader( data: *const c_void, // C++ vector pointer - value: *mut c_void, // Rust vector pointer - len: u32, // C++ vector length + value: *mut c_void, // Rust vector pointer + len: u32, // C++ vector length ) -> bool { + // SAFETY: // Allocate Vec capacity, data_len will set correctly by vec.push(). unsafe { allocate_vec_maybeuninit::(value, 0) }; let vec = &mut *(value as *mut Option>>); for index in 0..len { let mut vec_u8: Option> = None; + // SAFETY: let ok_status = unsafe { ipc_binding::CParcelReadStringElement( index, data, &mut vec_u8 as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { @@ -236,31 +244,31 @@ unsafe extern "C" fn on_string_reader( true } -pub fn vec_to_string(vec: Option>) -> Result { +pub fn vec_to_string(vec: Option>) -> IpcResult { let value = vec.map(|s| { // The vector includes a null-terminator and // we don't want the string to be null-terminated for Rust. - String::from_utf8(s).or(Err(-1)) + String::from_utf8(s).or(Err(IpcStatusCode::Failed)) }); if let Some(ret) = value { ret } else { error!(LOG_LABEL, "convert vector u8 to String fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } -pub fn vec_u16_to_string(vec: Option>) -> Result { +pub fn vec_u16_to_string(vec: Option>) -> IpcResult { let value = vec.map(|s| { // The vector includes a null-terminator and // we don't want the string to be null-terminated for Rust. let slice = &s[..]; - String::from_utf16(slice).or(Err(-1)) + String::from_utf16(slice).or(Err(IpcStatusCode::Failed)) }); if let Some(ret) = value { ret } else { error!(LOG_LABEL, "convert vector u16 to String fail"); - Err(-1) + Err(IpcStatusCode::Failed) } } diff --git a/src/ipc_rust/src/parcel/types/vector.rs b/src/ipc_rust/src/parcel/types/vector.rs index 26ee96d06f785f26432186f4155773f71482842a..4112428a0ce3c047338204ef0e11d73409757da4 100644 --- a/src/ipc_rust/src/parcel/types/vector.rs +++ b/src/ipc_rust/src/parcel/types/vector.rs @@ -16,27 +16,27 @@ use super::*; impl Serialize for Vec { - fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerArray::ser_array(&self[..], parcel) } } impl SerOption for Vec { - fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> Result<()> { + fn ser_option(this: Option<&Self>, parcel: &mut BorrowedMsgParcel<'_>) -> IpcResult<()> { SerOption::ser_option(this.map(Vec::as_slice), parcel) } } impl Deserialize for Vec { - fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> Result { + fn deserialize(parcel: &BorrowedMsgParcel<'_>) -> IpcResult { DeArray::de_array(parcel) .transpose() - .unwrap_or(Err(-1)) + .unwrap_or(Err(IpcStatusCode::Failed)) } } impl DeOption for Vec { - fn de_option(parcel: &BorrowedMsgParcel<'_>) -> Result> { + fn de_option(parcel: &BorrowedMsgParcel<'_>) -> IpcResult> { DeArray::de_array(parcel) } -} \ No newline at end of file +} diff --git a/src/ipc_rust/src/process.rs b/src/ipc_rust/src/process.rs index f673642ce22b1f36a3b0c0ed08db5631068081c3..e64984ee98c5af597fbf3c623d337d3301ce90ec 100644 --- a/src/ipc_rust/src/process.rs +++ b/src/ipc_rust/src/process.rs @@ -13,23 +13,23 @@ * limitations under the License. */ +use crate::parcel::{allocate_vec_with_buffer, vec_to_string}; use crate::{ - ipc_binding, MsgParcel, RemoteObj, IRemoteObj, InterfaceToken, String16, - Result, + ipc_binding, parse_status_code, IRemoteObj, InterfaceToken, IpcResult, IpcStatusCode, + MsgParcel, RemoteObj, String16, }; -use crate::parcel::{vec_to_string, allocate_vec_with_buffer,}; -use std::ffi::{CString, c_char, c_void}; -use hilog_rust::{info, hilog, HiLogLabel, LogType}; +use hilog_rust::{hilog, info, HiLogLabel, LogType}; +use std::ffi::{c_char, c_void, CString}; const LOG_LABEL: HiLogLabel = HiLogLabel { log_type: LogType::LogCore, domain: 0xd001510, - tag: "RustProcess" + tag: "RustProcess", }; /// Get proxy object of samgr -pub fn get_context_object() -> Option -{ +pub fn get_context_object() -> Option { + // SAFETY: unsafe { let samgr = ipc_binding::GetContextManager(); RemoteObj::from_raw(samgr) @@ -37,8 +37,7 @@ pub fn get_context_object() -> Option } /// Add a service to samgr -pub fn add_service(service: &RemoteObj, said: i32) -> Result<()> -{ +pub fn add_service(service: &RemoteObj, said: i32) -> IpcResult<()> { let samgr = get_context_object().expect("samgr is not null"); let mut data = MsgParcel::new().expect("MsgParcel is not null"); data.write(&InterfaceToken::new("ohos.samgr.accessToken"))?; @@ -51,12 +50,15 @@ pub fn add_service(service: &RemoteObj, said: i32) -> Result<()> let reply = samgr.send_request(3, &data, false)?; let reply_value: i32 = reply.read()?; info!(LOG_LABEL, "register service result: {}", reply_value); - if reply_value == 0 { Ok(())} else { Err(reply_value) } + if reply_value == 0 { + Ok(()) + } else { + Err(parse_status_code(reply_value)) + } } /// Get a service proxy from samgr -pub fn get_service(said: i32) -> Result -{ +pub fn get_service(said: i32) -> IpcResult { let samgr = get_context_object().expect("samgr is not null"); let mut data = MsgParcel::new().expect("MsgParcel is not null"); data.write(&InterfaceToken::new("ohos.samgr.accessToken"))?; @@ -69,8 +71,8 @@ pub fn get_service(said: i32) -> Result /// Make current thread join to the IPC/RPC work thread pool #[inline] -pub fn join_work_thread() -{ +pub fn join_work_thread() { + // SAFETY: unsafe { ipc_binding::JoinWorkThread(); } @@ -78,146 +80,126 @@ pub fn join_work_thread() /// Exit current thread from IPC/RPC work thread pool #[inline] -pub fn stop_work_thread() -{ - unsafe { - ipc_binding::StopWorkThread() - } +pub fn stop_work_thread() { + // SAFETY: + unsafe { ipc_binding::StopWorkThread() } } /// Get calling token ID of caller #[inline] -pub fn get_calling_token_id() -> u64 -{ - unsafe { - ipc_binding::GetCallingTokenId() - } +pub fn get_calling_token_id() -> u64 { + // SAFETY: + unsafe { ipc_binding::GetCallingTokenId() } } /// Get first calling token ID of caller #[inline] -pub fn get_first_token_id() -> u64 -{ - unsafe { - ipc_binding::GetFirstToekenId() - } +pub fn get_first_token_id() -> u64 { + // SAFETY: + unsafe { ipc_binding::GetFirstToekenId() } } /// Get self token id of current process #[inline] -pub fn get_self_token_id() -> u64 -{ - unsafe { - ipc_binding::GetSelfToekenId() - } +pub fn get_self_token_id() -> u64 { + // SAFETY: + unsafe { ipc_binding::GetSelfToekenId() } } /// Get calling process id of caller #[inline] -pub fn get_calling_pid() -> u64 -{ - unsafe { - ipc_binding::GetCallingPid() - } +pub fn get_calling_pid() -> u64 { + // SAFETY: + unsafe { ipc_binding::GetCallingPid() } } /// Get calling user id of caller #[inline] -pub fn get_calling_uid() -> u64 -{ - unsafe { - ipc_binding::GetCallingUid() - } +pub fn get_calling_uid() -> u64 { + // SAFETY: + unsafe { ipc_binding::GetCallingUid() } } /// Set the maximum number of threads #[inline] -pub fn set_max_work_thread(max_thread_num: i32) -> bool -{ - unsafe { - ipc_binding::SetMaxWorkThreadNum(max_thread_num) - } +pub fn set_max_work_thread(max_thread_num: i32) -> bool { + // SAFETY: + unsafe { ipc_binding::SetMaxWorkThreadNum(max_thread_num) } } /// Determine whether it is a local call #[inline] -pub fn is_local_calling() -> bool -{ - unsafe { - ipc_binding::IsLocalCalling() - } +pub fn is_local_calling() -> bool { + // SAFETY: + unsafe { ipc_binding::IsLocalCalling() } } /// Set calling identity #[inline] -pub fn set_calling_identity(identity: String) -> bool -{ +pub fn set_calling_identity(identity: String) -> bool { match CString::new(identity.as_str()) { Ok(name) => { - unsafe { - ipc_binding::SetCallingIdentity(name.as_ptr()) - } - }, + // SAFETY: + // Name is valid + unsafe { ipc_binding::SetCallingIdentity(name.as_ptr()) } + } Err(_) => false, } } /// get local device id #[inline] -pub fn get_local_device_id() -> Result -{ +pub fn get_local_device_id() -> IpcResult { let mut vec: Option> = None; + // SAFETY: let ok_status = unsafe { - // SAFETY: ipc_binding::GetLocalDeviceID( &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { vec_to_string(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } /// get calling device id #[inline] -pub fn get_calling_device_id() -> Result -{ +pub fn get_calling_device_id() -> IpcResult { let mut vec: Option> = None; + // SAFETY: let ok_status = unsafe { - // SAFETY: ipc_binding::GetCallingDeviceID( &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { vec_to_string(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } } /// reset calling identity #[inline] -pub fn reset_calling_identity() -> Result -{ +pub fn reset_calling_identity() -> IpcResult { let mut vec: Option> = None; + // SAFETY: let ok_status = unsafe { - // SAFETY: ipc_binding::ResetCallingIdentity( &mut vec as *mut _ as *mut c_void, - allocate_vec_with_buffer:: + allocate_vec_with_buffer::, ) }; if ok_status { vec_to_string(vec) } else { - Err(-1) + Err(IpcStatusCode::Failed) } -} \ No newline at end of file +}