* Copyright (c) 2022 Huawei Device Co., Ltd.
* 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.
*/
#include "vibrator_thread.h"
#include <sys/prctl.h>
#include "custom_vibration_matcher.h"
#ifdef OHOS_BUILD_ENABLE_QOS
#include "concurrent_task_client.h"
#include "qos.h"
#endif
#include "sensors_errors.h"
#undef LOG_TAG
#define LOG_TAG "VibratorThread"
namespace OHOS {
namespace Sensors {
namespace {
const std::string VIBRATE_CONTROL_THREAD_NAME = "OS_VibControl";
constexpr int32_t DELAY_TIME1 = 5;
constexpr int32_t DELAY_TIME2 = 10;
constexpr size_t RETRY_NUMBER = 6;
constexpr int32_t MAX_VIBRATE_COUNT = 1000;
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
constexpr size_t COMPOSITE_EFFECT_PART = 128;
#endif
}
bool VibratorThread::Run()
{
CALL_LOG_ENTER;
prctl(PR_SET_NAME, VIBRATE_CONTROL_THREAD_NAME.c_str());
#ifdef OHOS_BUILD_ENABLE_QOS
SetQosForThread();
#endif
VibrateInfo info = GetCurrentVibrateInfo();
VibratorIdentifierIPC identifier = GetCurrentVibrateParams();
std::vector<HdfWaveInformation> waveInfos = GetCurrentWaveInfo();
MISC_HILOGD("info.mode:%{public}s, deviceId:%{public}d, vibratorId:%{public}d",
info.mode.c_str(), identifier.deviceId, identifier.vibratorId);
if (info.mode == VIBRATE_TIME) {
int32_t ret = PlayOnce(info, identifier);
if (ret != SUCCESS) {
MISC_HILOGE("Play once vibration fail, package:%{public}s", info.packageName.c_str());
return false;
}
} else if (info.mode == VIBRATE_PRESET) {
int32_t ret = PlayEffect(info, identifier);
if (ret != SUCCESS) {
MISC_HILOGE("Play effect vibration fail, package:%{public}s", info.packageName.c_str());
return false;
}
} else if (info.mode == VIBRATE_CUSTOM_HD) {
int32_t ret = PlayCustomByHdHptic(info, identifier);
if (ret != SUCCESS) {
MISC_HILOGE("Play custom vibration by hd haptic fail, package:%{public}s", info.packageName.c_str());
return false;
}
} else if (info.mode == VIBRATE_CUSTOM_COMPOSITE_EFFECT || info.mode == VIBRATE_CUSTOM_COMPOSITE_TIME) {
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
int32_t ret = PlayCustomByCompositeEffect(info, identifier, waveInfos);
if (ret != SUCCESS) {
MISC_HILOGE("Play custom vibration by composite effect fail, package:%{public}s", info.packageName.c_str());
return false;
}
#endif
}
return false;
}
int32_t VibratorThread::PlayOnce(const VibrateInfo &info, const VibratorIdentifierIPC& identifier)
{
std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
int32_t ret = VibratorDevice.StartOnce(identifier, static_cast<uint32_t>(info.duration));
if (ret != SUCCESS) {
MISC_HILOGE("StartOnce fail, duration:%{public}d", info.duration);
return ERROR;
}
cv_.wait_for(vibrateLck, std::chrono::milliseconds(info.duration), [this] { return exitFlag_.load(); });
if (exitFlag_) {
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_ONCE);
#endif
MISC_HILOGD("Stop duration:%{public}d, package:%{public}s", info.duration, info.packageName.c_str());
return SUCCESS;
}
return SUCCESS;
}
void VibratorThread::HandleMultipleVibrations(const VibratorIdentifierIPC& identifier)
{
if (VibratorDevice.IsVibratorRunning(identifier)) {
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_PRESET);
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_HDHAPTIC);
for (size_t i = 0; i < RETRY_NUMBER; i++) {
if (!VibratorDevice.IsVibratorRunning(identifier)) {
MISC_HILOGI("No running vibration");
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_TIME1));
}
MISC_HILOGW("Unstopped vibration");
}
}
int32_t VibratorThread::PlayEffect(const VibrateInfo &info, const VibratorIdentifierIPC& identifier)
{
std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
if (info.count < 0 || info.count > MAX_VIBRATE_COUNT) {
MISC_HILOGE("Vibratorinfo's count is invalid, count:%{public}d", info.count);
return ERROR;
}
for (int32_t i = 0; i < info.count; ++i) {
std::string effect = info.effect;
int32_t duration = info.duration;
if (i >= 1) {
HandleMultipleVibrations(identifier);
duration += DELAY_TIME2;
}
int32_t ret = VibratorDevice.StartByIntensity(identifier, effect, info.intensity);
if (ret != SUCCESS) {
MISC_HILOGE("Vibrate effect %{public}s failed, ", effect.c_str());
return ERROR;
}
cv_.wait_for(vibrateLck, std::chrono::milliseconds(duration), [this] { return exitFlag_.load(); });
if (exitFlag_) {
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_PRESET);
#endif
MISC_HILOGD("Stop effect:%{public}s, package:%{public}s", effect.c_str(), info.packageName.c_str());
return SUCCESS;
}
}
return SUCCESS;
}
int32_t VibratorThread::PlayCustomByHdHptic(const VibrateInfo &info, const VibratorIdentifierIPC& identifier)
{
std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
const std::vector<VibratePattern> &patterns = info.package.patterns;
size_t patternSize = patterns.size();
for (size_t i = 0; i < patternSize; ++i) {
int32_t delayTime;
if (i == 0) {
delayTime = patterns[i].startTime;
} else {
delayTime = patterns[i].startTime - patterns[i - 1].startTime;
}
cv_.wait_for(vibrateLck, std::chrono::milliseconds(delayTime), [this] { return exitFlag_.load(); });
if (exitFlag_) {
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_HDHAPTIC);
#endif
MISC_HILOGD("Stop hd haptic, package:%{public}s", info.packageName.c_str());
return SUCCESS;
}
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
HandleMultipleVibrations(identifier);
#endif
int32_t ret = VibratorDevice.PlayPattern(identifier, patterns[i]);
if (ret != SUCCESS) {
MISC_HILOGE("Vibrate hd haptic failed");
return ERROR;
}
}
return SUCCESS;
}
VibrateInfo VibratorThread::copyInfoWithIndexEvents(const VibrateInfo& originalInfo,
const VibratorIdentifierIPC& identifier)
{
VibrateInfo newInfo = originalInfo;
VibratePackage newPackage;
int32_t parseDuration = 0;
for (const auto& pattern : originalInfo.package.patterns) {
VibratePattern newPattern;
newPattern.startTime = pattern.startTime;
newPattern.patternDuration = pattern.patternDuration;
for (const auto& event : pattern.events) {
if (event.index == 0 || event.index == identifier.position) {
newPattern.events.push_back(event);
parseDuration = event.duration;
}
}
if (!newPattern.events.empty()) {
newPackage.patterns.push_back(newPattern);
}
}
newInfo.package = newPackage;
newInfo.package.packageDuration = parseDuration;
return newInfo;
}
#ifdef HDF_DRIVERS_INTERFACE_VIBRATOR
int32_t VibratorThread::PlayCustomByCompositeEffect(const VibrateInfo &info, const VibratorIdentifierIPC& identifier,
const std::vector<HdfWaveInformation> &waveInfo)
{
CustomVibrationMatcher matcher(identifier, waveInfo);
HdfCompositeEffect hdfCompositeEffect;
VibrateInfo newInfo = copyInfoWithIndexEvents(info, identifier);
if (newInfo.mode == VIBRATE_CUSTOM_COMPOSITE_EFFECT) {
hdfCompositeEffect.type = HDF_EFFECT_TYPE_PRIMITIVE;
int32_t ret = matcher.TransformEffect(newInfo.package, hdfCompositeEffect.compositeEffects);
if (ret != SUCCESS) {
MISC_HILOGE("Transform pattern to predefined wave fail");
return ERROR;
}
} else if (newInfo.mode == VIBRATE_CUSTOM_COMPOSITE_TIME) {
hdfCompositeEffect.type = HDF_EFFECT_TYPE_TIME;
int32_t ret = matcher.TransformTime(info.package, hdfCompositeEffect.compositeEffects);
if (ret != SUCCESS) {
MISC_HILOGE("Transform pattern to time series fail");
return ERROR;
}
}
return PlayCompositeEffect(newInfo, hdfCompositeEffect, identifier);
}
int32_t VibratorThread::PlayCompositeEffect(const VibrateInfo &info, const HdfCompositeEffect &hdfCompositeEffect,
const VibratorIdentifierIPC& identifier)
{
std::unique_lock<std::mutex> vibrateLck(vibrateMutex_);
HdfCompositeEffect effectsPart;
effectsPart.type = hdfCompositeEffect.type;
size_t effectSize = hdfCompositeEffect.compositeEffects.size();
int32_t delayTime = 0;
for (size_t i = 0; i < effectSize; ++i) {
effectsPart.compositeEffects.push_back(hdfCompositeEffect.compositeEffects[i]);
if (effectsPart.type == HDF_EFFECT_TYPE_TIME) {
delayTime += hdfCompositeEffect.compositeEffects[i].timeEffect.delay;
} else if (effectsPart.type == HDF_EFFECT_TYPE_PRIMITIVE) {
delayTime += hdfCompositeEffect.compositeEffects[i].primitiveEffect.delay;
} else {
MISC_HILOGE("Effect type is valid");
return ERROR;
}
if ((effectsPart.compositeEffects.size() >= COMPOSITE_EFFECT_PART) || (i == (effectSize - 1))) {
int32_t ret = VibratorDevice.EnableCompositeEffect(identifier, effectsPart);
if (ret != SUCCESS) {
MISC_HILOGE("EnableCompositeEffect failed");
return ERROR;
}
cv_.wait_for(vibrateLck, std::chrono::milliseconds(delayTime), [this] { return exitFlag_.load(); });
delayTime = 0;
effectsPart.compositeEffects.clear();
}
if (exitFlag_) {
VibratorDevice.Stop(identifier, HDF_VIBRATOR_MODE_PRESET);
MISC_HILOGD("Stop composite effect part, package:%{public}s", info.packageName.c_str());
return SUCCESS;
}
}
return SUCCESS;
}
#endif
void VibratorThread::UpdateVibratorEffect(const VibrateInfo &info, const VibratorIdentifierIPC& identifier,
std::vector<HdfWaveInformation> &waveInfos)
{
std::unique_lock<std::mutex> lck(currentVibrationMutex_);
currentVibration_ = info;
currentVibrateParams_ = identifier;
waveInfos_ = waveInfos;
}
VibrateInfo VibratorThread::GetCurrentVibrateInfo()
{
std::unique_lock<std::mutex> lck(currentVibrationMutex_);
return currentVibration_;
}
VibratorIdentifierIPC VibratorThread::GetCurrentVibrateParams()
{
std::unique_lock<std::mutex> lck(currentVibrationMutex_);
return currentVibrateParams_;
}
std::vector<HdfWaveInformation> VibratorThread::GetCurrentWaveInfo()
{
std::unique_lock<std::mutex> lck(currentVibrationMutex_);
return waveInfos_;
}
void VibratorThread::SetExitStatus(bool status)
{
exitFlag_.store(status);
}
void VibratorThread::WakeUp()
{
MISC_HILOGD("Notify the vibratorThread");
cv_.notify_one();
}
void VibratorThread::ResetVibrateInfo()
{
std::unique_lock<std::mutex> lck(currentVibrationMutex_);
currentVibration_.mode = "";
currentVibration_.packageName = "";
currentVibration_.pid = -1;
currentVibration_.uid = -1;
currentVibration_.usage = 0;
currentVibration_.systemUsage = false;
currentVibration_.duration = 0;
currentVibration_.effect = "";
currentVibration_.count = 0;
currentVibration_.intensity = 0;
}
#ifdef OHOS_BUILD_ENABLE_QOS
void VibratorThread::SetQosForThread()
{
std::unordered_map<std::string, std::string> payload;
payload["pid"] = std::to_string(getpid());
OHOS::ConcurrentTask::ConcurrentTaskClient::GetInstance().RequestAuth(payload);
auto ret = OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE);
if (ret != 0) {
MISC_HILOGE("Set vibrator thread qos failed, ret:%{public}d", ret);
} else {
MISC_HILOGD("Set vibrator thread qos success");
}
}
#endif
}
}