1 /*
2 * Copyright (C) 2013 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 #define TLOG_TAG "stats-consumer"
17
18 #include <cstddef>
19
20 #include <inttypes.h>
21 #include <lib/tipc/tipc.h>
22 #include <lk/err_ptr.h>
23 #include <sys/auxv.h>
24 #include <sys/mman.h>
25 #include <trusty/sys/mman.h>
26 #include <trusty/time.h>
27 #include <trusty_log.h>
28 #include <uapi/mm.h>
29
30 #include <binder/RpcServerTrusty.h>
31 #include <binder/RpcTransportTipcTrusty.h>
32 #include <stdio.h>
33
34 #include <android/trusty/stats/tz/BnStats.h>
35 #include <android/trusty/stats/tz/IStatsSetter.h>
36 #include <consumer_consts.h>
37 #include <consumer_ctl.h>
38 #include <relayer_consts.h>
39
40 using android::String16;
41 using android::binder::Status;
42 using android::frameworks::stats::VendorAtom;
43 using android::frameworks::stats::VendorAtomValue;
44 using android::trusty::stats::tz::BnStats;
45 using android::trusty::stats::tz::IStatsSetter;
46
47 /* How long to sleep between spin lock checks */
48 constexpr uint64_t kSpinSleepMs = 10;
49
50 /**
51 * DOC: The consumer TA is exposing a test control port to share
52 * received vendorAtoms with the stats-test TA via shared memory.
53 * As soon as the test control port is connected, the consumer will connect
54 * to the producer and write the received atoms into the memory address
55 * shared with the stats-test TA.
56 */
57
58 class StatsConsumer;
59
60 static tipc_port_acl test_ctl_port_acl = {
61 .flags = IPC_PORT_ALLOW_TA_CONNECT,
62 .uuid_num = 0,
63 .uuids = nullptr,
64 .extra_data = nullptr,
65 };
66
67 static tipc_port test_ctl_port = {
68 .name = CONSUMER_PORT_TEST_CTL,
69 .msg_max_size = sizeof(ConsumerCtlMsg),
70 .msg_queue_len = 1,
71 .acl = &test_ctl_port_acl,
72 .priv = nullptr,
73 };
74
75 struct ConsumerCtx {
76 void* shm_ptr;
77 int atom_cnt;
78 android::sp<StatsConsumer> stats_consumer;
79 android::sp<IStatsSetter> istats_setter;
80 };
81
82 static ConsumerCtx consumer_ctx = {
83 .shm_ptr = nullptr,
84 .atom_cnt = -1,
85 };
86
87 /**
88 * test_ctl_on_connect() - on test_ctl connect the connection with
89 * the producer is made and vendorAtoms can be received
90 */
test_ctl_on_connect(const tipc_port * port,handle_t chan,const uuid * peer,void ** ctx_p)91 int test_ctl_on_connect(const tipc_port* port,
92 handle_t chan,
93 const uuid* peer,
94 void** ctx_p) {
95 if (consumer_ctx.atom_cnt != -1) {
96 *ctx_p = nullptr;
97 return ERR_BAD_STATE;
98 }
99
100 // make sure consumer is ready
101 assert(consumer_ctx.stats_consumer);
102 assert(!consumer_ctx.istats_setter);
103 assert(!consumer_ctx.shm_ptr);
104 assert(consumer_ctx.atom_cnt == -1);
105 consumer_ctx.atom_cnt = 0;
106
107 // connect to the producer
108 android::sp<android::RpcSession> sess_stats_setter;
109 android::sp<IStatsSetter> srv_stats_setter;
110
111 TLOGD("Connecting to the producer via IStatsSetter\n");
112 int rc = connect(RELAYER_PORT_ISTATS_SETTER_SECURE_WORLD,
113 IPC_CONNECT_WAIT_FOR_PORT);
114 TLOGD("connected to IStatsSetter\n");
115 if (rc < 0) {
116 TLOGE("Couldn't connect to IStatsSetter::PORT\n");
117 return rc;
118 }
119 android::sp<android::RpcSession> sess = android::RpcSession::make(
120 android::RpcTransportCtxFactoryTipcTrusty::make());
121 if (sess == nullptr) {
122 TLOGE("Couldn't make RPC session\n");
123 return ERR_GENERIC;
124 }
125 android::binder::unique_fd chan_fd;
126 chan_fd.reset(rc);
127 android::status_t status = sess->setupPreconnectedClient(
128 std::move(chan_fd), []() { return android::binder::unique_fd(); });
129 if (status != android::OK) {
130 TLOGE("error during setupPreconnectedClient\n");
131 return ERR_GENERIC;
132 }
133 android::sp<android::IBinder> root = sess->getRootObject();
134 if (root == nullptr) {
135 TLOGE("Couldn't get root object\n");
136 return ERR_GENERIC;
137 }
138 consumer_ctx.istats_setter = IStatsSetter::asInterface(root);
139 assert(consumer_ctx.istats_setter);
140 *ctx_p = &consumer_ctx;
141
142 return NO_ERROR;
143 }
144
145 /**
146 * test_ctl_on_message() - test_ctl on_message allows the consumer
147 * to receive the shared memory region to which received vendorAtoms will
148 * be copied.
149 */
test_ctl_on_message(const tipc_port * port,handle_t chan,void * ctx_)150 static int test_ctl_on_message(const tipc_port* port,
151 handle_t chan,
152 void* ctx_) {
153 assert(ctx_);
154 assert(port == &test_ctl_port);
155 ConsumerCtx* ctx = static_cast<ConsumerCtx*>(ctx_);
156 ConsumerCtlMsg request;
157
158 handle_t shm_handle = INVALID_IPC_HANDLE;
159 iovec iov = {
160 .iov_base = &request,
161 .iov_len = sizeof(request),
162 };
163 ipc_msg msg = {
164 .num_iov = 1,
165 .iov = &iov,
166 .num_handles = 1,
167 .handles = &shm_handle,
168 };
169
170 ipc_msg_info msg_inf;
171 int rc = get_msg(chan, &msg_inf);
172 if (rc) {
173 return rc;
174 }
175
176 if (msg_inf.num_handles != 1) {
177 TLOGE("Message had no handles\n");
178 put_msg(chan, msg_inf.id);
179 return ERR_INVALID_ARGS;
180 }
181
182 rc = read_msg(chan, msg_inf.id, 0, &msg);
183 put_msg(chan, msg_inf.id);
184 if (static_cast<size_t>(rc) != sizeof(request)) {
185 TLOGE("Failed to read message (rc=%d)\n", rc);
186 close(shm_handle);
187 return ERR_INVALID_ARGS;
188 }
189
190 size_t page_size = PAGE_SIZE; // getauxval(AT_PAGESZ);
191 switch (request.cmd) {
192 case CONSUMER_CTL_SHM_SHARE:
193 assert(!ctx->shm_ptr);
194 ctx->shm_ptr =
195 mmap(0, page_size, PROT_READ | PROT_WRITE, 0, shm_handle, 0);
196 if (ctx->shm_ptr == MAP_FAILED) {
197 close(shm_handle);
198 ctx->shm_ptr = nullptr;
199 TLOGE("Failed to mmap handle (rc=%d)\n", rc);
200 return ERR_NO_MEMORY;
201 }
202 break;
203 case CONSUMER_CTL_SHM_RECLAIM:
204 if (ctx->shm_ptr) {
205 munmap((void*)ctx->shm_ptr, page_size);
206 ctx->shm_ptr = nullptr;
207 }
208 break;
209 default:
210 TLOGE("Invalid cmd %d\n", request.cmd);
211 }
212
213 close(shm_handle);
214
215 return NO_ERROR;
216 }
217
test_ctl_on_channel_cleanup(void * ctx_)218 static void test_ctl_on_channel_cleanup(void* ctx_) {
219 return;
220 }
221
222 class StatsConsumer : public BnStats {
223 public:
reportVendorAtom(const VendorAtom & vendorAtom)224 Status reportVendorAtom(const VendorAtom& vendorAtom) {
225 TLOGD("reportVendorAtom atomId=%d.\n", vendorAtom.atomId);
226 if (consumer_ctx.shm_ptr) {
227 volatile ShmContent* content = (ShmContent*)consumer_ctx.shm_ptr;
228
229 // Spin and sleep until the other side consumes the active value
230 while (content->full.load(std::memory_order_acquire)) {
231 trusty_nanosleep(0, 0, MS_TO_NS(kSpinSleepMs));
232 }
233
234 TLOGD("content->atom_id=%d.\n", content->atom_id);
235 TLOGD("content->reverse_domain_name=%s.\n",
236 content->reverse_domain_name);
237 content->atom_id = vendorAtom.atomId;
238
239 size_t last_atom_str_idx = SHM_CONTENT_VENDOR_ATOM_STR_SIZE - 1;
240 strncpy((char*)content->reverse_domain_name,
241 android::String8(vendorAtom.reverseDomainName).c_str(),
242 last_atom_str_idx);
243 content->reverse_domain_name[last_atom_str_idx] = '\0';
244
245 int idx = 0;
246 for (const auto& input_value : vendorAtom.values) {
247 VendorAtomValue::Tag tag = input_value.getTag();
248 TLOGD("vendorAtom.values[%d].getTag = %d.\n", idx, tag);
249
250 volatile ShmVendorAtomValue& atom_val =
251 content->vendor_atom_values[idx];
252 switch (tag) {
253 case VendorAtomValue::intValue:
254 atom_val.i = input_value.get<VendorAtomValue::intValue>();
255 break;
256 case VendorAtomValue::longValue:
257 atom_val.l = input_value.get<VendorAtomValue::longValue>();
258 break;
259 case VendorAtomValue::floatValue:
260 atom_val.f = input_value.get<VendorAtomValue::floatValue>();
261 break;
262 case VendorAtomValue::stringValue: {
263 auto s = android::String8(
264 input_value.get<VendorAtomValue::stringValue>());
265 char* buf = const_cast<char*>(atom_val.s);
266 strncpy(buf, s.c_str(), last_atom_str_idx);
267 buf[last_atom_str_idx] = '\0';
268 break;
269 }
270 default:
271 TLOGE("Unexpected vendorAtom.values[%d].getTag = %d.\n",
272 idx, tag);
273 }
274 atom_val.tag = tag;
275
276 if (++idx >= SHM_CONTENT_VENDOR_ATOM_VALUES) {
277 break;
278 }
279 }
280 content->full.store(true, std::memory_order_release);
281 }
282 return Status::ok();
283 }
284 };
285
286 static tipc_srv_ops test_ctl_ops = {
287 .on_connect = test_ctl_on_connect,
288 .on_message = test_ctl_on_message,
289 .on_channel_cleanup = test_ctl_on_channel_cleanup,
290 };
291
main(void)292 int main(void) {
293 TLOGI("Starting StatsConsumer\n");
294
295 tipc_hset* hset = tipc_hset_create();
296 if (IS_ERR(hset)) {
297 TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
298 return EXIT_FAILURE;
299 }
300
301 const auto port_acl = android::RpcServerTrusty::PortAcl{
302 .flags = IPC_PORT_ALLOW_TA_CONNECT,
303 };
304
305 TLOGE("Creating Consumer (exposing IStats)\n");
306 consumer_ctx.stats_consumer = android::sp<StatsConsumer>::make();
307
308 int rc = tipc_add_service(hset, &test_ctl_port, 1, 1, &test_ctl_ops);
309 if (rc < 0) {
310 return rc;
311 }
312
313 uint32_t timeout = INFINITE_TIME;
314 do {
315 // Changing timeout interval from infinite to simulate a timer
316 rc = tipc_handle_event(hset, timeout);
317 if (consumer_ctx.shm_ptr) {
318 TLOGD("interacting with producer\n");
319 consumer_ctx.istats_setter->setInterface(
320 consumer_ctx.stats_consumer);
321 timeout = TIME_OUT_INTERVAL_MS;
322 TLOGD("timeout = TIME_OUT_INTERVAL_MS\n");
323 } else {
324 timeout = INFINITE_TIME;
325 TLOGD("timeout = INFINITE_TIME\n");
326 }
327 } while (rc == 0 || timeout == TIME_OUT_INTERVAL_MS);
328
329 TLOGE("stats-consumer service died\n");
330
331 return rc;
332 }
333