1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "system_sound_vibrator.h"
17 
18 #include <fcntl.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 
22 #ifdef SUPPORT_VIBRATOR
23 #include "vibrator_agent.h"
24 #endif
25 
26 #include "media_errors.h"
27 #include "system_sound_log.h"
28 
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "SystemSoundVibrator"};
31 }
32 
33 namespace OHOS {
34 namespace Media {
35 const std::string PREFIX = "fd://";
36 
37 #ifdef SUPPORT_VIBRATOR
38 const std::unordered_map<VibrationType, VibratorUsage> VIBRATOR_USAGE_MAP = {
39     {VibrationType::VIBRATION_RINGTONE, USAGE_RING},
40     {VibrationType::VIBRATION_SYSTEM_TONE, USAGE_NOTIFICATION},
41 };
42 
43 const std::unordered_map<VibrationType, int32_t> LOOP_COUNT_MAP = {
44     // Default loop count. Ringtone need be repeated.
45     {VibrationType::VIBRATION_RINGTONE, 10},
46     {VibrationType::VIBRATION_SYSTEM_TONE, 1},
47 };
48 
49 const std::unordered_map<VibrationType, std::string> EFFECT_ID_MAP = {
50     // Default effectId
51     {VibrationType::VIBRATION_RINGTONE, "haptic.ringtone.Dream_It_Possible"},
52     {VibrationType::VIBRATION_SYSTEM_TONE, "haptic.pattern.type4"},
53 };
54 #endif
55 
StartVibrator(VibrationType type)56 int32_t SystemSoundVibrator::StartVibrator(VibrationType type)
57 {
58     MEDIA_LOGD("StartVibrator: for vibration type %{public}d", type);
59     int32_t result = MSERR_OK;
60 #ifdef SUPPORT_VIBRATOR
61     bool setUsageRet = Sensors::SetUsage(VIBRATOR_USAGE_MAP.at(type));
62     bool setLoopRet = Sensors::SetLoopCount(LOOP_COUNT_MAP.at(type));
63     result = Sensors::StartVibrator(EFFECT_ID_MAP.at(type).c_str());
64     MEDIA_LOGI("StartVibrator: setUsageRet %{public}d, setLoopRet %{public}d, startRet %{public}d",
65         setUsageRet, setLoopRet, result);
66 #endif
67     return result;
68 }
69 
StartVibratorForSystemTone(const std::string & hapticUri)70 int32_t SystemSoundVibrator::StartVibratorForSystemTone(const std::string &hapticUri)
71 {
72     MEDIA_LOGI("Start vibrator with hapticUri %{public}s", hapticUri.c_str());
73     int32_t result = MSERR_OK;
74 #ifdef SUPPORT_VIBRATOR
75     if (!(Sensors::IsSupportVibratorCustom())) {
76         MEDIA_LOGE("Custom vibration is not supported for this device!");
77         return MSERR_INVALID_OPERATION;
78     }
79     int32_t fd = ExtractFd(hapticUri);
80     if (fd == -1) {
81         MEDIA_LOGI("hapticUri is not fd. Try to open it");
82         char realPathRes[PATH_MAX + 1] = {'\0'};
83         CHECK_AND_RETURN_RET_LOG((strlen(hapticUri.c_str()) < PATH_MAX) &&
84             (realpath(hapticUri.c_str(), realPathRes) != nullptr), MSERR_OPEN_FILE_FAILED, "Invalid file path length");
85         std::string realPathStr(realPathRes);
86         fd = open(realPathStr.c_str(), O_RDONLY);
87         if (fd == -1) {
88             MEDIA_LOGE("Failed to open hapticUri!");
89             return MSERR_OPEN_FILE_FAILED;
90         }
91     }
92 
93     struct stat64 statbuf = { 0 };
94     if (fstat64(fd, &statbuf) != 0) {
95         MEDIA_LOGE("Failed to open fd and get size.");
96         close(fd);
97         return MSERR_OPEN_FILE_FAILED;
98     }
99     Sensors::SetUsage(USAGE_NOTIFICATION);
100     Sensors::SetLoopCount(1);
101     result = Sensors::PlayVibratorCustom(fd, 0, statbuf.st_size);
102     close(fd);
103 #endif
104 
105     return result;
106 }
107 
ExtractFd(const std::string & hapticsUri)108 int32_t SystemSoundVibrator::ExtractFd(const std::string& hapticsUri)
109 {
110     if (hapticsUri.size() <= PREFIX.size() || hapticsUri.substr(0, PREFIX.length()) != PREFIX) {
111         MEDIA_LOGW("ExtractFd: Input does not start with the required PREFIX.");
112         return -1;
113     }
114 
115     std::string numberPart = hapticsUri.substr(PREFIX.length());
116     for (char c : numberPart) {
117         if (!std::isdigit(c)) {
118             MEDIA_LOGE("ExtractFd: The part after the PREFIX is not all digits.");
119             return -1;
120         }
121     }
122     return std::stoi(numberPart);
123 }
124 
StopVibrator()125 int32_t SystemSoundVibrator::StopVibrator()
126 {
127     int32_t result = MSERR_OK;
128 #ifdef SUPPORT_VIBRATOR
129     result = Sensors::Cancel();
130     MEDIA_LOGI("StopVibrator: %{public}d", result);
131 #endif
132     return result;
133 }
134 } // namesapce AudioStandard
135 } // namespace OHOS
136