293 lines
10 KiB
C++
293 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <log/log.h>
|
|
#include <utils/Trace.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <mutex>
|
|
|
|
#include "StatsBase.h"
|
|
#include "Vibrator.h"
|
|
|
|
constexpr int32_t DURATION_BUCKET_WIDTH = 50;
|
|
constexpr int32_t DURATION_50MS_BUCKET_COUNT = 20;
|
|
constexpr int32_t DURATION_BUCKET_COUNT = DURATION_50MS_BUCKET_COUNT + 1;
|
|
constexpr uint32_t MAX_TIME_MS = UINT16_MAX;
|
|
|
|
#ifndef ARRAY_SIZE
|
|
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
|
|
#endif
|
|
|
|
#ifdef HAPTIC_TRACE
|
|
static const char *kWaveformLookup[] = {"WAVEFORM_LONG_VIBRATION_EFFECT",
|
|
"WAVEFORM_RESERVED_1",
|
|
"WAVEFORM_CLICK",
|
|
"WAVEFORM_SHORT_VIBRATION_EFFECT",
|
|
"WAVEFORM_THUD",
|
|
"WAVEFORM_SPIN",
|
|
"WAVEFORM_QUICK_RISE",
|
|
"WAVEFORM_SLOW_RISE",
|
|
"WAVEFORM_QUICK_FALL",
|
|
"WAVEFORM_LIGHT_TICK",
|
|
"WAVEFORM_LOW_TICK",
|
|
"WAVEFORM_RESERVED_MFG_1",
|
|
"WAVEFORM_RESERVED_MFG_2",
|
|
"WAVEFORM_RESERVED_MFG_3",
|
|
"WAVEFORM_COMPOSE",
|
|
"WAVEFORM_PWLE",
|
|
"INVALID"};
|
|
static const char *kLatencyLookup[] = {"kWaveformEffectLatency", "kPrebakedEffectLatency",
|
|
"kCompositionEffectLatency", "kPwleEffectLatency",
|
|
"INVALID"};
|
|
static const char *kErrorLookup[] = {"kInitError",
|
|
"kHwApiError",
|
|
"kHwCalError",
|
|
"kComposeFailError",
|
|
"kAlsaFailError",
|
|
"kAsyncFailError",
|
|
"kBadTimeoutError",
|
|
"kBadAmplitudeError",
|
|
"kBadEffectError",
|
|
"kBadEffectStrengthError",
|
|
"kBadPrimitiveError",
|
|
"kBadCompositeError",
|
|
"kPwleConstructionFailError",
|
|
"kUnsupportedOpError",
|
|
"INVALID"};
|
|
|
|
const char *waveformToString(uint16_t index) {
|
|
return kWaveformLookup[(index < ARRAY_SIZE(kWaveformLookup)) ? index
|
|
: ARRAY_SIZE(kWaveformLookup) - 1];
|
|
}
|
|
|
|
const char *latencyToString(uint16_t index) {
|
|
return kLatencyLookup[(index < ARRAY_SIZE(kLatencyLookup)) ? index
|
|
: ARRAY_SIZE(kLatencyLookup) - 1];
|
|
}
|
|
|
|
const char *errorToString(uint16_t index) {
|
|
return kErrorLookup[(index < ARRAY_SIZE(kErrorLookup)) ? index : ARRAY_SIZE(kErrorLookup) - 1];
|
|
}
|
|
|
|
#define STATS_TRACE(...) \
|
|
ATRACE_NAME(__func__); \
|
|
ALOGD(__VA_ARGS__)
|
|
#else
|
|
#define STATS_TRACE(...) ATRACE_NAME(__func__)
|
|
#define waveformToString(x)
|
|
#define latencyToString(x)
|
|
#define errorToString(x)
|
|
#endif
|
|
|
|
namespace aidl {
|
|
namespace android {
|
|
namespace hardware {
|
|
namespace vibrator {
|
|
|
|
enum EffectLatency : uint16_t {
|
|
kWaveformEffectLatency = 0,
|
|
kPrebakedEffectLatency,
|
|
kCompositionEffectLatency,
|
|
kPwleEffectLatency,
|
|
|
|
kEffectLatencyCount
|
|
};
|
|
|
|
enum VibratorError : uint16_t {
|
|
kInitError = 0,
|
|
kHwApiError,
|
|
kHwCalError,
|
|
kComposeFailError,
|
|
kAlsaFailError,
|
|
kAsyncFailError,
|
|
kBadTimeoutError,
|
|
kBadAmplitudeError,
|
|
kBadEffectError,
|
|
kBadEffectStrengthError,
|
|
kBadPrimitiveError,
|
|
kBadCompositeError,
|
|
kPwleConstructionFailError,
|
|
kUnsupportedOpError,
|
|
|
|
kVibratorErrorCount
|
|
};
|
|
|
|
class StatsApi : public Vibrator::StatsApi, private StatsBase {
|
|
private:
|
|
static constexpr uint32_t BASE_CONTINUOUS_EFFECT_OFFSET = 32768;
|
|
enum WaveformIndex : uint16_t {
|
|
/* Physical waveform */
|
|
WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
|
|
WAVEFORM_RESERVED_INDEX_1 = 1,
|
|
WAVEFORM_CLICK_INDEX = 2,
|
|
WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
|
|
WAVEFORM_THUD_INDEX = 4,
|
|
WAVEFORM_SPIN_INDEX = 5,
|
|
WAVEFORM_QUICK_RISE_INDEX = 6,
|
|
WAVEFORM_SLOW_RISE_INDEX = 7,
|
|
WAVEFORM_QUICK_FALL_INDEX = 8,
|
|
WAVEFORM_LIGHT_TICK_INDEX = 9,
|
|
WAVEFORM_LOW_TICK_INDEX = 10,
|
|
WAVEFORM_RESERVED_MFG_1,
|
|
WAVEFORM_RESERVED_MFG_2,
|
|
WAVEFORM_RESERVED_MFG_3,
|
|
WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
/* OWT waveform */
|
|
WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
|
|
WAVEFORM_PWLE,
|
|
/*
|
|
* Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
|
|
* #define FF_GAIN 0x60 // 96 in decimal
|
|
* #define FF_MAX_EFFECTS FF_GAIN
|
|
*/
|
|
WAVEFORM_MAX_INDEX,
|
|
};
|
|
|
|
public:
|
|
StatsApi()
|
|
: StatsBase(std::string(std::getenv("STATS_INSTANCE"))),
|
|
mCurrentLatencyIndex(kEffectLatencyCount) {
|
|
mWaveformCounts = std::vector<int32_t>(WAVEFORM_MAX_INDEX, 0);
|
|
mDurationCounts = std::vector<int32_t>(DURATION_BUCKET_COUNT, 0);
|
|
mMinLatencies = std::vector<int32_t>(kEffectLatencyCount, 0);
|
|
mMaxLatencies = std::vector<int32_t>(kEffectLatencyCount, 0);
|
|
mLatencyTotals = std::vector<int32_t>(kEffectLatencyCount, 0);
|
|
mLatencyCounts = std::vector<int32_t>(kEffectLatencyCount, 0);
|
|
mErrorCounts = std::vector<int32_t>(kVibratorErrorCount, 0);
|
|
}
|
|
|
|
bool logPrimitive(uint16_t effectIndex) override {
|
|
STATS_TRACE("logPrimitive(effectIndex: %s)", waveformToString(effectIndex));
|
|
|
|
if (effectIndex >= WAVEFORM_MAX_PHYSICAL_INDEX ||
|
|
effectIndex == WAVEFORM_LONG_VIBRATION_EFFECT_INDEX ||
|
|
effectIndex == WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX) {
|
|
ALOGE("Invalid waveform index for logging primitive: %d", effectIndex);
|
|
return false;
|
|
}
|
|
|
|
{
|
|
std::scoped_lock<std::mutex> lock(mDataAccess);
|
|
mWaveformCounts[effectIndex]++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool logWaveform(uint16_t effectIndex, int32_t duration) override {
|
|
STATS_TRACE("logWaveform(effectIndex: %s, duration: %d)", waveformToString(effectIndex),
|
|
duration);
|
|
|
|
if (effectIndex != WAVEFORM_LONG_VIBRATION_EFFECT_INDEX &&
|
|
effectIndex != WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX + BASE_CONTINUOUS_EFFECT_OFFSET) {
|
|
ALOGE("Invalid waveform index for logging waveform: %d", effectIndex);
|
|
return false;
|
|
} else if (effectIndex ==
|
|
WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX + BASE_CONTINUOUS_EFFECT_OFFSET) {
|
|
effectIndex = WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX;
|
|
}
|
|
|
|
if (duration > MAX_TIME_MS || duration < 0) {
|
|
ALOGE("Invalid waveform duration for logging waveform: %d", duration);
|
|
return false;
|
|
}
|
|
|
|
{
|
|
std::scoped_lock<std::mutex> lock(mDataAccess);
|
|
mWaveformCounts[effectIndex]++;
|
|
if (duration < DURATION_BUCKET_WIDTH * DURATION_50MS_BUCKET_COUNT) {
|
|
mDurationCounts[duration / DURATION_BUCKET_WIDTH]++;
|
|
} else {
|
|
mDurationCounts[DURATION_50MS_BUCKET_COUNT]++;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool logError(uint16_t errorIndex) override {
|
|
STATS_TRACE("logError(errorIndex: %s)", errorToString(errorIndex));
|
|
|
|
if (errorIndex >= kVibratorErrorCount) {
|
|
ALOGE("Invalid index for logging error: %d", errorIndex);
|
|
return false;
|
|
}
|
|
|
|
{
|
|
std::scoped_lock<std::mutex> lock(mDataAccess);
|
|
mErrorCounts[errorIndex]++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool logLatencyStart(uint16_t latencyIndex) override {
|
|
STATS_TRACE("logLatencyStart(latencyIndex: %s)", latencyToString(latencyIndex));
|
|
|
|
if (latencyIndex >= kEffectLatencyCount) {
|
|
ALOGE("Invalid index for measuring latency: %d", latencyIndex);
|
|
return false;
|
|
}
|
|
|
|
mCurrentLatencyStart = std::chrono::steady_clock::now();
|
|
mCurrentLatencyIndex = latencyIndex;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool logLatencyEnd() override {
|
|
STATS_TRACE("logLatencyEnd()");
|
|
|
|
if (mCurrentLatencyIndex >= kEffectLatencyCount) {
|
|
return false;
|
|
}
|
|
|
|
int32_t latency = (std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::steady_clock::now() - mCurrentLatencyStart))
|
|
.count();
|
|
|
|
{
|
|
std::scoped_lock<std::mutex> lock(mDataAccess);
|
|
if (latency < mMinLatencies[mCurrentLatencyIndex] ||
|
|
mMinLatencies[mCurrentLatencyIndex] == 0) {
|
|
mMinLatencies[mCurrentLatencyIndex] = latency;
|
|
}
|
|
if (latency > mMaxLatencies[mCurrentLatencyIndex]) {
|
|
mMaxLatencies[mCurrentLatencyIndex] = latency;
|
|
}
|
|
mLatencyTotals[mCurrentLatencyIndex] += latency;
|
|
mLatencyCounts[mCurrentLatencyIndex]++;
|
|
}
|
|
|
|
mCurrentLatencyIndex = kEffectLatencyCount;
|
|
return true;
|
|
}
|
|
|
|
void debug(int fd) override { StatsBase::debug(fd); }
|
|
|
|
private:
|
|
uint16_t mCurrentLatencyIndex;
|
|
std::chrono::time_point<std::chrono::steady_clock> mCurrentLatencyStart;
|
|
};
|
|
|
|
} // namespace vibrator
|
|
} // namespace hardware
|
|
} // namespace android
|
|
} // namespace aidl
|