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