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 "audio_haptic_manager_impl.h"
17 
18 #include "audio_haptic_player_impl.h"
19 
20 #include "audio_haptic_log.h"
21 #include "media_errors.h"
22 
23 #include "isoundpool.h"
24 #include "player.h"
25 
26 namespace {
27 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO_NAPI, "AudioHapticManagerImpl"};
28 }
29 
30 namespace OHOS {
31 namespace Media {
32 const std::int32_t MAX_PLAYER_NUM = 128;
33 const int32_t ERROR = -1;
34 const std::string FDHEAD = "fd://";
35 
36 std::shared_ptr<AudioHapticManager> AudioHapticManagerFactory::audioHapticManager_ = nullptr;
37 std::mutex AudioHapticManagerFactory::audioHapticManagerMutex_;
38 
ExtractFd(const std::string & uri)39 static int32_t ExtractFd(const std::string& uri)
40 {
41     if (uri.size() <= FDHEAD.size() || uri.substr(0, FDHEAD.length()) != FDHEAD) {
42         return ERROR;
43     }
44 
45     std::string numberPart = uri.substr(FDHEAD.length());
46     for (char c : numberPart) {
47         if (!std::isdigit(c)) {
48             MEDIA_LOGE("ExtractFd: The part after the FDHEAD is not all digits.");
49             return ERROR;
50         }
51     }
52 
53     int32_t fd = atoi(numberPart.c_str());
54     return fd > 0 ? fd : ERROR;
55 }
56 
DupFdFromUri(const std::string & uri)57 static std::string DupFdFromUri(const std::string &uri)
58 {
59     MEDIA_LOGI("DupFdFromUri uri: %{public}s", uri.c_str());
60     if (uri.find(FDHEAD) == std::string::npos) {
61         return uri;
62     }
63 
64     int32_t fd = ExtractFd(uri);
65     if (fd == ERROR) {
66         MEDIA_LOGE("DupFdFromUri ExtractFd failed");
67         return "";
68     }
69 
70     int32_t dupFd = dup(fd);
71     if (dupFd == ERROR) {
72         MEDIA_LOGE("DupFdFromUri failed. uri: %{public}s", uri.c_str());
73         return "";
74     }
75     return FDHEAD + std::to_string(dupFd);
76 }
77 
CreateAudioHapticManager()78 std::shared_ptr<AudioHapticManager> AudioHapticManagerFactory::CreateAudioHapticManager()
79 {
80     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
81     if (audioHapticManager_ == nullptr) {
82         audioHapticManager_ = std::make_shared<AudioHapticManagerImpl>();
83     }
84     CHECK_AND_RETURN_RET_LOG(audioHapticManager_ != nullptr, nullptr, "Failed to create audio haptic manager object");
85 
86     return audioHapticManager_;
87 }
88 
AudioHapticManagerImpl()89 AudioHapticManagerImpl::AudioHapticManagerImpl()
90 {
91     audioHapticPlayerMap_.clear();
92     curPlayerIndex_ = 0;
93     curPlayerCount_ = 0;
94 }
95 
~AudioHapticManagerImpl()96 AudioHapticManagerImpl::~AudioHapticManagerImpl()
97 {
98     for (auto &[sourceId, info] : audioHapticPlayerMap_) {
99         (void)sourceId;
100         if (!info->audioUri_.empty()) {
101             int32_t fd = ExtractFd(info->audioUri_);
102             if (fd != ERROR) {
103                 close(fd);
104             }
105         }
106         if (!info->hapticSource_.hapticUri.empty()) {
107             int32_t fd = ExtractFd(info->hapticSource_.hapticUri);
108             if (fd != ERROR) {
109                 close(fd);
110             }
111         }
112     }
113     audioHapticPlayerMap_.clear();
114     curPlayerIndex_ = 0;
115     curPlayerCount_ = 0;
116 }
117 
RegisterSourceWithEffectId(const std::string & audioUri,const std::string & effectId)118 int32_t AudioHapticManagerImpl::RegisterSourceWithEffectId(const std::string &audioUri, const std::string &effectId)
119 {
120     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
121     if (effectId == "") {
122         MEDIA_LOGE("RegisterSourceWithEffectId failed. The effectId is empty!");
123         return -1;
124     }
125     if (curPlayerCount_ >= MAX_PLAYER_NUM) {
126         MEDIA_LOGE("RegisterSourceWithEffectId failed. curPlayerCount_: %{public}d", curPlayerCount_);
127         return -1;
128     }
129     curPlayerIndex_ = (curPlayerIndex_ + 1) % MAX_PLAYER_NUM;
130     while (audioHapticPlayerMap_[curPlayerIndex_] != nullptr) {
131         curPlayerIndex_ = (curPlayerIndex_ + 1) % MAX_PLAYER_NUM;
132     }
133     int32_t sourceId = curPlayerIndex_;
134     HapticSource sourceUri = {"", effectId};
135     audioHapticPlayerMap_[sourceId] = std::make_shared<AudioHapticPlayerInfo>(audioUri, sourceUri,
136         AUDIO_LATENCY_MODE_FAST, AudioStandard::StreamUsage::STREAM_USAGE_MUSIC);
137     curPlayerCount_ += 1;
138     MEDIA_LOGI("Finish to RegisterSourceWithEffectId. effectId: %{public}s, sourceId: %{public}d",
139         effectId.c_str(), sourceId);
140     return sourceId;
141 }
142 
RegisterSource(const std::string & audioUri,const std::string & hapticUri)143 int32_t AudioHapticManagerImpl::RegisterSource(const std::string &audioUri, const std::string &hapticUri)
144 {
145     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
146 
147     if (curPlayerCount_ >= MAX_PLAYER_NUM) {
148         MEDIA_LOGE("RegisterSource failed curPlayerCount_: %{public}d", curPlayerCount_);
149         return -1;
150     }
151     curPlayerIndex_ = (curPlayerIndex_ + 1) % MAX_PLAYER_NUM;
152     while (audioHapticPlayerMap_[curPlayerIndex_] != nullptr) {
153         curPlayerIndex_ = (curPlayerIndex_ + 1) % MAX_PLAYER_NUM;
154     }
155     std::string audioUriStr = DupFdFromUri(audioUri);
156     std::string hapticUriStr = DupFdFromUri(hapticUri);
157 
158     int32_t sourceId = curPlayerIndex_;
159     HapticSource sourceUri = {hapticUriStr, ""};
160     audioHapticPlayerMap_[sourceId] = std::make_shared<AudioHapticPlayerInfo>(audioUriStr, sourceUri,
161         AUDIO_LATENCY_MODE_NORMAL, AudioStandard::StreamUsage::STREAM_USAGE_MUSIC);
162     curPlayerCount_ += 1;
163     MEDIA_LOGI("Finish to RegisterSource. audioUri: %{public}s, hapticUri: %{public}s, sourceId: %{public}d",
164         audioUriStr.c_str(), hapticUriStr.c_str(), sourceId);
165     return sourceId;
166 }
167 
UnregisterSource(const int32_t & sourceID)168 int32_t AudioHapticManagerImpl::UnregisterSource(const int32_t &sourceID)
169 {
170     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
171 
172     if (audioHapticPlayerMap_.count(sourceID) == 0 || audioHapticPlayerMap_[sourceID] == nullptr) {
173         MEDIA_LOGE("UnregisterSource failed sourceID: %{public}d", sourceID);
174         return MSERR_INVALID_VAL;
175     }
176 
177     std::shared_ptr<AudioHapticPlayerInfo> info = audioHapticPlayerMap_[sourceID];
178     if (!info->audioUri_.empty()) {
179         int32_t fd = ExtractFd(info->audioUri_);
180         if (fd != ERROR) {
181             close(fd);
182         }
183     }
184     if (!info->hapticSource_.hapticUri.empty()) {
185         int32_t fd = ExtractFd(info->hapticSource_.hapticUri);
186         if (fd != ERROR) {
187             close(fd);
188         }
189     }
190 
191     audioHapticPlayerMap_[sourceID] = nullptr;
192     audioHapticPlayerMap_.erase(sourceID);
193     curPlayerCount_ -= 1;
194     MEDIA_LOGI("Finish to UnregisterSource. sourceId: %{public}d", sourceID);
195     return MSERR_OK;
196 }
197 
CheckAudioLatencyMode(const int32_t & sourceId,const AudioLatencyMode & latencyMode)198 bool AudioHapticManagerImpl::CheckAudioLatencyMode(const int32_t &sourceId, const AudioLatencyMode &latencyMode)
199 {
200     if (latencyMode != AUDIO_LATENCY_MODE_NORMAL && latencyMode != AUDIO_LATENCY_MODE_FAST) {
201         MEDIA_LOGE("The AudioLatencyMode %{public}d is invalid!", latencyMode);
202         return false;
203     }
204 
205     // effectId can only be used for low latency mode.
206     HapticSource source = audioHapticPlayerMap_[sourceId]->hapticSource_;
207     if (source.effectId != "" && latencyMode != AUDIO_LATENCY_MODE_FAST) {
208         MEDIA_LOGE("The effectId source can only be used for low latency mode!");
209         return false;
210     }
211 
212     return true;
213 }
214 
SetAudioLatencyMode(const int32_t & sourceId,const AudioLatencyMode & latencyMode)215 int32_t AudioHapticManagerImpl::SetAudioLatencyMode(const int32_t &sourceId, const AudioLatencyMode &latencyMode)
216 {
217     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
218     if (audioHapticPlayerMap_.count(sourceId) == 0 || audioHapticPlayerMap_[sourceId] == nullptr) {
219         MEDIA_LOGE("SetAudioLatencyMode: failed for invalid sourceID: %{public}d", sourceId);
220         return MSERR_INVALID_VAL;
221     }
222     if (!CheckAudioLatencyMode(sourceId, latencyMode)) {
223         MEDIA_LOGE("SetAudioLatencyMode: failed for invalid latencyMode: %{public}d", latencyMode);
224         return MSERR_INVALID_VAL;
225     }
226     audioHapticPlayerMap_[sourceId]->latencyMode_ = latencyMode;
227     return MSERR_OK;
228 }
229 
CheckAudioStreamUsage(const AudioStandard::StreamUsage & streamUsage)230 bool AudioHapticManagerImpl::CheckAudioStreamUsage(const AudioStandard::StreamUsage &streamUsage)
231 {
232     switch (streamUsage) {
233         case AudioStandard::STREAM_USAGE_MUSIC:
234         case AudioStandard::STREAM_USAGE_VOICE_COMMUNICATION:
235         case AudioStandard::STREAM_USAGE_VOICE_ASSISTANT:
236         case AudioStandard::STREAM_USAGE_ALARM:
237         case AudioStandard::STREAM_USAGE_VOICE_MESSAGE:
238         case AudioStandard::STREAM_USAGE_RINGTONE:
239         case AudioStandard::STREAM_USAGE_VOICE_RINGTONE:
240         case AudioStandard::STREAM_USAGE_NOTIFICATION:
241         case AudioStandard::STREAM_USAGE_ACCESSIBILITY:
242         case AudioStandard::STREAM_USAGE_SYSTEM:
243         case AudioStandard::STREAM_USAGE_MOVIE:
244         case AudioStandard::STREAM_USAGE_GAME:
245         case AudioStandard::STREAM_USAGE_AUDIOBOOK:
246         case AudioStandard::STREAM_USAGE_NAVIGATION:
247         case AudioStandard::STREAM_USAGE_DTMF:
248         case AudioStandard::STREAM_USAGE_ENFORCED_TONE:
249             return true;
250         default:
251             break;
252     }
253     MEDIA_LOGE("CheckAudioStreamUsage: streamUsage %{public}d is invalid", streamUsage);
254     return false;
255 }
256 
SetStreamUsage(const int32_t & sourceID,const AudioStandard::StreamUsage & streamUsage)257 int32_t AudioHapticManagerImpl::SetStreamUsage(const int32_t &sourceID, const AudioStandard::StreamUsage &streamUsage)
258 {
259     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
260     if (audioHapticPlayerMap_.count(sourceID) == 0 || audioHapticPlayerMap_[sourceID] == nullptr) {
261         MEDIA_LOGE("SetStreamUsage: failed for invalid sourceID: %{public}d", sourceID);
262         return MSERR_INVALID_VAL;
263     }
264     if (!CheckAudioStreamUsage(streamUsage)) {
265         MEDIA_LOGE("SetStreamUsage: failed for invalid latencyMode: %{public}d", streamUsage);
266         return MSERR_INVALID_VAL;
267     }
268     audioHapticPlayerMap_[sourceID]->streamUsage_ = streamUsage;
269     return MSERR_OK;
270 }
271 
CreatePlayer(const int32_t & sourceID,const AudioHapticPlayerOptions & audioHapticPlayerOptions)272 std::shared_ptr<AudioHapticPlayer> AudioHapticManagerImpl::CreatePlayer(const int32_t &sourceID,
273     const AudioHapticPlayerOptions &audioHapticPlayerOptions)
274 {
275     std::lock_guard<std::mutex> lock(audioHapticManagerMutex_);
276     if (audioHapticPlayerMap_.count(sourceID) == 0 || audioHapticPlayerMap_[sourceID] == nullptr) {
277         MEDIA_LOGE("CreatePlayer failed for sourceID: %{public}d", sourceID);
278         return nullptr;
279     }
280 
281     std::shared_ptr<AudioHapticPlayerInfo> audioHapticPlayerInfo = audioHapticPlayerMap_[sourceID];
282     AudioHapticPlayerParam param = AudioHapticPlayerParam(audioHapticPlayerOptions,
283         audioHapticPlayerInfo->audioUri_, audioHapticPlayerInfo->hapticSource_,
284         audioHapticPlayerInfo->latencyMode_, audioHapticPlayerInfo->streamUsage_);
285     std::shared_ptr<AudioHapticPlayer> audioHapticPlayer = AudioHapticPlayerFactory::CreateAudioHapticPlayer(param);
286 
287     if (audioHapticPlayer == nullptr) {
288         MEDIA_LOGE("CreatePlayer failed for sourceID: %{public}d", sourceID);
289         return nullptr;
290     }
291     return audioHapticPlayer;
292 }
293 } // namesapce AudioStandard
294 } // namespace OHOS
295