1 /*
2 * Copyright (c) 2022-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 "js_device_selection_listener.h"
17
18 #include "base/continuationmgr_log.h"
19 #include "js_runtime_utils.h"
20 #include "napi_common_util.h"
21
22 namespace OHOS {
23 namespace DistributedSchedule {
24 using namespace OHOS::AbilityRuntime;
25 namespace {
26 const std::string TAG = "JsDeviceSelectionListener";
27 }
28
OnDeviceConnect(const std::vector<ContinuationResult> & continuationResults)29 void JsDeviceSelectionListener::OnDeviceConnect(const std::vector<ContinuationResult>& continuationResults)
30 {
31 HILOGD("called.");
32 CallJsMethod(EVENT_CONNECT, continuationResults);
33 }
34
OnDeviceDisconnect(const std::vector<ContinuationResult> & continuationResults)35 void JsDeviceSelectionListener::OnDeviceDisconnect(const std::vector<ContinuationResult>& continuationResults)
36 {
37 HILOGD("called.");
38 CallJsMethod(EVENT_DISCONNECT, continuationResults);
39 }
40
AddCallback(const std::string & cbType,napi_value jsListenerObj)41 void JsDeviceSelectionListener::AddCallback(const std::string& cbType, napi_value jsListenerObj)
42 {
43 HILOGD("called.");
44 napi_ref tempRef = nullptr;
45 std::unique_ptr<NativeReference> callbackRef;
46 if (engine_ == nullptr) {
47 HILOGE("engine_ is nullptr");
48 return;
49 }
50 napi_create_reference(engine_, jsListenerObj, 1, &tempRef);
51 callbackRef.reset(reinterpret_cast<NativeReference *>(tempRef));
52 std::lock_guard<std::mutex> jsCallBackMapLock(jsCallBackMapMutex_);
53 jsCallBackMap_[cbType] = std::move(callbackRef);
54 HILOGD("jsCallBackMap_ cbType: %{public}s, size: %{public}u!",
55 cbType.c_str(), static_cast<uint32_t>(jsCallBackMap_.size()));
56 }
57
RemoveCallback(const std::string & cbType)58 void JsDeviceSelectionListener::RemoveCallback(const std::string& cbType)
59 {
60 HILOGD("called.");
61 std::lock_guard<std::mutex> jsCallBackMapLock(jsCallBackMapMutex_);
62 jsCallBackMap_.erase(cbType);
63 HILOGD("jsCallBackMap_ cbType: %{public}s, size: %{public}u!",
64 cbType.c_str(), static_cast<uint32_t>(jsCallBackMap_.size()));
65 }
66
CallJsMethod(const std::string & methodName,const std::vector<ContinuationResult> & continuationResults)67 void JsDeviceSelectionListener::CallJsMethod(const std::string& methodName,
68 const std::vector<ContinuationResult>& continuationResults)
69 {
70 HILOGD("methodName = %{public}s", methodName.c_str());
71 if (engine_ == nullptr) {
72 HILOGE("engine_ is nullptr");
73 return;
74 }
75 // js callback should run in js thread
76 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
77 ([this, methodName, continuationResults]
78 (napi_env env, NapiAsyncTask &task, int32_t status) {
79 napi_handle_scope scope = nullptr;
80 napi_open_handle_scope(env, &scope);
81 if (scope == nullptr) {
82 return;
83 }
84
85 CallJsMethodInner(methodName, continuationResults);
86
87 napi_close_handle_scope(env, scope);
88 });
89 napi_ref callback = nullptr;
90 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
91 NapiAsyncTask::Schedule("JsDeviceSelectionListener::OnDeviceConnect",
92 engine_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
93 }
94
CallJsMethodInner(const std::string & methodName,const std::vector<ContinuationResult> & continuationResults)95 void JsDeviceSelectionListener::CallJsMethodInner(const std::string& methodName,
96 const std::vector<ContinuationResult>& continuationResults)
97 {
98 std::lock_guard<std::mutex> jsCallBackMapLock(jsCallBackMapMutex_);
99 auto it = jsCallBackMap_.find(methodName);
100 if (it == jsCallBackMap_.end()) {
101 HILOGE("Callback method %s not found in jsCallBackMap_", methodName.c_str());
102 return;
103 }
104 napi_value method = jsCallBackMap_[methodName]->GetNapiValue();
105 if (method == nullptr) {
106 HILOGE("Failed to get %{public}s from object", methodName.c_str());
107 return;
108 }
109 napi_value argv[] = { WrapContinuationResultArray(engine_, continuationResults) };
110 napi_call_function(engine_, CreateJsUndefined(engine_), method, ArraySize(argv), argv, nullptr);
111 }
112
CallJsMethod(const std::string & methodName,const std::vector<std::string> & deviceIds)113 void JsDeviceSelectionListener::CallJsMethod(const std::string& methodName, const std::vector<std::string>& deviceIds)
114 {
115 HILOGD("methodName = %{public}s", methodName.c_str());
116 if (engine_ == nullptr) {
117 HILOGE("engine_ is nullptr");
118 return;
119 }
120 // js callback should run in js thread
121 std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
122 ([this, methodName, deviceIds]
123 (napi_env env, NapiAsyncTask &task, int32_t status) {
124 napi_handle_scope scope = nullptr;
125 napi_open_handle_scope(env, &scope);
126 if (scope == nullptr) {
127 return;
128 }
129 CallJsMethodInner(methodName, deviceIds);
130
131 napi_close_handle_scope(env, scope);
132 });
133 napi_ref callback = nullptr;
134 std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
135 NapiAsyncTask::Schedule("JsDeviceSelectionListener::OnDeviceDisconnect",
136 engine_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
137 }
138
CallJsMethodInner(const std::string & methodName,const std::vector<std::string> & deviceIds)139 void JsDeviceSelectionListener::CallJsMethodInner(const std::string& methodName,
140 const std::vector<std::string>& deviceIds)
141 {
142 std::lock_guard<std::mutex> jsCallBackMapLock(jsCallBackMapMutex_);
143 auto it = jsCallBackMap_.find(methodName);
144 if (it == jsCallBackMap_.end()) {
145 HILOGE("Callback method %s not found in jsCallBackMap_", methodName.c_str());
146 return;
147 }
148 napi_value method = jsCallBackMap_[methodName]->GetNapiValue();
149 if (method == nullptr) {
150 HILOGE("Failed to get %{public}s from object", methodName.c_str());
151 return;
152 }
153 napi_value argv[] = { WrapDeviceIdArray(engine_, deviceIds) };
154 napi_call_function(engine_, CreateJsUndefined(engine_), method, ArraySize(argv), argv, nullptr);
155 }
156
WrapContinuationResult(napi_env env,const ContinuationResult & continuationResult)157 napi_value JsDeviceSelectionListener::WrapContinuationResult(napi_env env,
158 const ContinuationResult& continuationResult)
159 {
160 napi_value objValue;
161 napi_create_object(env, &objValue);
162 SetKeyValue(env, objValue, "id", continuationResult.GetDeviceId());
163 SetKeyValue(env, objValue, "type", continuationResult.GetDeviceType());
164 SetKeyValue(env, objValue, "name", continuationResult.GetDeviceName());
165 return objValue;
166 }
167
WrapContinuationResultArray(napi_env env,const std::vector<ContinuationResult> & continuationResults)168 napi_value JsDeviceSelectionListener::WrapContinuationResultArray(napi_env env,
169 const std::vector<ContinuationResult>& continuationResults)
170 {
171 napi_value arrayValue;
172 napi_create_array_with_length(env, continuationResults.size(), &arrayValue);
173 uint32_t index = 0;
174 for (const auto& continuationResult : continuationResults) {
175 napi_set_element(env,
176 arrayValue, index++, WrapContinuationResult(env, continuationResult));
177 }
178 return arrayValue;
179 }
180
WrapDeviceIdArray(napi_env env,const std::vector<std::string> & deviceIds)181 napi_value JsDeviceSelectionListener::WrapDeviceIdArray(napi_env env,
182 const std::vector<std::string>& deviceIds)
183 {
184 napi_value arrayValue;
185 napi_create_array_with_length(env, deviceIds.size(), &arrayValue);
186 uint32_t index = 0;
187 for (const auto& deviceId : deviceIds) {
188 napi_set_element(env, arrayValue, index++, CreateJsValue(env, deviceId));
189 }
190 return arrayValue;
191 }
192
SetKeyValue(napi_env env,const napi_value object,const std::string & strKey,const std::string & strValue) const193 void JsDeviceSelectionListener::SetKeyValue(napi_env env,
194 const napi_value object, const std::string &strKey, const std::string &strValue) const
195 {
196 napi_value attrValue = nullptr;
197 napi_create_string_utf8(env, strValue.c_str(), NAPI_AUTO_LENGTH, &attrValue);
198 napi_set_named_property(env, object, strKey.c_str(), attrValue);
199 }
200 } // namespace DistributedSchedule
201 } // namespace OHOS