summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYao Chen2018-05-07 18:57:13 -0500
committerYao Chen2018-05-21 12:06:25 -0500
commitb3be9eaff57dfd344784fb7b803d1cc56d771294 (patch)
treec76f06e25136200c464dd2a86540a5556c647ffa /libstats
parentda6ab73420760da9fe39ba3f26068c6d1ad46537 (diff)
downloadplatform-system-core-b3be9eaff57dfd344784fb7b803d1cc56d771294.tar.gz
platform-system-core-b3be9eaff57dfd344784fb7b803d1cc56d771294.tar.xz
platform-system-core-b3be9eaff57dfd344784fb7b803d1cc56d771294.zip
Move libstatssocket from frameworks/base to system/core/
So that lmkd can build on PDK. Bug: 79349329 Test: builds locally Merged-In: I981e6ef9f9769b873640e5f169a9495ccea2f25c Change-Id: I981e6ef9f9769b873640e5f169a9495ccea2f25c (cherry picked from commit b13a102c0afec2c0a1fbdafb9a00e3d55450b28b)
Diffstat (limited to 'libstats')
-rw-r--r--libstats/Android.bp37
-rw-r--r--libstats/include/stats_event_list.h250
-rw-r--r--libstats/stats_event_list.c181
-rw-r--r--libstats/statsd_writer.c260
-rw-r--r--libstats/statsd_writer.h43
5 files changed, 771 insertions, 0 deletions
diff --git a/libstats/Android.bp b/libstats/Android.bp
new file mode 100644
index 000000000..d58f29417
--- /dev/null
+++ b/libstats/Android.bp
@@ -0,0 +1,37 @@
1//
2// Copyright (C) 2018 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17// ==========================================================
18// Native library to write stats log to statsd socket
19// ==========================================================
20cc_library_static {
21 name: "libstatssocket",
22 srcs: [
23 "stats_event_list.c",
24 "statsd_writer.c",
25 ],
26 cflags: [
27 "-Wall",
28 "-Werror",
29 "-DLIBLOG_LOG_TAG=1006",
30 "-DWRITE_TO_STATSD=1",
31 "-DWRITE_TO_LOGD=0",
32 ],
33 export_include_dirs: ["include"],
34 shared_libs: [
35 "liblog",
36 ],
37}
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
new file mode 100644
index 000000000..5d174ae03
--- /dev/null
+++ b/libstats/include/stats_event_list.h
@@ -0,0 +1,250 @@
1/*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_STATS_LOG_STATS_EVENT_LIST_H
18#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
19
20#include <log/log_event_list.h>
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25void reset_log_context(android_log_context ctx);
26int write_to_logger(android_log_context context, log_id_t id);
27
28#ifdef __cplusplus
29}
30#endif
31
32#ifdef __cplusplus
33/**
34 * A copy of android_log_event_list class.
35 *
36 * android_log_event_list is going to be deprecated soon, so copy it here to
37 * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this
38 * code.
39 */
40class stats_event_list {
41 private:
42 android_log_context ctx;
43 int ret;
44
45 stats_event_list(const stats_event_list&) = delete;
46 void operator=(const stats_event_list&) = delete;
47
48 public:
49 explicit stats_event_list(int tag) : ret(0) {
50 ctx = create_android_logger(static_cast<uint32_t>(tag));
51 }
52 explicit stats_event_list(log_msg& log_msg) : ret(0) {
53 ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
54 log_msg.entry.len - sizeof(uint32_t));
55 }
56 ~stats_event_list() { android_log_destroy(&ctx); }
57
58 int close() {
59 int retval = android_log_destroy(&ctx);
60 if (retval < 0) {
61 ret = retval;
62 }
63 return retval;
64 }
65
66 /* To allow above C calls to use this class as parameter */
67 operator android_log_context() const { return ctx; }
68
69 /* return errors or transmit status */
70 int status() const { return ret; }
71
72 int begin() {
73 int retval = android_log_write_list_begin(ctx);
74 if (retval < 0) {
75 ret = retval;
76 }
77 return ret;
78 }
79 int end() {
80 int retval = android_log_write_list_end(ctx);
81 if (retval < 0) {
82 ret = retval;
83 }
84 return ret;
85 }
86
87 stats_event_list& operator<<(int32_t value) {
88 int retval = android_log_write_int32(ctx, value);
89 if (retval < 0) {
90 ret = retval;
91 }
92 return *this;
93 }
94
95 stats_event_list& operator<<(uint32_t value) {
96 int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));
97 if (retval < 0) {
98 ret = retval;
99 }
100 return *this;
101 }
102
103 stats_event_list& operator<<(bool value) {
104 int retval = android_log_write_int32(ctx, value ? 1 : 0);
105 if (retval < 0) {
106 ret = retval;
107 }
108 return *this;
109 }
110
111 stats_event_list& operator<<(int64_t value) {
112 int retval = android_log_write_int64(ctx, value);
113 if (retval < 0) {
114 ret = retval;
115 }
116 return *this;
117 }
118
119 stats_event_list& operator<<(uint64_t value) {
120 int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));
121 if (retval < 0) {
122 ret = retval;
123 }
124 return *this;
125 }
126
127 stats_event_list& operator<<(const char* value) {
128 int retval = android_log_write_string8(ctx, value);
129 if (retval < 0) {
130 ret = retval;
131 }
132 return *this;
133 }
134
135#if defined(_USING_LIBCXX)
136 stats_event_list& operator<<(const std::string& value) {
137 int retval = android_log_write_string8_len(ctx, value.data(), value.length());
138 if (retval < 0) {
139 ret = retval;
140 }
141 return *this;
142 }
143#endif
144
145 stats_event_list& operator<<(float value) {
146 int retval = android_log_write_float32(ctx, value);
147 if (retval < 0) {
148 ret = retval;
149 }
150 return *this;
151 }
152
153 int write(log_id_t id = LOG_ID_EVENTS) {
154 /* facilitate -EBUSY retry */
155 if ((ret == -EBUSY) || (ret > 0)) {
156 ret = 0;
157 }
158 int retval = write_to_logger(ctx, id);
159 /* existing errors trump transmission errors */
160 if (!ret) {
161 ret = retval;
162 }
163 return ret;
164 }
165
166 /*
167 * Append<Type> methods removes any integer promotion
168 * confusion, and adds access to string with length.
169 * Append methods are also added for all types for
170 * convenience.
171 */
172
173 bool AppendInt(int32_t value) {
174 int retval = android_log_write_int32(ctx, value);
175 if (retval < 0) {
176 ret = retval;
177 }
178 return ret >= 0;
179 }
180
181 bool AppendLong(int64_t value) {
182 int retval = android_log_write_int64(ctx, value);
183 if (retval < 0) {
184 ret = retval;
185 }
186 return ret >= 0;
187 }
188
189 bool AppendString(const char* value) {
190 int retval = android_log_write_string8(ctx, value);
191 if (retval < 0) {
192 ret = retval;
193 }
194 return ret >= 0;
195 }
196
197 bool AppendString(const char* value, size_t len) {
198 int retval = android_log_write_string8_len(ctx, value, len);
199 if (retval < 0) {
200 ret = retval;
201 }
202 return ret >= 0;
203 }
204
205#if defined(_USING_LIBCXX)
206 bool AppendString(const std::string& value) {
207 int retval = android_log_write_string8_len(ctx, value.data(), value.length());
208 if (retval < 0) {
209 ret = retval;
210 }
211 return ret;
212 }
213
214 bool Append(const std::string& value) {
215 int retval = android_log_write_string8_len(ctx, value.data(), value.length());
216 if (retval < 0) {
217 ret = retval;
218 }
219 return ret;
220 }
221#endif
222
223 bool AppendFloat(float value) {
224 int retval = android_log_write_float32(ctx, value);
225 if (retval < 0) {
226 ret = retval;
227 }
228 return ret >= 0;
229 }
230
231 template <typename Tvalue>
232 bool Append(Tvalue value) {
233 *this << value;
234 return ret >= 0;
235 }
236
237 bool Append(const char* value, size_t len) {
238 int retval = android_log_write_string8_len(ctx, value, len);
239 if (retval < 0) {
240 ret = retval;
241 }
242 return ret >= 0;
243 }
244
245 android_log_list_element read() { return android_log_read_next(ctx); }
246 android_log_list_element peek() { return android_log_peek_next(ctx); }
247};
248
249#endif
250#endif // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
new file mode 100644
index 000000000..966bb08a2
--- /dev/null
+++ b/libstats/stats_event_list.c
@@ -0,0 +1,181 @@
1/*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "include/stats_event_list.h"
18
19#include <string.h>
20#include "statsd_writer.h"
21
22#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
23
24typedef struct {
25 uint32_t tag;
26 unsigned pos; /* Read/write position into buffer */
27 unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */
28 unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */
29 unsigned list_nest_depth;
30 unsigned len; /* Length or raw buffer. */
31 bool overflow;
32 bool list_stop; /* next call decrement list_nest_depth and issue a stop */
33 enum {
34 kAndroidLoggerRead = 1,
35 kAndroidLoggerWrite = 2,
36 } read_write_flag;
37 uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
38} android_log_context_internal;
39
40extern struct android_log_transport_write statsdLoggerWrite;
41
42static int __write_to_statsd_init(struct iovec* vec, size_t nr);
43static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
44
45// Similar to create_android_logger(), but instead of allocation a new buffer,
46// this function resets the buffer for resuse.
47void reset_log_context(android_log_context ctx) {
48 if (!ctx) {
49 return;
50 }
51 android_log_context_internal* context = (android_log_context_internal*)(ctx);
52 uint32_t tag = context->tag;
53 memset(context, 0, sizeof(android_log_context_internal));
54
55 context->tag = tag;
56 context->read_write_flag = kAndroidLoggerWrite;
57 size_t needed = sizeof(uint8_t) + sizeof(uint8_t);
58 if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
59 context->overflow = true;
60 }
61 /* Everything is a list */
62 context->storage[context->pos + 0] = EVENT_TYPE_LIST;
63 context->list[0] = context->pos + 1;
64 context->pos += needed;
65}
66
67int stats_write_list(android_log_context ctx) {
68 android_log_context_internal* context;
69 const char* msg;
70 ssize_t len;
71
72 context = (android_log_context_internal*)(ctx);
73 if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
74 return -EBADF;
75 }
76
77 if (context->list_nest_depth) {
78 return -EIO;
79 }
80
81 /* NB: if there was overflow, then log is truncated. Nothing reported */
82 context->storage[1] = context->count[0];
83 len = context->len = context->pos;
84 msg = (const char*)context->storage;
85 /* it's not a list */
86 if (context->count[0] <= 1) {
87 len -= sizeof(uint8_t) + sizeof(uint8_t);
88 if (len < 0) {
89 len = 0;
90 }
91 msg += sizeof(uint8_t) + sizeof(uint8_t);
92 }
93
94 struct iovec vec[2];
95 vec[0].iov_base = &context->tag;
96 vec[0].iov_len = sizeof(context->tag);
97 vec[1].iov_base = (void*)msg;
98 vec[1].iov_len = len;
99 return write_to_statsd(vec, 2);
100}
101
102int write_to_logger(android_log_context ctx, log_id_t id) {
103 int retValue = 0;
104
105 if (WRITE_TO_LOGD) {
106 retValue = android_log_write_list(ctx, id);
107 }
108
109 if (WRITE_TO_STATSD) {
110 // log_event_list's cast operator is overloaded.
111 int ret = stats_write_list(ctx);
112 // In debugging phase, we may write to both logd and statsd. Prefer to
113 // return statsd socket write error code here.
114 if (ret < 0) {
115 retValue = ret;
116 }
117 }
118
119 return retValue;
120}
121
122/* log_init_lock assumed */
123static int __write_to_statsd_initialize_locked() {
124 if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
125 if (statsdLoggerWrite.close) {
126 (*statsdLoggerWrite.close)();
127 return -ENODEV;
128 }
129 }
130 return 1;
131}
132
133static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
134 int ret, save_errno;
135 struct timespec ts;
136 size_t len, i;
137
138 for (len = i = 0; i < nr; ++i) {
139 len += vec[i].iov_len;
140 }
141 if (!len) {
142 return -EINVAL;
143 }
144
145 save_errno = errno;
146 clock_gettime(CLOCK_REALTIME, &ts);
147
148 ret = 0;
149
150 ssize_t retval;
151 retval = (*statsdLoggerWrite.write)(&ts, vec, nr);
152 if (ret >= 0) {
153 ret = retval;
154 }
155
156 errno = save_errno;
157 return ret;
158}
159
160static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
161 int ret, save_errno = errno;
162
163 statsd_writer_init_lock();
164
165 if (write_to_statsd == __write_to_statsd_init) {
166 ret = __write_to_statsd_initialize_locked();
167 if (ret < 0) {
168 statsd_writer_init_unlock();
169 errno = save_errno;
170 return ret;
171 }
172
173 write_to_statsd = __write_to_stats_daemon;
174 }
175
176 statsd_writer_init_unlock();
177
178 ret = write_to_statsd(vec, nr);
179 errno = save_errno;
180 return ret;
181} \ No newline at end of file
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
new file mode 100644
index 000000000..9953bba2e
--- /dev/null
+++ b/libstats/statsd_writer.c
@@ -0,0 +1,260 @@
1/*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "statsd_writer.h"
17
18#include <cutils/sockets.h>
19#include <endian.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <inttypes.h>
23#include <poll.h>
24#include <private/android_filesystem_config.h>
25#include <private/android_logger.h>
26#include <stdarg.h>
27#include <stdatomic.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/un.h>
34#include <time.h>
35#include <unistd.h>
36
37/* branchless on many architectures. */
38#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
39
40static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
41
42void statsd_writer_init_lock() {
43 /*
44 * If we trigger a signal handler in the middle of locked activity and the
45 * signal handler logs a message, we could get into a deadlock state.
46 */
47 pthread_mutex_lock(&log_init_lock);
48}
49
50int statd_writer_trylock() {
51 return pthread_mutex_trylock(&log_init_lock);
52}
53
54void statsd_writer_init_unlock() {
55 pthread_mutex_unlock(&log_init_lock);
56}
57
58static int statsdAvailable();
59static int statsdOpen();
60static void statsdClose();
61static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
62
63struct android_log_transport_write statsdLoggerWrite = {
64 .name = "statsd",
65 .sock = -EBADF,
66 .available = statsdAvailable,
67 .open = statsdOpen,
68 .close = statsdClose,
69 .write = statsdWrite,
70};
71
72/* log_init_lock assumed */
73static int statsdOpen() {
74 int i, ret = 0;
75
76 i = atomic_load(&statsdLoggerWrite.sock);
77 if (i < 0) {
78 int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
79 if (sock < 0) {
80 ret = -errno;
81 } else {
82 struct sockaddr_un un;
83 memset(&un, 0, sizeof(struct sockaddr_un));
84 un.sun_family = AF_UNIX;
85 strcpy(un.sun_path, "/dev/socket/statsdw");
86
87 if (TEMP_FAILURE_RETRY(
88 connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
89 ret = -errno;
90 switch (ret) {
91 case -ENOTCONN:
92 case -ECONNREFUSED:
93 case -ENOENT:
94 i = atomic_exchange(&statsdLoggerWrite.sock, ret);
95 /* FALLTHRU */
96 default:
97 break;
98 }
99 close(sock);
100 } else {
101 ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
102 if ((ret >= 0) && (ret != sock)) {
103 close(ret);
104 }
105 ret = 0;
106 }
107 }
108 }
109
110 return ret;
111}
112
113static void __statsdClose(int negative_errno) {
114 int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
115 if (sock >= 0) {
116 close(sock);
117 }
118}
119
120static void statsdClose() {
121 __statsdClose(-EBADF);
122}
123
124static int statsdAvailable() {
125 if (atomic_load(&statsdLoggerWrite.sock) < 0) {
126 if (access("/dev/socket/statsdw", W_OK) == 0) {
127 return 0;
128 }
129 return -EBADF;
130 }
131 return 1;
132}
133
134static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
135 ssize_t ret;
136 int sock;
137 static const unsigned headerLength = 1;
138 struct iovec newVec[nr + headerLength];
139 android_log_header_t header;
140 size_t i, payloadSize;
141 static atomic_int dropped;
142
143 sock = atomic_load(&statsdLoggerWrite.sock);
144 if (sock < 0) switch (sock) {
145 case -ENOTCONN:
146 case -ECONNREFUSED:
147 case -ENOENT:
148 break;
149 default:
150 return -EBADF;
151 }
152 /*
153 * struct {
154 * // what we provide to socket
155 * android_log_header_t header;
156 * // caller provides
157 * union {
158 * struct {
159 * char prio;
160 * char payload[];
161 * } string;
162 * struct {
163 * uint32_t tag
164 * char payload[];
165 * } binary;
166 * };
167 * };
168 */
169
170 header.tid = gettid();
171 header.realtime.tv_sec = ts->tv_sec;
172 header.realtime.tv_nsec = ts->tv_nsec;
173
174 newVec[0].iov_base = (unsigned char*)&header;
175 newVec[0].iov_len = sizeof(header);
176
177 // If we dropped events before, try to tell statsd.
178 if (sock >= 0) {
179 int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
180 if (snapshot) {
181 android_log_event_int_t buffer;
182 header.id = LOG_ID_STATS;
183 buffer.header.tag = htole32(LIBLOG_LOG_TAG);
184 buffer.payload.type = EVENT_TYPE_INT;
185 buffer.payload.data = htole32(snapshot);
186
187 newVec[headerLength].iov_base = &buffer;
188 newVec[headerLength].iov_len = sizeof(buffer);
189
190 ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
191 if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
192 atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
193 }
194 }
195 }
196
197 header.id = LOG_ID_STATS;
198
199 for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
200 newVec[i].iov_base = vec[i - headerLength].iov_base;
201 payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
202
203 if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
204 newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
205 if (newVec[i].iov_len) {
206 ++i;
207 }
208 break;
209 }
210 }
211
212 /*
213 * The write below could be lost, but will never block.
214 *
215 * ENOTCONN occurs if statsd has died.
216 * ENOENT occurs if statsd is not running and socket is missing.
217 * ECONNREFUSED occurs if we can not reconnect to statsd.
218 * EAGAIN occurs if statsd is overloaded.
219 */
220 if (sock < 0) {
221 ret = sock;
222 } else {
223 ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
224 if (ret < 0) {
225 ret = -errno;
226 }
227 }
228 switch (ret) {
229 case -ENOTCONN:
230 case -ECONNREFUSED:
231 case -ENOENT:
232 if (statd_writer_trylock()) {
233 return ret; /* in a signal handler? try again when less stressed
234 */
235 }
236 __statsdClose(ret);
237 ret = statsdOpen();
238 statsd_writer_init_unlock();
239
240 if (ret < 0) {
241 return ret;
242 }
243
244 ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
245 if (ret < 0) {
246 ret = -errno;
247 }
248 /* FALLTHRU */
249 default:
250 break;
251 }
252
253 if (ret > (ssize_t)sizeof(header)) {
254 ret -= sizeof(header);
255 } else if (ret == -EAGAIN) {
256 atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
257 }
258
259 return ret;
260}
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
new file mode 100644
index 000000000..82e14e04f
--- /dev/null
+++ b/libstats/statsd_writer.h
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2018, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
18#define ANDROID_STATS_LOG_STATS_WRITER_H
19
20#include <pthread.h>
21#include <stdatomic.h>
22#include <sys/socket.h>
23
24/**
25 * Internal lock should not be exposed. This is bad design.
26 * TODO: rewrite it in c++ code and encapsulate the functionality in a
27 * StatsdWriter class.
28 */
29void statsd_writer_init_lock();
30int statsd_writer_init_trylock();
31void statsd_writer_init_unlock();
32
33struct android_log_transport_write {
34 const char* name; /* human name to describe the transport */
35 atomic_int sock;
36 int (*available)(); /* Does not cause resources to be taken */
37 int (*open)(); /* can be called multiple times, reusing current resources */
38 void (*close)(); /* free up resources */
39 /* write log to transport, returns number of bytes propagated, or -errno */
40 int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
41};
42
43#endif // ANDROID_STATS_LOG_STATS_WRITER_H