diff --git a/services/time/include/ntp_trusted_time.h b/services/time/include/ntp_trusted_time.h index 566611346d7e66fc689329a8e43a5ddffb1f1c37..c04b5eba28c8af03368efba718a537aad8ac80fa 100644 --- a/services/time/include/ntp_trusted_time.h +++ b/services/time/include/ntp_trusted_time.h @@ -30,6 +30,8 @@ public: int64_t CurrentTimeMillis(); int64_t ElapsedRealtimeMillis(); std::chrono::steady_clock::time_point GetBootTimeNs(); + bool FindBestTimeResult(); + void ClearTimeResultCandidates(); class TimeResult : std::enable_shared_from_this { public: TimeResult(); @@ -37,8 +39,8 @@ public: ~TimeResult(); int64_t GetTimeMillis(); int64_t GetElapsedRealtimeMillis(); - int64_t CurrentTimeMillis(); - int64_t GetAgeMillis(); + int64_t CurrentTimeMillis(int64_t bootTime); + int64_t GetAgeMillis(int64_t bootTime); void Clear(); private: @@ -48,9 +50,12 @@ public: int64_t mElapsedRealtimeMillis; int64_t mCertaintyMillis; }; + bool IsTimeResultTrusted(std::shared_ptr timeResult); + int32_t GetSameTimeResultCount(std::shared_ptr candidateTimeResult); private: std::shared_ptr mTimeResult {}; + std::vector> TimeResultCandidates_ {}; static std::mutex mTimeResultMutex_; }; } // namespace MiscServices diff --git a/services/time/src/ntp_trusted_time.cpp b/services/time/src/ntp_trusted_time.cpp index edd931c43f56c867f301696042ba1919033b23cd..985234d02d11aa57a7fcd5641a80419fe157fba6 100644 --- a/services/time/src/ntp_trusted_time.cpp +++ b/services/time/src/ntp_trusted_time.cpp @@ -26,6 +26,9 @@ constexpr int64_t TIME_RESULT_UNINITED = -1; constexpr int64_t HALF = 2; constexpr int NANO_TO_SECOND = 1000000000; constexpr int64_t ONE_DAY = 86400000; +// RTC has 1.7s gap one day +constexpr int64_t MAX_TIME_DRIFT_IN_ONE_DAY = 2000; +constexpr int64_t MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS = 100; } // namespace std::mutex NtpTrustedTime::mTimeResultMutex_; @@ -41,19 +44,94 @@ bool NtpTrustedTime::ForceRefresh(const std::string &ntpServer) TIME_HILOGD(TIME_MODULE_SERVICE, "start"); SNTPClient client; if (client.RequestTime(ntpServer)) { + auto timeResult = std::make_shared(client.getNtpTime(), client.getNtpTimeReference(), + client.getRoundTripTime() / HALF); std::lock_guard lock(mTimeResultMutex_); - if (mTimeResult != nullptr) { - mTimeResult->Clear(); + if (IsTimeResultTrusted(timeResult)) { + return true; } - int64_t ntpCertainty = client.getRoundTripTime() / HALF; - mTimeResult = std::make_shared(client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty); - TIME_HILOGD(TIME_MODULE_SERVICE, "Get Ntp time result"); - return true; } TIME_HILOGD(TIME_MODULE_SERVICE, "false end"); return false; } +// needs to acquire the lock `mTimeResultMutex_` before calling this method +bool NtpTrustedTime::IsTimeResultTrusted(std::shared_ptr timeResult) +{ + // system has not got ntp time, push into candidate list + if (mTimeResult == nullptr) { + TimeResultCandidates_.push_back(timeResult); + return false; + } + // mTimeResult is invaild, push into candidate list + auto oldNtpTime = mTimeResult->CurrentTimeMillis(timeResult->GetElapsedRealtimeMillis()); + if (oldNtpTime == TIME_RESULT_UNINITED) { + TIME_HILOGW(TIME_MODULE_SERVICE, "mTimeResult time is invaild"); + TimeResultCandidates_.push_back(timeResult); + return false; + } + // mTimeResult is beyond max value of kernel gap, this server is untrusted + auto newNtpTime = timeResult->GetTimeMillis(); + if (std::abs(newNtpTime - oldNtpTime) > MAX_TIME_DRIFT_IN_ONE_DAY) { + TIME_HILOGW(TIME_MODULE_SERVICE, "NTP server is untrusted old:%{public}" PRId64 " new:%{public}" PRId64 "", + oldNtpTime, newNtpTime); + TimeResultCandidates_.push_back(timeResult); + return false; + } + // cause refresh time success, old value is invaild + TimeResultCandidates_.clear(); + mTimeResult = timeResult; + return true; +} + +int32_t NtpTrustedTime::GetSameTimeResultCount(std::shared_ptr candidateTimeResult) +{ + auto candidateBootTime = candidateTimeResult->GetElapsedRealtimeMillis(); + auto candidateRealTime = candidateTimeResult->GetTimeMillis(); + int count = 0; + for (size_t i = 0; i < TimeResultCandidates_.size(); i++) { + auto compareTime = TimeResultCandidates_[i]->CurrentTimeMillis(candidateBootTime); + if (std::abs(compareTime - candidateRealTime) < MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS) { + count += 1; + } + } + return count; +} + +// needs to acquire the lock `mTimeResultMutex_` before calling this method +bool NtpTrustedTime::FindBestTimeResult() +{ + if (TimeResultCandidates_.size() == 0 || TimeResultCandidates_.size() == 1) { + TIME_HILOGW(TIME_MODULE_SERVICE, "no or one candidate"); + return false; + } + + int32_t mostVotedTimeResultCount = 0; + std::shared_ptr mostVotedTimeResult; + for (size_t i = 0; i < TimeResultCandidates_.size(); i++) { + auto timeResult = TimeResultCandidates_[i]; + int32_t count = GetSameTimeResultCount(TimeResultCandidates_[i]); + if (count > mostVotedTimeResultCount) { + mostVotedTimeResultCount = count; + mostVotedTimeResult = timeResult; + } + } + + TimeResultCandidates_.clear(); + if (mostVotedTimeResultCount == 1) { + TIME_HILOGW(TIME_MODULE_SERVICE, "no best candidate"); + return false; + } else { + mTimeResult = mostVotedTimeResult; + return true; + } +} + +void NtpTrustedTime::ClearTimeResultCandidates() +{ + TimeResultCandidates_.clear(); +} + int64_t NtpTrustedTime::CurrentTimeMillis() { TIME_HILOGD(TIME_MODULE_SERVICE, "start"); @@ -63,7 +141,12 @@ int64_t NtpTrustedTime::CurrentTimeMillis() return TIME_RESULT_UNINITED; } TIME_HILOGD(TIME_MODULE_SERVICE, "end"); - return mTimeResult->CurrentTimeMillis(); + int64_t bootTime = 0; + int res = TimeUtils::GetBootTimeMs(bootTime); + if (res != E_TIME_OK) { + return TIME_RESULT_UNINITED; + } + return mTimeResult->CurrentTimeMillis(bootTime); } int64_t NtpTrustedTime::ElapsedRealtimeMillis() @@ -113,28 +196,21 @@ int64_t NtpTrustedTime::TimeResult::GetElapsedRealtimeMillis() return mElapsedRealtimeMillis; } -int64_t NtpTrustedTime::TimeResult::CurrentTimeMillis() +int64_t NtpTrustedTime::TimeResult::CurrentTimeMillis(int64_t bootTime) { if (mTimeMillis == 0 || mElapsedRealtimeMillis == 0) { TIME_HILOGD(TIME_MODULE_SERVICE, "Missing authoritative time source"); return TIME_RESULT_UNINITED; } - int64_t bootTime = 0; - int res = TimeUtils::GetBootTimeMs(bootTime); - if (res != E_TIME_OK) { - return TIME_RESULT_UNINITED; - } if (bootTime - mElapsedRealtimeMillis > ONE_DAY) { Clear(); return TIME_RESULT_UNINITED; } - return mTimeMillis + GetAgeMillis(); + return mTimeMillis + GetAgeMillis(bootTime); } -int64_t NtpTrustedTime::TimeResult::GetAgeMillis() +int64_t NtpTrustedTime::TimeResult::GetAgeMillis(int64_t bootTime) { - int64_t bootTime = 0; - TimeUtils::GetBootTimeMs(bootTime); return bootTime - this->mElapsedRealtimeMillis; } diff --git a/services/time/src/ntp_update_time.cpp b/services/time/src/ntp_update_time.cpp index f5f2cd638f9f0ab5261abd3038c2b6a7aacc797c..d72c467036f6180dadbfc1133ec1200cce5e8765 100644 --- a/services/time/src/ntp_update_time.cpp +++ b/services/time/src/ntp_update_time.cpp @@ -147,6 +147,7 @@ bool NtpUpdateTime::IsInUpdateInterval() return false; } +// needs to acquire the lock `requestMutex_` before calling this method NtpRefreshCode NtpUpdateTime::GetNtpTimeInner() { if (IsInUpdateInterval()) { @@ -158,12 +159,16 @@ NtpRefreshCode NtpUpdateTime::GetNtpTimeInner() ntpSpecList.insert(ntpSpecList.end(), ntpList.begin(), ntpList.end()); for (int i = 0; i < RETRY_TIMES; i++) { for (size_t j = 0; j < ntpSpecList.size(); j++) { - TIME_HILOGI(TIME_MODULE_SERVICE, "ntpServer is : %{public}s", ntpSpecList[j].c_str()); + TIME_HILOGI(TIME_MODULE_SERVICE, "ntpServer is:%{public}s", ntpSpecList[j].c_str()); if (NtpTrustedTime::GetInstance().ForceRefresh(ntpSpecList[j])) { return REFRESH_SUCCESS; } } + if (NtpTrustedTime::GetInstance().FindBestTimeResult()) { + return REFRESH_SUCCESS; + } } + NtpTrustedTime::GetInstance().ClearTimeResultCandidates(); return REFRESH_FAILED; } diff --git a/test/unittest/service_test/src/time_service_time_test.cpp b/test/unittest/service_test/src/time_service_time_test.cpp index 9be85083ed53867fd48fe5c3bf14e766d9ea3f94..094b612f661621018b69046e1bdba595f18b051c 100644 --- a/test/unittest/service_test/src/time_service_time_test.cpp +++ b/test/unittest/service_test/src/time_service_time_test.cpp @@ -58,6 +58,12 @@ const std::string AUTO_TIME_STATUS_ON = "ON"; const uint64_t TIMER_ID = 88888; constexpr int64_t MINUTE_TO_MILLISECOND = 60000; constexpr char BYTE_SNTP_MESSAGE = 0xD8; +constexpr int64_t MAX_TIME_DRIFT_IN_ONE_DAY = 2000; +constexpr int64_t MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS = 100; +constexpr int64_t ONE_DAY = 86400000; +constexpr int64_t ONE_HOUR = 3600000; +constexpr int64_t TWO_SECOND = 2000; +constexpr int64_t ONE_SECOND = 1000; static HapPolicyParams g_policyA = { .apl = APL_SYSTEM_CORE, @@ -678,6 +684,206 @@ HWTEST_F(TimeServiceTimeTest, NtpTrustedTime004, TestSize.Level0) EXPECT_EQ(res, 0); } +/** +* @tc.name: NtpTrustedTime005 +* @tc.desc: test func IsTimeResultTrusted. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime005, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + // test mTimeResult is nullptr + std::shared_ptr ntpTrustedTime = std::make_shared(); + ntpTrustedTime->mTimeResult = nullptr; + auto timeResult = std::make_shared(wallTime, bootTime, 0); + bool ret = true; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, false); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 1); + + // test mTimeResult is unavailable due to time out + auto wallTime1 = wallTime - 2 * ONE_DAY; + auto bootTime1 = bootTime - 2 * ONE_DAY; + ntpTrustedTime->mTimeResult = std::make_shared(wallTime1, bootTime1, 0); + ret = true; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, false); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 2); + + // test new result Within the margin of error + auto wallTime2 = wallTime - ONE_HOUR - MAX_TIME_DRIFT_IN_ONE_DAY; + auto bootTime2 = bootTime - ONE_HOUR; + ntpTrustedTime->mTimeResult = std::make_shared(wallTime2, bootTime2, 0); + ret = false; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, true); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetTimeMillis(), wallTime); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetElapsedRealtimeMillis(), bootTime); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 0); + + auto wallTime3 = wallTime - ONE_HOUR + MAX_TIME_DRIFT_IN_ONE_DAY; + auto bootTime3 = bootTime - ONE_HOUR; + ntpTrustedTime->mTimeResult = std::make_shared(wallTime3, bootTime3, 0); + ret = false; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, true); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetTimeMillis(), wallTime); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetElapsedRealtimeMillis(), bootTime); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 0); +} + +/** +* @tc.name: NtpTrustedTime006 +* @tc.desc: test func IsTimeResultTrusted. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime006, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + // test mTimeResult is nullptr + std::shared_ptr ntpTrustedTime = std::make_shared(); + auto timeResult = std::make_shared(wallTime, bootTime, 0); + + // test new result out of the margin of error + auto wallTime1 = wallTime - ONE_HOUR - MAX_TIME_DRIFT_IN_ONE_DAY - TWO_SECOND; + auto bootTime1 = bootTime - ONE_HOUR; + ntpTrustedTime->mTimeResult = std::make_shared(wallTime1, bootTime1, 0); + auto ret = true; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, false); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetTimeMillis(), wallTime1); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetElapsedRealtimeMillis(), bootTime1); + EXPECT_EQ(ret, false); + + auto wallTime2 = wallTime - ONE_HOUR + MAX_TIME_DRIFT_IN_ONE_DAY + TWO_SECOND; + auto bootTime2 = bootTime - ONE_HOUR; + ntpTrustedTime->mTimeResult = std::make_shared(wallTime2, bootTime2, 0); + ret = true; + ret = ntpTrustedTime->IsTimeResultTrusted(timeResult); + EXPECT_EQ(ret, false); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetTimeMillis(), wallTime2); + EXPECT_EQ(ntpTrustedTime->mTimeResult->GetElapsedRealtimeMillis(), bootTime2); +} + +/** +* @tc.name: NtpTrustedTime007 +* @tc.desc: test func FindBestTimeResult with 3 same candidates, return true. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime007, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + std::shared_ptr ntpTrustedTime = std::make_shared(); + + // three time is similar, vote for 3 candidate + auto timeResult1 = std::make_shared(wallTime, bootTime, 0); + auto wallTime2 = wallTime + ONE_SECOND + MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS; + auto bootTime2 = bootTime + ONE_SECOND; + auto timeResult2 = std::make_shared(wallTime2, bootTime2, 0); + auto wallTime3 = wallTime + TWO_SECOND + MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS / 2; + auto bootTime3 = bootTime + TWO_SECOND; + auto timeResult3 = std::make_shared(wallTime3, bootTime3, 0); + bool ret = true; + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult1); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult2); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult3); + ret = ntpTrustedTime->FindBestTimeResult(); + EXPECT_EQ(ret, true); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 0); +} + +/** +* @tc.name: NtpTrustedTime008 +* @tc.desc: test func FindBestTimeResult with 2 same candidates, return true. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime008, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + std::shared_ptr ntpTrustedTime = std::make_shared(); + + // two time is similar, vote for 3 candidate + auto timeResult1 = std::make_shared(wallTime, bootTime, 0); + auto wallTime2 = wallTime + TWO_SECOND; + auto bootTime2 = bootTime + ONE_SECOND; + auto timeResult2 = std::make_shared(wallTime2, bootTime2, 0); + auto wallTime3 = wallTime + TWO_SECOND; + auto bootTime3 = bootTime + TWO_SECOND; + auto timeResult3 = std::make_shared(wallTime3, bootTime3, 0); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult1); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult2); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult3); + bool ret = false; + ret = ntpTrustedTime->FindBestTimeResult(); + EXPECT_EQ(ret, true); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 0); +} + +/** +* @tc.name: NtpTrustedTime009 +* @tc.desc: test func FindBestTimeResult with no same candidates, return false. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime009, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + std::shared_ptr ntpTrustedTime = std::make_shared(); + + // test 3 timeResult got different time + auto timeResult1 = std::make_shared(wallTime, bootTime, 0); + auto wallTime2 = wallTime + TWO_SECOND; + auto bootTime2 = bootTime + ONE_SECOND; + auto timeResult2 = std::make_shared(wallTime2, bootTime2, 0); + auto wallTime3 = wallTime + TWO_SECOND + MAX_TIME_TOLERANCE_BETWEEN_NTP_SERVERS * 2; + auto bootTime3 = bootTime + TWO_SECOND; + auto timeResult3 = std::make_shared(wallTime3, bootTime3, 0); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult1); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult2); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult3); + bool ret = true; + ret = ntpTrustedTime->FindBestTimeResult(); + EXPECT_EQ(ret, false); + EXPECT_EQ(ntpTrustedTime->TimeResultCandidates_.size(), 0); +} + +/** +* @tc.name: NtpTrustedTime010 +* @tc.desc: test func FindBestTimeResult with 0 or 1 candidates. +* @tc.type: FUNC +*/ +HWTEST_F(TimeServiceTimeTest, NtpTrustedTime010, TestSize.Level0) +{ + int64_t wallTime = 0; + TimeUtils::GetWallTimeMs(wallTime); + int64_t bootTime = 0; + TimeUtils::GetBootTimeMs(bootTime); + std::shared_ptr ntpTrustedTime = std::make_shared(); + + bool ret = true; + ret = ntpTrustedTime->FindBestTimeResult(); + EXPECT_EQ(ret, false); + + auto timeResult = std::make_shared(wallTime, bootTime, 0); + ntpTrustedTime->TimeResultCandidates_.push_back(timeResult); + ret = ntpTrustedTime->FindBestTimeResult(); + EXPECT_EQ(ret, false); +} + /** * @tc.name: TimeTick001 * @tc.desc: Check RefreshNextTriggerTime.