From 4c7ecc48f359b45c64243391f0f484be97eca359 Mon Sep 17 00:00:00 2001 From: caochuan Date: Wed, 31 May 2023 10:59:16 +0800 Subject: [PATCH] feat:add tree Signed-off-by: caochuan --- .../include/file_access_service.h | 56 ++++++ .../include/holder_manager.h | 113 +++++++++++ .../src/file_access_service.cpp | 190 ++++++++++++++++++ utils/file_access_framework_errno.h | 3 + 4 files changed, 362 insertions(+) create mode 100644 services/native/file_access_service/include/holder_manager.h diff --git a/services/native/file_access_service/include/file_access_service.h b/services/native/file_access_service/include/file_access_service.h index 8ad0e962..afc984ec 100644 --- a/services/native/file_access_service/include/file_access_service.h +++ b/services/native/file_access_service/include/file_access_service.h @@ -16,18 +16,68 @@ #ifndef FILE_ACCESS_SERVICE_H #define FILE_ACCESS_SERVICE_H +#include #include #include #include +#include #include #include "file_access_service_stub.h" +#include "holder_manager.h" #include "iremote_object.h" #include "uri.h" namespace OHOS { namespace FileAccessFwk { +class ObserverContext { + public: + ObserverContext(sptr obs): obs_(obs) {} + virtual ~ObserverContext() = default; + sptr obs_ = nullptr; + void Ref() + { + if (ref_ == std::numeric_limits::max()) { + HILOG_ERROR("Ref is over max"); + return; + } + ref_++; + } + + void UnRef() + { + if (ref_ == 0) { + HILOG_ERROR("Ref is already zero"); + return; + } + ref_--; + } + + bool IsValid() + { + return ref_ > 0; + } + + bool EqualTo(std::shared_ptr observerContext) + { + return obs_->AsObject() == observerContext->obs_->AsObject(); + } + + private: + std::atomic ref_; +}; + +class ObserverNode { + public: + ObserverNode(const bool needChildNote): needChildNote_(needChildNote) {} + virtual ~ObserverNode() = default; + std::shared_ptr parent_; + std::vector> children_; + std::vector obsCodeList_; + bool needChildNote_; +}; + class FileAccessService final : public SystemAbility, public FileAccessServiceStub { DECLARE_SYSTEM_ABILITY(FileAccessService) @@ -45,11 +95,17 @@ public: int32_t OnChange(Uri uri, NotifyType notifyType) override; private: + void SendListNotify(const std::vector list, NotifyMessage ¬ifyMessage); + void RemoveRelations(std::string &uriStr, std::shared_ptr obsNode); + int FindUri(const std::string &uriStr, std::shared_ptr &outObsNode); FileAccessService(); bool IsServiceReady() const; static sptr instance_; bool ready_ = false; static std::mutex mutex_; + std::mutex nodeMutex_; + std::unordered_map> relationshipMap_; + HolderManager> obsManager_; }; } // namespace FileAccessFwk } // namespace OHOS diff --git a/services/native/file_access_service/include/holder_manager.h b/services/native/file_access_service/include/holder_manager.h new file mode 100644 index 00000000..fb287c46 --- /dev/null +++ b/services/native/file_access_service/include/holder_manager.h @@ -0,0 +1,113 @@ +/* + * 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 HOLDER_MANAGER_H_ +#define HOLDER_MANAGER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace OHOS { +namespace FileAccessFwk { +template +class HolderManager { +public: + static constexpr int CODE_CAN_NOT_FIND = 0; + HolderManager() {} + + ~HolderManager() + { + std::lock_guard guard(holderMutex_); + holder_.clear(); + } + + uint32_t save(Type content) + { + uint32_t id; + do { + id = genId(); + } while (exist(id)); + std::lock_guard guard(holderMutex_); + holder_.insert(std::pair(id, content)); + return id; + } + + Type get(uint32_t id) + { + std::lock_guard guard(holderMutex_); + auto iter = holder_.find(id); + if (iter != holder_.end()) { + return iter->second; + } + return Type(); + } + + Type pop(uint32_t id) + { + std::lock_guard guard(holderMutex_); + auto iter = holder_.find(id); + if (iter != holder_.end()) { + auto res = iter->second; + holder_.erase(id); + return res; + } + return Type(); + } + + void release(uint32_t id) + { + std::lock_guard guard(holderMutex_); + if (holder_.count(id)) { + holder_.erase(id); + } + } + + bool exist(uint32_t id) + { + std::lock_guard guard(holderMutex_); + return holder_.count(id); + } + + uint32_t getId(std::function func) + { + auto haveIter = find_if(holder_.begin(), holder_.end(), + [func](const std::pair type) { + return func(type.second); + }); + if (haveIter == holder_.end()) { + return CODE_CAN_NOT_FIND; + } + return haveIter->first; + } + +private: + // key is automatic growth number + std::map holder_; + std::mutex holderMutex_; + std::atomic gId_ = 1; + uint32_t genId() + { + return gId_++; + } +}; +} // namespace FileAccessFwk +} // namespace OHOS +#endif // FRAMEWORKS_INNERKITSIMPL_UTILS_INCLUDE_IMAGE_HOLDER_MANAGER_H_ diff --git a/services/native/file_access_service/src/file_access_service.cpp b/services/native/file_access_service/src/file_access_service.cpp index 2e8c3e5e..e1ccd19f 100644 --- a/services/native/file_access_service/src/file_access_service.cpp +++ b/services/native/file_access_service/src/file_access_service.cpp @@ -80,19 +80,209 @@ bool FileAccessService::IsServiceReady() const return ready_; } +static bool IsParentUri(const string &comparedUriStr, string &srcUriStr) +{ + if ((comparedUriStr.empty()) || (srcUriStr.empty())) { + HILOG_ERROR("Uri is empty"); + return false; + } + size_t slashIndex = srcUriStr.rfind("/"); + if (slashIndex != string::npos) { + if (comparedUriStr.compare(srcUriStr.substr(0, slashIndex)) == 0) { + return true; + } + } + return false; +} + +static bool IsChildUri(const string &comparedUriStr, string &srcUriStr) +{ + if ((comparedUriStr.empty()) || (srcUriStr.empty())) { + HILOG_ERROR("Uri is empty"); + return false; + } + size_t slashIndex = comparedUriStr.rfind("/"); + if (slashIndex != string::npos) { + if (srcUriStr.compare(comparedUriStr.substr(0, slashIndex)) == 0) { + return true; + } + } + return false; +} + int32_t FileAccessService::RegisterNotify(Uri uri, const sptr &observer, bool notifyForDescendants) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RegisterNotify"); + shared_ptr obsContext = make_shared(observer); + // find if obsManager_ has this callback. + uint32_t code = obsManager_.getId([obsContext](const shared_ptr &afterContext) { + return obsContext->EqualTo(afterContext); + }); + if (code == HolderManager>::CODE_CAN_NOT_FIND) { + // this is new callback, save this context + obsContext->Ref(); + code = obsManager_.save(obsContext); + } else { + // this callback is already in manager, add ref. + obsManager_.get(code)->Ref(); + } + string uriStr = uri.ToString(); + std::lock_guard lock(nodeMutex_); + auto iter = relationshipMap_.find(uriStr); + shared_ptr obsNode; + if (iter != relationshipMap_.end()) { + obsNode = iter->second; + // this node has this callback or not, if has this, unref manager. + auto haveCodeIter = find_if(obsNode->obsCodeList_.begin(), obsNode->obsCodeList_.end(), + [code](const uint32_t &listCode) { return code == listCode; }); + if (haveCodeIter != obsNode->obsCodeList_.end()) { + obsManager_.get(code)->UnRef(); + if (obsNode->needChildNote_ == notifyForDescendants) { + HILOG_DEBUG("Register same uri and same callback and same notifyForDescendants"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + // need modify obsNode notifyForDescendants + obsNode->needChildNote_ = notifyForDescendants; + HILOG_DEBUG("Register same uri and same callback but need modify notifyForDescendants"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + obsNode->obsCodeList_.push_back(code); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + obsNode = make_shared(notifyForDescendants); + // add new node relations. + for (auto &[comUri, node] : relationshipMap_) { + if (IsParentUri(comUri, uriStr)) { + obsNode->parent_ = node; + node->children_.push_back(obsNode); + } + if (IsChildUri(comUri, uriStr)) { + obsNode->children_.push_back(node); + node->parent_ = obsNode; + } + } + // obsCodeList_ is to save callback number + obsNode->obsCodeList_.push_back(code); + relationshipMap_.insert(make_pair(uriStr, obsNode)); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; +} + +void FileAccessService::RemoveRelations(string &uriStr, shared_ptr obsNode) +{ + std::lock_guard lock(nodeMutex_); + for (auto &[comUri, node] : relationshipMap_) { + auto childrenIter = find_if(node->children_.begin(), node->children_.end(), + [obsNode](const std::shared_ptr &obsListNode) { return obsNode == obsListNode; }); + if (childrenIter != node->children_.end()) { + node->children_.erase(childrenIter); + } + if (IsChildUri(comUri, uriStr)) { + node->parent_ = nullptr; + } + } + relationshipMap_.erase(uriStr); +} + +int FileAccessService::FindUri(const string &uriStr, shared_ptr &outObsNode) +{ + std::lock_guard lock(nodeMutex_); + auto iter = relationshipMap_.find(uriStr); + if (iter == relationshipMap_.end()) { + HILOG_ERROR("Can not find uri"); + return E_CAN_NOT_FIND_URI; + } + outObsNode = iter->second; return ERR_OK; } int32_t FileAccessService::UnregisterNotify(Uri uri, const sptr &observer) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "UnregisterNotify"); + if (observer->AsObject() == nullptr) { + HILOG_ERROR("UnregisterNotify failed with invalid observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_IPCS; + } + string uriStr = uri.ToString(); + shared_ptr obsNode; + if (FindUri(uriStr, obsNode) != ERR_OK) { + HILOG_ERROR("Can not find unregisterNotify uri"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CAN_NOT_FIND_URI; + } + // find if obsManager_ has this callback. + shared_ptr obsContext = make_shared(observer); + uint32_t code = obsManager_.getId([obsContext](const shared_ptr &afterContext) { + return obsContext->EqualTo(afterContext); + }); + if (code == HolderManager>::CODE_CAN_NOT_FIND) { + HILOG_ERROR("Can not find observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CALLBACK_IS_NOT_REGISTER; + } + // find if this node has this callback. + auto haveCodeIter = find_if(obsNode->obsCodeList_.begin(), obsNode->obsCodeList_.end(), + [code](const uint32_t &listCode) { return code == listCode; }); + if (haveCodeIter == obsNode->obsCodeList_.end()) { + HILOG_ERROR("Uri node observer list don not has this observer"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return E_CALLBACK_AND_URI_HAS_NOT_RELATIONS; + } + obsNode->obsCodeList_.erase(haveCodeIter); + obsManager_.get(code)->UnRef(); + // node has other observers, do not need remove. + if (obsNode->obsCodeList_.size() != 0) { + HILOG_DEBUG("Has code do not stopWatcher"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + // if data refcount is invalid, release this code. + if (!obsManager_.get(code)->IsValid()) { + obsManager_.release(code); + } + RemoveRelations(uriStr, obsNode); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ERR_OK; } +void FileAccessService::SendListNotify(const vector list, NotifyMessage ¬ifyMessage) +{ + for (uint32_t code : list) { + if (obsManager_.get(code) == nullptr) { + HILOG_ERROR("Failed to get obs code = %{private}ud", code); + } else { + obsManager_.get(code)->obs_->OnChange(notifyMessage); + } + } +} + int32_t FileAccessService::OnChange(Uri uri, NotifyType notifyType) { + StartTrace(HITRACE_TAG_FILEMANAGEMENT, "OnChange"); + string uriStr = uri.ToString(); + shared_ptr node; + if (FindUri(uriStr, node) != ERR_OK) { + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + HILOG_ERROR("Can not find onChange uri"); + return E_CAN_NOT_FIND_URI; + } + NotifyMessage notifyMessage; + notifyMessage.notifyType_ = notifyType; + std::vector uris {uriStr}; + notifyMessage.uris_ = uris; + SendListNotify(node->obsCodeList_, notifyMessage); + if ((node->parent_ == nullptr) || (!node->parent_->needChildNote_)) { + HILOG_DEBUG("Do not need notify parent"); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); + return ERR_OK; + } + SendListNotify(node->parent_->obsCodeList_, notifyMessage); + FinishTrace(HITRACE_TAG_FILEMANAGEMENT); return ERR_OK; } } // namespace FileAccessFwk diff --git a/utils/file_access_framework_errno.h b/utils/file_access_framework_errno.h index 93c0b841..3ee98347 100644 --- a/utils/file_access_framework_errno.h +++ b/utils/file_access_framework_errno.h @@ -36,6 +36,9 @@ enum { E_INIT_NOTIFY_AGENT, // Fail to init notification agent E_NOTIFY, // Fail to notify agent E_CONNECT, // Fail to connect file access extension ability + E_CALLBACK_AND_URI_HAS_NOT_RELATIONS, // Uri and callback do not has relations, can not unregister + E_CALLBACK_IS_NOT_REGISTER, // CallBack is not registered, can not unregister + E_CAN_NOT_FIND_URI, // Can not find registered uri E_PERMISSION = 201, // Permission verification failed E_PERMISSION_SYS, // is not system app E_COUNT -- Gitee