/****************************************************************************** * * Copyright 2021 Google, Inc. * * 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. * ******************************************************************************/ #define LOG_TAG "BtGdWakelock" #include "os/wakelock_manager.h" #include #include #include "os/internal/wakelock_native.h" #include "os/log.h" namespace bluetooth { namespace os { using internal::WakelockNative; using StatusCode = WakelockNative::StatusCode; uint64_t now_ms() { struct timespec ts = {}; if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) { LOG_ERROR("unable to get current time: %s", strerror(errno)); return 0; } return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL); } const std::string WakelockManager::kBtWakelockId = "bluetooth_gd_timer"; // Wakelock statistics for the "bluetooth_timer" struct WakelockManager::Stats { bool is_acquired = false; size_t acquired_count = 0; size_t released_count = 0; size_t acquired_errors = 0; size_t released_errors = 0; uint64_t min_acquired_interval_ms = 0; uint64_t max_acquired_interval_ms = 0; uint64_t last_acquired_interval_ms = 0; uint64_t total_acquired_interval_ms = 0; uint64_t last_acquired_timestamp_ms = 0; uint64_t last_released_timestamp_ms = 0; uint64_t last_reset_timestamp_ms = now_ms(); StatusCode last_acquired_error = StatusCode::SUCCESS; StatusCode last_released_error = StatusCode::SUCCESS; void Reset() { is_acquired = false; acquired_count = 0; released_count = 0; acquired_errors = 0; released_errors = 0; min_acquired_interval_ms = 0; max_acquired_interval_ms = 0; last_acquired_interval_ms = 0; total_acquired_interval_ms = 0; last_acquired_timestamp_ms = 0; last_released_timestamp_ms = 0; last_reset_timestamp_ms = now_ms(); last_acquired_error = StatusCode::SUCCESS; last_released_error = StatusCode::SUCCESS; } // Update the Bluetooth acquire wakelock statistics. // // This function should be called every time when the wakelock is acquired. // |acquired_status| is the status code that was return when the wakelock was // acquired. void UpdateAcquiredStats(StatusCode acquired_status) { const uint64_t just_now_ms = now_ms(); if (acquired_status != StatusCode::SUCCESS) { acquired_errors++; last_acquired_error = acquired_status; } if (is_acquired) { return; } is_acquired = true; acquired_count++; last_acquired_timestamp_ms = just_now_ms; } // Update the Bluetooth release wakelock statistics. // // This function should be called every time when the wakelock is released. // |released_status| is the status code that was return when the wakelock was // released. void UpdateReleasedStats(StatusCode released_status) { const uint64_t just_now_ms = now_ms(); if (released_status != StatusCode::SUCCESS) { released_errors++; last_released_error = released_status; } if (!is_acquired) { return; } is_acquired = false; released_count++; last_released_timestamp_ms = just_now_ms; // Compute the acquired interval and update the statistics uint64_t delta_ms = just_now_ms - last_acquired_timestamp_ms; if (delta_ms < min_acquired_interval_ms || released_count == 1) { min_acquired_interval_ms = delta_ms; } if (delta_ms > max_acquired_interval_ms) { max_acquired_interval_ms = delta_ms; } last_acquired_interval_ms = delta_ms; total_acquired_interval_ms += delta_ms; } flatbuffers::Offset GetDumpsysData( flatbuffers::FlatBufferBuilder* fb_builder, bool is_native) const { const uint64_t just_now_ms = now_ms(); // Compute the last acquired interval if the wakelock is still acquired uint64_t delta_ms = 0; uint64_t last_interval_ms = last_acquired_interval_ms; uint64_t min_interval_ms = min_acquired_interval_ms; uint64_t max_interval_ms = max_acquired_interval_ms; uint64_t avg_interval_ms = 0; if (is_acquired) { delta_ms = just_now_ms - last_acquired_timestamp_ms; if (delta_ms > max_interval_ms) { max_interval_ms = delta_ms; } if (delta_ms < min_interval_ms) { min_interval_ms = delta_ms; } last_interval_ms = delta_ms; } uint64_t total_interval_ms = total_acquired_interval_ms + delta_ms; if (acquired_count > 0) { avg_interval_ms = total_interval_ms / acquired_count; } WakelockManagerDataBuilder builder(*fb_builder); builder.add_title(fb_builder->CreateString("Bluetooth Wakelock Statistics")); builder.add_is_acquired(is_acquired); builder.add_is_native(is_native); builder.add_acquired_count(acquired_count); builder.add_released_count(released_count); builder.add_acquired_error_count(acquired_errors); builder.add_released_error_count(released_errors); builder.add_last_acquire_error_code(last_acquired_error); builder.add_last_release_error_code(last_released_error); builder.add_last_acquired_timestamp_millis(last_interval_ms); builder.add_last_released_timestamp_millis(last_released_timestamp_ms); builder.add_last_interval_millis(last_acquired_interval_ms); builder.add_max_interval_millis(max_interval_ms); builder.add_min_interval_millis(min_interval_ms); builder.add_avg_interval_millis(avg_interval_ms); builder.add_total_interval_millis(total_interval_ms); builder.add_total_time_since_reset_millis(just_now_ms - last_reset_timestamp_ms); return builder.Finish(); } }; void WakelockManager::SetOsCallouts(OsCallouts* callouts, Handler* handler) { std::lock_guard lock_guard(mutex_); if (initialized_) { LOG_WARN("Setting OS callouts after initialization can lead to wakelock leak!"); } os_callouts_ = callouts; os_callouts_handler_ = handler; is_native_ = (os_callouts_ == nullptr); if (is_native_) { ASSERT_LOG(os_callouts_handler_ != nullptr, "handler must not be null when callout is not null"); } LOG_INFO("set to %s", is_native_ ? "native" : "non-native"); } bool WakelockManager::Acquire() { std::lock_guard lock_guard(mutex_); if (!initialized_) { if (is_native_) { WakelockNative::Get().Initialize(); } initialized_ = true; } StatusCode status; if (is_native_) { status = WakelockNative::Get().Acquire(kBtWakelockId); } else { os_callouts_handler_->CallOn(os_callouts_, &OsCallouts::AcquireCallout, kBtWakelockId); status = StatusCode::SUCCESS; } pstats_->UpdateAcquiredStats(status); if (status != StatusCode::SUCCESS) { LOG_ERROR("unable to acquire wake lock, error code: %u", status); } return status == StatusCode ::SUCCESS; } bool WakelockManager::Release() { std::lock_guard lock_guard(mutex_); if (!initialized_) { if (is_native_) { WakelockNative::Get().Initialize(); } initialized_ = true; } StatusCode status; if (is_native_) { status = WakelockNative::Get().Release(kBtWakelockId); } else { os_callouts_handler_->CallOn(os_callouts_, &OsCallouts::ReleaseCallout, kBtWakelockId); status = StatusCode ::SUCCESS; } pstats_->UpdateReleasedStats(status); if (status != StatusCode::SUCCESS) { LOG_ERROR("unable to release wake lock, error code: %u", status); } return status == StatusCode ::SUCCESS; } void WakelockManager::CleanUp() { std::lock_guard lock_guard(mutex_); if (!initialized_) { LOG_ERROR("Already uninitialized"); return; } if (pstats_->is_acquired) { LOG_ERROR("Releasing wake lock as part of cleanup"); Release(); } if (is_native_) { WakelockNative::Get().CleanUp(); } pstats_->Reset(); initialized_ = false; } flatbuffers::Offset WakelockManager::GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) { std::lock_guard lock_guard(mutex_); return pstats_->GetDumpsysData(fb_builder, is_native_); } WakelockManager::WakelockManager() : pstats_(std::make_unique()) {} WakelockManager::~WakelockManager() = default; } // namespace os } // namespace bluetooth