• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 "HalCamera.h"
18 #include "StatsCollector.h"
19 #include "VirtualCamera.h"
20 
21 #include <processgroup/sched_policy.h>
22 #include <pthread.h>
23 
24 #include <android-base/file.h>
25 #include <android-base/strings.h>
26 #include <android-base/stringprintf.h>
27 #include <utils/SystemClock.h>
28 
29 namespace {
30 
31     const char* kSingleIndent = "\t";
32     const char* kDoubleIndent = "\t\t";
33     const char* kDumpAllDevices = "all";
34 
35 }
36 
37 namespace android {
38 namespace automotive {
39 namespace evs {
40 namespace V1_1 {
41 namespace implementation {
42 
43 using android::base::Error;
44 using android::base::EqualsIgnoreCase;
45 using android::base::Result;
46 using android::base::StringAppendF;
47 using android::base::StringPrintf;
48 using android::base::WriteStringToFd;
49 using android::hardware::automotive::evs::V1_1::BufferDesc;
50 
51 namespace {
52 
53 const auto kPeriodicCollectionInterval = 10s;
54 const auto kPeriodicCollectionCacheSize = 180;
55 const auto kMinCollectionInterval = 1s;
56 const auto kCustomCollectionMaxDuration = 30min;
57 const auto kMaxDumpHistory = 10;
58 
59 }
60 
handleMessage(const Message & message)61 void StatsCollector::handleMessage(const Message& message) {
62     const auto received = static_cast<CollectionEvent>(message.what);
63     Result<void> ret;
64     switch (received) {
65         case CollectionEvent::PERIODIC:
66             ret = handleCollectionEvent(received, &mPeriodicCollectionInfo);
67             break;
68 
69         case CollectionEvent::CUSTOM_START:
70             ret = handleCollectionEvent(received, &mCustomCollectionInfo);
71             break;
72 
73         case CollectionEvent::CUSTOM_END: {
74             AutoMutex lock(mMutex);
75             if (mCurrentCollectionEvent != CollectionEvent::CUSTOM_START) {
76                 LOG(WARNING) << "Ignoring a message to end custom collection "
77                              << "as current collection is " << toString(mCurrentCollectionEvent);
78                 return;
79             }
80 
81             // Starts a periodic collection
82             mLooper->removeMessages(this);
83             mCurrentCollectionEvent = CollectionEvent::PERIODIC;
84             mPeriodicCollectionInfo.lastCollectionTime = mLooper->now();
85             mLooper->sendMessage(this, CollectionEvent::PERIODIC);
86             return;
87         }
88 
89         default:
90             LOG(WARNING) << "Unknown event is received: " << received;
91             break;
92     }
93 
94     if (!ret.ok()) {
95         Mutex::Autolock lock(mMutex);
96         LOG(ERROR) << "Terminating data collection: "
97                    << ret.error();
98 
99         mCurrentCollectionEvent = CollectionEvent::TERMINATED;
100         mLooper->removeMessages(this);
101         mLooper->wake();
102     }
103 }
104 
105 
handleCollectionEvent(CollectionEvent event,CollectionInfo * info)106 Result<void> StatsCollector::handleCollectionEvent(CollectionEvent event,
107                                                    CollectionInfo* info) {
108     AutoMutex lock(mMutex);
109     if (mCurrentCollectionEvent != event) {
110         if (mCurrentCollectionEvent != CollectionEvent::TERMINATED) {
111             LOG(WARNING) << "Skipping " << toString(event) << " collection event "
112                          << "on collection event " << toString(mCurrentCollectionEvent);
113 
114             return {};
115         } else {
116             return Error() << "A collection has been terminated "
117                            << "while a current event was pending in the message queue.";
118         }
119     }
120 
121     if (info->maxCacheSize < 1) {
122         return Error() << "Maximum cache size must be greater than 0";
123     }
124 
125     using std::chrono::duration_cast;
126     using std::chrono::seconds;
127     if (info->interval < kMinCollectionInterval) {
128         LOG(WARNING) << "Collection interval of "
129                      << duration_cast<seconds>(info->interval).count()
130                      << " seconds for " << toString(event)
131                      << " collection cannot be shorter than "
132                      << duration_cast<seconds>(kMinCollectionInterval).count()
133                      << " seconds.";
134         info->interval = kMinCollectionInterval;
135     }
136 
137     auto ret = collectLocked(info);
138     if (!ret.ok()) {
139         return Error() << toString(event) << " collection failed: "
140                        << ret.error();
141     }
142 
143     // Arms a message for next periodic collection
144     info->lastCollectionTime += info->interval.count();
145     mLooper->sendMessageAtTime(info->lastCollectionTime, this, event);
146     return {};
147 }
148 
149 
collectLocked(CollectionInfo * info)150 Result<void> StatsCollector::collectLocked(CollectionInfo* info) REQUIRES(mMutex) {
151     for (auto&& [id, ptr] : mClientsToMonitor) {
152         auto pClient = ptr.promote();
153         if (!pClient) {
154             LOG(DEBUG) << id << " seems not alive.";
155             continue;
156         }
157 
158         // Pulls a snapshot and puts a timestamp
159         auto snapshot = pClient->getStats();
160         snapshot.timestamp = mLooper->now();
161 
162         // Removes the oldest record if cache is full
163         if (info->records[id].history.size() > info->maxCacheSize) {
164             info->records[id].history.pop_front();
165         }
166 
167         // Stores the latest record and the deltas
168         auto delta = snapshot - info->records[id].latest;
169         info->records[id].history.emplace_back(delta);
170         info->records[id].latest = snapshot;
171     }
172 
173     return {};
174 }
175 
176 
startCollection()177 Result<void> StatsCollector::startCollection() {
178     {
179         AutoMutex lock(mMutex);
180         if (mCurrentCollectionEvent != CollectionEvent::INIT ||
181             mCollectionThread.joinable()) {
182             return Error(INVALID_OPERATION)
183                    << "Camera usages collection is already running.";
184         }
185 
186         // Create the collection info w/ the default values
187         mPeriodicCollectionInfo = {
188             .interval = kPeriodicCollectionInterval,
189             .maxCacheSize = kPeriodicCollectionCacheSize,
190             .lastCollectionTime = 0,
191         };
192 
193     }
194 
195     // Starts a background worker thread
196     mCollectionThread = std::thread([&]() {
197         {
198             AutoMutex lock(mMutex);
199             if (mCurrentCollectionEvent != CollectionEvent::INIT) {
200                 LOG(ERROR) << "Skipping the statistics collection because "
201                            << "the current collection event is "
202                            << toString(mCurrentCollectionEvent);
203                 return;
204             }
205 
206             // Staring with a periodic collection
207             mCurrentCollectionEvent = CollectionEvent::PERIODIC;
208         }
209 
210         if (set_sched_policy(0, SP_BACKGROUND) != 0) {
211             PLOG(WARNING) << "Failed to set background scheduling prioirty";
212         }
213 
214         // Sets a looper for the communication
215         mLooper->setLooper(Looper::prepare(/*opts=*/0));
216 
217         // Starts collecting the usage statistics periodically
218         mLooper->sendMessage(this, CollectionEvent::PERIODIC);
219 
220         // Polls the messages until the collection is stopped.
221         bool isActive = true;
222         while (isActive) {
223             mLooper->pollAll(/*timeoutMillis=*/-1);
224             {
225                 AutoMutex lock(mMutex);
226                 isActive = mCurrentCollectionEvent != CollectionEvent::TERMINATED;
227             }
228         }
229     });
230 
231     auto ret = pthread_setname_np(mCollectionThread.native_handle(), "EvsUsageCollect");
232     if (ret != 0) {
233         PLOG(WARNING) << "Failed to name a collection thread";
234     }
235 
236     return {};
237 }
238 
239 
stopCollection()240 Result<void> StatsCollector::stopCollection() {
241     {
242         AutoMutex lock(mMutex);
243         if (mCurrentCollectionEvent == CollectionEvent::TERMINATED) {
244             LOG(WARNING) << "Camera usage data collection was stopped already.";
245             return {};
246         }
247 
248         LOG(INFO) << "Stopping a camera usage data collection";
249         mCurrentCollectionEvent = CollectionEvent::TERMINATED;
250     }
251 
252     // Join a background thread
253     if (mCollectionThread.joinable()) {
254         mLooper->removeMessages(this);
255         mLooper->wake();
256         mCollectionThread.join();
257     }
258 
259     return {};
260 }
261 
262 
startCustomCollection(std::chrono::nanoseconds interval,std::chrono::nanoseconds maxDuration)263 Result<void> StatsCollector::startCustomCollection(
264         std::chrono::nanoseconds interval,
265         std::chrono::nanoseconds maxDuration) {
266     using std::chrono::duration_cast;
267     using std::chrono::milliseconds;
268     if (interval < kMinCollectionInterval || maxDuration < kMinCollectionInterval) {
269         return Error(INVALID_OPERATION)
270                 << "Collection interval and maximum maxDuration must be >= "
271                 << duration_cast<milliseconds>(kMinCollectionInterval).count()
272                 << " milliseconds.";
273     }
274 
275     if (maxDuration > kCustomCollectionMaxDuration) {
276         return Error(INVALID_OPERATION)
277                 << "Collection maximum maxDuration must be less than "
278                 << duration_cast<milliseconds>(kCustomCollectionMaxDuration).count()
279                 << " milliseconds.";
280     }
281 
282     {
283         AutoMutex lock(mMutex);
284         if (mCurrentCollectionEvent != CollectionEvent::PERIODIC) {
285             return Error(INVALID_OPERATION)
286                     << "Cannot start a custom collection when "
287                     << "the current collection event " << toString(mCurrentCollectionEvent)
288                     << " != " << toString(CollectionEvent::PERIODIC) << " collection event";
289         }
290 
291         // Notifies the user if a preview custom collection result is
292         // not used yet.
293         if (mCustomCollectionInfo.records.size() > 0) {
294             LOG(WARNING) << "Previous custom collection result, which was done at "
295                          << mCustomCollectionInfo.lastCollectionTime
296                          << " has not pulled yet will be overwritten.";
297         }
298 
299         // Programs custom collection configurations
300         mCustomCollectionInfo = {
301                 .interval = interval,
302                 .maxCacheSize = std::numeric_limits<std::size_t>::max(),
303                 .lastCollectionTime = mLooper->now(),
304                 .records = {},
305         };
306 
307         mLooper->removeMessages(this);
308         nsecs_t uptime = mLooper->now() + maxDuration.count();
309         mLooper->sendMessageAtTime(uptime, this, CollectionEvent::CUSTOM_END);
310         mCurrentCollectionEvent = CollectionEvent::CUSTOM_START;
311         mLooper->sendMessage(this, CollectionEvent::CUSTOM_START);
312     }
313 
314     return {};
315 }
316 
317 
stopCustomCollection(std::string targetId)318 Result<std::string> StatsCollector::stopCustomCollection(std::string targetId) {
319     Mutex::Autolock lock(mMutex);
320     if (mCurrentCollectionEvent == CollectionEvent::CUSTOM_START) {
321         // Stops a running custom collection
322         mLooper->removeMessages(this);
323         mLooper->sendMessage(this, CollectionEvent::CUSTOM_END);
324     }
325 
326     auto ret = collectLocked(&mCustomCollectionInfo);
327     if (!ret.ok()) {
328         return Error() << toString(mCurrentCollectionEvent) << " collection failed: "
329                        << ret.error();
330     }
331 
332     // Prints out the all collected statistics
333     std::string buffer;
334     using std::chrono::duration_cast;
335     using std::chrono::seconds;
336     const intmax_t interval =
337         duration_cast<seconds>(mCustomCollectionInfo.interval).count();
338     if (EqualsIgnoreCase(targetId, kDumpAllDevices)) {
339         for (auto& [id, records] : mCustomCollectionInfo.records) {
340 
341             StringAppendF(&buffer, "%s\n"
342                                    "%sNumber of collections: %zu\n"
343                                    "%sCollection interval: %" PRIdMAX " secs\n",
344                                    id.c_str(),
345                                    kSingleIndent, records.history.size(),
346                                    kSingleIndent, interval);
347             auto it = records.history.rbegin();
348             while (it != records.history.rend()) {
349                 buffer += it++->toString(kDoubleIndent);
350             }
351         }
352 
353         // Clears the collection
354         mCustomCollectionInfo = {};
355     } else {
356         auto it = mCustomCollectionInfo.records.find(targetId);
357         if (it != mCustomCollectionInfo.records.end()) {
358             StringAppendF(&buffer, "%s\n"
359                                    "%sNumber of collections: %zu\n"
360                                    "%sCollection interval: %" PRIdMAX " secs\n",
361                                    targetId.c_str(),
362                                    kSingleIndent, it->second.history.size(),
363                                    kSingleIndent, interval);
364             auto recordIter = it->second.history.rbegin();
365             while (recordIter != it->second.history.rend()) {
366                 buffer += recordIter++->toString(kDoubleIndent);
367             }
368 
369             // Clears the collection
370             mCustomCollectionInfo = {};
371         } else {
372             // Keeps the collection as the users may want to execute a command
373             // again with a right device id
374             StringAppendF(&buffer, "%s has not been monitored.", targetId.c_str());
375         }
376     }
377 
378     return buffer;
379 }
380 
381 
registerClientToMonitor(android::sp<HalCamera> & camera)382 Result<void> StatsCollector::registerClientToMonitor(android::sp<HalCamera>& camera) {
383     if (!camera) {
384         return Error(BAD_VALUE) << "Given camera client is invalid";
385     }
386 
387     AutoMutex lock(mMutex);
388     const auto id = camera->getId();
389     if (mClientsToMonitor.find(id) != mClientsToMonitor.end()) {
390         LOG(WARNING) << id << " is already registered.";
391     } else {
392         mClientsToMonitor.insert_or_assign(id, camera);
393     }
394 
395     return {};
396 }
397 
398 
unregisterClientToMonitor(const std::string & id)399 Result<void> StatsCollector::unregisterClientToMonitor(const std::string& id) {
400     AutoMutex lock(mMutex);
401     auto entry = mClientsToMonitor.find(id);
402     if (entry != mClientsToMonitor.end()) {
403         mClientsToMonitor.erase(entry);
404     } else {
405         LOG(WARNING) << id << " has not been registerd.";
406     }
407 
408     return {};
409 }
410 
411 
toString(const CollectionEvent & event) const412 std::string StatsCollector::toString(const CollectionEvent& event) const {
413     switch(event) {
414         case CollectionEvent::INIT:
415             return "CollectionEvent::INIT";
416         case CollectionEvent::PERIODIC:
417             return "CollectionEvent::PERIODIC";
418         case CollectionEvent::CUSTOM_START:
419             return "CollectionEvent::CUSTOM_START";
420         case CollectionEvent::CUSTOM_END:
421             return "CollectionEvent::CUSTOM_END";
422         case CollectionEvent::TERMINATED:
423             return "CollectionEvent::TERMINATED";
424 
425         default:
426             return "Unknown";
427     }
428 }
429 
430 
toString(std::unordered_map<std::string,std::string> * usages,const char * indent)431 Result<void> StatsCollector::toString(std::unordered_map<std::string, std::string>* usages,
432                                       const char* indent) EXCLUDES(mMutex) {
433     std::string double_indent(indent);
434     double_indent += indent;
435 
436     {
437         AutoMutex lock(mMutex);
438         using std::chrono::duration_cast;
439         using std::chrono::seconds;
440         const intmax_t interval =
441             duration_cast<seconds>(mPeriodicCollectionInfo.interval).count();
442 
443         for (auto&& [id, records] : mPeriodicCollectionInfo.records) {
444             std::string buffer;
445             StringAppendF(&buffer, "%s\n"
446                                    "%sNumber of collections: %zu\n"
447                                    "%sCollection interval: %" PRIdMAX "secs\n",
448                                    id.c_str(),
449                                    indent, records.history.size(),
450                                    indent, interval);
451 
452             // Adding up to kMaxDumpHistory records
453             auto it = records.history.rbegin();
454             auto count = 0;
455             while (it != records.history.rend() && count < kMaxDumpHistory) {
456                 buffer += it->toString(double_indent.c_str());
457                 ++it;
458                 ++count;
459             }
460 
461             usages->insert_or_assign(id, std::move(buffer));
462         }
463     }
464 
465     return {};
466 }
467 
468 
469 } // namespace implementation
470 } // namespace V1_1
471 } // namespace evs
472 } // namespace automotive
473 } // namespace android
474 
475