1 /*
2 * Copyright (c) 2024-2025 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 #include "charging_sound.h"
16
17 #include <dlfcn.h>
18 #include <mutex>
19 #include <securec.h>
20 #include "audio_stream_info.h"
21 #include "battery_log.h"
22 #include "config_policy_utils.h"
23 #include "errors.h"
24 #include "player.h"
25
26 namespace OHOS {
27 namespace PowerMgr {
28 // static non-local initializations
29 constexpr const char* CHARGER_SOUND_DEFAULT_PATH = "/vendor/etc/battery/PowerConnected.ogg";
30 constexpr const char* CHARGER_SOUND_RELATIVE_PATH = "resource/media/audio/ui/PowerConnected.ogg";
31 std::mutex g_playerPtrMutex;
32
33 // this static object is used as constructor/destructor, for now only one single instance is allowed.
34 std::shared_ptr<ChargingSound> ChargingSound::instance_ = std::make_shared<ChargingSound>();
35
36 namespace {
ICUCleanUp()37 void ICUCleanUp()
38 {
39 void* icuHandle = dlopen("libhmicuuc.z.so", RTLD_LAZY);
40 if (!icuHandle) {
41 BATTERY_HILOGE(COMP_SVC, "%{public}s: open so failed", __func__);
42 return;
43 }
44 auto getIcuVersion = reinterpret_cast<const char* (*)(void)>(dlsym(icuHandle, "GetIcuVersion"));
45 if (!getIcuVersion) {
46 BATTERY_HILOGE(COMP_SVC, "find GetIcuVersion symbol failed");
47 dlclose(icuHandle);
48 return;
49 }
50 const char* version = getIcuVersion();
51 constexpr int maxLength = 100;
52 constexpr const char* icuCleanFuncName = "u_cleanup";
53 auto buffer = std::make_unique<char[]>(maxLength);
54 int ret = sprintf_s(buffer.get(), maxLength, "%s_%s", icuCleanFuncName, version);
55 if (ret < 0) {
56 BATTERY_HILOGE(COMP_SVC, "string operation failed");
57 dlclose(icuHandle);
58 return;
59 }
60 auto CleanUp = reinterpret_cast<void (*)(void)>(dlsym(icuHandle, buffer.get()));
61 if (!CleanUp) {
62 BATTERY_HILOGE(COMP_SVC, "find u_cleanup symbol failed");
63 dlclose(icuHandle);
64 }
65 CleanUp();
66 dlclose(icuHandle);
67 }
68 } // namespace
69
GetPath(const char * uri) const70 std::string ChargingSound::GetPath(const char* uri) const
71 {
72 std::string ret {};
73 char buf[MAX_PATH_LEN] = {0};
74 char* path = GetOneCfgFile(uri, buf, MAX_PATH_LEN);
75 if (path) {
76 ret = path;
77 }
78 return ret;
79 }
80
ChargingSound()81 ChargingSound::ChargingSound()
82 {
83 uri_ = GetPath(CHARGER_SOUND_RELATIVE_PATH);
84 if (uri_.empty()) {
85 BATTERY_HILOGE(COMP_SVC, "get sound path failed, using fallback path");
86 uri_ = std::string{CHARGER_SOUND_DEFAULT_PATH};
87 }
88 BATTERY_HILOGI(COMP_SVC, "ChargingSound instance created");
89 }
90
~ChargingSound()91 ChargingSound::~ChargingSound()
92 {
93 Release();
94 std::shared_ptr<Media::Player> tmp = std::atomic_load_explicit(&player_, std::memory_order_acquire);
95 if (tmp) {
96 tmp->ReleaseClientListener();
97 }
98 ICUCleanUp();
99 BATTERY_HILOGI(COMP_SVC, "ChargingSound instance destroyed");
100 }
101
Stop()102 void ChargingSound::Stop()
103 {
104 std::shared_ptr<Media::Player> tmp = std::atomic_load_explicit(&player_, std::memory_order_acquire);
105 if (tmp) {
106 tmp->Stop();
107 }
108 isPlaying_.store(false);
109 }
110
Release()111 void ChargingSound::Release()
112 {
113 std::shared_ptr<Media::Player> tmp = std::atomic_load_explicit(&player_, std::memory_order_acquire);
114 if (tmp) {
115 tmp->ReleaseSync();
116 }
117 isPlaying_.store(false);
118 }
119
Play()120 bool ChargingSound::Play()
121 {
122 std::shared_ptr<Media::Player> tmp = std::atomic_load_explicit(&player_, std::memory_order_acquire);
123 if (!tmp) {
124 std::lock_guard<std::mutex> lock(g_playerPtrMutex);
125 tmp = std::atomic_load_explicit(&player_, std::memory_order_relaxed);
126 if (!tmp) {
127 tmp = Media::PlayerFactory::CreatePlayer();
128 }
129 std::atomic_store_explicit(&player_, tmp, std::memory_order_release);
130 }
131 if (!tmp) {
132 BATTERY_HILOGE(COMP_SVC, "create player failed");
133 return false;
134 }
135 tmp->Reset(); // reset avplayer
136 int32_t ret = Media::MSERR_OK;
137 ret = tmp->SetSource(uri_);
138 if (ret != Media::MSERR_OK) {
139 BATTERY_HILOGE(COMP_SVC, "set stream source failed, ret=%{public}d", ret);
140 return false;
141 }
142 Media::Format format;
143 format.PutIntValue(Media::PlayerKeys::CONTENT_TYPE, AudioStandard::CONTENT_TYPE_UNKNOWN);
144 format.PutIntValue(Media::PlayerKeys::STREAM_USAGE, AudioStandard::STREAM_USAGE_SYSTEM);
145 ret = tmp->SetParameter(format);
146 if (ret != Media::MSERR_OK) {
147 BATTERY_HILOGE(COMP_SVC, "Set stream usage to Player failed, ret=%{public}d", ret);
148 return false;
149 }
150 ret = tmp->Prepare();
151 if (ret != Media::MSERR_OK) {
152 BATTERY_HILOGE(COMP_SVC, "prepare failed, ret=%{public}d", ret);
153 return false;
154 }
155 isPlaying_.store(true);
156 ret = tmp->Play();
157 if (ret != Media::MSERR_OK) {
158 BATTERY_HILOGE(COMP_SVC, "play failed, ret=%{public}d", ret);
159 isPlaying_.store(false);
160 return false;
161 }
162 return true;
163 }
164
IsPlaying()165 bool ChargingSound::IsPlaying()
166 {
167 return isPlaying_.load();
168 }
169
IsPlayingGlobal()170 bool ChargingSound::IsPlayingGlobal()
171 {
172 return instance_->IsPlaying();
173 }
174
PlayGlobal()175 bool ChargingSound::PlayGlobal()
176 {
177 bool ret = instance_->Play();
178 if (!ret) {
179 instance_->Release();
180 }
181 return ret;
182 }
183
ReleaseGlobal()184 void ChargingSound::ReleaseGlobal()
185 {
186 instance_->Release();
187 }
188
189 //APIs
ChargingSoundStart()190 bool ChargingSoundStart()
191 {
192 return ChargingSound::PlayGlobal();
193 }
194
IsPlaying()195 bool IsPlaying()
196 {
197 return ChargingSound::IsPlayingGlobal();
198 }
199
200 } // namespace PowerMgr
201 } // namespace OHOS