diff --git a/frameworks/native/consumer/src/datashare_helper.cpp b/frameworks/native/consumer/src/datashare_helper.cpp index 8802d8e244a43c468d489f2e035578aa10550053..bd5c395f0aec01f75f86b1b7a547a0cd28f89fe5 100644 --- a/frameworks/native/consumer/src/datashare_helper.cpp +++ b/frameworks/native/consumer/src/datashare_helper.cpp @@ -16,6 +16,7 @@ #include "datashare_helper.h" #include "connection_factory.h" +#include "concurrent_map.h" #include "data_ability_observer_interface.h" #include "data_ability_observer_stub.h" #include "dataobs_mgr_client.h" @@ -38,11 +39,20 @@ public: void OnChangeExt(const ChangeInfo &info); static DataShareObserver::ChangeInfo ConvertInfo(const AAFwk::ChangeInfo &info); static AAFwk::ChangeInfo ConvertInfo(const DataShareObserver::ChangeInfo &info); - + static sptr GetObserver(const Uri& uri, const std::shared_ptr &observer); + static bool FindObserver(const Uri& uri, const std::shared_ptr &observer); + static bool DeleteObserver(const Uri& uri, const std::shared_ptr &observer); private: + struct ObserverParam { + sptr obs_; + std::list uris_; + }; std::shared_ptr dataShareObserver_; + static ConcurrentMap observers_; }; +ConcurrentMap ObserverImpl::observers_; + DataShareHelper::DataShareHelper(const sptr &token, const Uri &uri, std::shared_ptr dataShareConnection) { @@ -574,13 +584,14 @@ void DataShareHelper::RegisterObserverExt(const Uri &uri, std::shared_ptr obs(new (std::nothrow) ObserverImpl(dataObserver)); + sptr obs = ObserverImpl::GetObserver(uri, dataObserver); if (obs == nullptr) { LOG_ERROR("new ObserverImpl failed"); return; } ErrCode ret = obsMgrClient->RegisterObserverExt(uri, obs, isDescendants); if (ret != ERR_OK) { + ObserverImpl::DeleteObserver(uri, dataObserver); LOG_ERROR("RegisterObserverExt failed"); } return; @@ -604,7 +615,13 @@ void DataShareHelper::UnregisterObserverExt(const Uri &uri, std::shared_ptr obs(new (std::nothrow) ObserverImpl(dataObserver)); + + if (!ObserverImpl::FindObserver(uri, dataObserver)) { + LOG_ERROR("observer not exit!"); + return; + } + + sptr obs = ObserverImpl::GetObserver(uri, dataObserver); if (obs == nullptr) { LOG_ERROR("new ObserverImpl failed"); return; @@ -612,6 +629,8 @@ void DataShareHelper::UnregisterObserverExt(const Uri &uri, std::shared_ptrUnregisterObserverExt(uri, obs); if (ret != ERR_OK) { LOG_ERROR("UnregisterObserverExt failed"); + } else { + ObserverImpl::DeleteObserver(uri, dataObserver); } return; } @@ -742,23 +761,7 @@ void ObserverImpl::OnChangeExt(const ChangeInfo &info) DataShareObserver::ChangeInfo ObserverImpl::ConvertInfo(const AAFwk::ChangeInfo &info) { DataShareObserver::ChangeInfo changeInfo; - switch (info.changeType_) { - case AAFwk::ChangeInfo::INSERT: - changeInfo.changeType_ = DataShareObserver::INSERT; - break; - case AAFwk::ChangeInfo::DELETE: - changeInfo.changeType_ = DataShareObserver::DELETE; - break; - case AAFwk::ChangeInfo::UPDATE: - changeInfo.changeType_ = DataShareObserver::UPDATE; - break; - case AAFwk::ChangeInfo::OTHER: - changeInfo.changeType_ = DataShareObserver::OTHER; - break; - default: - changeInfo.changeType_ = DataShareObserver::INVAILD; - break; - } + changeInfo.changeType_ = static_cast(info.changeType_); changeInfo.uris_ = std::move(info.uris_); changeInfo.data_ = info.data_; changeInfo.size_ = info.size_; @@ -768,29 +771,56 @@ DataShareObserver::ChangeInfo ObserverImpl::ConvertInfo(const AAFwk::ChangeInfo AAFwk::ChangeInfo ObserverImpl::ConvertInfo(const DataShareObserver::ChangeInfo &info) { AAFwk::ChangeInfo changeInfo; - switch (info.changeType_) { - case DataShareObserver::INSERT: - changeInfo.changeType_ = AAFwk::ChangeInfo::INSERT; - break; - case DataShareObserver::DELETE: - changeInfo.changeType_ = AAFwk::ChangeInfo::DELETE; - break; - case DataShareObserver::UPDATE: - changeInfo.changeType_ = AAFwk::ChangeInfo::UPDATE; - break; - case DataShareObserver::OTHER: - changeInfo.changeType_ = AAFwk::ChangeInfo::OTHER; - break; - default: - changeInfo.changeType_ = AAFwk::ChangeInfo::INVAILD; - break; - } + changeInfo.changeType_ = static_cast(info.changeType_); changeInfo.uris_ = std::move(info.uris_); - changeInfo.data_ = info.data_; + changeInfo.data_ = const_cast(info.data_); changeInfo.size_ = info.size_; return changeInfo; } +sptr ObserverImpl::GetObserver(const Uri& uri, const std::shared_ptr &observer) +{ + sptr result = nullptr; + observers_.Compute(observer.get(), [&result, &uri, &observer](const auto &key, auto &value) { + if (value.obs_ == nullptr) { + value.obs_ = new (std::nothrow) ObserverImpl(observer); + value.uris_.push_back(uri); + } else { + auto it = std::find(value.uris_.begin(), value.uris_.end(), uri); + if (it == value.uris_.end()) { + value.uris_.push_back(uri); + } + } + + result = value.obs_; + return result != nullptr; + }); + + return result; +} + +bool ObserverImpl::FindObserver(const Uri& uri, const std::shared_ptr &observer) +{ + auto result = observers_.Find(observer.get()); + if (result.first) { + auto it = std::find(result.second.uris_.begin(), result.second.uris_.end(), uri); + if (it == result.second.uris_.end()) { + return false; + } + } + return result.first; +} + +bool ObserverImpl::DeleteObserver(const Uri& uri, const std::shared_ptr &observer) +{ + return observers_.ComputeIfPresent(observer.get(), [&uri](auto &key, auto &value) { + value.uris_.remove_if([&uri](const auto &value) { + return uri == value; + }); + return !value.uris_.empty(); + }); +} + int DataShareHelper::AddQueryTemplate(const std::string &uri, int64_t subscriberId, Template &tpl) { int errNum = INVALID_VALUE; diff --git a/test/native/unittest/mediadatashare_test/include/mediadatashare_unit_test.h b/test/native/unittest/mediadatashare_test/include/mediadatashare_unit_test.h index 029305cb6569334926536d2d23be88b2680d4d5d..9bab4dacc81846e67ef2c588cf1cfcc6b8554b06 100644 --- a/test/native/unittest/mediadatashare_test/include/mediadatashare_unit_test.h +++ b/test/native/unittest/mediadatashare_test/include/mediadatashare_unit_test.h @@ -23,6 +23,8 @@ namespace OHOS { namespace Media { +using Uri = OHOS::Uri; +using ChangeInfo = OHOS::DataShare::DataShareObserver::ChangeInfo; class MediaDataShareUnitTest : public testing::Test { public: /* SetUpTestCase:The preset action of the test suite is executed before the first TestCase */ @@ -36,6 +38,8 @@ public: /* TearDown:Execute after each test case */ void TearDown(); + bool UrisEqual(std::list uri1, std::list uri2); + bool ChangeInfoEqual(const ChangeInfo &changeInfo, const ChangeInfo &expectChangeInfo); }; class IDataAbilityObserverTest : public AAFwk::IDataAbilityObserver { @@ -50,22 +54,6 @@ public: } }; -class DataShareObserverTest : public DataShare::DataShareObserver { -public: - DataShareObserverTest() {} - ~DataShareObserverTest() {} - - void OnChange(const ChangeInfo &changeInfo) override - { - changeInfo_ = changeInfo; - std::unique_lock lock(mutex_); - condition_.notify_one(); - } - - ChangeInfo changeInfo_; - std::mutex mutex_; - std::condition_variable condition_; -}; } // namespace Media } // namespace OHOS diff --git a/test/native/unittest/mediadatashare_test/src/mediadatashare_unit_test.cpp b/test/native/unittest/mediadatashare_test/src/mediadatashare_unit_test.cpp index 223438515e08f266939ffc9d197ea800fca35e90..9874492c214bb8948c20a4b097f90b3412993cb0 100755 --- a/test/native/unittest/mediadatashare_test/src/mediadatashare_unit_test.cpp +++ b/test/native/unittest/mediadatashare_test/src/mediadatashare_unit_test.cpp @@ -14,6 +14,7 @@ */ #define MLOG_TAG "DataShareUnitTest" +#include #include "mediadatashare_unit_test.h" #include "datashare_helper.h" @@ -31,6 +32,67 @@ using namespace testing::ext; namespace OHOS { namespace Media { +template +class ConditionLock { +public: + explicit ConditionLock() {} + ~ConditionLock() {} +public: + void Notify(const T &data) + { + std::lock_guard lock(mutex_); + data_ = data; + isSet_ = true; + cv_.notify_one(); + } + + T Wait() + { + std::unique_lock lock(mutex_); + cv_.wait_for(lock, std::chrono::seconds(INTERVAL), [this]() { return isSet_; }); + T data = data_; + cv_.notify_one(); + return data; + } + + void Clear() + { + std::lock_guard lock(mutex_); + isSet_ = false; + cv_.notify_one(); + } + +private: + bool isSet_ = false; + T data_; + std::mutex mutex_; + std::condition_variable cv_; + static constexpr int64_t INTERVAL = 5; +}; +class DataShareObserverTest : public DataShare::DataShareObserver { +public: + DataShareObserverTest() {} + ~DataShareObserverTest() {} + + void OnChange(const ChangeInfo &changeInfo) override + { + changeInfo_ = changeInfo; + data.Notify(changeInfo); + } + + void Clear() + { + changeInfo_.changeType_ = INVAILD; + changeInfo_.uris_.clear(); + changeInfo_.data_ = nullptr; + changeInfo_.size_ = 0; + data.Clear(); + } + + ChangeInfo changeInfo_; + ConditionLock data; +}; + constexpr int STORAGE_MANAGER_MANAGER_ID = 5003; std::string MEDIALIBRARY_DATA_URI_ERROR = "test:///media"; std::shared_ptr g_mediaDataShareHelper; @@ -53,6 +115,47 @@ std::shared_ptr CreateDataShareHelper(int32_t system return DataShare::DataShareHelper::Creator(remoteObj, MEDIALIBRARY_DATA_URI); } +bool MediaDataShareUnitTest::UrisEqual(std::list uri1, std::list uri2) +{ + if (uri1.size() != uri2.size()) { + return false; + } + auto cmp = [](const Uri &first, const Uri &second) { + return first.ToString() < second.ToString(); + }; + uri1.sort(cmp); + uri2.sort(cmp); + auto it1 = uri1.begin(); + auto it2 = uri2.begin(); + for (; it1 != uri1.end() && it2 != uri2.end(); it1++, it2++) { + if (!it1->Equals(*it2)) { + return false; + } + } + return true; +} + +bool MediaDataShareUnitTest::ChangeInfoEqual(const ChangeInfo &changeInfo, const ChangeInfo &expectChangeInfo) +{ + if (changeInfo.changeType_ != expectChangeInfo.changeType_) { + return false; + } + + if (!UrisEqual(changeInfo.uris_, expectChangeInfo.uris_)) { + return false; + } + + if (changeInfo.size_ != expectChangeInfo.size_) { + return false; + } + + if (changeInfo.data_ == nullptr && expectChangeInfo.data_ == nullptr) { + return true; + } + + return memcmp(changeInfo.data_, expectChangeInfo.data_, expectChangeInfo.size_) == 0; +} + void MediaDataShareUnitTest::SetUpTestCase(void) { vector perms; @@ -1178,59 +1281,71 @@ HWTEST_F(MediaDataShareUnitTest, MediaDataShare_ObserverExt_001, TestSize.Level0 Uri uri(MEDIALIBRARY_DATA_URI); std::shared_ptr dataObserver = std::make_shared(); helper->RegisterObserverExt(uri, dataObserver, true); - DataShare::DataShareValuesBucket valuesBucket; valuesBucket.Put(MEDIA_DATA_DB_TITLE, "Datashare_Observer_Test001"); int retVal = helper->Insert(uri, valuesBucket); EXPECT_EQ((retVal > 0), true); - helper->NotifyChangeExt({ DataShareObserver::ChangeType::INSERT, { uri } }); + ChangeInfo uriChanges = { DataShareObserver::ChangeType::INSERT, { uri } }; + helper->NotifyChangeExt(uriChanges); - { - unique_lock lock(dataObserver->mutex_); - if (dataObserver->condition_.wait_for(lock, 2s) == std::cv_status::no_timeout) { - EXPECT_EQ(dataObserver->changeInfo_.changeType_, DataShareObserver::ChangeType::INSERT); - EXPECT_EQ(dataObserver->changeInfo_.uris_.size(), 1); - EXPECT_EQ(dataObserver->changeInfo_.uris_.begin()->ToString(), uri.ToString()); - } else { - EXPECT_TRUE(false); - } - } + dataObserver->data.Wait(); + EXPECT_TRUE(ChangeInfoEqual(dataObserver->changeInfo_, uriChanges)); + dataObserver->Clear(); Uri descendantsUri(MEDIALIBRARY_DATA_URI + "/com.ohos.example"); helper->Insert(descendantsUri, valuesBucket); EXPECT_EQ((retVal > 0), true); - helper->NotifyChangeExt({ DataShareObserver::ChangeType::INSERT, { descendantsUri } }); - { - unique_lock lock(dataObserver->mutex_); - if (dataObserver->condition_.wait_for(lock, 2s) == std::cv_status::no_timeout) { - EXPECT_EQ(dataObserver->changeInfo_.changeType_, DataShareObserver::ChangeType::INSERT); - EXPECT_EQ(dataObserver->changeInfo_.uris_.size(), 1); - EXPECT_EQ(dataObserver->changeInfo_.uris_.begin()->ToString(), descendantsUri.ToString()); - } else { - EXPECT_TRUE(false); - } - } + ChangeInfo descendantsChanges = { DataShareObserver::ChangeType::INSERT, { descendantsUri } }; + helper->NotifyChangeExt(descendantsChanges); + + dataObserver->data.Wait(); + EXPECT_TRUE(ChangeInfoEqual(dataObserver->changeInfo_, descendantsChanges)); + dataObserver->Clear(); DataShare::DataSharePredicates deletePredicates; string selections = MEDIA_DATA_DB_TITLE + " = 'Datashare_Observer_Test001'"; deletePredicates.SetWhereClause(selections); retVal = helper->Delete(uri, deletePredicates); EXPECT_EQ((retVal >= 0), true); - helper->NotifyChangeExt({ DataShareObserver::ChangeType::DELETE, { uri } }); + char data[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; + ChangeInfo delChanges = { DataShareObserver::ChangeType::DELETE, { uri }, data, sizeof(data)/sizeof(data[0]) } ; + helper->NotifyChangeExt(delChanges); - { - unique_lock lock(dataObserver->mutex_); - if (dataObserver->condition_.wait_for(lock, 2s) == std::cv_status::no_timeout) { - EXPECT_EQ(dataObserver->changeInfo_.changeType_, DataShareObserver::ChangeType::DELETE); - EXPECT_EQ(dataObserver->changeInfo_.uris_.size(), 1); - EXPECT_EQ(dataObserver->changeInfo_.uris_.begin()->ToString(), uri.ToString()); - } else { - EXPECT_TRUE(false); - } - } + dataObserver->data.Wait(); + EXPECT_TRUE(ChangeInfoEqual(dataObserver->changeInfo_, delChanges)); + dataObserver->Clear(); helper->UnregisterObserverExt(uri, dataObserver); LOG_INFO("MediaDataShare_ObserverExt_001 end"); } + +HWTEST_F(MediaDataShareUnitTest, MediaDataShare_UnregisterObserverExt_001, TestSize.Level0) +{ + LOG_INFO("MediaDataShare_UnregisterObserverExt_001 start"); + std::shared_ptr helper = g_mediaDataShareHelper; + ASSERT_TRUE(helper != nullptr); + Uri uri(MEDIALIBRARY_DATA_URI); + std::shared_ptr dataObserver = std::make_shared(); + helper->RegisterObserverExt(uri, dataObserver, true); + + DataShare::DataShareValuesBucket valuesBucket; + valuesBucket.Put(MEDIA_DATA_DB_TITLE, "Datashare_Observer_Test001"); + int retVal = helper->Insert(uri, valuesBucket); + EXPECT_EQ((retVal > 0), true); + ChangeInfo uriChanges = { DataShareObserver::ChangeType::INSERT, { uri } }; + helper->NotifyChangeExt(uriChanges); + + dataObserver->data.Wait(); + EXPECT_TRUE(ChangeInfoEqual(dataObserver->changeInfo_, uriChanges)); + dataObserver->Clear(); + + helper->UnregisterObserverExt(uri, dataObserver); + helper->NotifyChangeExt({ DataShareObserver::ChangeType::DELETE, { uri } }); + + dataObserver->data.Wait(); + EXPECT_FALSE(ChangeInfoEqual(dataObserver->changeInfo_, uriChanges)); + dataObserver->Clear(); + LOG_INFO("MediaDataShare_UnregisterObserverExt_001 end"); +} } // namespace Media } // namespace OHOS \ No newline at end of file