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 int rc = munmap((void*)ctx->shm_ptr, page_size);
206 if (rc != NO_ERROR) {
207 TLOGW("munmap() failed: %d\n", rc);
208 }
209 ctx->shm_ptr = nullptr;
210 }
211 break;
212 default:
213 TLOGE("Invalid cmd %d\n", request.cmd);
214 }
215
216 close(shm_handle);
217
218 return NO_ERROR;
219 }
220
test_ctl_on_channel_cleanup(void * ctx_)221 static void test_ctl_on_channel_cleanup(void* ctx_) {
222 return;
223 }
224
225 class StatsConsumer : public BnStats {
226 public:
reportVendorAtom(const VendorAtom & vendorAtom)227 Status reportVendorAtom(const VendorAtom& vendorAtom) {
228 TLOGD("reportVendorAtom atomId=%d.\n", vendorAtom.atomId);
229 if (consumer_ctx.shm_ptr) {
230 volatile ShmContent* content = (ShmContent*)consumer_ctx.shm_ptr;
231
232 // Spin and sleep until the other side consumes the active value
233 while (content->full.load(std::memory_order_acquire)) {
234 trusty_nanosleep(0, 0, MS_TO_NS(kSpinSleepMs));
235 }
236
237 TLOGD("content->atom_id=%d.\n", content->atom_id);
238 TLOGD("content->reverse_domain_name=%s.\n",
239 content->reverse_domain_name);
240 content->atom_id = vendorAtom.atomId;
241
242 size_t last_atom_str_idx = SHM_CONTENT_VENDOR_ATOM_STR_SIZE - 1;
243 strncpy((char*)content->reverse_domain_name,
244 android::String8(vendorAtom.reverseDomainName).c_str(),
245 last_atom_str_idx);
246 content->reverse_domain_name[last_atom_str_idx] = '\0';
247
248 int idx = 0;
249 for (const auto& input_value : vendorAtom.values) {
250 VendorAtomValue::Tag tag = input_value.getTag();
251 TLOGD("vendorAtom.values[%d].getTag = %d.\n", idx, tag);
252
253 volatile ShmVendorAtomValue& atom_val =
254 content->vendor_atom_values[idx];
255 switch (tag) {
256 case VendorAtomValue::intValue:
257 atom_val.i = input_value.get<VendorAtomValue::intValue>();
258 break;
259 case VendorAtomValue::longValue:
260 atom_val.l = input_value.get<VendorAtomValue::longValue>();
261 break;
262 case VendorAtomValue::floatValue:
263 atom_val.f = input_value.get<VendorAtomValue::floatValue>();
264 break;
265 case VendorAtomValue::stringValue: {
266 auto s = android::String8(
267 input_value.get<VendorAtomValue::stringValue>());
268 char* buf = const_cast<char*>(atom_val.s);
269 strncpy(buf, s.c_str(), last_atom_str_idx);
270 buf[last_atom_str_idx] = '\0';
271 break;
272 }
273 default:
274 TLOGE("Unexpected vendorAtom.values[%d].getTag = %d.\n",
275 idx, tag);
276 }
277 atom_val.tag = tag;
278
279 if (++idx >= SHM_CONTENT_VENDOR_ATOM_VALUES) {
280 break;
281 }
282 }
283 content->full.store(true, std::memory_order_release);
284 }
285 return Status::ok();
286 }
287 };
288
289 static tipc_srv_ops test_ctl_ops = {
290 .on_connect = test_ctl_on_connect,
291 .on_message = test_ctl_on_message,
292 .on_channel_cleanup = test_ctl_on_channel_cleanup,
293 };
294
main(void)295 int main(void) {
296 TLOGI("Starting StatsConsumer\n");
297
298 tipc_hset* hset = tipc_hset_create();
299 if (IS_ERR(hset)) {
300 TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
301 return EXIT_FAILURE;
302 }
303
304 const auto port_acl = android::RpcServerTrusty::PortAcl{
305 .flags = IPC_PORT_ALLOW_TA_CONNECT,
306 };
307
308 TLOGE("Creating Consumer (exposing IStats)\n");
309 consumer_ctx.stats_consumer = android::sp<StatsConsumer>::make();
310
311 int rc = tipc_add_service(hset, &test_ctl_port, 1, 1, &test_ctl_ops);
312 if (rc < 0) {
313 return rc;
314 }
315
316 uint32_t timeout = INFINITE_TIME;
317 do {
318 // Changing timeout interval from infinite to simulate a timer
319 rc = tipc_handle_event(hset, timeout);
320 if (consumer_ctx.shm_ptr) {
321 TLOGD("interacting with producer\n");
322 consumer_ctx.istats_setter->setInterface(
323 consumer_ctx.stats_consumer);
324 timeout = TIME_OUT_INTERVAL_MS;
325 TLOGD("timeout = TIME_OUT_INTERVAL_MS\n");
326 } else {
327 timeout = INFINITE_TIME;
328 TLOGD("timeout = INFINITE_TIME\n");
329 }
330 } while (rc == 0 || timeout == TIME_OUT_INTERVAL_MS);
331
332 TLOGE("stats-consumer service died\n");
333
334 return rc;
335 }
336