OSDN Git Service

Add support for native kernel and callout wakelocks
authorPavlin Radoslavov <pavlin@google.com>
Mon, 24 Aug 2015 23:29:21 +0000 (16:29 -0700)
committerPavlin Radoslavov <pavlin@google.com>
Fri, 22 Jan 2016 01:38:18 +0000 (17:38 -0800)
 * 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

btcore/src/osi_module.c
btif/src/bluetooth.c
btif/src/btif_debug.c
osi/Android.mk
osi/include/alarm.h
osi/include/wakelock.h [new file with mode: 0644]
osi/src/alarm.c
osi/src/wakelock.c [new file with mode: 0644]
osi/test/AlarmTestHarness.cpp

index 3373555..e7a580d 100644 (file)
@@ -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_init(void) {
 
 future_t *osi_clean_up(void) {
   alarm_cleanup();
+  wakelock_cleanup();
   mutex_cleanup();
   return future_new_immediate(FUTURE_SUCCESS);
 }
index 204f90d..f2dde9d 100644 (file)
@@ -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 @@ int config_hci_snoop_log(uint8_t enable)
 }
 
 static int set_os_callouts(bt_os_callouts_t *callouts) {
-    bt_os_callouts = callouts;
+    wakelock_set_os_callouts(callouts);
     return BT_STATUS_SUCCESS;
 }
 
index bd7a844..7e8cec9 100644 (file)
@@ -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_init(void) {
 
 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
index 40cedc2..a332980 100644 (file)
@@ -48,7 +48,8 @@ btosiCommonSrc := \
     ./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 \
index 4b3e395..13c7d87 100644 (file)
@@ -57,12 +57,7 @@ void alarm_cancel(alarm_t *alarm);
 // 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 (file)
index 0000000..658a205
--- /dev/null
@@ -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);
index 9308ad2..b675e5f 100644 (file)
@@ -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 @@ struct alarm_t {
 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 reschedule_root_alarm(void);
 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_cancel(alarm_t *alarm) {
 }
 
 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 @@ void alarm_cleanup(void) {
   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 @@ static void reschedule_root_alarm(void) {
   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 @@ static void reschedule_root_alarm(void) {
 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 @@ static void callback_dispatch(UNUSED_ATTR void *context) {
   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 (file)
index 0000000..db997e9
--- /dev/null
@@ -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);
+}
index 39407dd..32fde6d 100644 (file)
@@ -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 @@ void AlarmTestHarness::SetUp() {
   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());