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<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<BlobInfo> & blobsList,const int displayActiveH,const int displayActiveV,bool & isPositionChanged) const1112 uint32_t HistogramDevice::getMatchBlobId(std::list<BlobInfo>& blobsList, const int displayActiveH,
1113                                          const int displayActiveV, bool& isPositionChanged) const {
1114     auto resultIt = blobsList.end();
1115 
1116     for (auto it = blobsList.begin(); it != blobsList.end(); ++it) {
1117         if (it->mDisplayActiveH == displayActiveH && it->mDisplayActiveV == displayActiveV) {
1118             resultIt = it;
1119             break;
1120         }
1121     }
1122 
1123     if (resultIt == blobsList.end()) return 0;
1124 
1125     // Move the matched config blob to the front
1126     if (resultIt != blobsList.begin()) {
1127         isPositionChanged = true;
1128         blobsList.splice(blobsList.begin(), blobsList, resultIt, std::next(resultIt));
1129     }
1130 
1131     return blobsList.begin()->mBlob->getId();
1132 }
1133 
getActiveBlobId(const std::list<BlobInfo> & blobsList) const1134 uint32_t HistogramDevice::getActiveBlobId(const std::list<BlobInfo>& blobsList) const {
1135     return blobsList.empty() ? 0 : blobsList.begin()->mBlob->getId();
1136 }
1137 
1138 // TODO: b/295990513 - Remove the if defined after kernel prebuilts are merged.
1139 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
createDrmConfig(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<void> & drmConfig,size_t & drmConfigLength) const1140 int HistogramDevice::createDrmConfig(const HistogramConfig& histogramConfig,
1141                                      const int displayActiveH, const int displayActiveV,
1142                                      std::shared_ptr<void>& drmConfig,
1143                                      size_t& drmConfigLength) const {
1144     int ret = NO_ERROR;
1145 
1146     if (UNLIKELY(!displayActiveH || !displayActiveV)) {
1147         HIST_LOG(I, "active mode (%dx%d) is not initialized, skip creation", displayActiveH,
1148                  displayActiveV);
1149         return NO_INIT;
1150     }
1151 
1152     HistogramRoiRect drmRoi, drmBlockingRoi;
1153     if (UNLIKELY(ret = convertRoi(histogramConfig.roi, drmRoi, displayActiveH, displayActiveV,
1154                                   ""))) {
1155         HIST_LOG(E, "failed to convert roi, ret(%d)", ret);
1156         return ret;
1157     }
1158     if (UNLIKELY(ret = convertRoi(histogramConfig.blockingRoi.value_or(DISABLED_ROI),
1159                                   drmBlockingRoi, displayActiveH, displayActiveV, "blocking "))) {
1160         HIST_LOG(E, "failed to convert blocking roi, ret(%d)", ret);
1161         return ret;
1162     }
1163 
1164     drmConfig = std::make_shared<struct histogram_channel_config>();
1165     struct histogram_channel_config* config = (struct histogram_channel_config*)drmConfig.get();
1166     config->roi.start_x = drmRoi.left;
1167     config->roi.start_y = drmRoi.top;
1168     config->roi.hsize = drmRoi.right - drmRoi.left;
1169     config->roi.vsize = drmRoi.bottom - drmRoi.top;
1170     if (drmBlockingRoi != DISABLED_ROI) {
1171         config->flags |= HISTOGRAM_FLAGS_BLOCKED_ROI;
1172         config->blocked_roi.start_x = drmBlockingRoi.left;
1173         config->blocked_roi.start_y = drmBlockingRoi.top;
1174         config->blocked_roi.hsize = drmBlockingRoi.right - drmBlockingRoi.left;
1175         config->blocked_roi.vsize = drmBlockingRoi.bottom - drmBlockingRoi.top;
1176     } else {
1177         config->flags &= ~HISTOGRAM_FLAGS_BLOCKED_ROI;
1178     }
1179     config->weights.weight_r = histogramConfig.weights.weightR;
1180     config->weights.weight_g = histogramConfig.weights.weightG;
1181     config->weights.weight_b = histogramConfig.weights.weightB;
1182     config->pos =
1183             (histogramConfig.samplePos == HistogramSamplePos::POST_POSTPROC) ? POST_DQE : PRE_DQE;
1184     config->threshold = calculateThreshold(drmRoi, displayActiveH, displayActiveV);
1185 
1186     drmConfigLength = sizeof(struct histogram_channel_config);
1187 
1188     return NO_ERROR;
1189 }
1190 #else
createDrmConfig(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<void> & drmConfig,size_t & drmConfigLength) const1191 int HistogramDevice::createDrmConfig(const HistogramConfig& histogramConfig,
1192                                      const int displayActiveH, const int displayActiveV,
1193                                      std::shared_ptr<void>& drmConfig,
1194                                      size_t& drmConfigLength) const {
1195     HIST_LOG(E, "not supported by kernel, struct histogram_channel_config is not defined");
1196     drmConfig = nullptr;
1197     drmConfigLength = 0;
1198     return INVALID_OPERATION;
1199 }
1200 #endif
1201 
createDrmConfigBlob(const HistogramConfig & histogramConfig,const int displayActiveH,const int displayActiveV,std::shared_ptr<PropertyBlob> & drmConfigBlob) const1202 int HistogramDevice::createDrmConfigBlob(const HistogramConfig& histogramConfig,
1203                                          const int displayActiveH, const int displayActiveV,
1204                                          std::shared_ptr<PropertyBlob>& drmConfigBlob) const {
1205     int ret = NO_ERROR;
1206     std::shared_ptr<void> drmConfig;
1207     size_t drmConfigLength = 0;
1208 
1209     if ((ret = createDrmConfig(histogramConfig, displayActiveH, displayActiveV, drmConfig,
1210                                drmConfigLength)))
1211         return ret;
1212 
1213     std::shared_ptr<PropertyBlob> drmConfigBlobTmp =
1214             std::make_shared<PropertyBlob>(mDrmDevice, drmConfig.get(), drmConfigLength);
1215     if ((ret = drmConfigBlobTmp->getError())) {
1216         HIST_LOG(E, "failed to create property blob, ret(%d)", ret);
1217         return ret;
1218     }
1219 
1220     // If success, drmConfigBlobTmp must contain a non-zero blobId
1221     drmConfigBlob = drmConfigBlobTmp;
1222 
1223     return NO_ERROR;
1224 }
1225 
resetConfigInfoStatus(std::shared_ptr<ConfigInfo> & configInfo)1226 void HistogramDevice::resetConfigInfoStatus(std::shared_ptr<ConfigInfo>& configInfo) {
1227     if (configInfo->mStatus == ConfigInfo::Status_t::HAS_CHANNEL_ASSIGNED)
1228         cleanupChannelInfo(configInfo->mChannelId);
1229     else if (configInfo->mStatus == ConfigInfo::Status_t::IN_INACTIVE_LIST) {
1230         mInactiveConfigItList.erase(configInfo->mInactiveListIt);
1231         configInfo->mInactiveListIt = mInactiveConfigItList.end();
1232     }
1233 
1234     configInfo->mStatus = ConfigInfo::Status_t::INITIALIZED;
1235 }
1236 
snapDisplayActiveSize() const1237 std::pair<int, int> HistogramDevice::snapDisplayActiveSize() const {
1238     ExynosDisplayDrmInterface* moduleDisplayInterface =
1239             static_cast<ExynosDisplayDrmInterface*>(mDisplay->mDisplayInterface.get());
1240     if (!moduleDisplayInterface) {
1241         HIST_LOG(E, "failed to get active size, moduleDisplayInterface is NULL");
1242         return std::make_pair(0, 0);
1243     }
1244 
1245     return std::make_pair(moduleDisplayInterface->getActiveModeHDisplay(),
1246                           moduleDisplayInterface->getActiveModeVDisplay());
1247 }
1248 
convertRoi(const HistogramRoiRect & requestedRoi,HistogramRoiRect & convertedRoi,const int displayActiveH,const int displayActiveV,const char * roiType) const1249 int HistogramDevice::convertRoi(const HistogramRoiRect& requestedRoi,
1250                                 HistogramRoiRect& convertedRoi, const int displayActiveH,
1251                                 const int displayActiveV, const char* roiType) const {
1252     int32_t panelH, panelV;
1253 
1254     {
1255         std::shared_lock lock(mHistogramCapabilityMutex);
1256         panelH = mHistogramCapability.fullResolutionWidth;
1257         panelV = mHistogramCapability.fullResolutionHeight;
1258     }
1259 
1260     HIST_LOG(V, "active: (%dx%d), panel: (%dx%d)", displayActiveH, displayActiveV, panelH, panelV);
1261 
1262     if (panelH < displayActiveH || displayActiveH < 0 || panelV < displayActiveV ||
1263         displayActiveV < 0) {
1264         HIST_LOG(E, "failed to convert %sroi, active: (%dx%d), panel: (%dx%d)", roiType,
1265                  displayActiveH, displayActiveV, panelH, panelV);
1266         return -EINVAL;
1267     }
1268 
1269     // Linear transform from full resolution to active resolution
1270     convertedRoi.left = requestedRoi.left * displayActiveH / panelH;
1271     convertedRoi.top = requestedRoi.top * displayActiveV / panelV;
1272     convertedRoi.right = requestedRoi.right * displayActiveH / panelH;
1273     convertedRoi.bottom = requestedRoi.bottom * displayActiveV / panelV;
1274 
1275     HIST_LOG(V, "working %sroi: %s", roiType, toString(convertedRoi).c_str());
1276 
1277     return NO_ERROR;
1278 }
1279 
dumpHistogramCapability(String8 & result) const1280 void HistogramDevice::dumpHistogramCapability(String8& result) const {
1281     std::shared_lock lock(mHistogramCapabilityMutex);
1282     // Append the histogram capability info to the dump string
1283     result.appendFormat("Histogram capability(%s):\n",
1284                         (mDisplay) ? (mDisplay->mDisplayName.c_str()) : "NULL");
1285     result.appendFormat("\tsupportMultiChannel: %s, ",
1286                         mHistogramCapability.supportMultiChannel ? "true" : "false");
1287     result.appendFormat("supportBlockingRoi: %s, ",
1288                         mHistogramCapability.supportBlockingRoi ? "true" : "false");
1289     result.appendFormat("supportQueryOpr: %s, ",
1290                         mHistogramCapability.supportQueryOpr ? "true" : "false");
1291     result.appendFormat("supportSamplePosList:");
1292     for (HistogramSamplePos samplePos : mHistogramCapability.supportSamplePosList) {
1293         result.appendFormat(" %s",
1294                             aidl::com::google::hardware::pixel::display::toString(samplePos)
1295                                     .c_str());
1296     }
1297     result.appendFormat("\n");
1298     result.appendFormat("\tchannelCount: %d, ", mHistogramCapability.channelCount);
1299     result.appendFormat("fullscreen roi: (0,0)x(%dx%d)\n", mHistogramCapability.fullResolutionWidth,
1300                         mHistogramCapability.fullResolutionHeight);
1301 }
1302 
1303 // TODO: b/295990513 - Remove the if defined after kernel prebuilts are merged.
1304 #if defined(EXYNOS_HISTOGRAM_CHANNEL_REQUEST)
dumpChannel(TableBuilder & tb,const uint8_t channelId) const1305 void HistogramDevice::dumpChannel(TableBuilder& tb, const uint8_t channelId) const {
1306     const ChannelInfo& channel = mChannels[channelId];
1307     auto configInfo = channel.mConfigInfo.lock();
1308     uint32_t blobId = configInfo ? getActiveBlobId(configInfo->mBlobsList) : 0;
1309     drmModePropertyBlobPtr blob = nullptr;
1310 
1311     // Get the histogram config blob
1312     if (blobId && mDrmDevice) {
1313         if ((blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId)) == nullptr) {
1314             HIST_BLOB_CH_LOG(E, blobId, channelId,
1315                              "drmModeGetPropertyBlob failed, blob is nullptr");
1316         }
1317     }
1318 
1319     tb.add("ID", (int)channelId);
1320     tb.add("status", toString(channel.mStatus));
1321     tb.add("configInfo", configInfo.get());
1322 
1323     if (!blob) {
1324         if (blobId)
1325             tb.add("blobId", String8::format("%u (Get failed)", blobId));
1326         else
1327             tb.add("blobId", "N/A");
1328         tb.add("workingRoi", "N/A");
1329         tb.add("workingBlockRoi", "N/A");
1330         tb.add("threshold", "N/A");
1331         tb.add("weightRGB", "N/A");
1332         tb.add("samplePos", "N/A");
1333         return;
1334     }
1335 
1336     const struct histogram_channel_config* config =
1337             reinterpret_cast<struct histogram_channel_config*>(blob->data);
1338     HistogramRoiRect workingRoi = {config->roi.start_x, config->roi.start_y,
1339                                    config->roi.start_x + config->roi.hsize,
1340                                    config->roi.start_y + config->roi.vsize};
1341     HistogramRoiRect workingBlockRoi = {config->blocked_roi.start_x, config->blocked_roi.start_y,
1342                                         config->blocked_roi.start_x + config->blocked_roi.hsize,
1343                                         config->blocked_roi.start_y + config->blocked_roi.vsize};
1344     tb.add("blobId", blobId);
1345     tb.add("workingRoi", toString(workingRoi));
1346     tb.add("workingBlockRoi", toString(workingBlockRoi));
1347     tb.add("threshold", config->threshold);
1348     tb.add("weightRGB",
1349            String8::format("(%" PRIu16 ",%" PRIu16 ",%" PRIu16 ")", config->weights.weight_r,
1350                            config->weights.weight_g, config->weights.weight_b));
1351     tb.add("samplePos", config->pos == POST_DQE ? "POST_DQE" : "PRE_DQE");
1352     drmModeFreePropertyBlob(blob);
1353 }
1354 #else
dumpChannel(TableBuilder & tb,const uint8_t channelId) const1355 void HistogramDevice::dumpChannel(TableBuilder& tb, const uint8_t channelId) const {}
1356 #endif
1357 
validateHistogramRequest(const ndk::SpAIBinder & token,const HistogramConfig & histogramConfig,HistogramErrorCode * histogramErrorCode) const1358 ndk::ScopedAStatus HistogramDevice::validateHistogramRequest(
1359         const ndk::SpAIBinder& token, const HistogramConfig& histogramConfig,
1360         HistogramErrorCode* histogramErrorCode) const {
1361     // validate the argument (histogramErrorCode)
1362     if (!histogramErrorCode) {
1363         HIST_LOG(E, "binder error, histogramErrorCode is nullptr");
1364         return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
1365     }
1366 
1367     // default histogramErrorCode: no error
1368     *histogramErrorCode = HistogramErrorCode::NONE;
1369 
1370     // validate the argument (token)
1371     if (token.get() == nullptr) {
1372         HIST_LOG(E, "BAD_TOKEN, token is nullptr");
1373         *histogramErrorCode = HistogramErrorCode::BAD_TOKEN;
1374         return ndk::ScopedAStatus::ok();
1375     }
1376 
1377     // validate the argument (histogramConfig)
1378     if ((*histogramErrorCode = validateHistogramConfig(histogramConfig)) !=
1379         HistogramErrorCode::NONE) {
1380         return ndk::ScopedAStatus::ok();
1381     }
1382 
1383     return ndk::ScopedAStatus::ok();
1384 }
1385 
validateHistogramConfig(const HistogramConfig & histogramConfig) const1386 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramConfig(
1387         const HistogramConfig& histogramConfig) const {
1388     HistogramErrorCode ret;
1389 
1390     std::shared_lock lock(mHistogramCapabilityMutex);
1391 
1392     if ((ret = validateHistogramRoi(histogramConfig.roi, "")) != HistogramErrorCode::NONE ||
1393         (ret = validateHistogramWeights(histogramConfig.weights)) != HistogramErrorCode::NONE ||
1394         (ret = validateHistogramSamplePos(histogramConfig.samplePos)) != HistogramErrorCode::NONE ||
1395         (ret = validateHistogramBlockingRoi(histogramConfig.blockingRoi)) !=
1396                 HistogramErrorCode::NONE) {
1397         return ret;
1398     }
1399 
1400     return HistogramErrorCode::NONE;
1401 }
1402 
validateHistogramRoi(const HistogramRoiRect & roi,const char * roiType) const1403 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramRoi(
1404         const HistogramRoiRect& roi, const char* roiType) const {
1405     if (roi == DISABLED_ROI) return HistogramErrorCode::NONE;
1406 
1407     if ((roi.left < 0) || (roi.top < 0) || (roi.right - roi.left <= 0) ||
1408         (roi.bottom - roi.top <= 0) || (roi.right > mHistogramCapability.fullResolutionWidth) ||
1409         (roi.bottom > mHistogramCapability.fullResolutionHeight)) {
1410         HIST_LOG(E, "BAD_ROI, %sroi: %s, full screen roi: (0,0)x(%dx%d)", roiType,
1411                  toString(roi).c_str(), mHistogramCapability.fullResolutionWidth,
1412                  mHistogramCapability.fullResolutionHeight);
1413         return HistogramErrorCode::BAD_ROI;
1414     }
1415 
1416     return HistogramErrorCode::NONE;
1417 }
1418 
validateHistogramWeights(const HistogramWeights & weights) const1419 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramWeights(
1420         const HistogramWeights& weights) const {
1421     if ((weights.weightR + weights.weightG + weights.weightB) != WEIGHT_SUM) {
1422         HIST_LOG(E, "BAD_WEIGHT, weights%s", toString(weights).c_str());
1423         return HistogramErrorCode::BAD_WEIGHT;
1424     }
1425 
1426     return HistogramErrorCode::NONE;
1427 }
1428 
validateHistogramSamplePos(const HistogramSamplePos & samplePos) const1429 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramSamplePos(
1430         const HistogramSamplePos& samplePos) const {
1431     for (HistogramSamplePos mSamplePos : mHistogramCapability.supportSamplePosList) {
1432         if (samplePos == mSamplePos) {
1433             return HistogramErrorCode::NONE;
1434         }
1435     }
1436 
1437     HIST_LOG(E, "BAD_POSITION, samplePos is %s",
1438              aidl::com::google::hardware::pixel::display::toString(samplePos).c_str());
1439     return HistogramErrorCode::BAD_POSITION;
1440 }
1441 
validateHistogramBlockingRoi(const std::optional<HistogramRoiRect> & blockingRoi) const1442 HistogramDevice::HistogramErrorCode HistogramDevice::validateHistogramBlockingRoi(
1443         const std::optional<HistogramRoiRect>& blockingRoi) const {
1444     // If the platform doesn't support blockingRoi, client should not enable blockingRoi
1445     if (mHistogramCapability.supportBlockingRoi == false) {
1446         if (blockingRoi.has_value() && blockingRoi.value() != DISABLED_ROI) {
1447             HIST_LOG(E, "BAD_ROI, platform doesn't support blockingRoi, requested: %s",
1448                      toString(blockingRoi.value()).c_str());
1449             return HistogramErrorCode::BAD_ROI;
1450         }
1451         return HistogramErrorCode::NONE;
1452     }
1453 
1454     // For the platform that supports blockingRoi, use the same validate rule as roi
1455     return validateHistogramRoi(blockingRoi.value_or(DISABLED_ROI), "blocking ");
1456 }
1457 
calculateThreshold(const HistogramRoiRect & roi,const int displayActiveH,const int displayActiveV) const1458 int HistogramDevice::calculateThreshold(const HistogramRoiRect& roi, const int displayActiveH,
1459                                         const int displayActiveV) const {
1460     // If roi is disabled, the targeted region is entire screen.
1461     int32_t roiH = (roi != DISABLED_ROI) ? (roi.right - roi.left) : displayActiveH;
1462     int32_t roiV = (roi != DISABLED_ROI) ? (roi.bottom - roi.top) : displayActiveV;
1463     int threshold = (roiV * roiH) >> 16;
1464     // TODO: b/294491895 - Check if the threshold plus one really need it
1465     return threshold + 1;
1466 }
1467 
toString(const ChannelStatus_t & status)1468 std::string HistogramDevice::toString(const ChannelStatus_t& status) {
1469     switch (status) {
1470         case ChannelStatus_t::RESERVED:
1471             return "RESERVED";
1472         case ChannelStatus_t::DISABLED:
1473             return "DISABLED";
1474         case ChannelStatus_t::CONFIG_PENDING:
1475             return "CONFIG_PENDING";
1476         case ChannelStatus_t::CONFIG_BLOB_ADDED:
1477             return "CONFIG_BLOB_ADDED";
1478         case ChannelStatus_t::CONFIG_COMMITTED:
1479             return "CONFIG_COMMITTED";
1480         case ChannelStatus_t::CONFIG_ERROR:
1481             return "CONFIG_ERROR";
1482         case ChannelStatus_t::DISABLE_PENDING:
1483             return "DISABLE_PENDING";
1484         case ChannelStatus_t::DISABLE_BLOB_ADDED:
1485             return "DISABLE_BLOB_ADDED";
1486         case ChannelStatus_t::DISABLE_ERROR:
1487             return "DISABLE_ERROR";
1488     }
1489 
1490     return "UNDEFINED";
1491 }
1492 
toString(const HistogramRoiRect & roi)1493 std::string HistogramDevice::toString(const HistogramRoiRect& roi) {
1494     if (roi == DISABLED_ROI) return "OFF";
1495 
1496     std::ostringstream os;
1497     os << "(" << roi.left << "," << roi.top << ")";
1498     os << "x";
1499     os << "(" << roi.right << "," << roi.bottom << ")";
1500     return os.str();
1501 }
1502 
toString(const HistogramWeights & weights)1503 std::string HistogramDevice::toString(const HistogramWeights& weights) {
1504     std::ostringstream os;
1505     os << "(";
1506     os << (int)weights.weightR << ",";
1507     os << (int)weights.weightG << ",";
1508     os << (int)weights.weightB;
1509     os << ")";
1510     return os.str();
1511 }
1512 
toString(const HistogramConfig & config)1513 std::string HistogramDevice::toString(const HistogramConfig& config) {
1514     std::ostringstream os;
1515     os << "roi:" << toString(config.roi) << ", ";
1516     os << "blockRoi:" << toString(config.blockingRoi.value_or(DISABLED_ROI)) << ", ";
1517     os << "weightRGB:" << toString(config.weights) << ", ";
1518     os << "samplePos:" << aidl::com::google::hardware::pixel::display::toString(config.samplePos);
1519     return os.str();
1520 }
1521 
dump(String8 & result,const char * prefix) const1522 void HistogramDevice::TokenInfo::dump(String8& result, const char* prefix) const {
1523     result.appendFormat("%sHistogram token %p:\n", prefix, mToken.get());
1524     result.appendFormat("%s\tpid: %d\n", prefix, mPid);
1525     if (!mConfigInfo) {
1526         result.append("%s\tconfigInfo: (nullptr)\n");
1527     }
1528 }
1529 
dump(String8 & result,const char * prefix) const1530 void HistogramDevice::ConfigInfo::dump(String8& result, const char* prefix) const {
1531     result.appendFormat("%sconfigInfo: %p -> ", prefix, this);
1532     if (mStatus == Status_t::HAS_CHANNEL_ASSIGNED)
1533         result.appendFormat("channelId: %d\n", mChannelId);
1534     else if (mStatus == Status_t::IN_INACTIVE_LIST)
1535         result.appendFormat("inactive list: queued\n");
1536     else
1537         result.appendFormat("inactive list: N/A\n");
1538     result.appendFormat("%s\trequestedConfig: %s\n", prefix, toString(mRequestedConfig).c_str());
1539     result.appendFormat("%s\tblobsList: ", prefix);
1540     if (!mBlobsList.empty()) {
1541         result.append("*");
1542         for (auto it = mBlobsList.begin(); it != mBlobsList.end(); ++it) {
1543             result.appendFormat("blob#%u(%dx%d) ", it->mBlob->getId(), it->mDisplayActiveH,
1544                                 it->mDisplayActiveV);
1545         }
1546     } else {
1547         result.append("none");
1548     }
1549     result.append("\n");
1550 }
1551 
PropertyBlob(DrmDevice * const drmDevice,const void * const blobData,const size_t blobLength)1552 HistogramDevice::PropertyBlob::PropertyBlob(DrmDevice* const drmDevice, const void* const blobData,
1553                                             const size_t blobLength)
1554       : mDrmDevice(drmDevice) {
1555     if (!mDrmDevice) {
1556         ALOGE("%s: mDrmDevice is nullptr", __func__);
1557         mError = BAD_VALUE;
1558         return;
1559     }
1560 
1561     if ((mError = mDrmDevice->CreatePropertyBlob(blobData, blobLength, &mBlobId))) {
1562         mBlobId = 0;
1563         ALOGE("%s: failed to create histogram config blob, ret(%d)", __func__, mError);
1564     } else if (!mBlobId) {
1565         mError = BAD_VALUE;
1566         ALOGE("%s: create histogram config blob successful, but blobId is 0", __func__);
1567     }
1568 }
1569 
~PropertyBlob()1570 HistogramDevice::PropertyBlob::~PropertyBlob() {
1571     if (mError) return;
1572 
1573     int ret = mDrmDevice->DestroyPropertyBlob(mBlobId);
1574     if (ret)
1575         ALOGE("%s: failed to destroy histogram config blob %d, ret(%d)", __func__, mBlobId, ret);
1576 }
1577 
getId() const1578 uint32_t HistogramDevice::PropertyBlob::getId() const {
1579     return mBlobId;
1580 }
1581 
getError() const1582 int HistogramDevice::PropertyBlob::getError() const {
1583     return mError;
1584 }
1585