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/util/system/debug_dump.h"
24 #include "chre_api/chre/version.h"
25 
26 #include <algorithm>
27 
28 namespace chre {
29 
30 constexpr size_t Nanoapp::kMaxSizeWakeupBuckets;
31 
32 Nanoapp::Nanoapp() {
33   // Push first bucket onto wakeup bucket queue
34   cycleWakeupBuckets(1);
35 }
36 
37 Nanoapp::~Nanoapp() {
38   const size_t totalAllocatedBytes = getTotalAllocatedBytes();
39 
40   if (totalAllocatedBytes > 0) {
41     // TODO: Consider asserting here
42     LOGE("Nanoapp ID=0x%016" PRIx64 " still has %zu allocated bytes!",
43          getAppId(), totalAllocatedBytes);
44   }
45 }
46 
47 bool Nanoapp::isRegisteredForBroadcastEvent(uint16_t eventType) const {
48   return (mRegisteredEvents.find(eventType) != mRegisteredEvents.size());
49 }
50 
51 bool Nanoapp::registerForBroadcastEvent(uint16_t eventId) {
52   if (isRegisteredForBroadcastEvent(eventId)) {
53     return false;
54   }
55 
56   if (!mRegisteredEvents.push_back(eventId)) {
57     FATAL_ERROR_OOM();
58   }
59 
60   return true;
61 }
62 
63 bool Nanoapp::unregisterForBroadcastEvent(uint16_t eventId) {
64   size_t registeredEventIndex = mRegisteredEvents.find(eventId);
65   if (registeredEventIndex == mRegisteredEvents.size()) {
66     return false;
67   }
68 
69   mRegisteredEvents.erase(registeredEventIndex);
70   return true;
71 }
72 
73 void Nanoapp::configureNanoappInfoEvents(bool enable) {
74   if (enable) {
75     registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
76     registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
77   } else {
78     unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
79     unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
80   }
81 }
82 
83 void Nanoapp::configureHostSleepEvents(bool enable) {
84   if (enable) {
85     registerForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
86     registerForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
87   } else {
88     unregisterForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
89     unregisterForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
90   }
91 }
92 
93 void Nanoapp::configureDebugDumpEvent(bool enable) {
94   if (enable) {
95     registerForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
96   } else {
97     unregisterForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
98   }
99 }
100 
101 Event *Nanoapp::processNextEvent() {
102   Event *event = mEventQueue.pop();
103 
104   CHRE_ASSERT_LOG(event != nullptr, "Tried delivering event, but queue empty");
105   if (event != nullptr) {
106     handleEvent(event->senderInstanceId, event->eventType, event->eventData);
107   }
108 
109   return event;
110 }
111 
112 void Nanoapp::blameHostWakeup() {
113   if (mWakeupBuckets.back() < UINT16_MAX) ++mWakeupBuckets.back();
114 }
115 
116 void Nanoapp::cycleWakeupBuckets(size_t numBuckets) {
117   numBuckets = std::min(numBuckets, kMaxSizeWakeupBuckets);
118   for (size_t i = 0; i < numBuckets; ++i) {
119     if (mWakeupBuckets.full()) {
120       mWakeupBuckets.erase(0);
121     }
122     mWakeupBuckets.push_back(0);
123   }
124 }
125 
126 void Nanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
127   debugDump.print(" Id=%" PRIu32 " 0x%016" PRIx64 " ", getInstanceId(),
128                   getAppId());
129   PlatformNanoapp::logStateToBuffer(debugDump);
130   debugDump.print(" v%" PRIu32 ".%" PRIu32 ".%" PRIu32 " tgtAPI=%" PRIu32
131                   ".%" PRIu32 " curAlloc=%zu peakAlloc=%zu",
132                   CHRE_EXTRACT_MAJOR_VERSION(getAppVersion()),
133                   CHRE_EXTRACT_MINOR_VERSION(getAppVersion()),
134                   CHRE_EXTRACT_PATCH_VERSION(getAppVersion()),
135                   CHRE_EXTRACT_MAJOR_VERSION(getTargetApiVersion()),
136                   CHRE_EXTRACT_MINOR_VERSION(getTargetApiVersion()),
137                   getTotalAllocatedBytes(), getPeakAllocatedBytes());
138   debugDump.print(" hostWakeups=[ cur->");
139   // Get buckets latest -> earliest except last one
140   for (size_t i = mWakeupBuckets.size() - 1; i > 0; --i) {
141     debugDump.print("%" PRIu16 ", ", mWakeupBuckets[i]);
142   }
143   // Earliest bucket gets no comma
144   debugDump.print("%" PRIu16 " ]\n", mWakeupBuckets.front());
145 }
146 
147 }  // namespace chre
148