1 /*
2  * Copyright (C) 2019 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ECOSession"
19 //#define DEBUG_ECO_SESSION
20 #include "eco/ECOSession.h"
21 
22 #include <binder/BinderService.h>
23 #include <cutils/atomic.h>
24 #include <inttypes.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <utils/Log.h>
29 
30 #include <algorithm>
31 #include <climits>
32 #include <cstring>
33 #include <ctime>
34 #include <string>
35 
36 #include "eco/ECODataKey.h"
37 #include "eco/ECODebug.h"
38 
39 namespace android {
40 namespace media {
41 namespace eco {
42 
43 using android::binder::Status;
44 using android::sp;
45 
46 #define RETURN_IF_ERROR(expr)         \
47     {                                 \
48         status_t _errorCode = (expr); \
49         if (_errorCode != true) {     \
50             return _errorCode;        \
51         }                             \
52     }
53 
54 // static
createECOSession(int32_t width,int32_t height,bool isCameraRecording)55 sp<ECOSession> ECOSession::createECOSession(int32_t width, int32_t height, bool isCameraRecording) {
56     // Only support up to 1080P.
57     // TODO: Support the same resolution as in EAF.
58     if (width <= 0 || height <= 0 || width > 5120 || height > 5120 ||
59         width > 1920 * 1080 / height) {
60         ECOLOGE("Failed to create ECOSession with w: %d, h: %d, isCameraRecording: %d", width,
61                 height, isCameraRecording);
62         return nullptr;
63     }
64     return new ECOSession(width, height, isCameraRecording);
65 }
66 
ECOSession(int32_t width,int32_t height,bool isCameraRecording)67 ECOSession::ECOSession(int32_t width, int32_t height, bool isCameraRecording)
68       : BnECOSession(),
69         mStopThread(false),
70         mLastReportedQp(0),
71         mListener(nullptr),
72         mProvider(nullptr),
73         mWidth(width),
74         mHeight(height),
75         mIsCameraRecording(isCameraRecording) {
76     ECOLOGI("ECOSession created with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
77             mIsCameraRecording);
78     mThread = std::thread(startThread, this);
79 
80     // Read the debug properies.
81     mLogStats = property_get_bool(kDebugLogStats, false);
82     mLogStatsEntries = mLogStats ? property_get_int32(kDebugLogStatsSize, 0) : 0;
83 
84     mLogInfo = property_get_bool(kDebugLogStats, false);
85     mLogInfoEntries = mLogInfo ? property_get_int32(kDebugLogInfosSize, 0) : 0;
86 
87     ECOLOGI("ECOSession debug settings: logStats: %s, entries: %d, logInfo: %s entries: %d",
88             mLogStats ? "true" : "false", mLogStatsEntries, mLogInfo ? "true" : "false",
89             mLogInfoEntries);
90 }
91 
~ECOSession()92 ECOSession::~ECOSession() {
93     mStopThread = true;
94 
95     mWorkerWaitCV.notify_all();
96     if (mThread.joinable()) {
97         ECOLOGD("ECOSession: join the thread");
98         mThread.join();
99     }
100     ECOLOGI("ECOSession destroyed with w: %d, h: %d, isCameraRecording: %d", mWidth, mHeight,
101             mIsCameraRecording);
102 }
103 
104 // static
startThread(ECOSession * session)105 void ECOSession::startThread(ECOSession* session) {
106     session->run();
107 }
108 
run()109 void ECOSession::run() {
110     ECOLOGD("ECOSession: starting main thread");
111 
112     while (!mStopThread) {
113         std::unique_lock<std::mutex> runLock(mStatsQueueLock);
114 
115         mWorkerWaitCV.wait(runLock, [this] {
116             return mStopThread == true || !mStatsQueue.empty() || mNewListenerAdded;
117         });
118 
119         if (mStopThread) return;
120 
121         std::scoped_lock<std::mutex> lock(mSessionLock);
122         if (mNewListenerAdded) {
123             // Check if there is any session info available.
124             ECOData sessionInfo = generateLatestSessionInfoEcoData();
125             if (!sessionInfo.isEmpty()) {
126                 Status status = mListener->onNewInfo(sessionInfo);
127                 if (!status.isOk()) {
128                     ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
129                             sessionInfo.debugString().c_str());
130                     // Remove the listener. The lock has been acquired outside this function.
131                     mListener = nullptr;
132                 }
133             }
134             mNewListenerAdded = false;
135         }
136 
137         if (!mStatsQueue.empty()) {
138             ECOData stats = mStatsQueue.front();
139             mStatsQueue.pop_front();
140             processStats(stats);  // TODO: Handle the error from processStats
141         }
142     }
143 
144     ECOLOGD("ECOSession: exiting main thread");
145 }
146 
processStats(const ECOData & stats)147 bool ECOSession::processStats(const ECOData& stats) {
148     ECOLOGV("%s: receive stats: %s", __FUNCTION__, stats.debugString().c_str());
149 
150     if (stats.getDataType() != ECOData::DATA_TYPE_STATS) {
151         ECOLOGE("Invalid stats. ECOData with type: %s", stats.getDataTypeString().c_str());
152         return false;
153     }
154 
155     // Get the type of the stats.
156     std::string statsType;
157     if (stats.findString(KEY_STATS_TYPE, &statsType) != ECODataStatus::OK) {
158         ECOLOGE("Invalid stats ECOData without statsType");
159         return false;
160     }
161 
162     if (statsType.compare(VALUE_STATS_TYPE_SESSION) == 0) {
163         processSessionStats(stats);
164     } else if (statsType.compare(VALUE_STATS_TYPE_FRAME) == 0) {
165         processFrameStats(stats);
166     } else {
167         ECOLOGE("processStats:: Failed to process stats as ECOData contains unknown stats type");
168         return false;
169     }
170 
171     return true;
172 }
173 
processSessionStats(const ECOData & stats)174 void ECOSession::processSessionStats(const ECOData& stats) {
175     ECOLOGV("processSessionStats");
176 
177     ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
178     info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
179 
180     ECODataKeyValueIterator iter(stats);
181     while (iter.hasNext()) {
182         ECOData::ECODataKeyValuePair entry = iter.next();
183         const std::string& key = entry.first;
184         const ECOData::ECODataValueType value = entry.second;
185         ECOLOGV("Processing key: %s", key.c_str());
186         if (!key.compare(KEY_STATS_TYPE)) {
187             // Skip the key KEY_STATS_TYPE as that has been parsed already.
188             continue;
189         } else if (!key.compare(ENCODER_TYPE)) {
190             mCodecType = std::get<int32_t>(value);
191             ECOLOGV("codec type is %d", mCodecType);
192         } else if (!key.compare(ENCODER_PROFILE)) {
193             mCodecProfile = std::get<int32_t>(value);
194             ECOLOGV("codec profile is %d", mCodecProfile);
195         } else if (!key.compare(ENCODER_LEVEL)) {
196             mCodecLevel = std::get<int32_t>(value);
197             ECOLOGV("codec level is %d", mCodecLevel);
198         } else if (!key.compare(ENCODER_TARGET_BITRATE_BPS)) {
199             mTargetBitrateBps = std::get<int32_t>(value);
200             ECOLOGV("codec target bitrate is %d", mTargetBitrateBps);
201         } else if (!key.compare(ENCODER_KFI_FRAMES)) {
202             mKeyFrameIntervalFrames = std::get<int32_t>(value);
203             ECOLOGV("codec kfi is %d", mKeyFrameIntervalFrames);
204         } else if (!key.compare(ENCODER_FRAMERATE_FPS)) {
205             mFramerateFps = std::get<float>(value);
206             ECOLOGV("codec framerate is %f", mFramerateFps);
207         } else if (!key.compare(ENCODER_INPUT_WIDTH)) {
208             int32_t width = std::get<int32_t>(value);
209             if (width != mWidth) {
210                 ECOLOGW("Codec width: %d, expected: %d", width, mWidth);
211             }
212             ECOLOGV("codec input width is %d", width);
213         } else if (!key.compare(ENCODER_INPUT_HEIGHT)) {
214             int32_t height = std::get<int32_t>(value);
215             if (height != mHeight) {
216                 ECOLOGW("Codec height: %d, expected: %d", height, mHeight);
217             }
218             ECOLOGV("codec input height is %d", height);
219         } else if (!key.compare(ENCODER_OUTPUT_WIDTH)) {
220             mOutputWidth = std::get<int32_t>(value);
221             if (mOutputWidth != mWidth) {
222                 ECOLOGW("Codec output width: %d, expected: %d", mOutputWidth, mWidth);
223             }
224             ECOLOGV("codec output width is %d", mOutputWidth);
225         } else if (!key.compare(ENCODER_OUTPUT_HEIGHT)) {
226             mOutputHeight = std::get<int32_t>(value);
227             if (mOutputHeight != mHeight) {
228                 ECOLOGW("Codec output height: %d, expected: %d", mOutputHeight, mHeight);
229             }
230             ECOLOGV("codec output height is %d", mOutputHeight);
231         } else {
232             ECOLOGW("Unknown session stats key %s from provider.", key.c_str());
233             continue;
234         }
235         info.set(key, value);
236     }
237 
238     if (mListener != nullptr) {
239         Status status = mListener->onNewInfo(info);
240         if (!status.isOk()) {
241             ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
242                     info.debugString().c_str());
243             // Remove the listener. The lock has been acquired outside this function.
244             mListener = nullptr;
245         }
246     }
247 }
248 
generateLatestSessionInfoEcoData()249 ECOData ECOSession::generateLatestSessionInfoEcoData() {
250     bool hasInfo = false;
251 
252     ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
253 
254     if (mOutputWidth != -1) {
255         info.setInt32(ENCODER_OUTPUT_WIDTH, mOutputWidth);
256         hasInfo = true;
257     }
258 
259     if (mOutputHeight != -1) {
260         info.setInt32(ENCODER_OUTPUT_HEIGHT, mOutputHeight);
261         hasInfo = true;
262     }
263 
264     if (mCodecType != -1) {
265         info.setInt32(ENCODER_TYPE, mCodecType);
266         hasInfo = true;
267     }
268 
269     if (mCodecProfile != -1) {
270         info.setInt32(ENCODER_PROFILE, mCodecProfile);
271         hasInfo = true;
272     }
273 
274     if (mCodecLevel != -1) {
275         info.setInt32(ENCODER_LEVEL, mCodecLevel);
276         hasInfo = true;
277     }
278 
279     if (mTargetBitrateBps != -1) {
280         info.setInt32(ENCODER_TARGET_BITRATE_BPS, mTargetBitrateBps);
281         hasInfo = true;
282     }
283 
284     if (mKeyFrameIntervalFrames != -1) {
285         info.setInt32(ENCODER_KFI_FRAMES, mKeyFrameIntervalFrames);
286         hasInfo = true;
287     }
288 
289     if (mFramerateFps > 0) {
290         info.setFloat(ENCODER_FRAMERATE_FPS, mFramerateFps);
291         hasInfo = true;
292     }
293 
294     if (hasInfo) {
295         info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_SESSION);
296     }
297     return info;
298 }
299 
processFrameStats(const ECOData & stats)300 void ECOSession::processFrameStats(const ECOData& stats) {
301     ECOLOGD("processFrameStats");
302 
303     bool needToNotifyListener = false;
304     ECOData info(ECOData::DATA_TYPE_INFO, systemTime(SYSTEM_TIME_BOOTTIME));
305     info.setString(KEY_INFO_TYPE, VALUE_INFO_TYPE_FRAME);
306 
307     ECODataKeyValueIterator iter(stats);
308     while (iter.hasNext()) {
309         ECOData::ECODataKeyValuePair entry = iter.next();
310         const std::string& key = entry.first;
311         const ECOData::ECODataValueType value = entry.second;
312         ECOLOGD("Processing %s key", key.c_str());
313 
314         if (!key.compare(KEY_STATS_TYPE)) {
315             // Skip the key KEY_STATS_TYPE as that has been parsed already.
316             continue;
317         } else if (!key.compare(FRAME_NUM) || !key.compare(FRAME_PTS_US) ||
318                    !key.compare(FRAME_TYPE) || !key.compare(FRAME_SIZE_BYTES) ||
319                    !key.compare(ENCODER_ACTUAL_BITRATE_BPS) ||
320                    !key.compare(ENCODER_FRAMERATE_FPS)) {
321             // Only process the keys that are supported by ECOService 1.0.
322             info.set(key, value);
323         } else if (!key.compare(FRAME_AVG_QP)) {
324             // Check the qp to see if need to notify the listener.
325             const int32_t currAverageQp = std::get<int32_t>(value);
326 
327             // Check if the delta between current QP and last reported QP is larger than the
328             // threshold specified by the listener.
329             const bool largeQPChangeDetected =
330                     abs(currAverageQp - mLastReportedQp) > mListenerQpCondition.mQpChangeThreshold;
331 
332             // Check if the qp is going from below threshold to beyond threshold.
333             const bool exceedQpBlockinessThreshold =
334                     (mLastReportedQp <= mListenerQpCondition.mQpBlocknessThreshold &&
335                      currAverageQp > mListenerQpCondition.mQpBlocknessThreshold);
336 
337             // Check if the qp is going from beyond threshold to below threshold.
338             const bool fallBelowQpBlockinessThreshold =
339                     (mLastReportedQp > mListenerQpCondition.mQpBlocknessThreshold &&
340                      currAverageQp <= mListenerQpCondition.mQpBlocknessThreshold);
341 
342             // Notify the listener if any of the above three conditions met.
343             if (largeQPChangeDetected || exceedQpBlockinessThreshold ||
344                 fallBelowQpBlockinessThreshold) {
345                 mLastReportedQp = currAverageQp;
346                 needToNotifyListener = true;
347             }
348 
349             info.set(key, value);
350         } else {
351             ECOLOGW("Unknown frame stats key %s from provider.", key.c_str());
352         }
353     }
354 
355     if (needToNotifyListener && mListener != nullptr) {
356         Status status = mListener->onNewInfo(info);
357         if (!status.isOk()) {
358             ECOLOGE("%s: Failed to publish info: %s due to binder error", __FUNCTION__,
359                     info.debugString().c_str());
360             // Remove the listener. The lock has been acquired outside this function.
361             mListener = nullptr;
362         }
363     }
364 }
365 
getIsCameraRecording(bool * _aidl_return)366 Status ECOSession::getIsCameraRecording(bool* _aidl_return) {
367     std::scoped_lock<std::mutex> lock(mSessionLock);
368     *_aidl_return = mIsCameraRecording;
369     return binder::Status::ok();
370 }
371 
addStatsProvider(const sp<::android::media::eco::IECOServiceStatsProvider> & provider,const::android::media::eco::ECOData & config,bool * status)372 Status ECOSession::addStatsProvider(
373         const sp<::android::media::eco::IECOServiceStatsProvider>& provider,
374         const ::android::media::eco::ECOData& config, bool* status) {
375     ::android::String16 name;
376     Status result = provider->getName(&name);
377     if (!result.isOk()) {
378         // This binder transaction failure may due to permission issue.
379         *status = false;
380         ALOGE("Failed to get provider name");
381         return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get provider name");
382     }
383 
384     ECOLOGV("Try to add stats provider name: %s uid: %d pid %d", ::android::String8(name).string(),
385             IPCThreadState::self()->getCallingUid(), IPCThreadState::self()->getCallingPid());
386 
387     if (provider == nullptr) {
388         ECOLOGE("%s: provider must not be null", __FUNCTION__);
389         *status = false;
390         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null provider given to addStatsProvider");
391     }
392 
393     std::scoped_lock<std::mutex> lock(mSessionLock);
394 
395     if (mProvider != nullptr) {
396         ::android::String16 name;
397         mProvider->getName(&name);
398         String8 errorMsg = String8::format(
399                 "ECOService 1.0 only supports one stats provider, current provider: %s",
400                 ::android::String8(name).string());
401         ECOLOGE("%s", errorMsg.string());
402         *status = false;
403         return STATUS_ERROR(ERROR_ALREADY_EXISTS, errorMsg.string());
404     }
405 
406     // TODO: Handle the provider config.
407     if (config.getDataType() != ECOData::DATA_TYPE_STATS_PROVIDER_CONFIG) {
408         ECOLOGE("Provider config is invalid");
409         *status = false;
410         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider config is invalid");
411     }
412 
413     mProvider = provider;
414     mProviderName = name;
415     *status = true;
416     return binder::Status::ok();
417 }
418 
removeStatsProvider(const sp<::android::media::eco::IECOServiceStatsProvider> & provider,bool * status)419 Status ECOSession::removeStatsProvider(
420         const sp<::android::media::eco::IECOServiceStatsProvider>& provider, bool* status) {
421     std::scoped_lock<std::mutex> lock(mSessionLock);
422     // Check if the provider is the same as current provider for the session.
423     if (IInterface::asBinder(provider) != IInterface::asBinder(mProvider)) {
424         *status = false;
425         ECOLOGE("Failed to remove provider");
426         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Provider does not match");
427     }
428 
429     mProvider = nullptr;
430     *status = true;
431     return binder::Status::ok();
432 }
433 
addInfoListener(const sp<::android::media::eco::IECOServiceInfoListener> & listener,const::android::media::eco::ECOData & config,bool * status)434 Status ECOSession::addInfoListener(
435         const sp<::android::media::eco::IECOServiceInfoListener>& listener,
436         const ::android::media::eco::ECOData& config, bool* status) {
437     ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
438     std::scoped_lock<std::mutex> lock(mSessionLock);
439 
440     ::android::String16 name;
441     Status result = listener->getName(&name);
442     if (!result.isOk()) {
443         // This binder transaction failure may due to permission issue.
444         *status = false;
445         ALOGE("Failed to get listener name");
446         return STATUS_ERROR(ERROR_PERMISSION_DENIED, "Failed to get listener name");
447     }
448 
449     if (mListener != nullptr) {
450         ECOLOGE("ECOService 1.0 only supports one listener");
451         *status = false;
452         return STATUS_ERROR(ERROR_ALREADY_EXISTS, "ECOService 1.0 only supports one listener");
453     }
454 
455     if (listener == nullptr) {
456         ECOLOGE("%s: listener must not be null", __FUNCTION__);
457         *status = false;
458         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Null listener given to addInfoListener");
459     }
460 
461     if (config.getDataType() != ECOData::DATA_TYPE_INFO_LISTENER_CONFIG) {
462         *status = false;
463         ECOLOGE("%s: listener config is invalid", __FUNCTION__);
464         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is invalid");
465     }
466 
467     if (config.isEmpty()) {
468         *status = false;
469         ECOLOGE("Listener must provide listening criterion");
470         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is empty");
471     }
472 
473     // For ECOService 1.0, listener must specify the two threshold in order to receive info.
474     if (config.findInt32(KEY_LISTENER_QP_BLOCKINESS_THRESHOLD,
475                          &mListenerQpCondition.mQpBlocknessThreshold) != ECODataStatus::OK ||
476         config.findInt32(KEY_LISTENER_QP_CHANGE_THRESHOLD,
477                          &mListenerQpCondition.mQpChangeThreshold) != ECODataStatus::OK ||
478         mListenerQpCondition.mQpBlocknessThreshold < ENCODER_MIN_QP ||
479         mListenerQpCondition.mQpBlocknessThreshold > ENCODER_MAX_QP) {
480         *status = false;
481         ECOLOGE("%s: listener config is invalid", __FUNCTION__);
482         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "listener config is not valid");
483     }
484 
485     ECOLOGD("Info listener name: %s uid: %d pid %d", ::android::String8(name).string(),
486             IPCThreadState::self()->getCallingUid(), IPCThreadState::self()->getCallingPid());
487 
488     mListener = listener;
489     mListenerName = name;
490     mNewListenerAdded = true;
491     mWorkerWaitCV.notify_all();
492 
493     *status = true;
494     return binder::Status::ok();
495 }
496 
removeInfoListener(const sp<::android::media::eco::IECOServiceInfoListener> & listener,bool * _aidl_return)497 Status ECOSession::removeInfoListener(
498         const sp<::android::media::eco::IECOServiceInfoListener>& listener, bool* _aidl_return) {
499     std::scoped_lock<std::mutex> lock(mSessionLock);
500     // Check if the listener is the same as current listener for the session.
501     if (IInterface::asBinder(listener) != IInterface::asBinder(mListener)) {
502         *_aidl_return = false;
503         ECOLOGE("Failed to remove listener");
504         return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, "Listener does not match");
505     }
506 
507     mListener = nullptr;
508     mNewListenerAdded = false;
509     *_aidl_return = true;
510     return binder::Status::ok();
511 }
512 
pushNewStats(const::android::media::eco::ECOData & stats,bool * _aidl_return)513 Status ECOSession::pushNewStats(const ::android::media::eco::ECOData& stats, bool* _aidl_return) {
514     ECOLOGV("ECOSession get new stats type: %s", stats.getDataTypeString().c_str());
515     std::unique_lock<std::mutex> lock(mStatsQueueLock);
516     mStatsQueue.push_back(stats);
517     mWorkerWaitCV.notify_all();
518     *_aidl_return = true;
519     return binder::Status::ok();
520 }
521 
getWidth(int32_t * _aidl_return)522 Status ECOSession::getWidth(int32_t* _aidl_return) {
523     std::scoped_lock<std::mutex> lock(mSessionLock);
524     *_aidl_return = mWidth;
525     return binder::Status::ok();
526 }
527 
getHeight(int32_t * _aidl_return)528 Status ECOSession::getHeight(int32_t* _aidl_return) {
529     std::scoped_lock<std::mutex> lock(mSessionLock);
530     *_aidl_return = mHeight;
531     return binder::Status::ok();
532 }
533 
getNumOfListeners(int32_t * _aidl_return)534 Status ECOSession::getNumOfListeners(int32_t* _aidl_return) {
535     std::scoped_lock<std::mutex> lock(mSessionLock);
536     *_aidl_return = (mListener == nullptr ? 0 : 1);
537     return binder::Status::ok();
538 }
539 
getNumOfProviders(int32_t * _aidl_return)540 Status ECOSession::getNumOfProviders(int32_t* _aidl_return) {
541     std::scoped_lock<std::mutex> lock(mSessionLock);
542     *_aidl_return = (mProvider == nullptr ? 0 : 1);
543     return binder::Status::ok();
544 }
545 
binderDied(const wp<IBinder> &)546 /*virtual*/ void ECOSession::binderDied(const wp<IBinder>& /*who*/) {
547     ECOLOGV("binderDied");
548 }
549 
dump(int fd,const Vector<String16> &)550 status_t ECOSession::dump(int fd, const Vector<String16>& /*args*/) {
551     std::scoped_lock<std::mutex> lock(mSessionLock);
552     dprintf(fd, "\n== Session Info: ==\n\n");
553     dprintf(fd,
554             "Width: %d Height: %d isCameraRecording: %d, target-bitrate: %d bps codetype: %d "
555             "profile: %d level: %d\n",
556             mWidth, mHeight, mIsCameraRecording, mTargetBitrateBps, mCodecType, mCodecProfile,
557             mCodecLevel);
558     if (mProvider != nullptr) {
559         dprintf(fd, "Provider: %s \n", ::android::String8(mProviderName).string());
560     }
561     if (mListener != nullptr) {
562         dprintf(fd, "Listener: %s \n", ::android::String8(mListenerName).string());
563     }
564     dprintf(fd, "\n===================\n\n");
565 
566     return NO_ERROR;
567 }
568 
logStats(const ECOData & data)569 void ECOSession::logStats(const ECOData& data) {
570     // Check if mLogStats is true;
571     if (!mLogStats || mLogStatsEntries == 0) return;
572 
573     // Check if we need to remove the old entry.
574     if (mStatsDebugBuffer.size() >= mLogStatsEntries) {
575         mStatsDebugBuffer.pop_front();
576     }
577 
578     mStatsDebugBuffer.push_back(data);
579 }
580 
logInfos(const ECOData & data)581 void ECOSession::logInfos(const ECOData& data) {
582     // Check if mLogInfo is true;
583     if (!mLogInfo || mLogInfoEntries == 0) return;
584 
585     // Check if we need to remove the old entry.
586     if (mInfosDebugBuffer.size() >= mLogInfoEntries) {
587         mInfosDebugBuffer.pop_front();
588     }
589 
590     mInfosDebugBuffer.push_back(data);
591 }
592 
593 }  // namespace eco
594 }  // namespace media
595 }  // namespace android
596