Add support for native kernel and callout wakelocks
* Moved wakelock-related code to osi/src/wakelock.c
The API is in osi/include/wakelock.h
* Use wakelock_set_os_callouts() to specify native kernel
or callout-based wakelock.
On Android, wakelock_set_os_callouts() is called to
set the bt_os_callouts_t callbacks into the Java layer.
* Renamed alarm_set_wake_lock_paths() to wakelock_set_paths()
Also, added Bluetooth Wakelock Statistics to the bugreport.
Sample output:
$ adb shell dumpsys bluetooth_manager
...
Bluetooth Wakelock Statistics:
Wakelock is acquired : false
Wakelock acquired/released count : 5 / 5
Wakelock acquired/released errors : 0 / 0
Wakelock last acquired time (ms) : 1524
Wakelock acquired time min/max/avg (ms) : 1511 / 8104 / 3167
Wakelock total acquired time (ms) : 15836
Bluetooth total run time (ms) : 44123
Bug: 26645431
Change-Id: I42bfb7db5b016719faea39e47ebc77c3ad02467b
diff --git a/btcore/src/osi_module.c b/btcore/src/osi_module.c
index 3373555..e7a580d 100644
--- a/btcore/src/osi_module.c
+++ b/btcore/src/osi_module.c
@@ -25,6 +25,7 @@
#include "osi/include/log.h"
#include "osi/include/mutex.h"
#include "osi/include/osi.h"
+#include "osi/include/wakelock.h"
future_t *osi_init(void) {
mutex_init();
@@ -33,6 +34,7 @@
future_t *osi_clean_up(void) {
alarm_cleanup();
+ wakelock_cleanup();
mutex_cleanup();
return future_new_immediate(FUTURE_SUCCESS);
}
diff --git a/btif/src/bluetooth.c b/btif/src/bluetooth.c
index 204f90d..f2dde9d 100644
--- a/btif/src/bluetooth.c
+++ b/btif/src/bluetooth.c
@@ -53,6 +53,7 @@
#include "osi/include/allocation_tracker.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
+#include "osi/include/wakelock.h"
#include "stack_manager.h"
#include "btif_config.h"
@@ -68,9 +69,6 @@
bt_callbacks_t *bt_hal_cbacks = NULL;
-/** Operating System specific callouts for resource management */
-bt_os_callouts_t *bt_os_callouts = NULL;
-
/************************************************************************************
** Externs
************************************************************************************/
@@ -423,7 +421,7 @@
}
static int set_os_callouts(bt_os_callouts_t *callouts) {
- bt_os_callouts = callouts;
+ wakelock_set_os_callouts(callouts);
return BT_STATUS_SUCCESS;
}
diff --git a/btif/src/btif_debug.c b/btif/src/btif_debug.c
index bd7a844..7e8cec9 100644
--- a/btif/src/btif_debug.c
+++ b/btif/src/btif_debug.c
@@ -23,6 +23,7 @@
#include "btif/include/btif_debug_btsnoop.h"
#include "btif/include/btif_debug_conn.h"
#include "include/bt_target.h"
+#include "osi/include/wakelock.h"
void btif_debug_init(void) {
#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
@@ -32,6 +33,7 @@
void btif_debug_dump(int fd) {
btif_debug_conn_dump(fd);
+ wakelock_debug_dump(fd);
#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
btif_debug_btsnoop_dump(fd);
#endif
diff --git a/osi/Android.mk b/osi/Android.mk
index 40cedc2..a332980 100644
--- a/osi/Android.mk
+++ b/osi/Android.mk
@@ -48,7 +48,8 @@
./src/socket_utils/socket_local_client.c \
./src/socket_utils/socket_local_server.c \
./src/thread.c \
- ./src/time.c
+ ./src/time.c \
+ ./src/wakelock.c
btosiCommonTestSrc := \
./test/AlarmTestHarness.cpp \
diff --git a/osi/include/alarm.h b/osi/include/alarm.h
index 4b3e395..13c7d87 100644
--- a/osi/include/alarm.h
+++ b/osi/include/alarm.h
@@ -57,12 +57,7 @@
// TODO: Remove this function once PM timers can be re-factored
period_ms_t alarm_get_remaining_ms(const alarm_t *alarm);
-// Alarm-related state cleanup
+// Cleanup the alarm internal state.
+// This function should be called by the OSI module cleanup during
+// graceful shutdown.
void alarm_cleanup(void);
-
-// This function should not need to be called normally.
-// /sys/power/wake_{|un}lock are used by default.
-// This is not guaranteed to have any effect after an alarm has been
-// set with alarm_set.
-// If |lock_path| or |unlock_path| are NULL, that path is not changed.
-void alarm_set_wake_lock_paths(const char *lock_path, const char *unlock_path);
diff --git a/osi/include/wakelock.h b/osi/include/wakelock.h
new file mode 100644
index 0000000..658a205
--- /dev/null
+++ b/osi/include/wakelock.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Copyright (C) 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.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdbool.h>
+#include <hardware/bluetooth.h>
+
+// Set the Bluetooth OS callouts to |callouts|.
+// This function should be called when native kernel wakelocks are not used
+// directly. If this function is not called, or |callouts| is NULL, then native
+// kernel wakelocks will be used.
+void wakelock_set_os_callouts(bt_os_callouts_t *callouts);
+
+// Acquire the Bluetooth wakelock.
+// The function is thread safe.
+// Return true on success, otherwise false.
+bool wakelock_acquire(void);
+
+// Release the Bluetooth wakelock.
+// The function is thread safe.
+// Return true on success, otherwise false.
+bool wakelock_release(void);
+
+// Cleanup the wakelock internal state.
+// This function should be called by the OSI module cleanup during
+// graceful shutdown.
+void wakelock_cleanup(void);
+
+// This function should not need to be called normally.
+// /sys/power/wake_{|un}lock are used by default.
+// This is not guaranteed to have any effect after an alarm has been
+// set with alarm_set.
+// If |lock_path| or |unlock_path| are NULL, that path is not changed.
+void wakelock_set_paths(const char *lock_path, const char *unlock_path);
+
+// Dump wakelock-related debug info to the |fd| file descriptor.
+// The caller is responsible for closing the |fd|.
+void wakelock_debug_dump(int fd);
diff --git a/osi/src/alarm.c b/osi/src/alarm.c
index 9308ad2..b675e5f 100644
--- a/osi/src/alarm.c
+++ b/osi/src/alarm.c
@@ -24,7 +24,6 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
-#include <limits.h>
#include <malloc.h>
#include <pthread.h>
#include <signal.h>
@@ -39,6 +38,7 @@
#include "osi/include/osi.h"
#include "osi/include/semaphore.h"
#include "osi/include/thread.h"
+#include "osi/include/wakelock.h"
// Make callbacks run at high thread priority. Some callbacks are used for audio
// related timer tasks as well as re-transmissions etc. Since we at this point
@@ -70,15 +70,6 @@
int64_t TIMER_INTERVAL_FOR_WAKELOCK_IN_MS = 3000;
static const clockid_t CLOCK_ID = CLOCK_BOOTTIME;
static const clockid_t CLOCK_ID_ALARM = CLOCK_BOOTTIME_ALARM;
-static const char *WAKE_LOCK_ID = "bluetooth_timer";
-static char *DEFAULT_WAKE_LOCK_PATH = "/sys/power/wake_lock";
-static char *DEFAULT_WAKE_UNLOCK_PATH = "/sys/power/wake_unlock";
-static char *wake_lock_path = NULL;
-static char *wake_unlock_path = NULL;
-static ssize_t locked_id_len = -1;
-static pthread_once_t wake_fds_initialized = PTHREAD_ONCE_INIT;
-static int wake_lock_fd = INVALID_FD;
-static int wake_unlock_fd = INVALID_FD;
// This mutex ensures that the |alarm_set|, |alarm_cancel|, and alarm callback
// functions execute serially and not concurrently. As a result, this mutex also
@@ -102,9 +93,6 @@
static void timer_callback(void *data);
static void callback_dispatch(void *context);
static bool timer_create_internal(const clockid_t clock_id, timer_t *timer);
-static void initialize_wake_fds(void);
-static bool acquire_wake_lock(void);
-static bool release_wake_lock(void);
alarm_t *alarm_new(void) {
// Make sure we have a list we can insert alarms into.
@@ -217,16 +205,6 @@
}
void alarm_cleanup(void) {
- if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH) {
- osi_free(wake_lock_path);
- wake_lock_path = NULL;
- }
-
- if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH) {
- osi_free(wake_unlock_path);
- wake_unlock_path = NULL;
- }
-
// If lazy_initialize never ran there is nothing else to do
if (!alarms)
return;
@@ -242,26 +220,9 @@
list_free(alarms);
alarms = NULL;
- wake_fds_initialized = PTHREAD_ONCE_INIT;
-
pthread_mutex_destroy(&monitor);
}
-void alarm_set_wake_lock_paths(const char *lock_path, const char *unlock_path) {
- if (lock_path) {
- if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH)
- osi_free(wake_lock_path);
- wake_lock_path = osi_strndup(lock_path, PATH_MAX);
- }
-
- if (unlock_path) {
- if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH)
- osi_free(wake_unlock_path);
- wake_unlock_path = osi_strndup(unlock_path, PATH_MAX);
- }
-
-}
-
static bool lazy_initialize(void) {
assert(alarms == NULL);
@@ -385,7 +346,7 @@
const int64_t next_expiration = next->deadline - now();
if (next_expiration < TIMER_INTERVAL_FOR_WAKELOCK_IN_MS) {
if (!timer_set) {
- if (!acquire_wake_lock()) {
+ if (!wakelock_acquire()) {
LOG_ERROR(LOG_TAG, "%s unable to acquire wake lock", __func__);
goto done;
}
@@ -427,7 +388,7 @@
done:
timer_set = timer_time.it_value.tv_sec != 0 || timer_time.it_value.tv_nsec != 0;
if (timer_was_set && !timer_set) {
- release_wake_lock();
+ wakelock_release();
}
if (timer_settime(timer, TIMER_ABSTIME, &timer_time, NULL) == -1)
@@ -505,74 +466,6 @@
LOG_DEBUG(LOG_TAG, "%s Callback thread exited", __func__);
}
-static void initialize_wake_fds(void) {
- LOG_DEBUG(LOG_TAG, "%s opening wake locks", __func__);
-
- if (!wake_lock_path)
- wake_lock_path = DEFAULT_WAKE_LOCK_PATH;
-
- wake_lock_fd = open(wake_lock_path, 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, strerror(errno));
- }
-
- if (!wake_unlock_path)
- wake_unlock_path = DEFAULT_WAKE_UNLOCK_PATH;
-
- wake_unlock_fd = open(wake_unlock_path, 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, strerror(errno));
- }
-}
-
-static bool acquire_wake_lock(void) {
- pthread_once(&wake_fds_initialized, initialize_wake_fds);
-
- if (wake_lock_fd == INVALID_FD) {
- LOG_ERROR(LOG_TAG, "%s lock not acquired, invalid fd", __func__);
- return false;
- }
-
- if (wake_unlock_fd == INVALID_FD) {
- LOG_ERROR(LOG_TAG, "%s not acquiring lock: can't release lock", __func__);
- return false;
- }
-
- 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 false;
- } 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 true;
-}
-
-static bool release_wake_lock(void) {
- pthread_once(&wake_fds_initialized, initialize_wake_fds);
-
- if (wake_unlock_fd == INVALID_FD) {
- LOG_ERROR(LOG_TAG, "%s lock not released, invalid fd", __func__);
- return false;
- }
-
- 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 true;
-}
-
static bool timer_create_internal(const clockid_t clock_id, timer_t *timer) {
assert(timer != NULL);
diff --git a/osi/src/wakelock.c b/osi/src/wakelock.c
new file mode 100644
index 0000000..db997e9
--- /dev/null
+++ b/osi/src/wakelock.c
@@ -0,0 +1,382 @@
+/******************************************************************************
+ *
+ * Copyright (C) 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <hardware/bluetooth.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.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"
+
+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 char *DEFAULT_WAKE_LOCK_PATH = "/sys/power/wake_lock";
+static char *DEFAULT_WAKE_UNLOCK_PATH = "/sys/power/wake_unlock";
+static char *wake_lock_path = NULL;
+static char *wake_unlock_path = NULL;
+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;
+ period_ms_t min_acquired_interval_ms;
+ period_ms_t max_acquired_interval_ms;
+ period_ms_t last_acquired_interval_ms;
+ period_ms_t total_acquired_interval_ms;
+ period_ms_t last_acquired_timestamp_ms;
+ period_ms_t last_released_timestamp_ms;
+ period_ms_t last_reset_timestamp_ms;
+} 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 pthread_mutex_t monitor;
+
+static bool wakelock_acquire_callout(void);
+static bool wakelock_acquire_native(void);
+static bool wakelock_release_callout(void);
+static bool 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);
+
+ if (is_native)
+ return wakelock_acquire_native();
+ else
+ return wakelock_acquire_callout();
+}
+
+static bool wakelock_acquire_callout(void)
+{
+ const bt_status_t status =
+ wakelock_os_callouts->acquire_wake_lock(WAKE_LOCK_ID);
+ 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 bool wakelock_acquire_native(void)
+{
+ if (wake_lock_fd == INVALID_FD) {
+ LOG_ERROR(LOG_TAG, "%s lock not acquired, invalid fd", __func__);
+ return false;
+ }
+
+ if (wake_unlock_fd == INVALID_FD) {
+ LOG_ERROR(LOG_TAG, "%s not acquiring lock: can't release lock", __func__);
+ return false;
+ }
+
+ 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 false;
+ } 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 true;
+}
+
+bool wakelock_release(void) {
+ pthread_once(&initialized, wakelock_initialize);
+
+ if (is_native)
+ return wakelock_release_native();
+ else
+ return wakelock_release_callout();
+}
+
+static bool wakelock_release_callout(void)
+{
+ const bt_status_t status =
+ wakelock_os_callouts->release_wake_lock(WAKE_LOCK_ID);
+ update_wakelock_released_stats(status);
+
+ return (status == BT_STATUS_SUCCESS);
+}
+
+static bool wakelock_release_native(void)
+{
+ if (wake_unlock_fd == INVALID_FD) {
+ LOG_ERROR(LOG_TAG, "%s lock not released, invalid fd", __func__);
+ return false;
+ }
+
+ 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 true;
+}
+
+static void wakelock_initialize(void)
+{
+ pthread_mutex_init(&monitor, NULL);
+ 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)
+ wake_lock_path = DEFAULT_WAKE_LOCK_PATH;
+
+ wake_lock_fd = open(wake_lock_path, 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, strerror(errno));
+ }
+
+ if (!wake_unlock_path)
+ wake_unlock_path = DEFAULT_WAKE_UNLOCK_PATH;
+
+ wake_unlock_fd = open(wake_unlock_path, 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, strerror(errno));
+ }
+}
+
+void wakelock_cleanup(void) {
+ if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH) {
+ osi_free(wake_lock_path);
+ wake_lock_path = NULL;
+ }
+
+ if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH) {
+ osi_free(wake_unlock_path);
+ wake_unlock_path = NULL;
+ }
+
+ initialized = PTHREAD_ONCE_INIT;
+ pthread_mutex_destroy(&monitor);
+}
+
+void wakelock_set_paths(const char *lock_path, const char *unlock_path) {
+ if (lock_path) {
+ if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH)
+ osi_free(wake_lock_path);
+ wake_lock_path = osi_strndup(lock_path, PATH_MAX);
+ }
+
+ if (unlock_path) {
+ if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH)
+ osi_free(wake_unlock_path);
+ wake_unlock_path = osi_strndup(unlock_path, PATH_MAX);
+ }
+}
+
+static period_ms_t now(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) {
+ pthread_mutex_lock(&monitor);
+
+ 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();
+
+ pthread_mutex_unlock(&monitor);
+}
+
+//
+// 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 period_ms_t now_ms = now();
+
+ pthread_mutex_lock(&monitor);
+
+ if (acquired_status != BT_STATUS_SUCCESS)
+ wakelock_stats.acquired_errors++;
+
+ if (wakelock_stats.is_acquired) {
+ pthread_mutex_unlock(&monitor);
+ return;
+ }
+
+ wakelock_stats.is_acquired = true;
+ wakelock_stats.acquired_count++;
+ wakelock_stats.last_acquired_timestamp_ms = now_ms;
+
+ pthread_mutex_unlock(&monitor);
+}
+
+//
+// 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 period_ms_t now_ms = now();
+
+ pthread_mutex_lock(&monitor);
+
+ if (released_status != BT_STATUS_SUCCESS)
+ wakelock_stats.released_errors++;
+
+ if (!wakelock_stats.is_acquired) {
+ pthread_mutex_unlock(&monitor);
+ return;
+ }
+
+ wakelock_stats.is_acquired = false;
+ wakelock_stats.released_count++;
+ wakelock_stats.last_released_timestamp_ms = now_ms;
+
+ // Compute the acquired interval and update the statistics
+ period_ms_t delta_ms = 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;
+
+ pthread_mutex_unlock(&monitor);
+}
+
+void wakelock_debug_dump(int fd) {
+ const period_ms_t now_ms = now();
+
+ // Need to keep track for lock errors - e.g., the "monitor" mutex
+ // might not be initialized
+ const int lock_error = pthread_mutex_lock(&monitor);
+
+ // Compute the last acquired interval if the wakelock is still acquired
+ period_ms_t delta_ms = 0;
+ period_ms_t last_interval = wakelock_stats.last_acquired_interval_ms;
+ period_ms_t min_interval = wakelock_stats.min_acquired_interval_ms;
+ period_ms_t max_interval = wakelock_stats.max_acquired_interval_ms;
+ period_ms_t ave_interval = 0;
+
+ if (wakelock_stats.is_acquired) {
+ delta_ms = now_ms - wakelock_stats.last_acquired_timestamp_ms;
+ if (delta_ms > max_interval)
+ max_interval = delta_ms;
+ if (delta_ms < min_interval)
+ min_interval = delta_ms;
+ last_interval = delta_ms;
+ }
+ period_ms_t total_interval =
+ wakelock_stats.total_acquired_interval_ms + delta_ms;
+
+ if (wakelock_stats.acquired_count > 0)
+ ave_interval = total_interval / wakelock_stats.acquired_count;
+
+ dprintf(fd, "\nBluetooth Wakelock Statistics:\n");
+ dprintf(fd, " Wakelock is acquired : %s\n",
+ wakelock_stats.is_acquired? "true" : "false");
+ dprintf(fd, " Wakelock acquired/released count : %zu / %zu\n",
+ wakelock_stats.acquired_count, wakelock_stats.released_count);
+ dprintf(fd, " Wakelock acquired/released errors : %zu / %zu\n",
+ wakelock_stats.acquired_errors, wakelock_stats.released_errors);
+ dprintf(fd, " Wakelock last acquired time (ms) : %llu\n",
+ (unsigned long long)last_interval);
+ dprintf(fd, " Wakelock acquired time min/max/avg (ms) : %llu / %llu / %llu\n",
+ (unsigned long long)min_interval, (unsigned long long)max_interval,
+ (unsigned long long)ave_interval);
+ dprintf(fd, " Wakelock total acquired time (ms) : %llu\n",
+ (unsigned long long)total_interval);
+ dprintf(fd, " Bluetooth total run time (ms) : %llu\n",
+ (unsigned long long)(now_ms - wakelock_stats.last_reset_timestamp_ms));
+
+ if (lock_error == 0)
+ pthread_mutex_unlock(&monitor);
+}
diff --git a/osi/test/AlarmTestHarness.cpp b/osi/test/AlarmTestHarness.cpp
index 39407dd..32fde6d 100644
--- a/osi/test/AlarmTestHarness.cpp
+++ b/osi/test/AlarmTestHarness.cpp
@@ -27,6 +27,7 @@
extern "C" {
#include "osi/include/alarm.h"
#include "osi/include/allocation_tracker.h"
+#include "osi/include/wakelock.h"
}
static timer_t timer;
@@ -71,11 +72,12 @@
creat(lock_path_.c_str(), S_IRWXU);
creat(unlock_path_.c_str(), S_IRWXU);
- alarm_set_wake_lock_paths(lock_path_.c_str(), unlock_path_.c_str());
+ wakelock_set_paths(lock_path_.c_str(), unlock_path_.c_str());
}
void AlarmTestHarness::TearDown() {
alarm_cleanup();
+ wakelock_cleanup();
// clean up the temp wake lock directory
unlink(lock_path_.c_str());