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