• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright (C) 2015 Google, Inc.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #define LOG_TAG "bt_osi_wakelock"
20 
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <hardware/bluetooth.h>
25 #include <inttypes.h>
26 #include <limits.h>
27 #include <pthread.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include "osi/include/alarm.h"
35 #include "osi/include/allocator.h"
36 #include "osi/include/log.h"
37 #include "osi/include/metrics.h"
38 #include "osi/include/osi.h"
39 #include "osi/include/thread.h"
40 #include "osi/include/wakelock.h"
41 
42 static bt_os_callouts_t *wakelock_os_callouts = NULL;
43 static bool is_native = true;
44 
45 static const clockid_t CLOCK_ID = CLOCK_BOOTTIME;
46 static const char *WAKE_LOCK_ID = "bluetooth_timer";
47 static char *DEFAULT_WAKE_LOCK_PATH = "/sys/power/wake_lock";
48 static char *DEFAULT_WAKE_UNLOCK_PATH = "/sys/power/wake_unlock";
49 static char *wake_lock_path = NULL;
50 static char *wake_unlock_path = NULL;
51 static ssize_t locked_id_len = -1;
52 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
53 static int wake_lock_fd = INVALID_FD;
54 static int wake_unlock_fd = INVALID_FD;
55 
56 // Wakelock statistics for the "bluetooth_timer"
57 typedef struct {
58   bool is_acquired;
59   size_t acquired_count;
60   size_t released_count;
61   size_t acquired_errors;
62   size_t released_errors;
63   period_ms_t min_acquired_interval_ms;
64   period_ms_t max_acquired_interval_ms;
65   period_ms_t last_acquired_interval_ms;
66   period_ms_t total_acquired_interval_ms;
67   period_ms_t last_acquired_timestamp_ms;
68   period_ms_t last_released_timestamp_ms;
69   period_ms_t last_reset_timestamp_ms;
70   int last_acquired_error;
71   int last_released_error;
72 } wakelock_stats_t;
73 
74 static wakelock_stats_t wakelock_stats;
75 
76 // This mutex ensures that the functions that update and dump the statistics
77 // are executed serially.
78 static pthread_mutex_t monitor;
79 
80 static bt_status_t wakelock_acquire_callout(void);
81 static bt_status_t wakelock_acquire_native(void);
82 static bt_status_t wakelock_release_callout(void);
83 static bt_status_t wakelock_release_native(void);
84 static void wakelock_initialize(void);
85 static void wakelock_initialize_native(void);
86 static void reset_wakelock_stats(void);
87 static void update_wakelock_acquired_stats(bt_status_t acquired_status);
88 static void update_wakelock_released_stats(bt_status_t released_status);
89 
wakelock_set_os_callouts(bt_os_callouts_t * callouts)90 void wakelock_set_os_callouts(bt_os_callouts_t *callouts)
91 {
92   wakelock_os_callouts = callouts;
93   is_native = (wakelock_os_callouts == NULL);
94   LOG_INFO(LOG_TAG, "%s set to %s",
95            __func__, (is_native)? "native" : "non-native");
96 }
97 
wakelock_acquire(void)98 bool wakelock_acquire(void) {
99   pthread_once(&initialized, wakelock_initialize);
100 
101   bt_status_t status = BT_STATUS_FAIL;
102 
103   if (is_native)
104     status = wakelock_acquire_native();
105   else
106     status = wakelock_acquire_callout();
107 
108   update_wakelock_acquired_stats(status);
109 
110   if (status != BT_STATUS_SUCCESS)
111     LOG_ERROR(LOG_TAG, "%s unable to acquire wake lock: %d", __func__, status);
112 
113   return (status == BT_STATUS_SUCCESS);
114 }
115 
wakelock_acquire_callout(void)116 static bt_status_t wakelock_acquire_callout(void) {
117   return wakelock_os_callouts->acquire_wake_lock(WAKE_LOCK_ID);
118 }
119 
wakelock_acquire_native(void)120 static bt_status_t wakelock_acquire_native(void) {
121   if (wake_lock_fd == INVALID_FD) {
122     LOG_ERROR(LOG_TAG, "%s lock not acquired, invalid fd", __func__);
123     return BT_STATUS_PARM_INVALID;
124   }
125 
126   if (wake_unlock_fd == INVALID_FD) {
127     LOG_ERROR(LOG_TAG, "%s not acquiring lock: can't release lock", __func__);
128     return BT_STATUS_PARM_INVALID;
129   }
130 
131   long lock_name_len = strlen(WAKE_LOCK_ID);
132   locked_id_len = write(wake_lock_fd, WAKE_LOCK_ID, lock_name_len);
133   if (locked_id_len == -1) {
134     LOG_ERROR(LOG_TAG, "%s wake lock not acquired: %s",
135               __func__, strerror(errno));
136     return BT_STATUS_FAIL;
137   } else if (locked_id_len < lock_name_len) {
138     // TODO (jamuraa): this is weird. maybe we should release and retry.
139     LOG_WARN(LOG_TAG, "%s wake lock truncated to %zd chars",
140              __func__, locked_id_len);
141   }
142   return BT_STATUS_SUCCESS;
143 }
144 
wakelock_release(void)145 bool wakelock_release(void) {
146   pthread_once(&initialized, wakelock_initialize);
147 
148   bt_status_t status = BT_STATUS_FAIL;
149 
150   if (is_native)
151     status = wakelock_release_native();
152   else
153     status = wakelock_release_callout();
154 
155   update_wakelock_released_stats(status);
156 
157   return (status == BT_STATUS_SUCCESS);
158 }
159 
wakelock_release_callout(void)160 static bt_status_t wakelock_release_callout(void) {
161   return wakelock_os_callouts->release_wake_lock(WAKE_LOCK_ID);
162 }
163 
wakelock_release_native(void)164 static bt_status_t wakelock_release_native(void) {
165   if (wake_unlock_fd == INVALID_FD) {
166     LOG_ERROR(LOG_TAG, "%s lock not released, invalid fd", __func__);
167     return BT_STATUS_PARM_INVALID;
168   }
169 
170   ssize_t wrote_name_len = write(wake_unlock_fd, WAKE_LOCK_ID, locked_id_len);
171   if (wrote_name_len == -1) {
172     LOG_ERROR(LOG_TAG, "%s can't release wake lock: %s",
173               __func__, strerror(errno));
174   } else if (wrote_name_len < locked_id_len) {
175     LOG_ERROR(LOG_TAG, "%s lock release only wrote %zd, assuming released",
176               __func__, wrote_name_len);
177   }
178   return BT_STATUS_SUCCESS;
179 }
180 
wakelock_initialize(void)181 static void wakelock_initialize(void) {
182   pthread_mutex_init(&monitor, NULL);
183   reset_wakelock_stats();
184 
185   if (is_native)
186     wakelock_initialize_native();
187 }
188 
wakelock_initialize_native(void)189 static void wakelock_initialize_native(void) {
190   LOG_DEBUG(LOG_TAG, "%s opening wake locks", __func__);
191 
192   if (!wake_lock_path)
193     wake_lock_path = DEFAULT_WAKE_LOCK_PATH;
194 
195   wake_lock_fd = open(wake_lock_path, O_RDWR | O_CLOEXEC);
196   if (wake_lock_fd == INVALID_FD) {
197     LOG_ERROR(LOG_TAG, "%s can't open wake lock %s: %s",
198               __func__, wake_lock_path, strerror(errno));
199   }
200 
201   if (!wake_unlock_path)
202     wake_unlock_path = DEFAULT_WAKE_UNLOCK_PATH;
203 
204   wake_unlock_fd = open(wake_unlock_path, O_RDWR | O_CLOEXEC);
205   if (wake_unlock_fd == INVALID_FD) {
206     LOG_ERROR(LOG_TAG, "%s can't open wake unlock %s: %s",
207               __func__, wake_unlock_path, strerror(errno));
208   }
209 }
210 
wakelock_cleanup(void)211 void wakelock_cleanup(void) {
212   if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH)
213     osi_free_and_reset((void **)&wake_lock_path);
214 
215   if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH)
216     osi_free_and_reset((void **)&wake_unlock_path);
217 
218   initialized = PTHREAD_ONCE_INIT;
219   pthread_mutex_destroy(&monitor);
220 }
221 
wakelock_set_paths(const char * lock_path,const char * unlock_path)222 void wakelock_set_paths(const char *lock_path, const char *unlock_path) {
223   if (lock_path) {
224     if (wake_lock_path && wake_lock_path != DEFAULT_WAKE_LOCK_PATH)
225       osi_free(wake_lock_path);
226     wake_lock_path = osi_strndup(lock_path, PATH_MAX);
227   }
228 
229   if (unlock_path) {
230     if (wake_unlock_path && wake_unlock_path != DEFAULT_WAKE_UNLOCK_PATH)
231       osi_free(wake_unlock_path);
232     wake_unlock_path = osi_strndup(unlock_path, PATH_MAX);
233   }
234 }
235 
now(void)236 static period_ms_t now(void) {
237   struct timespec ts;
238   if (clock_gettime(CLOCK_ID, &ts) == -1) {
239     LOG_ERROR(LOG_TAG, "%s unable to get current time: %s",
240               __func__, strerror(errno));
241     return 0;
242   }
243 
244   return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
245 }
246 
247 // Reset the Bluetooth wakelock statistics.
248 // This function is thread-safe.
reset_wakelock_stats(void)249 static void reset_wakelock_stats(void) {
250   pthread_mutex_lock(&monitor);
251 
252   wakelock_stats.is_acquired = false;
253   wakelock_stats.acquired_count = 0;
254   wakelock_stats.released_count = 0;
255   wakelock_stats.acquired_errors = 0;
256   wakelock_stats.released_errors = 0;
257   wakelock_stats.min_acquired_interval_ms = 0;
258   wakelock_stats.max_acquired_interval_ms = 0;
259   wakelock_stats.last_acquired_interval_ms = 0;
260   wakelock_stats.total_acquired_interval_ms = 0;
261   wakelock_stats.last_acquired_timestamp_ms = 0;
262   wakelock_stats.last_released_timestamp_ms = 0;
263   wakelock_stats.last_reset_timestamp_ms = now();
264 
265   pthread_mutex_unlock(&monitor);
266 }
267 
268 //
269 // Update the Bluetooth acquire wakelock statistics.
270 //
271 // This function should be called every time when the wakelock is acquired.
272 // |acquired_status| is the status code that was return when the wakelock was
273 // acquired.
274 // This function is thread-safe.
275 //
update_wakelock_acquired_stats(bt_status_t acquired_status)276 static void update_wakelock_acquired_stats(bt_status_t acquired_status) {
277   const period_ms_t now_ms = now();
278 
279   pthread_mutex_lock(&monitor);
280 
281   if (acquired_status != BT_STATUS_SUCCESS) {
282     wakelock_stats.acquired_errors++;
283     wakelock_stats.last_acquired_error = acquired_status;
284   }
285 
286   if (wakelock_stats.is_acquired) {
287     pthread_mutex_unlock(&monitor);
288     return;
289   }
290 
291   wakelock_stats.is_acquired = true;
292   wakelock_stats.acquired_count++;
293   wakelock_stats.last_acquired_timestamp_ms = now_ms;
294 
295   pthread_mutex_unlock(&monitor);
296 
297   metrics_log_wake_event(WAKE_EVENT_ACQUIRED, "", "", now_ms);
298 }
299 
300 //
301 // Update the Bluetooth release wakelock statistics.
302 //
303 // This function should be called every time when the wakelock is released.
304 // |released_status| is the status code that was return when the wakelock was
305 // released.
306 // This function is thread-safe.
307 //
update_wakelock_released_stats(bt_status_t released_status)308 static void update_wakelock_released_stats(bt_status_t released_status) {
309   const period_ms_t now_ms = now();
310 
311   pthread_mutex_lock(&monitor);
312 
313   if (released_status != BT_STATUS_SUCCESS) {
314     wakelock_stats.released_errors++;
315     wakelock_stats.last_released_error = released_status;
316   }
317 
318   if (!wakelock_stats.is_acquired) {
319     pthread_mutex_unlock(&monitor);
320     return;
321   }
322 
323   wakelock_stats.is_acquired = false;
324   wakelock_stats.released_count++;
325   wakelock_stats.last_released_timestamp_ms = now_ms;
326 
327   // Compute the acquired interval and update the statistics
328   period_ms_t delta_ms = now_ms - wakelock_stats.last_acquired_timestamp_ms;
329   if (delta_ms < wakelock_stats.min_acquired_interval_ms ||
330       wakelock_stats.released_count == 1) {
331     wakelock_stats.min_acquired_interval_ms = delta_ms;
332   }
333   if (delta_ms > wakelock_stats.max_acquired_interval_ms) {
334     wakelock_stats.max_acquired_interval_ms = delta_ms;
335   }
336   wakelock_stats.last_acquired_interval_ms = delta_ms;
337   wakelock_stats.total_acquired_interval_ms += delta_ms;
338 
339   pthread_mutex_unlock(&monitor);
340 
341   metrics_log_wake_event(WAKE_EVENT_RELEASED, "", "", now_ms);
342 }
343 
wakelock_debug_dump(int fd)344 void wakelock_debug_dump(int fd) {
345   const period_ms_t now_ms = now();
346 
347   // Need to keep track for lock errors - e.g., the "monitor" mutex
348   // might not be initialized
349   const int lock_error = pthread_mutex_lock(&monitor);
350 
351   // Compute the last acquired interval if the wakelock is still acquired
352   period_ms_t delta_ms = 0;
353   period_ms_t last_interval = wakelock_stats.last_acquired_interval_ms;
354   period_ms_t min_interval = wakelock_stats.min_acquired_interval_ms;
355   period_ms_t max_interval = wakelock_stats.max_acquired_interval_ms;
356   period_ms_t ave_interval = 0;
357 
358   if (wakelock_stats.is_acquired) {
359     delta_ms = now_ms - wakelock_stats.last_acquired_timestamp_ms;
360     if (delta_ms > max_interval)
361       max_interval = delta_ms;
362     if (delta_ms < min_interval)
363       min_interval = delta_ms;
364     last_interval = delta_ms;
365   }
366   period_ms_t total_interval =
367     wakelock_stats.total_acquired_interval_ms + delta_ms;
368 
369   if (wakelock_stats.acquired_count > 0)
370     ave_interval = total_interval / wakelock_stats.acquired_count;
371 
372   dprintf(fd, "\nBluetooth Wakelock Statistics:\n");
373   dprintf(fd, "  Is acquired                    : %s\n",
374           wakelock_stats.is_acquired? "true" : "false");
375   dprintf(fd, "  Acquired/released count        : %zu / %zu\n",
376           wakelock_stats.acquired_count, wakelock_stats.released_count);
377   dprintf(fd, "  Acquired/released error count  : %zu / %zu\n",
378           wakelock_stats.acquired_errors, wakelock_stats.released_errors);
379   dprintf(fd, "  Last acquire/release error code: %d / %d\n",
380           wakelock_stats.last_acquired_error, wakelock_stats.last_released_error);
381   dprintf(fd, "  Last acquired time (ms)        : %llu\n",
382           (unsigned long long)last_interval);
383   dprintf(fd, "  Acquired time min/max/avg (ms) : %llu / %llu / %llu\n",
384           (unsigned long long)min_interval, (unsigned long long)max_interval,
385           (unsigned long long)ave_interval);
386   dprintf(fd, "  Total acquired time (ms)       : %llu\n",
387           (unsigned long long)total_interval);
388   dprintf(fd, "  Total run time (ms)            : %llu\n",
389           (unsigned long long)(now_ms - wakelock_stats.last_reset_timestamp_ms));
390 
391   if (lock_error == 0)
392     pthread_mutex_unlock(&monitor);
393 }
394