• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
18 
19 #include "HistogramDevice.h"
20 
21 #include <drm/samsung_drm.h>
22 
23 #include <sstream>
24 #include <string>
25 
26 #include "ExynosDisplayDrmInterface.h"
27 #include "ExynosHWCHelper.h"
28 #include "android-base/macros.h"
29 
30 /**
31  * histogramOnBinderDied
32  *
33  * The binderdied callback function which is registered in registerHistogram and would trigger
34  * unregisterHistogram to cleanup the resources.
35  *
36  * @cookie pointer to the TokenInfo of the binder object.
37  */
histogramOnBinderDied(void * cookie)38 static void histogramOnBinderDied(void* cookie) {
39     HistogramDevice::HistogramErrorCode histogramErrorCode;
40     HistogramDevice::TokenInfo* tokenInfo = (HistogramDevice::TokenInfo*)cookie;
41     ATRACE_NAME(String8::format("%s pid=%d", __func__, tokenInfo->mPid).c_str());
42     ALOGI("%s: process %d with token(%p) is died", __func__, tokenInfo->mPid,
43           tokenInfo->mToken.get());
44 
45     // release the histogram resources
46     tokenInfo->mHistogramDevice->unregisterHistogram(tokenInfo->mToken, &histogramErrorCode);
47     if (histogramErrorCode != HistogramDevice::HistogramErrorCode::NONE) {
48         ALOGW("%s: failed to unregisterHistogram, error(%s)", __func__,
49               aidl::com::google::hardware::pixel::display::toString(histogramErrorCode).c_str());
50     }
51 }
52 
HistogramDevice(ExynosDisplay * const display,const uint8_t channelCount,const std::vector<uint8_t> reservedChannels)53 HistogramDevice::HistogramDevice(ExynosDisplay* const display, const uint8_t channelCount,
54                                  const std::vector<uint8_t> reservedChannels)
55       : mDisplay(display) {
56     // TODO: b/295786065 - Get available channels from crtc property.
57     initChannels(channelCount, reservedChannels);
58 
59     // Create the death recipient which will be deleted in the destructor
60     mDeathRecipient = AIBinder_DeathRecipient_new(histogramOnBinderDied);
61 }
62 
~HistogramDevice()63 HistogramDevice::~HistogramDevice() {
64     if (mDeathRecipient) {
65         AIBinder_DeathRecipient_delete(mDeathRecipient);
66     }
67 }
68 
initDrm(DrmDevice & device,const DrmCrtc & crtc)69 void HistogramDevice::initDrm(DrmDevice& device, const DrmCrtc& crtc) {
70     // TODO: b/295786065 - Get available channels from crtc property.
71     ATRACE_NAME("HistogramDevice::initDrm");
72 
73     {
74         std::unique_lock<std::mutex> lock(mInitDrmDoneMutex);
75         ::android::base::ScopedLockAssertion lock_assertion(mInitDrmDoneMutex);
76         ATRACE_NAME("mInitDrmDoneMutex");
77         if (mInitDrmDone) {
78             HIST_LOG(W, "should be called only once, ignore!");
79             return;
80         }
81 
82         initHistogramCapability(crtc.histogram_channel_property(0).id() != 0);
83         mDrmDevice = &device;
84         mInitDrmDone = true;
85         mInitDrmDone_cv.notify_all();
86     }
87 
88     // print the histogram capability
89     String8 logString;
90     dumpHistogramCapability(logString);
91     ALOGI("%s", logString.c_str());
92     HIST_LOG(D, "successfully");
93 }
94 
waitInitDrmDone() const95 bool HistogramDevice::waitInitDrmDone() const {
96     ATRACE_CALL();
97     std::unique_lock<std::mutex> lock(mInitDrmDoneMutex);
98     ::android::base::ScopedLockAssertion lock_assertion(mInitDrmDoneMutex);
99 
100     mInitDrmDone_cv.wait_for(lock, std::chrono::milliseconds(50), [this]() -> bool {
101         ::android::base::ScopedLockAssertion lock_assertion(mInitDrmDoneMutex);
102         return mInitDrmDone;
103     });
104 
105     return mInitDrmDone;
106 }
107 
getHistogramCapability(HistogramCapability * histogramCapability) const108 ndk::ScopedAStatus HistogramDevice::getHistogramCapability(
109         HistogramCapability* histogramCapability) const {
110     ATRACE_CALL();
111 
112     if (!histogramCapability) {
113         HIST_LOG(E, "binder error, histogramCapability is nullptr");
114         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
115     }
116 
117     if (waitInitDrmDone() == false) {
118         HIST_LOG(E, "initDrm is not completed yet");
119         return errorToStatus(HistogramErrorCode::TRY_AGAIN);
120     }
121 
122     std::shared_lock lock(mHistogramCapabilityMutex);
123     *histogramCapability = mHistogramCapability;
124 
125     return ndk::ScopedAStatus::ok();
126 }
127 
128 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
registerHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode)129 ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder& token,
130                                                       const HistogramConfig& histogramConfig,
131                                                       HistogramErrorCode* histogramErrorCode) {
132     ATRACE_CALL();
133 
134     if (waitInitDrmDone() == false) {
135         HIST_LOG(E, "initDrm is not completed yet");
136         // TODO: b/323158344 - add retry error in HistogramErrorCode and return here.
137         return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
138     }
139 
140     {
141         std::shared_lock lock(mHistogramCapabilityMutex);
142         if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
143             HIST_LOG(E, "multi-channel interface is not supported");
144             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
145         }
146     }
147 
148     ndk::ScopedAStatus binderStatus =
149             validateHistogramRequest(token, histogramConfig, histogramErrorCode);
150     if (!binderStatus.isOk() || *histogramErrorCode != HistogramErrorCode::NONE) {
151         HIST_LOG(E, "validateHistogramRequest failed");
152         return binderStatus;
153     }
154 
155     // Create the histogram config blob if possible, early creation can reduce critical section
156     int ret;
157     const auto [displayActiveH, displayActiveV] = snapDisplayActiveSize();
158     std::shared_ptr<PropertyBlob> drmConfigBlob;
159     if ((ret = createDrmConfigBlob(histogramConfig, displayActiveH, displayActiveV, drmConfigBlob)))
160         HIST_LOG(D, "createDrmConfigBlob failed, skip creation, ret(%d)", ret);
161 
162     bool needRefresh = false;
163 
164     {
165         // Insert new client's token into mTokenInfoMap
166         SCOPED_HIST_LOCK(mHistogramMutex);
167         auto [it, emplaceResult] =
168                 mTokenInfoMap.try_emplace(token.get(), this, token, AIBinder_getCallingPid());
169         if (!emplaceResult) {
170             HIST_LOG(E, "BAD_TOKEN, token(%p) is already registered", token.get());
171             *histogramErrorCode = HistogramErrorCode::BAD_TOKEN;
172             return ndk::ScopedAStatus::ok();
173         }
174         auto tokenInfo = &it->second;
175 
176         /* In previous design, histogram client is attached to the histogram channel directly. Now
177          * we use struct ConfigInfo to maintain the config metadata. We can benefit from this
178          * design:
179          * 1. More elegantly to change the applied config of the histogram channels (basics of
180          *    virtualization).
181          * 2. We may be able to share the same struct ConfigInfo for different histogram clients
182          *    when the histogramConfigs via registerHistogram are the same. */
183         auto& configInfo = tokenInfo->mConfigInfo;
184         replaceConfigInfo(configInfo, &histogramConfig);
185 
186         // Attach the histogram drmConfigBlob to the configInfo
187         if (drmConfigBlob)
188             configInfo->mBlobsList.emplace_front(displayActiveH, displayActiveV, drmConfigBlob);
189 
190         needRefresh = scheduler();
191 
192         /* link the binder object (token) to the death recipient. When the binder object is
193          * destructed, the callback function histogramOnBinderDied can release the histogram
194          * resources automatically. */
195         binder_status_t status;
196         if ((status = AIBinder_linkToDeath(token.get(), mDeathRecipient, tokenInfo))) {
197             /* Not return error due to the AIBinder_linkToDeath because histogram function can
198              * still work */
199             HIST_CH_LOG(E, configInfo->mChannelId, "token(%p): AIBinder_linkToDeath error, ret(%d)",
200                         token.get(), status);
201         }
202     }
203 
204     if (needRefresh) {
205         ATRACE_NAME("HistogramOnRefresh");
206         mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
207     }
208 
209     HIST_LOG(D, "register client successfully");
210 
211     return ndk::ScopedAStatus::ok();
212 }
213 #else
registerHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode)214 ndk::ScopedAStatus HistogramDevice::registerHistogram(const ndk::SpAIBinder& token,
215                                                       const HistogramConfig& histogramConfig,
216                                                       HistogramErrorCode* histogramErrorCode) {
217     ATRACE_CALL();
218     HIST_LOG(E, "multi-channel interface is not supported");
219     return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
220 }
221 #endif
222 
queryHistogram(const ndk::SpAIBinder & token,std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode)223 ndk::ScopedAStatus HistogramDevice::queryHistogram(const ndk::SpAIBinder& token,
224                                                    std::vector<char16_t>* histogramBuffer,
225                                                    HistogramErrorCode* histogramErrorCode) {
226     ATRACE_CALL();
227 
228     {
229         std::shared_lock lock(mHistogramCapabilityMutex);
230         if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
231             HIST_LOG(E, "multi-channel interface is not supported");
232             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
233         }
234     }
235 
236     // validate the argument (histogramBuffer)
237     if (!histogramBuffer) {
238         HIST_LOG(E, "binder error, histogramBuffer is nullptr");
239         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
240     }
241 
242     // validate the argument (histogramErrorCode)
243     if (!histogramErrorCode) {
244         HIST_LOG(E, "binder error, histogramErrorCode is nullptr");
245         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
246     }
247 
248     getHistogramData(token, histogramBuffer, histogramErrorCode);
249 
250     return ndk::ScopedAStatus::ok();
251 }
252 
reconfigHistogram(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode)253 ndk::ScopedAStatus HistogramDevice::reconfigHistogram(const ndk::SpAIBinder& token,
254                                                       const HistogramConfig& histogramConfig,
255                                                       HistogramErrorCode* histogramErrorCode) {
256     ATRACE_CALL();
257 
258     {
259         std::shared_lock lock(mHistogramCapabilityMutex);
260         if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
261             HIST_LOG(E, "multi-channel interface is not supported");
262             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
263         }
264     }
265 
266     ndk::ScopedAStatus binderStatus =
267             validateHistogramRequest(token, histogramConfig, histogramErrorCode);
268     if (!binderStatus.isOk() || *histogramErrorCode != HistogramErrorCode::NONE) {
269         HIST_LOG(E, "validateHistogramRequest failed");
270         return binderStatus;
271     }
272 
273     // Create the histogram config blob if possible, early creation can reduce critical section
274     int ret;
275     const auto [displayActiveH, displayActiveV] = snapDisplayActiveSize();
276     std::shared_ptr<PropertyBlob> drmConfigBlob;
277     if ((ret = createDrmConfigBlob(histogramConfig, displayActiveH, displayActiveV, drmConfigBlob)))
278         HIST_LOG(D, "createDrmConfigBlob failed, skip creation, ret(%d)", ret);
279 
280     bool needRefresh = false;
281 
282     {
283         // Search the registered tokenInfo
284         TokenInfo* tokenInfo = nullptr;
285         SCOPED_HIST_LOCK(mHistogramMutex);
286         if ((*histogramErrorCode = searchTokenInfo(token, tokenInfo)) != HistogramErrorCode::NONE) {
287             HIST_LOG(E, "searchTokenInfo failed, error(%s)",
288                      aidl::com::google::hardware::pixel::display::toString(*histogramErrorCode)
289                              .c_str());
290             return ndk::ScopedAStatus::ok();
291         }
292 
293         // Change the histogram configInfo
294         auto& configInfo = tokenInfo->mConfigInfo;
295         replaceConfigInfo(configInfo, &histogramConfig);
296 
297         // Attach the histogram drmConfigBlob to the configInfo
298         if (drmConfigBlob)
299             configInfo->mBlobsList.emplace_front(displayActiveH, displayActiveV, drmConfigBlob);
300 
301         if (configInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED) needRefresh = true;
302     }
303 
304     if (needRefresh) {
305         ATRACE_NAME("HistogramOnRefresh");
306         mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
307     }
308 
309     return ndk::ScopedAStatus::ok();
310 }
311 
unregisterHistogram(const ndk::SpAIBinder & token,HistogramErrorCode * histogramErrorCode)312 ndk::ScopedAStatus HistogramDevice::unregisterHistogram(const ndk::SpAIBinder& token,
313                                                         HistogramErrorCode* histogramErrorCode) {
314     ATRACE_CALL();
315 
316     {
317         std::shared_lock lock(mHistogramCapabilityMutex);
318         if (UNLIKELY(!mHistogramCapability.supportMultiChannel)) {
319             HIST_LOG(E, "multi-channel interface is not supported");
320             return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
321         }
322     }
323 
324     // validate the argument (histogramErrorCode)
325     if (!histogramErrorCode) {
326         HIST_LOG(E, "binder error, histogramErrorCode is nullptr");
327         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
328     }
329 
330     // default histogramErrorCode: no error
331     *histogramErrorCode = HistogramErrorCode::NONE;
332 
333     bool needRefresh = false;
334 
335     {
336         // Search the registered tokenInfo
337         TokenInfo* tokenInfo = nullptr;
338         SCOPED_HIST_LOCK(mHistogramMutex);
339         if ((*histogramErrorCode = searchTokenInfo(token, tokenInfo)) != HistogramErrorCode::NONE) {
340             HIST_LOG(E, "searchTokenInfo failed, error(%s)",
341                      aidl::com::google::hardware::pixel::display::toString(*histogramErrorCode)
342                              .c_str());
343             return ndk::ScopedAStatus::ok();
344         }
345 
346         // Clear the histogram configInfo
347         replaceConfigInfo(tokenInfo->mConfigInfo, nullptr);
348 
349         /*
350          * If AIBinder is alive, the unregisterHistogram is triggered from the histogram client, and
351          * we need to unlink the binder object from death notification. If AIBinder is already dead,
352          * the unregisterHistogram is triggered from binderdied callback, no need to unlink here.
353          */
354         if (LIKELY(AIBinder_isAlive(token.get()))) {
355             binder_status_t status;
356             if ((status = AIBinder_unlinkToDeath(token.get(), mDeathRecipient, tokenInfo))) {
357                 // Not return error due to the AIBinder_unlinkToDeath
358                 HIST_LOG(E, "AIBinder_unlinkToDeath error for token(%p), ret(%d)", token.get(),
359                          status);
360             }
361         }
362 
363         // Delete the corresponding TokenInfo after the binder object is already unlinked.
364         mTokenInfoMap.erase(token.get());
365         tokenInfo = nullptr;
366 
367         needRefresh = scheduler();
368     }
369 
370     if (needRefresh) {
371         ATRACE_NAME("HistogramOnRefresh");
372         mDisplay->mDevice->onRefresh(mDisplay->mDisplayId);
373     }
374 
375     HIST_LOG(D, "unregister client successfully");
376 
377     return ndk::ScopedAStatus::ok();
378 }
379 
_handleDrmEvent(void * event,uint32_t blobId,char16_t * buffer)380 void HistogramDevice::_handleDrmEvent(void* event, uint32_t blobId, char16_t* buffer) {
381     ATRACE_NAME(String8::format("handleHistogramEvent(blob#%u)", blobId).c_str());
382 
383     std::shared_ptr<BlobIdData> blobIdData;
384     searchOrCreateBlobIdData(blobId, false, blobIdData);
385     if (!blobIdData) {
386         HIST_BLOB_LOG(W, blobId, "no condition var allocated, ignore the event(%p)", event);
387         return;
388     }
389 
390     std::unique_lock<std::mutex> lock(blobIdData->mDataCollectingMutex);
391     ::android::base::ScopedLockAssertion lock_assertion(blobIdData->mDataCollectingMutex);
392     ATRACE_NAME(String8::format("mDataCollectingMutex(blob#%u)", blobId));
393     // Check if the histogram blob is collecting the histogram data
394     if (UNLIKELY(blobIdData->mCollectStatus == CollectStatus_t::NOT_STARTED)) {
395         HIST_BLOB_LOG(W, blobId, "ignore the event(%p), collectStatus is NOT_STARTED", event);
396     } else {
397         std::memcpy(blobIdData->mData, buffer, HISTOGRAM_BIN_COUNT * sizeof(char16_t));
398         blobIdData->mCollectStatus = CollectStatus_t::COLLECTED;
399         blobIdData->mDataCollecting_cv.notify_all();
400     }
401 }
402 
handleDrmEvent(void * event)403 void HistogramDevice::handleDrmEvent(void* event) {
404     int ret = NO_ERROR;
405     uint32_t blobId = 0, channelId = 0;
406     char16_t* buffer;
407 
408     if ((ret = parseDrmEvent(event, channelId, buffer))) {
409         HIST_LOG(E, "parseDrmEvent failed, ret(%d)", ret);
410         return;
411     }
412 
413     // For the old kernel without blob id query supports, fake the blobId with channelId.
414     // In this hack way can prevent some duplicate codes just for channel id as well.
415     // In the future, all kernel will support blob id query. And can remove the hack.
416     blobId = channelId;
417     _handleDrmEvent(event, blobId, buffer);
418 }
419 
handleContextDrmEvent(void * event)420 void HistogramDevice::handleContextDrmEvent(void* event) {
421     int ret = NO_ERROR;
422     uint32_t blobId = 0;
423     char16_t* buffer;
424 
425     if ((ret = parseContextDrmEvent(event, blobId, buffer))) {
426         HIST_LOG(E, "parseContextDrmEvent failed, ret(%d)", ret);
427         return;
428     }
429 
430     _handleDrmEvent(event, blobId, buffer);
431 }
432 
prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq)433 void HistogramDevice::prepareAtomicCommit(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq) {
434     {
435         std::shared_lock lock(mHistogramCapabilityMutex);
436         // Skip multi channel histogram commit if not supported.
437         if (!mHistogramCapability.supportMultiChannel) return;
438     }
439 
440     ATRACE_NAME("HistogramAtomicCommit");
441 
442     ExynosDisplayDrmInterface* moduleDisplayInterface =
443             static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
444     if (!moduleDisplayInterface) {
445         HIST_LOG(E, "failed to send atomic commit, moduleDisplayInterface is NULL");
446         return;
447     }
448 
449     const auto [displayActiveH, displayActiveV] = snapDisplayActiveSize();
450     SCOPED_HIST_LOCK(mHistogramMutex);
451 
452     // Loop through every used channel and set config blob if needed.
453     for (auto it = mUsedChannels.begin(); it != mUsedChannels.end();) {
454         uint8_t channelId = *it;
455         ChannelInfo& channel = mChannels[channelId];
456 
457         if (channel.mStatus == ChannelStatus_t::CONFIG_COMMITTED ||
458             channel.mStatus == ChannelStatus_t::CONFIG_PENDING) {
459             std::shared_ptr<ConfigInfo> configInfo = channel.mConfigInfo.lock();
460             if (!configInfo) {
461                 HIST_CH_LOG(E, channelId, "expired configInfo, review code!");
462                 it = cleanupChannelInfo(channelId);
463                 continue;
464             }
465             setChannelConfigBlob(drmReq, channelId, moduleDisplayInterface, displayActiveH,
466                                  displayActiveV, configInfo);
467         }
468 
469         ++it;
470     }
471 
472     // Loop through every free channels and disable channel if needed
473     for (auto it = mFreeChannels.begin(); it != mFreeChannels.end(); ++it) {
474         uint8_t channelId = *it;
475         ChannelInfo& channel = mChannels[channelId];
476         if (channel.mStatus == ChannelStatus_t::DISABLE_PENDING)
477             clearChannelConfigBlob(drmReq, channelId, moduleDisplayInterface);
478     }
479 }
480 
postAtomicCommit()481 void HistogramDevice::postAtomicCommit() {
482     {
483         std::shared_lock lock(mHistogramCapabilityMutex);
484         // Skip multi channel histogram commit if not supported.
485         if (!mHistogramCapability.supportMultiChannel) return;
486     }
487 
488     ATRACE_CALL();
489 
490     {
491         SCOPED_HIST_LOCK(mHistogramMutex);
492 
493         // Atomic commit is success, loop through every channel and update the channel status
494         for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) {
495             ChannelInfo& channel = mChannels[channelId];
496 
497             switch (channel.mStatus) {
498                 case ChannelStatus_t::CONFIG_BLOB_ADDED:
499                     channel.mStatus = ChannelStatus_t::CONFIG_COMMITTED;
500                     break;
501                 case ChannelStatus_t::DISABLE_BLOB_ADDED:
502                     channel.mStatus = ChannelStatus_t::DISABLED;
503                     break;
504                 default:
505                     break;
506             }
507         }
508     }
509 
510     postAtomicCommitCleanup();
511 }
512 
dump(String8 & result) const513 void HistogramDevice::dump(String8& result) const {
514     {
515         std::shared_lock lock(mHistogramCapabilityMutex);
516         // Do not dump the Histogram Device if it is not supported.
517         if (!mHistogramCapability.supportMultiChannel) {
518             return;
519         }
520     }
521 
522     ATRACE_NAME("HistogramDump");
523 
524     // print the histogram capability
525     dumpHistogramCapability(result);
526     result.append("\n");
527 
528     SCOPED_HIST_LOCK(mHistogramMutex);
529 
530     // print the tokens and the requested configs
531     for (const auto& [_, tokenInfo] : mTokenInfoMap) {
532         tokenInfo.dump(result);
533         if (tokenInfo.mConfigInfo) {
534             tokenInfo.mConfigInfo->dump(result, "\t");
535         }
536     }
537     dumpInternalConfigs(result);
538     result.append("\n");
539 
540     // print the histogram channel info
541     result.append("Histogram channel info (applied to kernel):\n");
542     for (uint8_t channelId = 0; channelId < mChannels.size(); ++channelId) {
543         // TODO: b/294489887 - Use buildForMiniDump can eliminate the redundant rows.
544         TableBuilder tb;
545         dumpChannel(tb, channelId);
546         result.append(tb.build().c_str());
547     }
548     result.append("\n");
549 
550     // print the inactive list
551     result.append("Histogram inactive list:");
552     if (mInactiveConfigItList.empty()) {
553         result.append(" none\n");
554     } else {
555         result.append("\n");
556         int i = 1;
557         for (const auto& configInfo : mInactiveConfigItList)
558             result.appendFormat("\t%d. configInfo: %p\n", i++, configInfo.lock().get());
559     }
560     result.append("\n");
561     result.append("-----End of Histogram dump-----\n");
562 }
563 
initChannels(const uint8_t channelCount,const std::vector<uint8_t> & reservedChannels)564 void HistogramDevice::initChannels(const uint8_t channelCount,
565                                    const std::vector<uint8_t>& reservedChannels) {
566     ATRACE_CALL();
567     HIST_LOG(I, "init with %u channels", channelCount);
568 
569     SCOPED_HIST_LOCK(mHistogramMutex);
570     mChannels.resize(channelCount);
571 
572     for (const uint8_t reservedChannelId : reservedChannels) {
573         if (reservedChannelId < mChannels.size()) {
574             mChannels[reservedChannelId].mStatus = ChannelStatus_t::RESERVED;
575         } else {
576             HIST_CH_LOG(W, reservedChannelId,
577                         "invalid channel cannot be reserved (channelCount: %u)", channelCount);
578         }
579     }
580 
581     for (uint8_t channelId = 0; channelId < channelCount; ++channelId) {
582         if (mChannels[channelId].mStatus == ChannelStatus_t::RESERVED) {
583             HIST_CH_LOG(D, channelId, "channel reserved for driver");
584             continue;
585         }
586 
587         mFreeChannels.push_back(channelId);
588     }
589 }
590 
initHistogramCapability(const bool supportMultiChannel)591 void HistogramDevice::initHistogramCapability(const bool supportMultiChannel) {
592     ATRACE_CALL();
593     uint8_t channelCount = 0;
594     {
595         SCOPED_HIST_LOCK(mHistogramMutex);
596         channelCount = mChannels.size();
597     }
598 
599     ExynosDisplayDrmInterface* moduleDisplayInterface =
600             static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
601 
602     SCOPED_HIST_LOCK(mHistogramCapabilityMutex);
603     if (!moduleDisplayInterface) {
604         HIST_LOG(E, "failed to get panel full resolution, moduleDisplayInterface is NULL");
605         mHistogramCapability.fullResolutionWidth = 0;
606         mHistogramCapability.fullResolutionHeight = 0;
607     } else {
608         mHistogramCapability.fullResolutionWidth =
609                 moduleDisplayInterface->getPanelFullResolutionHSize();
610         mHistogramCapability.fullResolutionHeight =
611                 moduleDisplayInterface->getPanelFullResolutionVSize();
612     }
613     mHistogramCapability.channelCount = channelCount;
614     mHistogramCapability.supportMultiChannel = supportMultiChannel;
615     mHistogramCapability.supportSamplePosList.push_back(HistogramSamplePos::POST_POSTPROC);
616     mHistogramCapability.supportBlockingRoi = false;
617     mHistogramCapability.supportQueryOpr = false;
618     initPlatformHistogramCapability();
619 }
620 
replaceConfigInfo(std::shared_ptr<ConfigInfo> & configInfo,const HistogramConfig * histogramConfig)621 void HistogramDevice::replaceConfigInfo(std::shared_ptr<ConfigInfo>& configInfo,
622                                         const HistogramConfig* histogramConfig) {
623     ATRACE_CALL();
624 
625     // Capture the old ConfigInfo reference
626     std::shared_ptr<ConfigInfo> oldConfigInfo = configInfo;
627 
628     // Populate the new ConfigInfo object based on the histogramConfig pointer
629     configInfo = (histogramConfig) ? std::make_shared<ConfigInfo>(*histogramConfig) : nullptr;
630 
631     if (!oldConfigInfo && !configInfo) {
632         return;
633     } else if (!oldConfigInfo && configInfo) { // Case #1: registerHistogram
634         addConfigToInactiveList(configInfo);
635     } else if (oldConfigInfo && configInfo) { // Case #2: reconfigHistogram
636         if (oldConfigInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED) {
637             configInfo->mStatus = ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED;
638             configInfo->mChannelId = oldConfigInfo->mChannelId;
639             mChannels[configInfo->mChannelId].mStatus = ChannelStatus_t::CONFIG_PENDING;
640             mChannels[configInfo->mChannelId].mConfigInfo = configInfo;
641         } else if (oldConfigInfo->mStatus == ConfigInfo::Status_t::IN_INACTIVE_LIST) {
642             configInfo->mStatus = ConfigInfo::Status_t::IN_INACTIVE_LIST;
643             configInfo->mInactiveListIt = oldConfigInfo->mInactiveListIt;
644             *(configInfo->mInactiveListIt) = configInfo;
645         } else {
646             addConfigToInactiveList(configInfo);
647         }
648     } else if (oldConfigInfo && !configInfo) { // Case #3: unregisterHistogram
649         if (oldConfigInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED)
650             cleanupChannelInfo(oldConfigInfo->mChannelId);
651         else if (oldConfigInfo->mStatus == ConfigInfo::Status_t::IN_INACTIVE_LIST)
652             mInactiveConfigItList.erase(oldConfigInfo->mInactiveListIt);
653 
654         oldConfigInfo->mStatus = ConfigInfo::Status_t::INITIALIZED;
655     }
656 
657     // Cleanup the blobIdData
658     if (oldConfigInfo) {
659         SCOPED_HIST_LOCK(mBlobIdDataMutex);
660         for (const auto& blobInfo : oldConfigInfo->mBlobsList)
661             mBlobIdDataMap.erase(blobInfo.mBlob->getId());
662     }
663 }
664 
searchTokenInfo(const ndk::SpAIBinder & token,TokenInfo * & tokenInfo)665 HistogramDevice::HistogramErrorCode HistogramDevice::searchTokenInfo(const ndk::SpAIBinder& token,
666                                                                      TokenInfo*& tokenInfo) {
667     auto it = mTokenInfoMap.find(token.get());
668 
669     if (it == mTokenInfoMap.end()) {
670         HIST_LOG(E, "BAD_TOKEN, token(%p) is not registered", token.get());
671         tokenInfo = nullptr;
672         return HistogramErrorCode::BAD_TOKEN;
673     }
674 
675     tokenInfo = &it->second;
676     return HistogramErrorCode::NONE;
677 }
678 
swapInConfigInfo(std::shared_ptr<ConfigInfo> & configInfo)679 std::list<std::weak_ptr<HistogramDevice::ConfigInfo>>::iterator HistogramDevice::swapInConfigInfo(
680         std::shared_ptr<ConfigInfo>& configInfo) {
681     // Acquire a free histogram channel, pdate used and free channels
682     const uint8_t channelId = mFreeChannels.front();
683     mFreeChannels.pop_front();
684     mUsedChannels.insert(channelId);
685 
686     // update the ChannelInfo
687     ChannelInfo& channel = mChannels[channelId];
688     channel.mStatus = ChannelStatus_t::CONFIG_PENDING;
689     channel.mConfigInfo = configInfo;
690 
691     // update the configInfo and the inactive list
692     configInfo->mStatus = ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED;
693     configInfo->mChannelId = channelId;
694     auto it = mInactiveConfigItList.erase(configInfo->mInactiveListIt);
695     configInfo->mInactiveListIt = mInactiveConfigItList.end();
696 
697     return it;
698 }
699 
swapOutConfigInfo(uint8_t channelId)700 void HistogramDevice::swapOutConfigInfo(uint8_t channelId) {
701     // Update used and free channels
702     mFreeChannels.push_back(channelId);
703     mUsedChannels.erase(channelId);
704 
705     // update the ChannelInfo
706     ChannelInfo& channel = mChannels[channelId];
707     std::shared_ptr<ConfigInfo> configInfo = channel.mConfigInfo.lock();
708     channel.mStatus = ChannelStatus_t::DISABLE_PENDING;
709     channel.mConfigInfo.reset();
710 
711     // update the configInfo and the inactive list
712     if (configInfo) {
713         uint32_t blobId = getActiveBlobId(configInfo->mBlobsList);
714         HIST_BLOB_CH_LOG(I, blobId, channelId, "configInfo(%p) is swapped out", configInfo.get());
715         addConfigToInactiveList(configInfo);
716     } else
717         HIST_CH_LOG(E, channelId, "expired configInfo, review code!");
718 }
719 
addConfigToInactiveList(const std::shared_ptr<ConfigInfo> & configInfo,bool addToFront)720 void HistogramDevice::addConfigToInactiveList(const std::shared_ptr<ConfigInfo>& configInfo,
721                                               bool addToFront) {
722     configInfo->mChannelId = -1;
723     configInfo->mStatus = ConfigInfo::Status_t::IN_INACTIVE_LIST;
724     if (addToFront) {
725         configInfo->mInactiveListIt =
726                 mInactiveConfigItList.emplace(mInactiveConfigItList.begin(), configInfo);
727     } else {
728         configInfo->mInactiveListIt =
729                 mInactiveConfigItList.emplace(mInactiveConfigItList.end(), configInfo);
730     }
731 }
732 
scheduler()733 bool HistogramDevice::scheduler() {
734     ATRACE_CALL();
735 
736     bool needRefresh = false;
737 
738     for (auto it = mInactiveConfigItList.begin(); it != mInactiveConfigItList.end();) {
739         if (mFreeChannels.empty()) break;
740 
741         auto configInfo = it->lock();
742         if (!configInfo) {
743             HIST_LOG(W, "find expired configInfo ptr in mInactiveConfigItList, review code!");
744             it = mInactiveConfigItList.erase(it);
745             continue;
746         }
747 
748         // Requires an onRefresh call to apply the config change of the channel
749         needRefresh = true;
750 
751         // Swap in the config
752         it = swapInConfigInfo(configInfo);
753     }
754 
755     return needRefresh;
756 }
757 
searchOrCreateBlobIdData(uint32_t blobId,bool create,std::shared_ptr<BlobIdData> & blobIdData)758 void HistogramDevice::searchOrCreateBlobIdData(uint32_t blobId, bool create,
759                                                std::shared_ptr<BlobIdData>& blobIdData) {
760     ATRACE_CALL();
761     blobIdData = nullptr;
762     SCOPED_HIST_LOCK(mBlobIdDataMutex);
763 
764     auto it = mBlobIdDataMap.find(blobId);
765     if (it != mBlobIdDataMap.end()) {
766         blobIdData = it->second;
767     } else if (create) {
768         std::shared_ptr<BlobIdData> blobIdDataTmp = std::make_shared<BlobIdData>();
769         mBlobIdDataMap.emplace(blobId, blobIdDataTmp);
770         blobIdData = blobIdDataTmp;
771     }
772 }
773 
getChanIdBlobId(const ndk::SpAIBinder & token,HistogramErrorCode * histogramErrorCode,int & channelId,uint32_t & blobId)774 void HistogramDevice::getChanIdBlobId(const ndk::SpAIBinder& token,
775                                       HistogramErrorCode* histogramErrorCode, int& channelId,
776                                       uint32_t& blobId) {
777     ATRACE_CALL();
778     TokenInfo* tokenInfo = nullptr;
779     channelId = -1;
780     blobId = 0;
781 
782     SCOPED_HIST_LOCK(mHistogramMutex);
783     if ((*histogramErrorCode = searchTokenInfo(token, tokenInfo)) != HistogramErrorCode::NONE) {
784         HIST_LOG(E, "searchTokenInfo failed, ret(%s)",
785                  aidl::com::google::hardware::pixel::display::toString(*histogramErrorCode)
786                          .c_str());
787         return;
788     }
789 
790     std::shared_ptr<ConfigInfo>& configInfo = tokenInfo->mConfigInfo;
791     if (configInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED)
792         channelId = configInfo->mChannelId;
793     else
794         channelId = -1;
795 #if defined(EXYNOS_CONTEXT_HISTOGRAM_EVENT_REQUEST)
796     blobId = getActiveBlobId(configInfo->mBlobsList);
797 
798     if (!blobId) {
799         HIST_BLOB_CH_LOG(E, blobId, channelId, "CONFIG_HIST_ERROR, blob is not created yet");
800         *histogramErrorCode = HistogramErrorCode::CONFIG_HIST_ERROR;
801         return;
802     }
803 #else
804     // For the old kernel without blob id query supports, fake the blobId with channelId.
805     // In this hack way can prevent some duplicate codes just for channel id as well.
806     // In the future, all kernel will support blob id query. And can remove the hack.
807     blobId = channelId;
808     if (channelId < 0) {
809         HIST_BLOB_CH_LOG(E, blobId, channelId, "CONFIG_HIST_ERROR, no channel executes config");
810         *histogramErrorCode = HistogramErrorCode::CONFIG_HIST_ERROR;
811         return;
812     }
813 #endif
814 }
815 
getHistogramData(const ndk::SpAIBinder & token,std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode)816 void HistogramDevice::getHistogramData(const ndk::SpAIBinder& token,
817                                        std::vector<char16_t>* histogramBuffer,
818                                        HistogramErrorCode* histogramErrorCode) {
819     ATRACE_CALL();
820 
821     // default histogramErrorCode: no error
822     *histogramErrorCode = HistogramErrorCode::NONE;
823 
824     // Get the current channelId and active blobId
825     int channelId;
826     uint32_t blobId;
827     getChanIdBlobId(token, histogramErrorCode, channelId, blobId);
828     if (*histogramErrorCode != HistogramErrorCode::NONE) return;
829 
830     std::cv_status cv_status;
831 
832     {
833         // Get the moduleDisplayInterface pointer
834         ExynosDisplayDrmInterface* moduleDisplayInterface =
835                 static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
836         if (!moduleDisplayInterface) {
837             *histogramErrorCode = HistogramErrorCode::ENABLE_HIST_ERROR;
838             HIST_BLOB_CH_LOG(E, blobId, channelId,
839                              "ENABLE_HIST_ERROR, moduleDisplayInterface is NULL");
840             return;
841         }
842 
843         // Use shared_ptr to keep the blobIdData which will be used by receiveBlobIdData and
844         // receiveBlobIdData.
845         std::shared_ptr<BlobIdData> blobIdData;
846         searchOrCreateBlobIdData(blobId, true, blobIdData);
847 
848         std::unique_lock<std::mutex> lock(blobIdData->mDataCollectingMutex);
849         ::android::base::ScopedLockAssertion lock_assertion(blobIdData->mDataCollectingMutex);
850         ATRACE_NAME(String8::format("mDataCollectingMutex(blob#%u)", blobId));
851 
852         // Request the drmEvent of the blobId (with mDataCollectingMutex held)
853         requestBlobIdData(moduleDisplayInterface, histogramErrorCode, channelId, blobId,
854                           blobIdData);
855         if (*histogramErrorCode != HistogramErrorCode::NONE) return;
856 
857         // Receive the drmEvent of the blobId (with mDataCollectingMutex held)
858         cv_status = receiveBlobIdData(moduleDisplayInterface, histogramBuffer, histogramErrorCode,
859                                       channelId, blobId, blobIdData, lock);
860     }
861 
862     // Check the query result and clear the buffer if needed (no lock is held now)
863     checkQueryResult(histogramBuffer, histogramErrorCode, channelId, blobId, cv_status);
864 }
865 
requestBlobIdData(ExynosDisplayDrmInterface * const moduleDisplayInterface,HistogramErrorCode * histogramErrorCode,const int channelId,const uint32_t blobId,const std::shared_ptr<BlobIdData> & blobIdData)866 void HistogramDevice::requestBlobIdData(ExynosDisplayDrmInterface* const moduleDisplayInterface,
867                                         HistogramErrorCode* histogramErrorCode, const int channelId,
868                                         const uint32_t blobId,
869                                         const std::shared_ptr<BlobIdData>& blobIdData) {
870     ATRACE_CALL();
871     int ret;
872 
873 #if defined(EXYNOS_CONTEXT_HISTOGRAM_EVENT_REQUEST)
874     /* Send the ioctl request (histogram_event_request_ioctl) which increases the ref_cnt of the
875      * blobId request. The drm event is sent back with data when available. Must call
876      * sendContextHistogramIoctl(CANCEL) to decrease the ref_cnt after the request. */
877     if ((ret = moduleDisplayInterface->sendContextHistogramIoctl(ContextHistogramIoctl_t::REQUEST,
878                                                                  blobId)) != NO_ERROR) {
879         *histogramErrorCode = HistogramErrorCode::ENABLE_HIST_ERROR;
880         HIST_BLOB_CH_LOG(E, blobId, channelId,
881                          "ENABLE_HIST_ERROR, sendContextHistogramIoctl(REQUEST) failed, ret(%d)",
882                          ret);
883         return;
884     }
885 #else
886     /* Send the ioctl request (histogram_channel_request_ioctl) which increases the ref_cnt of the
887      * blobId request. The drm event is sent back with data when available. Must call
888      * sendHistogramChannelIoctl(CANCEL) to decrease the ref_cnt after the request. */
889     if ((ret = moduleDisplayInterface->sendHistogramChannelIoctl(HistogramChannelIoctl_t::REQUEST,
890                                                                  blobId)) != NO_ERROR) {
891         *histogramErrorCode = HistogramErrorCode::ENABLE_HIST_ERROR;
892         HIST_BLOB_CH_LOG(E, blobId, channelId,
893                          "ENABLE_HIST_ERROR, sendHistogramChannelIoctl(REQUEST) failed, ret(%d)",
894                          ret);
895         return;
896     }
897 #endif
898     blobIdData->mCollectStatus = CollectStatus_t::COLLECTING;
899 }
900 
receiveBlobIdData(ExynosDisplayDrmInterface * const moduleDisplayInterface,std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode,const int channelId,const uint32_t blobId,const std::shared_ptr<BlobIdData> & blobIdData,std::unique_lock<std::mutex> & lock)901 std::cv_status HistogramDevice::receiveBlobIdData(
902         ExynosDisplayDrmInterface* const moduleDisplayInterface,
903         std::vector<char16_t>* histogramBuffer, HistogramErrorCode* histogramErrorCode,
904         const int channelId, const uint32_t blobId, const std::shared_ptr<BlobIdData>& blobIdData,
905         std::unique_lock<std::mutex>& lock) {
906     ATRACE_CALL();
907 
908     // Wait until the condition variable is notified or timeout.
909     std::cv_status cv_status = std::cv_status::no_timeout;
910     if (blobIdData->mCollectStatus != CollectStatus_t::COLLECTED) {
911         ATRACE_NAME(String8::format("waitDrmEvent(noMutex,blob#%u)", blobId).c_str());
912         cv_status = blobIdData->mDataCollecting_cv.wait_for(lock, std::chrono::milliseconds(50));
913     }
914 
915     // Wait for the drm event is finished, decrease ref_cnt.
916     int ret;
917 #if defined(EXYNOS_CONTEXT_HISTOGRAM_EVENT_REQUEST)
918     if ((ret = moduleDisplayInterface->sendContextHistogramIoctl(ContextHistogramIoctl_t::CANCEL,
919                                                                  blobId)) != NO_ERROR) {
920         HIST_BLOB_CH_LOG(W, blobId, channelId, "sendContextHistogramIoctl(CANCEL) failed, ret(%d)",
921                          ret);
922     }
923 #else
924     if ((ret = moduleDisplayInterface->sendHistogramChannelIoctl(HistogramChannelIoctl_t::CANCEL,
925                                                                  blobId)) != NO_ERROR) {
926         HIST_BLOB_CH_LOG(W, blobId, channelId, "sendHistogramChannelIoctl(CANCEL) failed, ret(%d)",
927                          ret);
928     }
929 #endif
930 
931     /*
932      * Case #1: timeout occurs, status is not COLLECTED
933      * Case #2: no timeout, status is not COLLECTED
934      * Case #3: timeout occurs, status is COLLECTED
935      * Case #4: no timeout, status is COLLECTED
936      */
937     if (blobIdData->mCollectStatus == CollectStatus_t::COLLECTED) {
938         cv_status = std::cv_status::no_timeout; // ignore the timeout in Case #3
939         // Copy the histogram data from histogram info to histogramBuffer
940         histogramBuffer->assign(blobIdData->mData, blobIdData->mData + HISTOGRAM_BIN_COUNT);
941     } else {
942         // Case #1 and Case #2 will be checked by checkQueryResult
943         *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
944         blobIdData->mCollectStatus = CollectStatus_t::NOT_STARTED;
945     }
946 
947     return cv_status;
948 }
949 
checkQueryResult(std::vector<char16_t> * histogramBuffer,HistogramErrorCode * histogramErrorCode,const int channelId,const uint32_t blobId,const std::cv_status cv_status) const950 void HistogramDevice::checkQueryResult(std::vector<char16_t>* histogramBuffer,
951                                        HistogramErrorCode* histogramErrorCode, const int channelId,
952                                        const uint32_t blobId,
953                                        const std::cv_status cv_status) const {
954     ATRACE_CALL();
955 
956     /*
957      * It may take for a while in isSecureContentPresenting() and isPowerModeOff().
958      * We should not hold any lock from the caller.
959      */
960     if (mDisplay->isSecureContentPresenting()) {
961         HIST_BLOB_CH_LOG(V, blobId, channelId,
962                          "DRM_PLAYING, data is not available when secure content is presenting");
963         *histogramErrorCode = HistogramErrorCode::DRM_PLAYING;
964     } else if (*histogramErrorCode != HistogramErrorCode::NONE) {
965         if (cv_status == std::cv_status::timeout) {
966             if (mDisplay->isPowerModeOff()) {
967                 HIST_BLOB_CH_LOG(W, blobId, channelId, "DISPLAY_POWEROFF, data is not available");
968                 *histogramErrorCode = HistogramErrorCode::DISPLAY_POWEROFF;
969             } else {
970                 HIST_BLOB_CH_LOG(E, blobId, channelId, "BAD_HIST_DATA, no event is handled");
971                 *histogramErrorCode = HistogramErrorCode::BAD_HIST_DATA;
972             }
973         } else {
974             HIST_BLOB_CH_LOG(I, blobId, channelId, "RRS detected, cv is notified without data");
975         }
976     }
977 
978     if (*histogramErrorCode != HistogramErrorCode::NONE) {
979         // Clear the histogramBuffer when error occurs
980         histogramBuffer->assign(HISTOGRAM_BIN_COUNT, 0);
981     }
982 
983     ATRACE_NAME(aidl::com::google::hardware::pixel::display::toString(*histogramErrorCode).c_str());
984 }
985 
986 // TODO: b/295990513 - Remove the if defined after kernel prebuilts are merged.
987 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
parseDrmEvent(const void * const event,uint32_t & channelId,char16_t * & buffer) const988 int HistogramDevice::parseDrmEvent(const void* const event, uint32_t& channelId,
989                                    char16_t*& buffer) const {
990     ATRACE_NAME(String8::format("parseHistogramDrmEvent(%p)", event).c_str());
991     if (!event) {
992         HIST_LOG(E, "event is NULL");
993         return BAD_VALUE;
994     }
995 
996     const struct exynos_drm_histogram_channel_event* const histogram_channel_event =
997             (struct exynos_drm_histogram_channel_event*)event;
998     channelId = histogram_channel_event->hist_id;
999     buffer = (char16_t*)&histogram_channel_event->bins;
1000     return NO_ERROR;
1001 }
1002 #else
parseDrmEvent(const void * const event,uint32_t & channelId,char16_t * & buffer) const1003 int HistogramDevice::parseDrmEvent(const void* const event, uint32_t& channelId,
1004                                    char16_t*& buffer) const {
1005     HIST_LOG(E,
1006              "not supported by kernel, struct exynos_drm_histogram_channel_event is not defined");
1007     channelId = 0;
1008     buffer = nullptr;
1009     return INVALID_OPERATION;
1010 }
1011 #endif
1012 
1013 #if defined(EXYNOS_CONTEXT_HISTOGRAM_EVENT_REQUEST)
parseContextDrmEvent(const void * const event,uint32_t & blobId,char16_t * & buffer) const1014 int HistogramDevice::parseContextDrmEvent(const void* const event, uint32_t& blobId,
1015                                           char16_t*& buffer) const {
1016     ATRACE_NAME(String8::format("parseHistogramDrmEvent(%p)", event).c_str());
1017     if (!event) {
1018         HIST_LOG(E, "event is NULL");
1019         return BAD_VALUE;
1020     }
1021 
1022     const struct exynos_drm_context_histogram_event* const context_histogram_event =
1023             (struct exynos_drm_context_histogram_event*)event;
1024     blobId = context_histogram_event->user_handle;
1025     buffer = (char16_t*)&context_histogram_event->bins;
1026     return NO_ERROR;
1027 }
1028 #else
parseContextDrmEvent(const void * const event,uint32_t & blobId,char16_t * & buffer) const1029 int HistogramDevice::parseContextDrmEvent(const void* const event, uint32_t& blobId,
1030                                           char16_t*& buffer) const {
1031     HIST_LOG(E,
1032              "not supported by kernel, struct exynos_drm_context_histogram_event is not defined");
1033     blobId = 0;
1034     buffer = nullptr;
1035     return INVALID_OPERATION;
1036 }
1037 #endif
1038 
cleanupChannelInfo(const uint8_t channelId)1039 std::set<const uint8_t>::iterator HistogramDevice::cleanupChannelInfo(const uint8_t channelId) {
1040     mChannels[channelId].mStatus = ChannelStatus_t::DISABLE_PENDING;
1041     mChannels[channelId].mConfigInfo.reset();
1042     mFreeChannels.push_back(channelId);
1043     return mUsedChannels.erase(mUsedChannels.find(channelId));
1044 }
1045 
setChannelConfigBlob(ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq,const uint8_t channelId,ExynosDisplayDrmInterface * const moduleDisplayInterface,const int displayActiveH,const int displayActiveV,const std::shared_ptr<ConfigInfo> & configInfo)1046 void HistogramDevice::setChannelConfigBlob(ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq,
1047                                            const uint8_t channelId,
1048                                            ExynosDisplayDrmInterface* const moduleDisplayInterface,
1049                                            const int displayActiveH, const int displayActiveV,
1050                                            const std::shared_ptr<ConfigInfo>& configInfo) {
1051     ATRACE_NAME(String8::format("%s(chan#%u)", __func__, channelId));
1052     ChannelInfo& channel = mChannels[channelId];
1053     int ret;
1054     bool isRRS = false;
1055     uint32_t blobId = getMatchBlobId(configInfo->mBlobsList, displayActiveH, displayActiveV, isRRS);
1056 
1057     // Early return for config blob already committed and no RRS occurs.
1058     if (channel.mStatus == ChannelStatus_t::CONFIG_COMMITTED && blobId && !isRRS) return;
1059 
1060     // Create the histogram config blob when no matched blob found.
1061     if (!blobId) {
1062         std::shared_ptr<PropertyBlob> drmConfigBlob;
1063         ret = createDrmConfigBlob(configInfo->mRequestedConfig, displayActiveH, displayActiveV,
1064                                   drmConfigBlob);
1065         if (ret || !drmConfigBlob) {
1066             if (ret == NO_INIT) {
1067                 HIST_CH_LOG(D, channelId, "skip channel config");
1068                 channel.mStatus = ChannelStatus_t::CONFIG_PENDING;
1069             } else {
1070                 HIST_CH_LOG(E, channelId, "createDrmConfigBlob failed, ret(%d)", ret);
1071                 channel.mStatus = ChannelStatus_t::CONFIG_ERROR;
1072             }
1073             return;
1074         }
1075 
1076         // Attach the histogram drmConfigBlob to the configInfo
1077         if (!configInfo->mBlobsList.empty()) isRRS = true;
1078         configInfo->mBlobsList.emplace_front(displayActiveH, displayActiveV, drmConfigBlob);
1079         blobId = drmConfigBlob->getId();
1080     }
1081 
1082     if (channel.mStatus == ChannelStatus_t::CONFIG_COMMITTED && isRRS) {
1083         HIST_BLOB_CH_LOG(I, blobId, channelId, "RRS (%dx%d) detected", displayActiveH,
1084                          displayActiveV);
1085     }
1086 
1087     // Add histogram config blob to atomic commit
1088     if ((ret = moduleDisplayInterface->setHistogramChannelConfigBlob(drmReq, channelId, blobId))) {
1089         HIST_BLOB_CH_LOG(E, blobId, channelId, "setHistogramChannelConfigBlob failed, ret(%d)",
1090                          ret);
1091         channel.mStatus = ChannelStatus_t::CONFIG_ERROR;
1092     } else {
1093         channel.mStatus = ChannelStatus_t::CONFIG_BLOB_ADDED;
1094     }
1095 }
1096 
clearChannelConfigBlob(ExynosDisplayDrmInterface::DrmModeAtomicReq & drmReq,const uint8_t channelId,ExynosDisplayDrmInterface * const moduleDisplayInterface)1097 void HistogramDevice::clearChannelConfigBlob(
1098         ExynosDisplayDrmInterface::DrmModeAtomicReq& drmReq, const uint8_t channelId,
1099         ExynosDisplayDrmInterface* const moduleDisplayInterface) {
1100     ATRACE_NAME(String8::format("%s(chan#%u)", __func__, channelId));
1101     ChannelInfo& channel = mChannels[channelId];
1102     int ret;
1103 
1104     if ((ret = moduleDisplayInterface->clearHistogramChannelConfigBlob(drmReq, channelId))) {
1105         HIST_CH_LOG(E, channelId, "clearHistogramChannelConfigBlob failed, ret(%d)", ret);
1106         channel.mStatus = ChannelStatus_t::DISABLE_ERROR;
1107     } else {
1108         channel.mStatus = ChannelStatus_t::DISABLE_BLOB_ADDED;
1109     }
1110 }
1111 
getMatchBlobId(std::list<const BlobInfo> & blobsList,const int displayActiveH,const int displayActiveV,bool & isPositionChanged) const1112 uint32_t HistogramDevice::getMatchBlobId(std::list<const BlobInfo>& blobsList,
1113                                          const int displayActiveH, const int displayActiveV,
1114                                          bool& isPositionChanged) const {
1115     auto resultIt = blobsList.end();
1116 
1117     for (auto it = blobsList.begin(); it != blobsList.end(); ++it) {
1118         if (it->mDisplayActiveH == displayActiveH && it->mDisplayActiveV == displayActiveV) {
1119             resultIt = it;
1120             break;
1121         }
1122     }
1123 
1124     if (resultIt == blobsList.end()) return 0;
1125 
1126     // Move the matched config blob to the front
1127     if (resultIt != blobsList.begin()) {
1128         isPositionChanged = true;
1129         blobsList.splice(blobsList.begin(), blobsList, resultIt, std::next(resultIt));
1130     }
1131 
1132     return blobsList.begin()->mBlob->getId();
1133 }
1134 
getActiveBlobId(const std::list<const BlobInfo> & blobsList) const1135 uint32_t HistogramDevice::getActiveBlobId(const std::list<const BlobInfo>& blobsList) const {
1136     return blobsList.empty() ? 0 : blobsList.begin()->mBlob->getId();
1137 }
1138 
1139 // TODO: b/295990513 - Remove the if defined after kernel prebuilts are merged.
1140 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
createDrmConfig(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<void> & drmConfig,size_t & drmConfigLength) const1141 int HistogramDevice::createDrmConfig(const HistogramConfig& histogramConfig,
1142                                      const int displayActiveH, const int displayActiveV,
1143                                      std::shared_ptr<void>& drmConfig,
1144                                      size_t& drmConfigLength) const {
1145     int ret = NO_ERROR;
1146 
1147     if (UNLIKELY(!displayActiveH || !displayActiveV)) {
1148         HIST_LOG(I, "active mode (%dx%d) is not initialized, skip creation", displayActiveH,
1149                  displayActiveV);
1150         return NO_INIT;
1151     }
1152 
1153     HistogramRoiRect drmRoi, drmBlockingRoi;
1154     if (UNLIKELY(ret = convertRoi(histogramConfig.roi, drmRoi, displayActiveH, displayActiveV,
1155                                   ""))) {
1156         HIST_LOG(E, "failed to convert roi, ret(%d)", ret);
1157         return ret;
1158     }
1159     if (UNLIKELY(ret = convertRoi(histogramConfig.blockingRoi.value_or(DISABLED_ROI),
1160                                   drmBlockingRoi, displayActiveH, displayActiveV, "blocking "))) {
1161         HIST_LOG(E, "failed to convert blocking roi, ret(%d)", ret);
1162         return ret;
1163     }
1164 
1165     drmConfig = std::make_shared<struct histogram_channel_config>();
1166     struct histogram_channel_config* config = (struct histogram_channel_config*)drmConfig.get();
1167     config->roi.start_x = drmRoi.left;
1168     config->roi.start_y = drmRoi.top;
1169     config->roi.hsize = drmRoi.right - drmRoi.left;
1170     config->roi.vsize = drmRoi.bottom - drmRoi.top;
1171     if (drmBlockingRoi != DISABLED_ROI) {
1172         config->flags |= HISTOGRAM_FLAGS_BLOCKED_ROI;
1173         config->blocked_roi.start_x = drmBlockingRoi.left;
1174         config->blocked_roi.start_y = drmBlockingRoi.top;
1175         config->blocked_roi.hsize = drmBlockingRoi.right - drmBlockingRoi.left;
1176         config->blocked_roi.vsize = drmBlockingRoi.bottom - drmBlockingRoi.top;
1177     } else {
1178         config->flags &= ~HISTOGRAM_FLAGS_BLOCKED_ROI;
1179     }
1180     config->weights.weight_r = histogramConfig.weights.weightR;
1181     config->weights.weight_g = histogramConfig.weights.weightG;
1182     config->weights.weight_b = histogramConfig.weights.weightB;
1183     config->pos =
1184             (histogramConfig.samplePos == HistogramSamplePos::POST_POSTPROC) ? POST_DQE : PRE_DQE;
1185     config->threshold = calculateThreshold(drmRoi, displayActiveH, displayActiveV);
1186 
1187     drmConfigLength = sizeof(struct histogram_channel_config);
1188 
1189     return NO_ERROR;
1190 }
1191 #else
createDrmConfig(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<void> & drmConfig,size_t & drmConfigLength) const1192 int HistogramDevice::createDrmConfig(const HistogramConfig& histogramConfig,
1193                                      const int displayActiveH, const int displayActiveV,
1194                                      std::shared_ptr<void>& drmConfig,
1195                                      size_t& drmConfigLength) const {
1196     HIST_LOG(E, "not supported by kernel, struct histogram_channel_config is not defined");
1197     drmConfig = nullptr;
1198     drmConfigLength = 0;
1199     return INVALID_OPERATION;
1200 }
1201 #endif
1202 
createDrmConfigBlob(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<PropertyBlob> & drmConfigBlob) const1203 int HistogramDevice::createDrmConfigBlob(const HistogramConfig& histogramConfig,
1204                                          const int displayActiveH, const int displayActiveV,
1205                                          std::shared_ptr<PropertyBlob>& drmConfigBlob) const {
1206     int ret = NO_ERROR;
1207     std::shared_ptr<void> drmConfig;
1208     size_t drmConfigLength = 0;
1209 
1210     if ((ret = createDrmConfig(histogramConfig, displayActiveH, displayActiveV, drmConfig,
1211                                drmConfigLength)))
1212         return ret;
1213 
1214     std::shared_ptr<PropertyBlob> drmConfigBlobTmp =
1215             std::make_shared<PropertyBlob>(mDrmDevice, drmConfig.get(), drmConfigLength);
1216     if ((ret = drmConfigBlobTmp->getError())) {
1217         HIST_LOG(E, "failed to create property blob, ret(%d)", ret);
1218         return ret;
1219     }
1220 
1221     // If success, drmConfigBlobTmp must contain a non-zero blobId
1222     drmConfigBlob = drmConfigBlobTmp;
1223 
1224     return NO_ERROR;
1225 }
1226 
resetConfigInfoStatus(std::shared_ptr<ConfigInfo> & configInfo)1227 void HistogramDevice::resetConfigInfoStatus(std::shared_ptr<ConfigInfo>& configInfo) {
1228     if (configInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED)
1229         cleanupChannelInfo(configInfo->mChannelId);
1230     else if (configInfo->mStatus == ConfigInfo::Status_t::IN_INACTIVE_LIST) {
1231         mInactiveConfigItList.erase(configInfo->mInactiveListIt);
1232         configInfo->mInactiveListIt = mInactiveConfigItList.end();
1233     }
1234 
1235     configInfo->mStatus = ConfigInfo::Status_t::INITIALIZED;
1236 }
1237 
snapDisplayActiveSize() const1238 std::pair<int, int> HistogramDevice::snapDisplayActiveSize() const {
1239     ExynosDisplayDrmInterface* moduleDisplayInterface =
1240             static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
1241     if (!moduleDisplayInterface) {
1242         HIST_LOG(E, "failed to get active size, moduleDisplayInterface is NULL");
1243         return std::make_pair(0, 0);
1244     }
1245 
1246     return std::make_pair(moduleDisplayInterface->getActiveModeHDisplay(),
1247                           moduleDisplayInterface->getActiveModeVDisplay());
1248 }
1249 
convertRoi(const HistogramRoiRect & requestedRoi,HistogramRoiRect & convertedRoi,const int displayActiveH,const int displayActiveV,const char * roiType) const1250 int HistogramDevice::convertRoi(const HistogramRoiRect& requestedRoi,
1251                                 HistogramRoiRect& convertedRoi, const int displayActiveH,
1252                                 const int displayActiveV, const char* roiType) const {
1253     int32_t panelH, panelV;
1254 
1255     {
1256         std::shared_lock lock(mHistogramCapabilityMutex);
1257         panelH = mHistogramCapability.fullResolutionWidth;
1258         panelV = mHistogramCapability.fullResolutionHeight;
1259     }
1260 
1261     HIST_LOG(V, "active: (%dx%d), panel: (%dx%d)", displayActiveH, displayActiveV, panelH, panelV);
1262 
1263     if (panelH < displayActiveH || displayActiveH < 0 || panelV < displayActiveV ||
1264         displayActiveV < 0) {
1265         HIST_LOG(E, "failed to convert %sroi, active: (%dx%d), panel: (%dx%d)", roiType,
1266                  displayActiveH, displayActiveV, panelH, panelV);
1267         return -EINVAL;
1268     }
1269 
1270     // Linear transform from full resolution to active resolution
1271     convertedRoi.left = requestedRoi.left * displayActiveH / panelH;
1272     convertedRoi.top = requestedRoi.top * displayActiveV / panelV;
1273     convertedRoi.right = requestedRoi.right * displayActiveH / panelH;
1274     convertedRoi.bottom = requestedRoi.bottom * displayActiveV / panelV;
1275 
1276     HIST_LOG(V, "working %sroi: %s", roiType, toString(convertedRoi).c_str());
1277 
1278     return NO_ERROR;
1279 }
1280 
dumpHistogramCapability(String8 & result) const1281 void HistogramDevice::dumpHistogramCapability(String8& result) const {
1282     std::shared_lock lock(mHistogramCapabilityMutex);
1283     // Append the histogram capability info to the dump string
1284     result.appendFormat("Histogram capability(%s):\n",
1285                         (mDisplay) ? (mDisplay->mDisplayName.c_str()) : "NULL");
1286     result.appendFormat("\tsupportMultiChannel: %s, ",
1287                         mHistogramCapability.supportMultiChannel ? "true" : "false");
1288     result.appendFormat("supportBlockingRoi: %s, ",
1289                         mHistogramCapability.supportBlockingRoi ? "true" : "false");
1290     result.appendFormat("supportQueryOpr: %s, ",
1291                         mHistogramCapability.supportQueryOpr ? "true" : "false");
1292     result.appendFormat("supportSamplePosList:");
1293     for (HistogramSamplePos samplePos : mHistogramCapability.supportSamplePosList) {
1294         result.appendFormat(" %s",
1295                             aidl::com::google::hardware::pixel::display::toString(samplePos)
1296                                     .c_str());
1297     }
1298     result.appendFormat("\n");
1299     result.appendFormat("\tchannelCount: %d, ", mHistogramCapability.channelCount);
1300     result.appendFormat("fullscreen roi: (0,0)x(%dx%d)\n", mHistogramCapability.fullResolutionWidth,
1301                         mHistogramCapability.fullResolutionHeight);
1302 }
1303 
1304 // TODO: b/295990513 - Remove the if defined after kernel prebuilts are merged.
1305 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
dumpChannel(TableBuilder & tb,const uint8_t channelId) const1306 void HistogramDevice::dumpChannel(TableBuilder& tb, const uint8_t channelId) const {
1307     const ChannelInfo& channel = mChannels[channelId];
1308     auto configInfo = channel.mConfigInfo.lock();
1309     uint32_t blobId = configInfo ? getActiveBlobId(configInfo->mBlobsList) : 0;
1310     drmModePropertyBlobPtr blob = nullptr;
1311 
1312     // Get the histogram config blob
1313     if (blobId && mDrmDevice) {
1314         if ((blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId)) == nullptr) {
1315             HIST_BLOB_CH_LOG(E, blobId, channelId,
1316                              "drmModeGetPropertyBlob failed, blob is nullptr");
1317         }
1318     }
1319 
1320     tb.add("ID", (int)channelId);
1321     tb.add("status", toString(channel.mStatus));
1322     tb.add("configInfo", configInfo.get());
1323 
1324     if (!blob) {
1325         if (blobId)
1326             tb.add("blobId", String8::format("%u (Get failed)", blobId));
1327         else
1328             tb.add("blobId", "N/A");
1329         tb.add("workingRoi", "N/A");
1330         tb.add("workingBlockRoi", "N/A");
1331         tb.add("threshold", "N/A");
1332         tb.add("weightRGB", "N/A");
1333         tb.add("samplePos", "N/A");
1334         return;
1335     }
1336 
1337     const struct histogram_channel_config* config =
1338             reinterpret_cast<struct histogram_channel_config*>(blob->data);
1339     HistogramRoiRect workingRoi = {config->roi.start_x, config->roi.start_y,
1340                                    config->roi.start_x + config->roi.hsize,
1341                                    config->roi.start_y + config->roi.vsize};
1342     HistogramRoiRect workingBlockRoi = {config->blocked_roi.start_x, config->blocked_roi.start_y,
1343                                         config->blocked_roi.start_x + config->blocked_roi.hsize,
1344                                         config->blocked_roi.start_y + config->blocked_roi.vsize};
1345     tb.add("blobId", blobId);
1346     tb.add("workingRoi", toString(workingRoi));
1347     tb.add("workingBlockRoi", toString(workingBlockRoi));
1348     tb.add("threshold", config->threshold);
1349     tb.add("weightRGB",
1350            String8::format("(%" PRIu16 ",%" PRIu16 ",%" PRIu16 ")", config->weights.weight_r,
1351                            config->weights.weight_g, config->weights.weight_b));
1352     tb.add("samplePos", config->pos == POST_DQE ? "POST_DQE" : "PRE_DQE");
1353     drmModeFreePropertyBlob(blob);
1354 }
1355 #else
dumpChannel(TableBuilder & tb,const uint8_t channelId) const1356 void HistogramDevice::dumpChannel(TableBuilder& tb, const uint8_t channelId) const {}
1357 #endif
1358 
validateHistogramRequest(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode) const1359 ndk::ScopedAStatus HistogramDevice::validateHistogramRequest(
1360         const ndk::SpAIBinder& token, const HistogramConfig& histogramConfig,
1361         HistogramErrorCode* histogramErrorCode) const {
1362     // validate the argument (histogramErrorCode)
1363     if (!histogramErrorCode) {
1364         HIST_LOG(E, "binder error, histogramErrorCode is nullptr");
1365         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
1366     }
1367 
1368     // default histogramErrorCode: no error
1369     *histogramErrorCode = HistogramErrorCode::NONE;
1370 
1371     // validate the argument (token)
1372     if (token.get() == nullptr) {
1373         HIST_LOG(E, "BAD_TOKEN, token is nullptr");
1374         *histogramErrorCode = HistogramErrorCode::BAD_TOKEN;
1375         return ndk::ScopedAStatus::ok();
1376     }
1377 
1378     // validate the argument (histogramConfig)
1379     if ((*histogramErrorCode = validateHistogramConfig(histogramConfig)) !=
1380         HistogramErrorCode::NONE) {
1381         return ndk::ScopedAStatus::ok();
1382     }
1383 
1384     return ndk::ScopedAStatus::ok();
1385 }
1386 
validateHistogramConfig(const HistogramConfig & histogramConfig) const1387 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramConfig(
1388         const HistogramConfig& histogramConfig) const {
1389     HistogramErrorCode ret;
1390 
1391     std::shared_lock lock(mHistogramCapabilityMutex);
1392 
1393     if ((ret = validateHistogramRoi(histogramConfig.roi, "")) != HistogramErrorCode::NONE ||
1394         (ret = validateHistogramWeights(histogramConfig.weights)) != HistogramErrorCode::NONE ||
1395         (ret = validateHistogramSamplePos(histogramConfig.samplePos)) != HistogramErrorCode::NONE ||
1396         (ret = validateHistogramBlockingRoi(histogramConfig.blockingRoi)) !=
1397                 HistogramErrorCode::NONE) {
1398         return ret;
1399     }
1400 
1401     return HistogramErrorCode::NONE;
1402 }
1403 
validateHistogramRoi(const HistogramRoiRect & roi,const char * roiType) const1404 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramRoi(
1405         const HistogramRoiRect& roi, const char* roiType) const {
1406     if (roi == DISABLED_ROI) return HistogramErrorCode::NONE;
1407 
1408     if ((roi.left < 0) || (roi.top < 0) || (roi.right - roi.left <= 0) ||
1409         (roi.bottom - roi.top <= 0) || (roi.right > mHistogramCapability.fullResolutionWidth) ||
1410         (roi.bottom > mHistogramCapability.fullResolutionHeight)) {
1411         HIST_LOG(E, "BAD_ROI, %sroi: %s, full screen roi: (0,0)x(%dx%d)", roiType,
1412                  toString(roi).c_str(), mHistogramCapability.fullResolutionWidth,
1413                  mHistogramCapability.fullResolutionHeight);
1414         return HistogramErrorCode::BAD_ROI;
1415     }
1416 
1417     return HistogramErrorCode::NONE;
1418 }
1419 
validateHistogramWeights(const HistogramWeights & weights) const1420 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramWeights(
1421         const HistogramWeights& weights) const {
1422     if ((weights.weightR + weights.weightG + weights.weightB) != WEIGHT_SUM) {
1423         HIST_LOG(E, "BAD_WEIGHT, weights%s", toString(weights).c_str());
1424         return HistogramErrorCode::BAD_WEIGHT;
1425     }
1426 
1427     return HistogramErrorCode::NONE;
1428 }
1429 
validateHistogramSamplePos(const HistogramSamplePos & samplePos) const1430 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramSamplePos(
1431         const HistogramSamplePos& samplePos) const {
1432     for (HistogramSamplePos mSamplePos : mHistogramCapability.supportSamplePosList) {
1433         if (samplePos == mSamplePos) {
1434             return HistogramErrorCode::NONE;
1435         }
1436     }
1437 
1438     HIST_LOG(E, "BAD_POSITION, samplePos is %s",
1439              aidl::com::google::hardware::pixel::display::toString(samplePos).c_str());
1440     return HistogramErrorCode::BAD_POSITION;
1441 }
1442 
validateHistogramBlockingRoi(const std::optional<HistogramRoiRect> & blockingRoi) const1443 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramBlockingRoi(
1444         const std::optional<HistogramRoiRect>& blockingRoi) const {
1445     // If the platform doesn't support blockingRoi, client should not enable blockingRoi
1446     if (mHistogramCapability.supportBlockingRoi == false) {
1447         if (blockingRoi.has_value() && blockingRoi.value() != DISABLED_ROI) {
1448             HIST_LOG(E, "BAD_ROI, platform doesn't support blockingRoi, requested: %s",
1449                      toString(blockingRoi.value()).c_str());
1450             return HistogramErrorCode::BAD_ROI;
1451         }
1452         return HistogramErrorCode::NONE;
1453     }
1454 
1455     // For the platform that supports blockingRoi, use the same validate rule as roi
1456     return validateHistogramRoi(blockingRoi.value_or(DISABLED_ROI), "blocking ");
1457 }
1458 
calculateThreshold(const HistogramRoiRect & roi,const int displayActiveH,const int displayActiveV) const1459 int HistogramDevice::calculateThreshold(const HistogramRoiRect& roi, const int displayActiveH,
1460                                         const int displayActiveV) const {
1461     // If roi is disabled, the targeted region is entire screen.
1462     int32_t roiH = (roi != DISABLED_ROI) ? (roi.right - roi.left) : displayActiveH;
1463     int32_t roiV = (roi != DISABLED_ROI) ? (roi.bottom - roi.top) : displayActiveV;
1464     int threshold = (roiV * roiH) >> 16;
1465     // TODO: b/294491895 - Check if the threshold plus one really need it
1466     return threshold + 1;
1467 }
1468 
toString(const ChannelStatus_t & status)1469 std::string HistogramDevice::toString(const ChannelStatus_t& status) {
1470     switch (status) {
1471         case ChannelStatus_t::RESERVED:
1472             return "RESERVED";
1473         case ChannelStatus_t::DISABLED:
1474             return "DISABLED";
1475         case ChannelStatus_t::CONFIG_PENDING:
1476             return "CONFIG_PENDING";
1477         case ChannelStatus_t::CONFIG_BLOB_ADDED:
1478             return "CONFIG_BLOB_ADDED";
1479         case ChannelStatus_t::CONFIG_COMMITTED:
1480             return "CONFIG_COMMITTED";
1481         case ChannelStatus_t::CONFIG_ERROR:
1482             return "CONFIG_ERROR";
1483         case ChannelStatus_t::DISABLE_PENDING:
1484             return "DISABLE_PENDING";
1485         case ChannelStatus_t::DISABLE_BLOB_ADDED:
1486             return "DISABLE_BLOB_ADDED";
1487         case ChannelStatus_t::DISABLE_ERROR:
1488             return "DISABLE_ERROR";
1489     }
1490 
1491     return "UNDEFINED";
1492 }
1493 
toString(const HistogramRoiRect & roi)1494 std::string HistogramDevice::toString(const HistogramRoiRect& roi) {
1495     if (roi == DISABLED_ROI) return "OFF";
1496 
1497     std::ostringstream os;
1498     os << "(" << roi.left << "," << roi.top << ")";
1499     os << "x";
1500     os << "(" << roi.right << "," << roi.bottom << ")";
1501     return os.str();
1502 }
1503 
toString(const HistogramWeights & weights)1504 std::string HistogramDevice::toString(const HistogramWeights& weights) {
1505     std::ostringstream os;
1506     os << "(";
1507     os << (int)weights.weightR << ",";
1508     os << (int)weights.weightG << ",";
1509     os << (int)weights.weightB;
1510     os << ")";
1511     return os.str();
1512 }
1513 
toString(const HistogramConfig & config)1514 std::string HistogramDevice::toString(const HistogramConfig& config) {
1515     std::ostringstream os;
1516     os << "roi:" << toString(config.roi) << ", ";
1517     os << "blockRoi:" << toString(config.blockingRoi.value_or(DISABLED_ROI)) << ", ";
1518     os << "weightRGB:" << toString(config.weights) << ", ";
1519     os << "samplePos:" << aidl::com::google::hardware::pixel::display::toString(config.samplePos);
1520     return os.str();
1521 }
1522 
dump(String8 & result,const char * prefix) const1523 void HistogramDevice::TokenInfo::dump(String8& result, const char* prefix) const {
1524     result.appendFormat("%sHistogram token %p:\n", prefix, mToken.get());
1525     result.appendFormat("%s\tpid: %d\n", prefix, mPid);
1526     if (!mConfigInfo) {
1527         result.append("%s\tconfigInfo: (nullptr)\n");
1528     }
1529 }
1530 
dump(String8 & result,const char * prefix) const1531 void HistogramDevice::ConfigInfo::dump(String8& result, const char* prefix) const {
1532     result.appendFormat("%sconfigInfo: %p -> ", prefix, this);
1533     if (mStatus == Status_t::HAS_CHANNEL_ASSIGNED)
1534         result.appendFormat("channelId: %d\n", mChannelId);
1535     else if (mStatus == Status_t::IN_INACTIVE_LIST)
1536         result.appendFormat("inactive list: queued\n");
1537     else
1538         result.appendFormat("inactive list: N/A\n");
1539     result.appendFormat("%s\trequestedConfig: %s\n", prefix, toString(mRequestedConfig).c_str());
1540     result.appendFormat("%s\tblobsList: ", prefix);
1541     if (!mBlobsList.empty()) {
1542         result.append("*");
1543         for (auto it = mBlobsList.begin(); it != mBlobsList.end(); ++it) {
1544             result.appendFormat("blob#%u(%dx%d) ", it->mBlob->getId(), it->mDisplayActiveH,
1545                                 it->mDisplayActiveV);
1546         }
1547     } else {
1548         result.append("none");
1549     }
1550     result.append("\n");
1551 }
1552 
PropertyBlob(DrmDevice * const drmDevice,const void * const blobData,const size_t blobLength)1553 HistogramDevice::PropertyBlob::PropertyBlob(DrmDevice* const drmDevice, const void* const blobData,
1554                                             const size_t blobLength)
1555       : mDrmDevice(drmDevice) {
1556     if (!mDrmDevice) {
1557         ALOGE("%s: mDrmDevice is nullptr", __func__);
1558         mError = BAD_VALUE;
1559         return;
1560     }
1561 
1562     if ((mError = mDrmDevice->CreatePropertyBlob(blobData, blobLength, &mBlobId))) {
1563         mBlobId = 0;
1564         ALOGE("%s: failed to create histogram config blob, ret(%d)", __func__, mError);
1565     } else if (!mBlobId) {
1566         mError = BAD_VALUE;
1567         ALOGE("%s: create histogram config blob successful, but blobId is 0", __func__);
1568     }
1569 }
1570 
~PropertyBlob()1571 HistogramDevice::PropertyBlob::~PropertyBlob() {
1572     if (mError) return;
1573 
1574     int ret = mDrmDevice->DestroyPropertyBlob(mBlobId);
1575     if (ret)
1576         ALOGE("%s: failed to destroy histogram config blob %d, ret(%d)", __func__, mBlobId, ret);
1577 }
1578 
getId() const1579 uint32_t HistogramDevice::PropertyBlob::getId() const {
1580     return mBlobId;
1581 }
1582 
getError() const1583 int HistogramDevice::PropertyBlob::getError() const {
1584     return mError;
1585 }
1586