1 /*
2 * Copyright (C) 2016 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 "chre/core/nanoapp.h"
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/platform/assert.h"
21 #include "chre/platform/fatal_error.h"
22 #include "chre/platform/log.h"
23 #include "chre/platform/tracing.h"
24 #include "chre/util/system/debug_dump.h"
25 #include "chre_api/chre/gnss.h"
26 #include "chre_api/chre/version.h"
27
28 #include <algorithm>
29 #include <cstdint>
30
31 #if CHRE_FIRST_SUPPORTED_API_VERSION < CHRE_API_VERSION_1_5
32 #define CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
33 #endif
34
35 namespace chre {
36
37 constexpr size_t Nanoapp::kMaxSizeWakeupBuckets;
38
Nanoapp()39 Nanoapp::Nanoapp() {
40 // Push first bucket onto wakeup bucket queue
41 cycleWakeupBuckets(1);
42 }
43
start()44 bool Nanoapp::start() {
45 traceRegisterNanoapp(getInstanceId(), getAppName());
46 mIsInNanoappStart = true;
47 bool success = PlatformNanoapp::start();
48 mIsInNanoappStart = false;
49 return success;
50 }
51
isRegisteredForBroadcastEvent(const Event * event) const52 bool Nanoapp::isRegisteredForBroadcastEvent(const Event *event) const {
53 bool registered = false;
54 uint16_t eventType = event->eventType;
55 uint16_t targetGroupIdMask = event->targetAppGroupMask;
56
57 // The host endpoint notification is a special case, because it requires
58 // explicit registration using host endpoint IDs rather than masks.
59 if (eventType == CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION) {
60 const auto *data =
61 static_cast<const chreHostEndpointNotification *>(event->eventData);
62 registered = isRegisteredForHostEndpointNotifications(data->hostEndpointId);
63 } else {
64 size_t foundIndex = registrationIndex(eventType);
65 if (foundIndex < mRegisteredEvents.size()) {
66 const EventRegistration ® = mRegisteredEvents[foundIndex];
67 if (targetGroupIdMask & reg.groupIdMask) {
68 registered = true;
69 }
70 }
71 }
72 return registered;
73 }
74
registerForBroadcastEvent(uint16_t eventType,uint16_t groupIdMask)75 void Nanoapp::registerForBroadcastEvent(uint16_t eventType,
76 uint16_t groupIdMask) {
77 size_t foundIndex = registrationIndex(eventType);
78 if (foundIndex < mRegisteredEvents.size()) {
79 mRegisteredEvents[foundIndex].groupIdMask |= groupIdMask;
80 } else if (!mRegisteredEvents.push_back(
81 EventRegistration(eventType, groupIdMask))) {
82 FATAL_ERROR_OOM();
83 }
84 }
85
unregisterForBroadcastEvent(uint16_t eventType,uint16_t groupIdMask)86 void Nanoapp::unregisterForBroadcastEvent(uint16_t eventType,
87 uint16_t groupIdMask) {
88 size_t foundIndex = registrationIndex(eventType);
89 if (foundIndex < mRegisteredEvents.size()) {
90 EventRegistration ® = mRegisteredEvents[foundIndex];
91 reg.groupIdMask &= ~groupIdMask;
92 if (reg.groupIdMask == 0) {
93 mRegisteredEvents.erase(foundIndex);
94 }
95 }
96 }
97
configureNanoappInfoEvents(bool enable)98 void Nanoapp::configureNanoappInfoEvents(bool enable) {
99 if (enable) {
100 registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
101 registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
102 } else {
103 unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
104 unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
105 }
106 }
107
configureHostSleepEvents(bool enable)108 void Nanoapp::configureHostSleepEvents(bool enable) {
109 if (enable) {
110 registerForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
111 registerForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
112 } else {
113 unregisterForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
114 unregisterForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
115 }
116 }
117
configureDebugDumpEvent(bool enable)118 void Nanoapp::configureDebugDumpEvent(bool enable) {
119 if (enable) {
120 registerForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
121 } else {
122 unregisterForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
123 }
124 }
125
configureUserSettingEvent(uint8_t setting,bool enable)126 void Nanoapp::configureUserSettingEvent(uint8_t setting, bool enable) {
127 if (enable) {
128 registerForBroadcastEvent(CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT + setting);
129 } else {
130 unregisterForBroadcastEvent(CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT +
131 setting);
132 }
133 }
134
processEvent(Event * event)135 void Nanoapp::processEvent(Event *event) {
136 Nanoseconds eventStartTime = SystemTime::getMonotonicTime();
137 traceNanoappHandleEventStart(getInstanceId(), event->eventType);
138 if (event->eventType == CHRE_EVENT_GNSS_DATA) {
139 handleGnssMeasurementDataEvent(event);
140 } else {
141 handleEvent(event->senderInstanceId, event->eventType, event->eventData);
142 }
143 traceNanoappHandleEventEnd(getInstanceId());
144 Nanoseconds eventProcessTime =
145 SystemTime::getMonotonicTime() - eventStartTime;
146 if (Milliseconds(eventProcessTime) >= Milliseconds(100)) {
147 LOGE("Nanoapp 0x%" PRIx64 " took %" PRIu64
148 " ms to process event type %" PRIu16,
149 getAppId(), Milliseconds(eventProcessTime).getMilliseconds(),
150 event->eventType);
151 }
152 mEventProcessTime.addValue(Milliseconds(eventProcessTime).getMilliseconds());
153 }
154
blameHostWakeup()155 void Nanoapp::blameHostWakeup() {
156 if (mWakeupBuckets.back() < UINT16_MAX) ++mWakeupBuckets.back();
157 if (mNumWakeupsSinceBoot < UINT32_MAX) ++mNumWakeupsSinceBoot;
158 }
159
cycleWakeupBuckets(size_t numBuckets)160 void Nanoapp::cycleWakeupBuckets(size_t numBuckets) {
161 numBuckets = std::min(numBuckets, kMaxSizeWakeupBuckets);
162 for (size_t i = 0; i < numBuckets; ++i) {
163 if (mWakeupBuckets.full()) {
164 mWakeupBuckets.erase(0);
165 }
166 mWakeupBuckets.push_back(0);
167 }
168 }
169
logStateToBuffer(DebugDumpWrapper & debugDump) const170 void Nanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
171 debugDump.print(" Id=%" PRIu16 " 0x%016" PRIx64 " ", getInstanceId(),
172 getAppId());
173 PlatformNanoapp::logStateToBuffer(debugDump);
174 debugDump.print(" v%" PRIu32 ".%" PRIu32 ".%" PRIu32 " tgtAPI=%" PRIu32
175 ".%" PRIu32 " curAlloc=%zu peakAlloc=%zu",
176 CHRE_EXTRACT_MAJOR_VERSION(getAppVersion()),
177 CHRE_EXTRACT_MINOR_VERSION(getAppVersion()),
178 CHRE_EXTRACT_PATCH_VERSION(getAppVersion()),
179 CHRE_EXTRACT_MAJOR_VERSION(getTargetApiVersion()),
180 CHRE_EXTRACT_MINOR_VERSION(getTargetApiVersion()),
181 getTotalAllocatedBytes(), getPeakAllocatedBytes());
182 debugDump.print(" hostWakeups=[ cur->");
183 // Get buckets latest -> earliest except last one
184 for (size_t i = mWakeupBuckets.size() - 1; i > 0; --i) {
185 debugDump.print("%" PRIu16 ", ", mWakeupBuckets[i]);
186 }
187 // Earliest bucket gets no comma
188 debugDump.print("%" PRIu16 " ]", mWakeupBuckets.front());
189
190 // Print total wakeups since boot
191 debugDump.print(" totWakeups=%" PRIu32 " ", mNumWakeupsSinceBoot);
192
193 // Print mean and max event process time
194 debugDump.print("eventProcessTimeMs: mean=%" PRIu64 ", max=%" PRIu64 "\n",
195 mEventProcessTime.getMean(), mEventProcessTime.getMax());
196 }
197
permitPermissionUse(uint32_t permission) const198 bool Nanoapp::permitPermissionUse(uint32_t permission) const {
199 return !supportsAppPermissions() ||
200 ((getAppPermissions() & permission) == permission);
201 }
202
registrationIndex(uint16_t eventType) const203 size_t Nanoapp::registrationIndex(uint16_t eventType) const {
204 size_t foundIndex = 0;
205 for (; foundIndex < mRegisteredEvents.size(); ++foundIndex) {
206 const EventRegistration ® = mRegisteredEvents[foundIndex];
207 if (reg.eventType == eventType) {
208 break;
209 }
210 }
211 return foundIndex;
212 }
213
handleGnssMeasurementDataEvent(const Event * event)214 void Nanoapp::handleGnssMeasurementDataEvent(const Event *event) {
215 #ifdef CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
216 const struct chreGnssDataEvent *data =
217 static_cast<const struct chreGnssDataEvent *>(event->eventData);
218 if (getTargetApiVersion() < CHRE_API_VERSION_1_5 &&
219 data->measurement_count > CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5) {
220 chreGnssDataEvent localEvent;
221 memcpy(&localEvent, data, sizeof(struct chreGnssDataEvent));
222 localEvent.measurement_count = CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5;
223 handleEvent(event->senderInstanceId, event->eventType, &localEvent);
224 } else
225 #endif // CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
226 {
227 handleEvent(event->senderInstanceId, event->eventType, event->eventData);
228 }
229 }
230
configureHostEndpointNotifications(uint16_t hostEndpointId,bool enable)231 bool Nanoapp::configureHostEndpointNotifications(uint16_t hostEndpointId,
232 bool enable) {
233 bool success = true;
234 bool registered = isRegisteredForHostEndpointNotifications(hostEndpointId);
235 if (enable && !registered) {
236 success = mRegisteredHostEndpoints.push_back(hostEndpointId);
237 if (!success) {
238 LOG_OOM();
239 }
240 } else if (!enable && registered) {
241 size_t index = mRegisteredHostEndpoints.find(hostEndpointId);
242 mRegisteredHostEndpoints.erase(index);
243 }
244
245 return success;
246 }
247
publishRpcServices(struct chreNanoappRpcService * services,size_t numServices)248 bool Nanoapp::publishRpcServices(struct chreNanoappRpcService *services,
249 size_t numServices) {
250 if (!mIsInNanoappStart) {
251 LOGE("publishRpcServices must be called from nanoappStart");
252 return false;
253 }
254
255 const size_t startSize = mRpcServices.size();
256 const size_t endSize = startSize + numServices;
257 if (endSize > kMaxRpcServices) {
258 return false;
259 }
260
261 mRpcServices.reserve(endSize);
262
263 bool success = true;
264
265 for (size_t i = 0; i < numServices; i++) {
266 if (!mRpcServices.push_back(services[i])) {
267 LOG_OOM();
268 success = false;
269 break;
270 }
271 }
272
273 if (success && mRpcServices.size() > 1) {
274 for (size_t i = 0; i < mRpcServices.size() - 1; i++) {
275 for (size_t j = i + 1; j < mRpcServices.size(); j++) {
276 if (mRpcServices[i].id == mRpcServices[j].id) {
277 LOGE("Service id = 0x%016" PRIx64 " can only be published once",
278 mRpcServices[i].id);
279 success = false;
280 }
281 }
282 }
283 }
284
285 if (!success) {
286 mRpcServices.resize(startSize);
287 }
288
289 return success;
290 }
291
linkHeapBlock(HeapBlockHeader * header)292 void Nanoapp::linkHeapBlock(HeapBlockHeader *header) {
293 header->data.next = mFirstHeader;
294 mFirstHeader = header;
295 }
296
unlinkHeapBlock(HeapBlockHeader * header)297 void Nanoapp::unlinkHeapBlock(HeapBlockHeader *header) {
298 if (mFirstHeader == nullptr) {
299 // The list is empty.
300 return;
301 }
302
303 if (header == mFirstHeader) {
304 mFirstHeader = header->data.next;
305 return;
306 }
307
308 HeapBlockHeader *previous = mFirstHeader;
309 HeapBlockHeader *current = mFirstHeader->data.next;
310
311 while (current != nullptr) {
312 if (current == header) {
313 previous->data.next = current->data.next;
314 break;
315 }
316 previous = current;
317 current = current->data.next;
318 }
319 }
320
321 } // namespace chre
322