blob: ea7b99a3351b0c4c4116c09b2cdb4b7d09aba000 [file] [log] [blame]
/******************************************************************************
*
* 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 <cerrno>
#include <mutex>
#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<WakelockManagerData> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<WakelockManagerData> WakelockManager::GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) {
std::lock_guard<std::recursive_mutex> lock_guard(mutex_);
return pstats_->GetDumpsysData(fb_builder, is_native_);
}
WakelockManager::WakelockManager() : pstats_(std::make_unique<Stats>()) {}
WakelockManager::~WakelockManager() = default;
} // namespace os
} // namespace bluetooth