/****************************************************************************** * * Copyright 2015 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 "bt_osi_wakelock" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/logging.h" #include "common/metrics.h" #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "osi/include/log.h" #include "osi/include/osi.h" #include "osi/include/thread.h" #include "osi/include/wakelock.h" using bluetooth::common::BluetoothMetricsLogger; static bt_os_callouts_t* wakelock_os_callouts = NULL; static bool is_native = true; static const clockid_t CLOCK_ID = CLOCK_BOOTTIME; static const char* WAKE_LOCK_ID = "bluetooth_timer"; static const std::string DEFAULT_WAKE_LOCK_PATH = "/sys/power/wake_lock"; static const std::string DEFAULT_WAKE_UNLOCK_PATH = "/sys/power/wake_unlock"; static std::string wake_lock_path; static std::string wake_unlock_path; static ssize_t locked_id_len = -1; static pthread_once_t initialized = PTHREAD_ONCE_INIT; static int wake_lock_fd = INVALID_FD; static int wake_unlock_fd = INVALID_FD; // Wakelock statistics for the "bluetooth_timer" typedef struct { bool is_acquired; size_t acquired_count; size_t released_count; size_t acquired_errors; size_t released_errors; uint64_t min_acquired_interval_ms; uint64_t max_acquired_interval_ms; uint64_t last_acquired_interval_ms; uint64_t total_acquired_interval_ms; uint64_t last_acquired_timestamp_ms; uint64_t last_released_timestamp_ms; uint64_t last_reset_timestamp_ms; int last_acquired_error; int last_released_error; } wakelock_stats_t; static wakelock_stats_t wakelock_stats; // This mutex ensures that the functions that update and dump the statistics // are executed serially. static std::mutex stats_mutex; static bt_status_t wakelock_acquire_callout(void); static bt_status_t wakelock_acquire_native(void); static bt_status_t wakelock_release_callout(void); static bt_status_t wakelock_release_native(void); static void wakelock_initialize(void); static void wakelock_initialize_native(void); static void reset_wakelock_stats(void); static void update_wakelock_acquired_stats(bt_status_t acquired_status); static void update_wakelock_released_stats(bt_status_t released_status); void wakelock_set_os_callouts(bt_os_callouts_t* callouts) { wakelock_os_callouts = callouts; is_native = (wakelock_os_callouts == NULL); LOG_INFO(LOG_TAG, "%s set to %s", __func__, (is_native) ? "native" : "non-native"); } bool wakelock_acquire(void) { pthread_once(&initialized, wakelock_initialize); bt_status_t status = BT_STATUS_FAIL; if (is_native) status = wakelock_acquire_native(); else status = wakelock_acquire_callout(); update_wakelock_acquired_stats(status); if (status != BT_STATUS_SUCCESS) LOG_ERROR(LOG_TAG, "%s unable to acquire wake lock: %d", __func__, status); return (status == BT_STATUS_SUCCESS); } static bt_status_t wakelock_acquire_callout(void) { return static_cast( wakelock_os_callouts->acquire_wake_lock(WAKE_LOCK_ID)); } static bt_status_t wakelock_acquire_native(void) { if (wake_lock_fd == INVALID_FD) { LOG_ERROR(LOG_TAG, "%s lock not acquired, invalid fd", __func__); return BT_STATUS_PARM_INVALID; } if (wake_unlock_fd == INVALID_FD) { LOG_ERROR(LOG_TAG, "%s not acquiring lock: can't release lock", __func__); return BT_STATUS_PARM_INVALID; } long lock_name_len = strlen(WAKE_LOCK_ID); locked_id_len = write(wake_lock_fd, WAKE_LOCK_ID, lock_name_len); if (locked_id_len == -1) { LOG_ERROR(LOG_TAG, "%s wake lock not acquired: %s", __func__, strerror(errno)); return BT_STATUS_FAIL; } else if (locked_id_len < lock_name_len) { // TODO (jamuraa): this is weird. maybe we should release and retry. LOG_WARN(LOG_TAG, "%s wake lock truncated to %zd chars", __func__, locked_id_len); } return BT_STATUS_SUCCESS; } bool wakelock_release(void) { pthread_once(&initialized, wakelock_initialize); bt_status_t status = BT_STATUS_FAIL; if (is_native) status = wakelock_release_native(); else status = wakelock_release_callout(); update_wakelock_released_stats(status); return (status == BT_STATUS_SUCCESS); } static bt_status_t wakelock_release_callout(void) { return static_cast( wakelock_os_callouts->release_wake_lock(WAKE_LOCK_ID)); } static bt_status_t wakelock_release_native(void) { if (wake_unlock_fd == INVALID_FD) { LOG_ERROR(LOG_TAG, "%s lock not released, invalid fd", __func__); return BT_STATUS_PARM_INVALID; } ssize_t wrote_name_len = write(wake_unlock_fd, WAKE_LOCK_ID, locked_id_len); if (wrote_name_len == -1) { LOG_ERROR(LOG_TAG, "%s can't release wake lock: %s", __func__, strerror(errno)); } else if (wrote_name_len < locked_id_len) { LOG_ERROR(LOG_TAG, "%s lock release only wrote %zd, assuming released", __func__, wrote_name_len); } return BT_STATUS_SUCCESS; } static void wakelock_initialize(void) { reset_wakelock_stats(); if (is_native) wakelock_initialize_native(); } static void wakelock_initialize_native(void) { LOG_DEBUG(LOG_TAG, "%s opening wake locks", __func__); if (wake_lock_path.empty()) wake_lock_path = DEFAULT_WAKE_LOCK_PATH; wake_lock_fd = open(wake_lock_path.c_str(), O_RDWR | O_CLOEXEC); if (wake_lock_fd == INVALID_FD) { LOG_ERROR(LOG_TAG, "%s can't open wake lock %s: %s", __func__, wake_lock_path.c_str(), strerror(errno)); CHECK(wake_lock_fd != INVALID_FD); } if (wake_unlock_path.empty()) wake_unlock_path = DEFAULT_WAKE_UNLOCK_PATH; wake_unlock_fd = open(wake_unlock_path.c_str(), O_RDWR | O_CLOEXEC); if (wake_unlock_fd == INVALID_FD) { LOG_ERROR(LOG_TAG, "%s can't open wake unlock %s: %s", __func__, wake_unlock_path.c_str(), strerror(errno)); CHECK(wake_unlock_fd != INVALID_FD); } } void wakelock_cleanup(void) { if (wakelock_stats.is_acquired) { LOG_ERROR(LOG_TAG, "%s releasing wake lock as part of cleanup", __func__); wakelock_release(); } wake_lock_path.clear(); wake_unlock_path.clear(); initialized = PTHREAD_ONCE_INIT; } void wakelock_set_paths(const char* lock_path, const char* unlock_path) { if (lock_path) wake_lock_path = lock_path; if (unlock_path) wake_unlock_path = unlock_path; } static uint64_t now_ms(void) { struct timespec ts; if (clock_gettime(CLOCK_ID, &ts) == -1) { LOG_ERROR(LOG_TAG, "%s unable to get current time: %s", __func__, strerror(errno)); return 0; } return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL); } // Reset the Bluetooth wakelock statistics. // This function is thread-safe. static void reset_wakelock_stats(void) { std::lock_guard lock(stats_mutex); wakelock_stats.is_acquired = false; wakelock_stats.acquired_count = 0; wakelock_stats.released_count = 0; wakelock_stats.acquired_errors = 0; wakelock_stats.released_errors = 0; wakelock_stats.min_acquired_interval_ms = 0; wakelock_stats.max_acquired_interval_ms = 0; wakelock_stats.last_acquired_interval_ms = 0; wakelock_stats.total_acquired_interval_ms = 0; wakelock_stats.last_acquired_timestamp_ms = 0; wakelock_stats.last_released_timestamp_ms = 0; wakelock_stats.last_reset_timestamp_ms = now_ms(); } // // 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. // This function is thread-safe. // static void update_wakelock_acquired_stats(bt_status_t acquired_status) { const uint64_t just_now_ms = now_ms(); std::lock_guard lock(stats_mutex); if (acquired_status != BT_STATUS_SUCCESS) { wakelock_stats.acquired_errors++; wakelock_stats.last_acquired_error = acquired_status; } if (wakelock_stats.is_acquired) { return; } wakelock_stats.is_acquired = true; wakelock_stats.acquired_count++; wakelock_stats.last_acquired_timestamp_ms = just_now_ms; BluetoothMetricsLogger::GetInstance()->LogWakeEvent( bluetooth::common::WAKE_EVENT_ACQUIRED, "", "", 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. // This function is thread-safe. // static void update_wakelock_released_stats(bt_status_t released_status) { const uint64_t just_now_ms = now_ms(); std::lock_guard lock(stats_mutex); if (released_status != BT_STATUS_SUCCESS) { wakelock_stats.released_errors++; wakelock_stats.last_released_error = released_status; } if (!wakelock_stats.is_acquired) { return; } wakelock_stats.is_acquired = false; wakelock_stats.released_count++; wakelock_stats.last_released_timestamp_ms = just_now_ms; // Compute the acquired interval and update the statistics uint64_t delta_ms = just_now_ms - wakelock_stats.last_acquired_timestamp_ms; if (delta_ms < wakelock_stats.min_acquired_interval_ms || wakelock_stats.released_count == 1) { wakelock_stats.min_acquired_interval_ms = delta_ms; } if (delta_ms > wakelock_stats.max_acquired_interval_ms) { wakelock_stats.max_acquired_interval_ms = delta_ms; } wakelock_stats.last_acquired_interval_ms = delta_ms; wakelock_stats.total_acquired_interval_ms += delta_ms; BluetoothMetricsLogger::GetInstance()->LogWakeEvent( bluetooth::common::WAKE_EVENT_RELEASED, "", "", just_now_ms); } void wakelock_debug_dump(int fd) { const uint64_t just_now_ms = now_ms(); std::lock_guard lock(stats_mutex); // Compute the last acquired interval if the wakelock is still acquired uint64_t delta_ms = 0; uint64_t last_interval_ms = wakelock_stats.last_acquired_interval_ms; uint64_t min_interval_ms = wakelock_stats.min_acquired_interval_ms; uint64_t max_interval_ms = wakelock_stats.max_acquired_interval_ms; uint64_t avg_interval_ms = 0; if (wakelock_stats.is_acquired) { delta_ms = just_now_ms - wakelock_stats.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 = wakelock_stats.total_acquired_interval_ms + delta_ms; if (wakelock_stats.acquired_count > 0) avg_interval_ms = total_interval_ms / wakelock_stats.acquired_count; dprintf(fd, "\nBluetooth Wakelock Statistics:\n"); dprintf(fd, " Is acquired : %s\n", wakelock_stats.is_acquired ? "true" : "false"); dprintf(fd, " Acquired/released count : %zu / %zu\n", wakelock_stats.acquired_count, wakelock_stats.released_count); dprintf(fd, " Acquired/released error count : %zu / %zu\n", wakelock_stats.acquired_errors, wakelock_stats.released_errors); dprintf(fd, " Last acquire/release error code: %d / %d\n", wakelock_stats.last_acquired_error, wakelock_stats.last_released_error); dprintf(fd, " Last acquired time (ms) : %llu\n", (unsigned long long)last_interval_ms); dprintf(fd, " Acquired time min/max/avg (ms) : %llu / %llu / %llu\n", (unsigned long long)min_interval_ms, (unsigned long long)max_interval_ms, (unsigned long long)avg_interval_ms); dprintf(fd, " Total acquired time (ms) : %llu\n", (unsigned long long)total_interval_ms); dprintf(fd, " Total run time (ms) : %llu\n", (unsigned long long)(just_now_ms - wakelock_stats.last_reset_timestamp_ms)); }