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 #ifndef LOG_TAG
16 #define LOG_TAG "NapiAudioSessionMgr"
17 #endif
18 
19 #include "napi_audio_error.h"
20 #include "napi_param_utils.h"
21 #include "napi_audio_enum.h"
22 #include "audio_errors.h"
23 #include "audio_info.h"
24 #include "napi_audio_session_callback.h"
25 #include "napi_audio_session_manager.h"
26 
27 namespace OHOS {
28 namespace AudioStandard {
29 using namespace std;
30 using namespace HiviewDFX;
31 static __thread napi_ref g_sessionMgrConstructor = nullptr;
32 
33 const std::string AUDIO_SESSION_MGR_NAPI_CLASS_NAME = "AudioSessionManager";
34 
NapiAudioSessionMgr()35 NapiAudioSessionMgr::NapiAudioSessionMgr()
36     : env_(nullptr), audioSessionMngr_(nullptr) {}
37 
38 NapiAudioSessionMgr::~NapiAudioSessionMgr() = default;
39 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)40 void NapiAudioSessionMgr::Destructor(napi_env env, void *nativeObject, void *finalizeHint)
41 {
42     if (nativeObject != nullptr) {
43         auto obj = static_cast<NapiAudioSessionMgr *>(nativeObject);
44         ObjectRefMap<NapiAudioSessionMgr>::DecreaseRef(obj);
45     }
46     AUDIO_INFO_LOG("Destructor is successful");
47 }
48 
Construct(napi_env env,napi_callback_info info)49 napi_value NapiAudioSessionMgr::Construct(napi_env env, napi_callback_info info)
50 {
51     AUDIO_DEBUG_LOG("Construct");
52     napi_status status;
53     napi_value result = nullptr;
54     NapiParamUtils::GetUndefinedValue(env);
55 
56     size_t argc = ARGS_TWO;
57     napi_value argv[ARGS_TWO] = {0};
58     napi_value thisVar = nullptr;
59     void *data = nullptr;
60     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
61     unique_ptr<NapiAudioSessionMgr> napiSessionMgr = make_unique<NapiAudioSessionMgr>();
62     CHECK_AND_RETURN_RET_LOG(napiSessionMgr != nullptr, result, "No memory");
63 
64     napiSessionMgr->env_ = env;
65     napiSessionMgr->audioSessionMngr_ = AudioSessionManager::GetInstance();
66     ObjectRefMap<NapiAudioSessionMgr>::Insert(napiSessionMgr.get());
67 
68     status = napi_wrap(env, thisVar, static_cast<void*>(napiSessionMgr.get()),
69         NapiAudioSessionMgr::Destructor, nullptr, nullptr);
70     if (status != napi_ok) {
71         ObjectRefMap<NapiAudioSessionMgr>::Erase(napiSessionMgr.get());
72         return result;
73     }
74     napiSessionMgr.release();
75     return thisVar;
76 }
77 
Init(napi_env env,napi_value exports)78 napi_value NapiAudioSessionMgr::Init(napi_env env, napi_value exports)
79 {
80     napi_status status;
81     napi_value constructor;
82     napi_value result = nullptr;
83     const int32_t refCount = ARGS_ONE;
84     napi_get_undefined(env, &result);
85 
86     napi_property_descriptor audio_session_mgr_properties[] = {
87         DECLARE_NAPI_FUNCTION("on", On),
88         DECLARE_NAPI_FUNCTION("off", Off),
89         DECLARE_NAPI_FUNCTION("activateAudioSession", ActivateAudioSession),
90         DECLARE_NAPI_FUNCTION("deactivateAudioSession", DeactivateAudioSession),
91         DECLARE_NAPI_FUNCTION("isAudioSessionActivated", IsAudioSessionActivated),
92     };
93 
94     status = napi_define_class(env, AUDIO_SESSION_MGR_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Construct, nullptr,
95         sizeof(audio_session_mgr_properties) / sizeof(audio_session_mgr_properties[PARAM0]),
96         audio_session_mgr_properties, &constructor);
97     CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_define_class fail");
98 
99     status = napi_create_reference(env, constructor, refCount, &g_sessionMgrConstructor);
100     CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_create_reference fail");
101     status = napi_set_named_property(env, exports, AUDIO_SESSION_MGR_NAPI_CLASS_NAME.c_str(), constructor);
102     CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "napi_set_named_property fail");
103     return exports;
104 }
105 
CreateSessionManagerWrapper(napi_env env)106 napi_value NapiAudioSessionMgr::CreateSessionManagerWrapper(napi_env env)
107 {
108     napi_status status;
109     napi_value result = nullptr;
110     napi_value constructor;
111 
112     status = napi_get_reference_value(env, g_sessionMgrConstructor, &constructor);
113     if (status != napi_ok) {
114         AUDIO_ERR_LOG("Failed in CreateSessionManagerWrapper, %{public}d", status);
115         goto fail;
116     }
117     status = napi_new_instance(env, constructor, PARAM0, nullptr, &result);
118     if (status != napi_ok) {
119         AUDIO_ERR_LOG("napi_new_instance failed, status:%{public}d", status);
120         goto fail;
121     }
122     return result;
123 
124 fail:
125     napi_get_undefined(env, &result);
126     return result;
127 }
128 
CheckContextStatus(std::shared_ptr<AudioSessionMgrAsyncContext> context)129 bool NapiAudioSessionMgr::CheckContextStatus(std::shared_ptr<AudioSessionMgrAsyncContext> context)
130 {
131     CHECK_AND_RETURN_RET_LOG(context != nullptr, false, "context object is nullptr.");
132     if (context->native == nullptr) {
133         context->SignError(NAPI_ERR_SYSTEM);
134         return false;
135     }
136     return true;
137 }
138 
ActivateAudioSession(napi_env env,napi_callback_info info)139 napi_value NapiAudioSessionMgr::ActivateAudioSession(napi_env env, napi_callback_info info)
140 {
141     auto context = std::make_shared<AudioSessionMgrAsyncContext>();
142     if (context == nullptr) {
143         AUDIO_ERR_LOG("ActivateAudioSession failed : no memory");
144         NapiAudioError::ThrowError(env, "ActivateAudioSession failed : no memory", NAPI_ERR_NO_MEMORY);
145         return NapiParamUtils::GetUndefinedValue(env);
146     }
147 
148     auto inputParser = [env, context](size_t argc, napi_value *argv) {
149         NAPI_CHECK_ARGS_RETURN_VOID(context, argc >= ARGS_ONE, "invalid arguments", NAPI_ERR_INVALID_PARAM);
150         context->status = NapiParamUtils::GetAudioSessionStrategy(env, context->audioSessionStrategy, argv[PARAM0]);
151         NAPI_CHECK_ARGS_RETURN_VOID(context, context->status == napi_ok, "getAudioSessionStrategy failed",
152             NAPI_ERR_INVALID_PARAM);
153     };
154     context->GetCbInfo(env, info, inputParser);
155 
156     if ((context->status != napi_ok) && (context->errCode == NAPI_ERR_INPUT_INVALID)) {
157         NapiAudioError::ThrowError(env, context->errCode, context->errMessage);
158         return NapiParamUtils::GetUndefinedValue(env);
159     }
160 
161     auto executor = [context]() {
162         CHECK_AND_RETURN_LOG(CheckContextStatus(context), "context object state is error.");
163         auto obj = reinterpret_cast<NapiAudioSessionMgr*>(context->native);
164         ObjectRefMap objectGuard(obj);
165         auto *napiSessionMgr = objectGuard.GetPtr();
166         context->intValue = napiSessionMgr->audioSessionMngr_->ActivateAudioSession(context->audioSessionStrategy);
167         if (context->intValue != SUCCESS) {
168             context->SignError(NAPI_ERR_SYSTEM);
169         }
170     };
171 
172     auto complete = [env, context](napi_value &output) {
173         NapiParamUtils::SetValueInt32(env, context->intValue, output);
174     };
175     return NapiAsyncWork::Enqueue(env, context, "ActivateAudioSession", executor, complete);
176 }
177 
DeactivateAudioSession(napi_env env,napi_callback_info info)178 napi_value NapiAudioSessionMgr::DeactivateAudioSession(napi_env env, napi_callback_info info)
179 {
180     auto context = std::make_shared<AudioSessionMgrAsyncContext>();
181     if (context == nullptr) {
182         AUDIO_ERR_LOG("DeactivateAudioSession failed : no memory");
183         NapiAudioError::ThrowError(env, "DeactivateAudioSession failed : no memory", NAPI_ERR_NO_MEMORY);
184         return NapiParamUtils::GetUndefinedValue(env);
185     }
186     context->GetCbInfo(env, info);
187 
188     auto executor = [context]() {
189         CHECK_AND_RETURN_LOG(CheckContextStatus(context), "context object state is error.");
190         auto obj = reinterpret_cast<NapiAudioSessionMgr*>(context->native);
191         ObjectRefMap objectGuard(obj);
192         auto *napiSessionMgr = objectGuard.GetPtr();
193         context->intValue = napiSessionMgr->audioSessionMngr_->DeactivateAudioSession();
194         if (context->intValue != SUCCESS) {
195             context->SignError(NAPI_ERR_SYSTEM);
196         }
197     };
198 
199     auto complete = [env, context](napi_value &output) {
200         NapiParamUtils::SetValueInt32(env, context->intValue, output);
201     };
202     return NapiAsyncWork::Enqueue(env, context, "DeactivateAudioSession", executor, complete);
203 }
204 
IsAudioSessionActivated(napi_env env,napi_callback_info info)205 napi_value NapiAudioSessionMgr::IsAudioSessionActivated(napi_env env, napi_callback_info info)
206 {
207     napi_value result = nullptr;
208     auto context = std::make_shared<AudioSessionMgrAsyncContext>();
209     if (context == nullptr) {
210         AUDIO_ERR_LOG("IsAudioSessionActivated failed : no memory");
211         NapiAudioError::ThrowError(env, "IsAudioSessionActivated failed : no memory", NAPI_ERR_NO_MEMORY);
212         return NapiParamUtils::GetUndefinedValue(env);
213     }
214     context->GetCbInfo(env, info);
215 
216     CHECK_AND_RETURN_RET_LOG(CheckContextStatus(context), result,  "context object state is error.");
217     auto obj = reinterpret_cast<NapiAudioSessionMgr*>(context->native);
218     ObjectRefMap objectGuard(obj);
219     auto *napiSessionMgr = objectGuard.GetPtr();
220     if (napiSessionMgr == nullptr || napiSessionMgr->audioSessionMngr_ == nullptr) {
221         AUDIO_ERR_LOG("The napiSessionMgr or audioSessionMngr is nullptr");
222         return nullptr;
223     }
224     context->isActive = napiSessionMgr->audioSessionMngr_->IsAudioSessionActivated();
225     NapiParamUtils::SetValueBoolean(env, context->isActive, result);
226     return result;
227 }
228 
RegisterCallback(napi_env env,napi_value jsThis,napi_value * args,const std::string & cbName)229 void NapiAudioSessionMgr::RegisterCallback(napi_env env, napi_value jsThis,
230     napi_value *args, const std::string &cbName)
231 {
232     if (!cbName.compare(AUDIOSESSION_CALLBACK_NAME)) {
233         NapiAudioSessionMgr *napiSessionMgr = nullptr;
234         napi_status status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiSessionMgr));
235         CHECK_AND_RETURN_LOG((status == napi_ok) && (napiSessionMgr != nullptr) &&
236             (napiSessionMgr->audioSessionMngr_ != nullptr), "Failed to retrieve session mgr napi instance.");
237         RegisterAudioSessionCallback(env, args, cbName, napiSessionMgr);
238     } else {
239         AUDIO_ERR_LOG("NapiAudioSessionMgr::No such callback supported");
240         NapiAudioError::ThrowError(env, NAPI_ERR_INVALID_PARAM,
241             "parameter verification failed: The param of type is not supported");
242     }
243 }
244 
RegisterAudioSessionCallback(napi_env env,napi_value * args,const std::string & cbName,NapiAudioSessionMgr * napiSessionMgr)245 void NapiAudioSessionMgr::RegisterAudioSessionCallback(napi_env env, napi_value *args,
246     const std::string &cbName, NapiAudioSessionMgr *napiSessionMgr)
247 {
248     if (!napiSessionMgr->audioSessionCallbackNapi_) {
249         napiSessionMgr->audioSessionCallbackNapi_ = std::make_shared<NapiAudioSessionCallback>(env);
250         CHECK_AND_RETURN_LOG(napiSessionMgr->audioSessionCallbackNapi_ != nullptr,
251             "NapiAudioSessionMgr: Memory Allocation Failed !!");
252 
253         int32_t ret =
254             napiSessionMgr->audioSessionMngr_->SetAudioSessionCallback(napiSessionMgr->audioSessionCallbackNapi_);
255         CHECK_AND_RETURN_LOG(ret == SUCCESS, "Registering of AudioSessionDeactiveEvent Callback Failed");
256     }
257 
258     std::shared_ptr<NapiAudioSessionCallback> cb =
259         std::static_pointer_cast<NapiAudioSessionCallback>(napiSessionMgr->audioSessionCallbackNapi_);
260     cb->SaveCallbackReference(args[PARAM1]);
261 
262     AUDIO_INFO_LOG("OnRendererStateChangeCallback is successful");
263 }
264 
On(napi_env env,napi_callback_info info)265 napi_value NapiAudioSessionMgr::On(napi_env env, napi_callback_info info)
266 {
267     const size_t requireArgc = ARGS_TWO;
268     size_t argc = ARGS_THREE;
269 
270     napi_value undefinedResult = nullptr;
271     napi_get_undefined(env, &undefinedResult);
272 
273     napi_value args[requireArgc + PARAM1] = {nullptr, nullptr, nullptr};
274     napi_value jsThis = nullptr;
275     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
276     CHECK_AND_RETURN_RET_LOG(status == napi_ok && argc == requireArgc, NapiAudioError::ThrowErrorAndReturn(env,
277         NAPI_ERR_INPUT_INVALID, "mandatory parameters are left unspecified"), "status for arguments error");
278 
279     napi_valuetype eventType = napi_undefined;
280     napi_typeof(env, args[PARAM0], &eventType);
281     CHECK_AND_RETURN_RET_LOG(eventType == napi_string, NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_INPUT_INVALID,
282         "incorrect parameter types: The type of eventType must be string"), "eventType error");
283     std::string callbackName = NapiParamUtils::GetStringArgument(env, args[PARAM0]);
284     AUDIO_DEBUG_LOG("AudioStreamMgrNapi: On callbackName: %{public}s", callbackName.c_str());
285 
286     napi_valuetype handler = napi_undefined;
287     napi_typeof(env, args[PARAM1], &handler);
288     CHECK_AND_RETURN_RET_LOG(
289         handler == napi_function, NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_INPUT_INVALID,
290         "incorrect parameter types: The type of handler must be function"), "handler is invalid");
291     RegisterCallback(env, jsThis, args, callbackName);
292     return undefinedResult;
293 }
294 
UnregisterCallback(napi_env env,napi_value jsThis)295 void NapiAudioSessionMgr::UnregisterCallback(napi_env env, napi_value jsThis)
296 {
297     AUDIO_INFO_LOG("UnregisterCallback");
298     NapiAudioSessionMgr *napiSessionMgr = nullptr;
299     napi_status status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiSessionMgr));
300     CHECK_AND_RETURN_LOG((status == napi_ok) && (napiSessionMgr != nullptr) &&
301         (napiSessionMgr->audioSessionMngr_ != nullptr), "Failed to retrieve session mgr  instance.");
302 
303     int32_t ret = napiSessionMgr->audioSessionMngr_->UnsetAudioSessionCallback();
304     if (ret) {
305         AUDIO_ERR_LOG("Unset AudioSessionCallback Failed");
306         return;
307     }
308     if (napiSessionMgr->audioSessionCallbackNapi_ != nullptr) {
309         napiSessionMgr->audioSessionCallbackNapi_.reset();
310         napiSessionMgr->audioSessionCallbackNapi_ = nullptr;
311     }
312     AUDIO_ERR_LOG("Unset AudioSessionCallback Success");
313 }
314 
UnregisterCallbackCarryParam(napi_env env,napi_value jsThis,napi_value * args,size_t len)315 void NapiAudioSessionMgr::UnregisterCallbackCarryParam(napi_env env, napi_value jsThis, napi_value *args, size_t len)
316 {
317     AUDIO_INFO_LOG("UnregisterCallback");
318     NapiAudioSessionMgr *napiSessionMgr = nullptr;
319     napi_status status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&napiSessionMgr));
320     CHECK_AND_RETURN_LOG((status == napi_ok) && (napiSessionMgr != nullptr) &&
321         (napiSessionMgr->audioSessionMngr_ != nullptr), "Failed to retrieve session mgr  instance.");
322     if (!napiSessionMgr->audioSessionCallbackNapi_) {
323         napiSessionMgr->audioSessionCallbackNapi_ = std::make_shared<NapiAudioSessionCallback>(env);
324         CHECK_AND_RETURN_LOG(napiSessionMgr->audioSessionCallbackNapi_ != nullptr,
325             "Memory Allocation Failed !!");
326         int32_t ret =
327             napiSessionMgr->audioSessionMngr_->UnsetAudioSessionCallback(napiSessionMgr->audioSessionCallbackNapi_);
328         CHECK_AND_RETURN_LOG(ret == SUCCESS, "Unregister Callback CarryParam Failed");
329     }
330     std::shared_ptr<NapiAudioSessionCallback> cb =
331         std::static_pointer_cast<NapiAudioSessionCallback>(napiSessionMgr->audioSessionCallbackNapi_);
332     cb->SaveCallbackReference(args[PARAM0]);
333     AUDIO_ERR_LOG("Unset AudioSessionCallback Success");
334 }
335 
Off(napi_env env,napi_callback_info info)336 napi_value NapiAudioSessionMgr::Off(napi_env env, napi_callback_info info)
337 {
338     const size_t requireArgc = ARGS_ONE;
339     size_t argc = PARAM2;
340 
341     napi_value undefinedResult = nullptr;
342     napi_get_undefined(env, &undefinedResult);
343 
344     napi_value args[requireArgc + PARAM1] = {nullptr, nullptr};
345     napi_value jsThis = nullptr;
346     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
347     if (status != napi_ok || argc < requireArgc) {
348         AUDIO_ERR_LOG("Off fail to napi_get_cb_info/Requires min 1 parameters");
349         NapiAudioError::ThrowError(env, NAPI_ERR_INPUT_INVALID, "mandatory parameters are left unspecified");
350         return undefinedResult;
351     }
352 
353     napi_valuetype eventType = napi_undefined;
354     napi_typeof(env, args[PARAM0], &eventType);
355     CHECK_AND_RETURN_RET_LOG(eventType == napi_string, NapiAudioError::ThrowErrorAndReturn(env, NAPI_ERR_INPUT_INVALID,
356         "incorrect parameter types: The type of eventType must be string"), "event error");
357     std::string callbackName = NapiParamUtils::GetStringArgument(env, args[PARAM0]);
358     if (!callbackName.compare(AUDIOSESSION_CALLBACK_NAME)) {
359         napi_valuetype handler = napi_undefined;
360         napi_typeof(env, args[PARAM1], &handler);
361         if (handler == napi_function) {
362             UnregisterCallbackCarryParam(env, jsThis, args, sizeof(args));
363         } else {
364             UnregisterCallback(env, jsThis);
365         }
366     } else {
367         AUDIO_ERR_LOG("NapiAudioSessionMgr::No such callback supported");
368         NapiAudioError::ThrowError(env, NAPI_ERR_INVALID_PARAM,
369             "parameter verification failed: The param of type is not supported");
370     }
371     return undefinedResult;
372 }
373 }  // namespace AudioStandard
374 }  // namespace OHOS
375