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 <android/binder_ibinder.h>
20 #include <android/binder_status.h>
21 #include <cutils/properties.h>
22 
23 #include "ExynosHWCHelper.h"
24 #include "ExynosPrimaryDisplayModule.h"
25 
26 #define DISP_STR(disp) (disp)->mDisplayName.c_str()
27 
28 #define OP_MANAGER_LOGI(disp, msg, ...) \
29     ALOGI("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
30 #define OP_MANAGER_LOGW(disp, msg, ...) \
31     ALOGW("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
32 #define OP_MANAGER_LOGE(disp, msg, ...) \
33     ALOGE("[%s] OperationRateManager::%s:" msg, DISP_STR(disp), __func__, ##__VA_ARGS__)
34 
35 static constexpr int64_t kQueryPeriodNanosecs = std::chrono::nanoseconds(100ms).count();
36 
37 using namespace zumapro;
38 
ExynosPrimaryDisplayModule(uint32_t index,ExynosDevice * device,const std::string & displayName)39 ExynosPrimaryDisplayModule::ExynosPrimaryDisplayModule(uint32_t index, ExynosDevice* device,
40                                                        const std::string& displayName)
41       : gs201::ExynosPrimaryDisplayModule(index, device, displayName) {
42     int32_t hs_hz = property_get_int32("vendor.primarydisplay.op.hs_hz", 0);
43     int32_t ns_hz = property_get_int32("vendor.primarydisplay.op.ns_hz", 0);
44 
45     if (hs_hz && ns_hz) {
46         mOperationRateManager = std::make_unique<OperationRateManager>(this, hs_hz, ns_hz);
47     }
48 }
49 
~ExynosPrimaryDisplayModule()50 ExynosPrimaryDisplayModule::~ExynosPrimaryDisplayModule() {}
51 
validateWinConfigData()52 int32_t ExynosPrimaryDisplayModule::validateWinConfigData() {
53     return ExynosDisplay::validateWinConfigData();
54 }
55 
OperationRateManager(ExynosPrimaryDisplay * display,int32_t hsHz,int32_t nsHz)56 ExynosPrimaryDisplayModule::OperationRateManager::OperationRateManager(
57         ExynosPrimaryDisplay* display, int32_t hsHz, int32_t nsHz)
58       : gs201::ExynosPrimaryDisplayModule::OperationRateManager(),
59         mDisplay(display),
60         mDisplayHsOperationRate(hsHz),
61         mDisplayNsOperationRate(nsHz),
62         mDisplayPeakRefreshRate(0),
63         mDisplayRefreshRate(0),
64         mDisplayLastDbv(0),
65         mDisplayDbv(0),
66         mDisplayHsSwitchMinDbv(0),
67         mDisplayPowerMode(HWC2_POWER_MODE_ON),
68         mDisplayLowBatteryModeEnabled(false),
69         mHistogramQueryWorker(nullptr) {
70     mDisplayNsMinDbv = property_get_int32("vendor.primarydisplay.op.ns_min_dbv", 0);
71     mDisplayTargetOperationRate = mDisplayHsOperationRate;
72     OP_MANAGER_LOGI(mDisplay, "Op Rate: NS=%d HS=%d NsMinDbv=%d", mDisplayNsOperationRate,
73                     mDisplayHsOperationRate, mDisplayNsMinDbv);
74 
75     float histDeltaTh =
76             static_cast<float>(property_get_int32("vendor.primarydisplay.op.hist_delta_th", 0));
77     if (histDeltaTh) {
78         mHistogramQueryWorker = std::make_unique<HistogramQueryWorker>(this, histDeltaTh);
79         mDisplayHsSwitchMinDbv =
80                 property_get_int32("vendor.primarydisplay.op.hs_switch_min_dbv", 0);
81     }
82 }
83 
~OperationRateManager()84 ExynosPrimaryDisplayModule::OperationRateManager::~OperationRateManager() {}
85 
getTargetOperationRate() const86 int32_t ExynosPrimaryDisplayModule::OperationRateManager::getTargetOperationRate() const {
87     if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE ||
88         mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) {
89         return kLowPowerOperationRate;
90     } else {
91         return mDisplayTargetOperationRate;
92     }
93 }
94 
onPeakRefreshRate(uint32_t rate)95 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPeakRefreshRate(uint32_t rate) {
96     char rateStr[PROP_VALUE_MAX];
97     std::sprintf(rateStr, "%d", rate);
98 
99     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: rate=%d",
100                      rate);
101 
102     Mutex::Autolock lock(mLock);
103     if (property_set("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr) < 0) {
104         OP_MANAGER_LOGE(mDisplay,
105                         "failed to set property persist.primarydisplay.op.peak_refresh_rate");
106     }
107     mDisplayPeakRefreshRate = rate;
108     return 0;
109 }
110 
onLowPowerMode(bool enabled)111 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onLowPowerMode(bool enabled) {
112     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "enabled=%d", enabled);
113 
114     Mutex::Autolock lock(mLock);
115     mDisplayLowBatteryModeEnabled = enabled;
116     return 0;
117 }
118 
onConfig(hwc2_config_t cfg)119 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onConfig(hwc2_config_t cfg) {
120     Mutex::Autolock lock(mLock);
121     int32_t targetRefreshRate = mDisplay->getRefreshRate(cfg);
122     if (mHistogramQueryWorker && mHistogramQueryWorker->isRuntimeResolutionConfig() &&
123         mDisplayRefreshRate == targetRefreshRate) {
124         mHistogramQueryWorker->updateConfig(mDisplay->mXres, mDisplay->mYres);
125         // skip op update for Runtime Resolution config
126         return 0;
127     }
128     mDisplayRefreshRate = targetRefreshRate;
129     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: rate=%d",
130                      mDisplayRefreshRate);
131     updateOperationRateLocked(DispOpCondition::SET_CONFIG);
132     return 0;
133 }
134 
onBrightness(uint32_t dbv)135 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onBrightness(uint32_t dbv) {
136     Mutex::Autolock lock(mLock);
137     if (dbv == 0 || mDisplayLastDbv == dbv) return 0;
138     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: dbv=%d", dbv);
139     mDisplayDbv = dbv;
140 
141     /*
142         Update peak_refresh_rate from persist/vendor prop after a brightness change.
143         1. Otherwise there will be NS-HS-NS switch during the onPowerMode.
144         2. When constructor is called, persist property is not ready yet and returns 0.
145     */
146     if (!mDisplayPeakRefreshRate) {
147         char rateStr[PROP_VALUE_MAX];
148         int32_t vendorPeakRefreshRate = 0, persistPeakRefreshRate = 0;
149         if (property_get("persist.vendor.primarydisplay.op.peak_refresh_rate", rateStr, "0") >= 0 &&
150             atoi(rateStr) > 0) {
151             persistPeakRefreshRate = atoi(rateStr);
152             mDisplayPeakRefreshRate = persistPeakRefreshRate;
153         } else {
154             vendorPeakRefreshRate =
155                     property_get_int32("vendor.primarydisplay.op.peak_refresh_rate", 0);
156             mDisplayPeakRefreshRate = vendorPeakRefreshRate;
157         }
158 
159         DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
160                          "OperationRateManager: peak_refresh_rate=%d[vendor: %d|persist %d]",
161                          mDisplayPeakRefreshRate, vendorPeakRefreshRate, persistPeakRefreshRate);
162     }
163 
164     return updateOperationRateLocked(DispOpCondition::SET_DBV);
165 }
166 
onPowerMode(int32_t mode)167 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onPowerMode(int32_t mode) {
168     std::string modeName = "Unknown";
169     if (mode == HWC2_POWER_MODE_ON) {
170         modeName = "On";
171     } else if (mode == HWC2_POWER_MODE_OFF) {
172         modeName = "Off";
173     } else if (mode == HWC2_POWER_MODE_DOZE || mode == HWC2_POWER_MODE_DOZE_SUSPEND) {
174         modeName = "LP";
175     }
176 
177     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "OperationRateManager: mode=%s",
178                      modeName.c_str());
179 
180     Mutex::Autolock lock(mLock);
181     mDisplayPowerMode = static_cast<hwc2_power_mode_t>(mode);
182     return updateOperationRateLocked(DispOpCondition::PANEL_SET_POWER);
183 }
184 
onHistogram()185 int32_t ExynosPrimaryDisplayModule::OperationRateManager::onHistogram() {
186     Mutex::Autolock lock(mLock);
187     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
188                      "histogram reach to the luma delta threshold");
189     return updateOperationRateLocked(DispOpCondition::HISTOGRAM_DELTA);
190 }
191 
updateOperationRateLocked(const DispOpCondition cond)192 int32_t ExynosPrimaryDisplayModule::OperationRateManager::updateOperationRateLocked(
193         const DispOpCondition cond) {
194     int32_t ret = HWC2_ERROR_NONE, dbv;
195 
196     ATRACE_CALL();
197     if (cond == DispOpCondition::SET_DBV) {
198         dbv = mDisplayDbv;
199     } else {
200         dbv = mDisplayLastDbv;
201     }
202 
203     int32_t desiredOpRate = mDisplayHsOperationRate;
204     bool isSteadyLowRefreshRate =
205             (mDisplayPeakRefreshRate && mDisplayPeakRefreshRate <= mDisplayNsOperationRate) ||
206             mDisplayLowBatteryModeEnabled;
207     bool isDbvInBlockingZone = mDisplayLowBatteryModeEnabled ? (dbv < mDisplayNsMinDbv)
208                                                              : (dbv < mDisplayHsSwitchMinDbv);
209     int32_t effectiveOpRate = 0;
210 
211     // check minimal operation rate needed
212     if (isSteadyLowRefreshRate && mDisplayRefreshRate <= mDisplayNsOperationRate) {
213         desiredOpRate = mDisplayNsOperationRate;
214     }
215     // check blocking zone
216     if (isDbvInBlockingZone) {
217         DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
218                          "OperationRateManager: in blocking zone (dbv %d, min %d)", dbv,
219                          mDisplayNsMinDbv);
220         desiredOpRate = mDisplayHsOperationRate;
221     }
222 
223     if (mDisplayPowerMode == HWC2_POWER_MODE_DOZE ||
224         mDisplayPowerMode == HWC2_POWER_MODE_DOZE_SUSPEND) {
225         mDisplayTargetOperationRate = kLowPowerOperationRate;
226         desiredOpRate = mDisplayTargetOperationRate;
227         effectiveOpRate = desiredOpRate;
228     } else if (mDisplayPowerMode != HWC2_POWER_MODE_ON) {
229         if (mHistogramQueryWorker) {
230             DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
231                              "histogram stopQuery due to power off");
232             mHistogramQueryWorker->stopQuery();
233         }
234         return ret;
235     }
236 
237     if (cond == DispOpCondition::SET_CONFIG) {
238         if (mDisplayRefreshRate <= mDisplayHsOperationRate) {
239             if (!mHistogramQueryWorker) {
240                 if (mDisplayRefreshRate > mDisplayNsOperationRate) {
241                     effectiveOpRate = mDisplayHsOperationRate;
242                 }
243             } else {
244                 if (mDisplayRefreshRate == mDisplayTargetOperationRate && !isDbvInBlockingZone) {
245                     DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
246                                      "histogram stopQuery due to the same config");
247                     mHistogramQueryWorker->stopQuery();
248                 }
249                 if (!isDbvInBlockingZone) {
250                     if (mDisplayLowBatteryModeEnabled &&
251                         (!mDisplayHsSwitchMinDbv || dbv < mDisplayHsSwitchMinDbv)) {
252                         // delay the switch of NS->HS until conditions are satisfied
253                         desiredOpRate = mDisplayRefreshRate;
254                     } else if (mDisplayRefreshRate > mDisplayNsOperationRate) {
255                         // will switch to HS immediately
256                         effectiveOpRate = mDisplayHsOperationRate;
257                     }
258                 }
259             }
260         }
261     } else if (cond == DispOpCondition::PANEL_SET_POWER) {
262         if (mDisplayPowerMode == HWC2_POWER_MODE_ON) {
263             mDisplayTargetOperationRate = getTargetOperationRate();
264         }
265         effectiveOpRate = desiredOpRate;
266     } else if (cond == DispOpCondition::SET_DBV) {
267         // TODO: tune brightness delta for different brightness curve and values
268         int32_t delta = abs(dbv - mDisplayLastDbv);
269         if (!mHistogramQueryWorker) {
270             if (desiredOpRate == mDisplayHsOperationRate || delta > kBrightnessDeltaThreshold) {
271                 effectiveOpRate = desiredOpRate;
272             }
273         } else {
274             if (delta > kBrightnessDeltaThreshold) {
275                 effectiveOpRate = desiredOpRate;
276                 DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
277                                  "histogram stopQuery due to dbv delta");
278                 mHistogramQueryWorker->stopQuery();
279             }
280         }
281         mDisplayLastDbv = dbv;
282         if (effectiveOpRate > kLowPowerOperationRate &&
283             (effectiveOpRate != mDisplayTargetOperationRate)) {
284             DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate,
285                              "OperationRateManager: brightness delta=%d", delta);
286         } else {
287             if (!mHistogramQueryWorker ||
288                 (desiredOpRate == mDisplayNsOperationRate && isDbvInBlockingZone)) {
289                 return ret;
290             }
291         }
292     } else if (cond == DispOpCondition::HISTOGRAM_DELTA) {
293         effectiveOpRate = desiredOpRate;
294     }
295 
296     if (!mDisplay->isConfigSettingEnabled() && effectiveOpRate == mDisplayNsOperationRate) {
297         OP_MANAGER_LOGI(mDisplay, "rate switching is disabled, skip NS op rate update");
298         return ret;
299     } else if (effectiveOpRate > kLowPowerOperationRate &&
300                effectiveOpRate != mDisplayTargetOperationRate) {
301         mDisplayTargetOperationRate = effectiveOpRate;
302         OP_MANAGER_LOGI(mDisplay, "set target operation rate %d", effectiveOpRate);
303     }
304 
305     if (mHistogramQueryWorker && mDisplayTargetOperationRate != desiredOpRate) {
306         DISPLAY_STR_LOGD(DISP_STR(mDisplay), eDebugOperationRate, "histogram startQuery");
307         mHistogramQueryWorker->startQuery();
308     }
309 
310     OP_MANAGER_LOGI(mDisplay,
311                     "Target@%d(desired:%d) | Refresh@%d(peak:%d), Battery:%s, DBV:%d(NsMin:%d, "
312                     "HsSwitchMin:%d)",
313                     mDisplayTargetOperationRate, desiredOpRate, mDisplayRefreshRate,
314                     mDisplayPeakRefreshRate, mDisplayLowBatteryModeEnabled ? "Low" : "OK",
315                     mDisplayLastDbv, mDisplayNsMinDbv, mDisplayHsSwitchMinDbv);
316     return ret;
317 }
318 
checkPreblendingRequirement()319 void ExynosPrimaryDisplayModule::checkPreblendingRequirement() {
320     if (!hasDisplayColor()) {
321         DISPLAY_LOGD(eDebugTDM, "%s is skipped because of no displaycolor", __func__);
322         return;
323     }
324 
325     String8 log;
326     int count = 0;
327 
328     auto checkPreblending = [&](const int idx, ExynosMPPSource* mppSrc) -> int {
329         auto* colorManager = getColorManager();
330         if (!colorManager) return false;
331         auto& dpp = colorManager->getDppForLayer(mppSrc);
332         mppSrc->mNeedPreblending =
333                 dpp.EotfLut().enable | dpp.Gm().enable | dpp.Dtm().enable | dpp.OetfLut().enable;
334         if (hwcCheckDebugMessages(eDebugTDM)) {
335             log.appendFormat(" i=%d,pb(%d-%d,%d,%d,%d)", idx, mppSrc->mNeedPreblending,
336                              dpp.EotfLut().enable, dpp.Gm().enable, dpp.Dtm().enable,
337                              dpp.OetfLut().enable);
338         }
339         return mppSrc->mNeedPreblending;
340     };
341 
342     // for client target
343     count += checkPreblending(-1, &mClientCompositionInfo);
344 
345     // for normal layers
346     for (size_t i = 0; i < mLayers.size(); ++i) {
347         count += checkPreblending(i, mLayers[i]);
348     }
349     DISPLAY_LOGD(eDebugTDM, "disp(%d),cnt=%d%s", mDisplayId, count, log.c_str());
350 }
351 
HistogramQueryWorker(OperationRateManager * opRateManager,float deltaThreshold)352 ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::HistogramQueryWorker(
353         OperationRateManager* opRateManager, float deltaThreshold)
354       : Worker("HistogramQueryWorker", HAL_PRIORITY_URGENT_DISPLAY),
355         mOpRateManager(opRateManager),
356         mSpAIBinder(nullptr),
357         mReady(false),
358         mQueryMode(false),
359         mHistogramLumaDeltaThreshold(deltaThreshold),
360         mPrevHistogramLuma(0) {
361     InitWorker();
362 }
363 
~HistogramQueryWorker()364 ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::~HistogramQueryWorker() {
365     unprepare();
366     mReady = false;
367     Exit();
368 }
369 
stub_OnCreate(void *)370 void* stub_OnCreate(void*) {
371     return nullptr;
372 }
373 
stub_OnDestroy(void *)374 void stub_OnDestroy(void*) {}
375 
stub_OnTransact(AIBinder *,transaction_code_t,const AParcel *,AParcel *)376 binder_status_t stub_OnTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
377     return STATUS_OK;
378 }
379 
prepare()380 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::prepare() {
381     AIBinder_Class* binderClass = AIBinder_Class_define("disp_op_query_worker", stub_OnCreate,
382                                                         stub_OnDestroy, stub_OnTransact);
383     AIBinder* aibinder = AIBinder_new(binderClass, nullptr);
384     mSpAIBinder.set(aibinder);
385 
386     if (mSpAIBinder.get()) {
387         // assign (0, 0, 0, 0) to indicate full screen roi since display probably isn't ready
388         mConfig.roi.left = 0;
389         mConfig.roi.top = 0;
390         mConfig.roi.right = 0;
391         mConfig.roi.bottom = 0;
392         mConfig.weights.weightR = kHistogramConfigWeightR;
393         mConfig.weights.weightG = kHistogramConfigWeightG;
394         mConfig.weights.weightB = kHistogramConfigWeightB;
395         mConfig.samplePos = HistogramDevice::HistogramSamplePos::POST_POSTPROC;
396 
397         HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
398         ndk::ScopedAStatus status =
399                 mOpRateManager->mDisplay->mHistogramController->registerHistogram(mSpAIBinder,
400                                                                                   mConfig, &err);
401         if (!status.isOk()) {
402             OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to register histogram (binder err)");
403             return;
404         }
405         if (err != HistogramDevice::HistogramErrorCode::NONE) {
406             OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to register histogram (hist err)");
407             return;
408         }
409     } else {
410         OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to get binder for histogram");
411         return;
412     }
413 
414     // assign panel resolution for isRuntimeResolutionConfig()
415     mConfig.roi.right = mOpRateManager->mDisplay->mXres;
416     mConfig.roi.bottom = mOpRateManager->mDisplay->mYres;
417     mReady = true;
418     OP_MANAGER_LOGI(mOpRateManager->mDisplay, "register histogram successfully");
419 }
420 
unprepare()421 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::unprepare() {
422     if (!mReady) return;
423 
424     HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
425     mOpRateManager->mDisplay->mHistogramController->unregisterHistogram(mSpAIBinder, &err);
426     if (err != HistogramDevice::HistogramErrorCode::NONE) {
427         OP_MANAGER_LOGE(mOpRateManager->mDisplay, "failed to unregister histogram");
428     }
429 }
430 
431 bool ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::
isRuntimeResolutionConfig() const432         isRuntimeResolutionConfig() const {
433     if (!mReady) return false;
434 
435     if (mConfig.roi.right == mOpRateManager->mDisplay->mXres ||
436         mConfig.roi.bottom == mOpRateManager->mDisplay->mYres) {
437         return false;
438     }
439 
440     // histogram will change roi automatically, no need to reconfig
441     DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
442                      "histogram %dx%d->%dx%d", mConfig.roi.right, mConfig.roi.bottom,
443                      mOpRateManager->mDisplay->mXres, mOpRateManager->mDisplay->mYres);
444     return true;
445 }
446 
updateConfig(uint32_t xres,uint32_t yres)447 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::updateConfig(
448         uint32_t xres, uint32_t yres) {
449     mConfig.roi.right = xres;
450     mConfig.roi.bottom = yres;
451 }
452 
startQuery()453 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::startQuery() {
454     if (!mReady) return;
455 
456     Signal();
457 }
458 
stopQuery()459 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::stopQuery() {
460     mQueryMode = false;
461 }
462 
Routine()463 void ExynosPrimaryDisplayModule::OperationRateManager::HistogramQueryWorker::Routine() {
464     if (!mOpRateManager->mDisplay->mHistogramController) return;
465 
466     if (!mReady) {
467         prepare();
468         return;
469     }
470 
471     int ret;
472     // WaitForSignalOrExitLocked() needs to be enclosed by Lock() and Unlock()
473     Lock();
474     if (!mQueryMode) {
475         DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
476                          "histogram wait for signal");
477         ret = WaitForSignalOrExitLocked();
478         mQueryMode = true;
479         mPrevHistogramLuma = 0;
480     } else {
481         ret = WaitForSignalOrExitLocked(kQueryPeriodNanosecs);
482     }
483     if (ret == -EINTR) {
484         OP_MANAGER_LOGE(mOpRateManager->mDisplay, "histogram failed to wait for signal");
485         mQueryMode = false;
486         Unlock();
487         return;
488     }
489     Unlock();
490 
491     HistogramDevice::HistogramErrorCode err = HistogramDevice::HistogramErrorCode::NONE;
492     std::vector<char16_t> data;
493     ndk::ScopedAStatus status =
494             mOpRateManager->mDisplay->mHistogramController->queryHistogram(mSpAIBinder, &data,
495                                                                            &err);
496     if (status.isOk() && err != HistogramDevice::HistogramErrorCode::BAD_TOKEN) {
497         if (!data.size()) {
498             OP_MANAGER_LOGW(mOpRateManager->mDisplay, "histogram data is empty");
499             return;
500         }
501 
502         float lumaSum = 0;
503         int count = 0;
504         for (int i = 0; i < data.size(); i++) {
505             lumaSum += i * static_cast<int>(data[i]);
506             count += static_cast<int>(data[i]);
507         }
508         if (!count) {
509             OP_MANAGER_LOGW(mOpRateManager->mDisplay, "histogram count is 0");
510             return;
511         }
512 
513         float luma = lumaSum / count;
514         float lumaDelta = abs(luma - mPrevHistogramLuma);
515         DISPLAY_STR_LOGD(DISP_STR(mOpRateManager->mDisplay), eDebugOperationRate,
516                          "histogram luma %f, delta %f, th %f", luma, lumaDelta,
517                          mHistogramLumaDeltaThreshold);
518         if (mPrevHistogramLuma && lumaDelta > mHistogramLumaDeltaThreshold) {
519             mQueryMode = false;
520             mOpRateManager->onHistogram();
521             mOpRateManager->mDisplay->handleTargetOperationRate();
522         }
523         mPrevHistogramLuma = luma;
524     } else {
525         OP_MANAGER_LOGE(mOpRateManager->mDisplay, "histogram failed to query");
526     }
527 }
528