1 /*
2  * Copyright (c) 2024 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_uiservice_uiext_connection.h"
17 
18 #include "ability_business_error.h"
19 #include "hilog_tag_wrapper.h"
20 #include "js_error_utils.h"
21 #include "js_ui_service_proxy.h"
22 #include "napi_common_want.h"
23 #include "ui_extension_servicehost_stub_impl.h"
24 
25 namespace OHOS {
26 namespace AbilityRuntime {
27 constexpr size_t ARGC_ONE = 1;
28 
29 namespace UIServiceConnection {
30 static std::map<UIExtensionConnectionKey, sptr<JSUIServiceUIExtConnection>, key_compare> gUiServiceExtConnects;
31 static std::recursive_mutex gUiServiceExtConnectsLock;
32 static int64_t gUiServiceExtConnectSn = 0;
33 
AddUIServiceExtensionConnection(AAFwk::Want & want,sptr<JSUIServiceUIExtConnection> & connection)34 void AddUIServiceExtensionConnection(AAFwk::Want& want, sptr<JSUIServiceUIExtConnection>& connection)
35 {
36     std::lock_guard<std::recursive_mutex> lock(gUiServiceExtConnectsLock);
37     UIExtensionConnectionKey key;
38     key.id = gUiServiceExtConnectSn;
39     key.want = want;
40     connection->SetConnectionId(key.id);
41     gUiServiceExtConnects.emplace(key, connection);
42     if (gUiServiceExtConnectSn < INT32_MAX) {
43         gUiServiceExtConnectSn++;
44     } else {
45         gUiServiceExtConnectSn = 0;
46     }
47 }
48 
RemoveUIServiceExtensionConnection(const int64_t & connectId)49 void RemoveUIServiceExtensionConnection(const int64_t& connectId)
50 {
51     std::lock_guard<std::recursive_mutex> lock(gUiServiceExtConnectsLock);
52     auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(),
53         [&connectId](const auto &obj) {
54             return connectId == obj.first.id;
55         });
56     if (item != gUiServiceExtConnects.end()) {
57         TAG_LOGI(AAFwkTag::UI_EXT, "found, erase");
58         gUiServiceExtConnects.erase(item);
59     } else {
60         TAG_LOGI(AAFwkTag::UI_EXT, "not found");
61     }
62     TAG_LOGI(AAFwkTag::CONTEXT, "Connects new size:%{public}zu", gUiServiceExtConnects.size());
63 }
64 
FindUIServiceExtensionConnection(const int64_t & connectId,AAFwk::Want & want,sptr<JSUIServiceUIExtConnection> & connection)65 void FindUIServiceExtensionConnection(const int64_t& connectId, AAFwk::Want& want,
66     sptr<JSUIServiceUIExtConnection>& connection)
67 {
68     std::lock_guard<std::recursive_mutex> lock(gUiServiceExtConnectsLock);
69     TAG_LOGI(AAFwkTag::UI_EXT, "connection:%{public}d", static_cast<int32_t>(connectId));
70     auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(),
71         [&connectId](const auto &obj) {
72             return connectId == obj.first.id;
73         });
74     if (item != gUiServiceExtConnects.end()) {
75         want = item->first.want;
76         connection = item->second;
77         TAG_LOGI(AAFwkTag::UI_EXT, "found");
78     } else {
79         TAG_LOGI(AAFwkTag::UI_EXT, "not found");
80     }
81 }
82 
FindUIServiceExtensionConnection(napi_env env,AAFwk::Want & want,napi_value callback,sptr<JSUIServiceUIExtConnection> & connection)83 void FindUIServiceExtensionConnection(napi_env env, AAFwk::Want& want, napi_value callback,
84     sptr<JSUIServiceUIExtConnection>& connection)
85 {
86     std::lock_guard<std::recursive_mutex> lock(gUiServiceExtConnectsLock);
87     auto item = std::find_if(gUiServiceExtConnects.begin(), gUiServiceExtConnects.end(),
88         [&want, env, callback](const auto &obj) {
89         bool wantEquals = (obj.first.want.GetElement() == want.GetElement());
90         std::unique_ptr<NativeReference>& tempCallbackPtr = obj.second->GetJsConnectionObject();
91         bool callbackObjectEquals =
92             JSUIServiceUIExtConnection::IsJsCallbackObjectEquals(env, tempCallbackPtr, callback);
93         return wantEquals && callbackObjectEquals;
94     });
95     if (item == gUiServiceExtConnects.end()) {
96         return;
97     }
98     connection = item->second;
99 }
100 }
101 
JSUIServiceUIExtConnection(napi_env env)102 JSUIServiceUIExtConnection::JSUIServiceUIExtConnection(napi_env env) : JSUIExtensionConnection(env)
103 {
104     TAG_LOGI(AAFwkTag::UISERVC_EXT, "JSUIServiceUIExtConnection");
105     wptr<JSUIServiceUIExtConnection> weakthis = this;
106     serviceHostStub_ = sptr<UIExtensionServiceHostStubImpl>::MakeSptr(weakthis);
107 }
108 
~JSUIServiceUIExtConnection()109 JSUIServiceUIExtConnection::~JSUIServiceUIExtConnection()
110 {
111     TAG_LOGI(AAFwkTag::UISERVC_EXT, "~JSUIServiceUIExtConnection");
112     serviceHostStub_ = nullptr;
113     napiAsyncTask_.reset();
114     ReleaseNativeReference(serviceProxyObject_.release());
115 }
116 
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)117 void JSUIServiceUIExtConnection::HandleOnAbilityConnectDone(
118     const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int resultCode)
119 {
120     if (napiAsyncTask_ != nullptr) {
121         TAG_LOGI(AAFwkTag::UISERVC_EXT, "HandleOnAbilityConnectDone, CreateJsUIServiceProxy");
122         sptr<UIExtensionServiceHostStubImpl> hostStub = GetServiceHostStub();
123         sptr<IRemoteObject> hostProxy = nullptr;
124         if (hostStub != nullptr) {
125             hostProxy = hostStub->AsObject();
126         }
127         napi_value proxy = AAFwk::JsUIServiceProxy::CreateJsUIServiceProxy(env_, remoteObject,
128             connectionId_, hostProxy);
129         SetProxyObject(proxy);
130         napiAsyncTask_->ResolveWithNoError(env_, proxy);
131 
132         ResolveDuplicatedPendingTask(env_, proxy);
133     } else {
134         TAG_LOGE(AAFwkTag::UISERVC_EXT, "HandleOnAbilityConnectDone, napiAsyncTask_ null");
135     }
136     napiAsyncTask_.reset();
137 }
138 
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)139 void JSUIServiceUIExtConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
140     int resultCode)
141 {
142     if (napiAsyncTask_ != nullptr) {
143         napi_value innerError = CreateJsError(env_, AbilityErrorCode::ERROR_CODE_INNER);
144         napiAsyncTask_->Reject(env_, innerError);
145         RejectDuplicatedPendingTask(env_, innerError);
146         napiAsyncTask_ = nullptr;
147     }
148     CallJsOnDisconnect();
149     SetProxyObject(nullptr);
150     RemoveConnectionObject();
151     duplicatedPendingTaskList_.clear();
152     UIServiceConnection::RemoveUIServiceExtensionConnection(connectionId_);
153 }
154 
SetNapiAsyncTask(std::shared_ptr<NapiAsyncTask> & task)155 void JSUIServiceUIExtConnection::SetNapiAsyncTask(std::shared_ptr<NapiAsyncTask>& task)
156 {
157     napiAsyncTask_ = task;
158 }
159 
AddDuplicatedPendingTask(std::unique_ptr<NapiAsyncTask> & task)160 void JSUIServiceUIExtConnection::AddDuplicatedPendingTask(std::unique_ptr<NapiAsyncTask>& task)
161 {
162     duplicatedPendingTaskList_.push_back(std::move(task));
163 }
164 
ResolveDuplicatedPendingTask(napi_env env,napi_value proxy)165 void JSUIServiceUIExtConnection::ResolveDuplicatedPendingTask(napi_env env, napi_value proxy)
166 {
167     TAG_LOGI(AAFwkTag::UISERVC_EXT, "called, size %{public}zu", duplicatedPendingTaskList_.size());
168     for (auto &task : duplicatedPendingTaskList_) {
169         if (task != nullptr) {
170             task->ResolveWithNoError(env, proxy);
171         }
172     }
173     duplicatedPendingTaskList_.clear();
174 }
175 
RejectDuplicatedPendingTask(napi_env env,napi_value error)176 void JSUIServiceUIExtConnection::RejectDuplicatedPendingTask(napi_env env, napi_value error)
177 {
178     TAG_LOGI(AAFwkTag::UISERVC_EXT, "called, size %{public}zu", duplicatedPendingTaskList_.size());
179     for (auto &task : duplicatedPendingTaskList_) {
180         if (task != nullptr) {
181             task->Reject(env, error);
182         }
183     }
184     duplicatedPendingTaskList_.clear();
185 }
186 
SetProxyObject(napi_value proxy)187 void JSUIServiceUIExtConnection::SetProxyObject(napi_value proxy)
188 {
189     TAG_LOGI(AAFwkTag::UISERVC_EXT, "SetProxyObject");
190     serviceProxyObject_.reset();
191     if (proxy != nullptr) {
192         napi_ref ref = nullptr;
193         napi_create_reference(env_, proxy, 1, &ref);
194         serviceProxyObject_ = std::unique_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
195     }
196 }
197 
GetProxyObject()198 napi_value JSUIServiceUIExtConnection::GetProxyObject()
199 {
200     if (serviceProxyObject_ == nullptr) {
201         return nullptr;
202     }
203     return serviceProxyObject_->GetNapiValue();
204 }
205 
OnSendData(OHOS::AAFwk::WantParams & data)206 int32_t JSUIServiceUIExtConnection::OnSendData(OHOS::AAFwk::WantParams &data)
207 {
208     wptr<JSUIServiceUIExtConnection> connection = this;
209     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
210         ([connection, wantParams = data](napi_env env, NapiAsyncTask &task, int32_t status) {
211             sptr<JSUIServiceUIExtConnection> connectionSptr = connection.promote();
212             if (!connectionSptr) {
213                 TAG_LOGE(AAFwkTag::UISERVC_EXT, "connectionSptr nullptr");
214                 return;
215             }
216             connectionSptr->HandleOnSendData(wantParams);
217         });
218 
219     napi_ref callback = nullptr;
220     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
221     NapiAsyncTask::Schedule("JSUIServiceUIExtConnection::SendData",
222         env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
223 
224     return static_cast<int32_t>(AbilityErrorCode::ERROR_OK);
225 }
226 
HandleOnSendData(const OHOS::AAFwk::WantParams & data)227 void JSUIServiceUIExtConnection::HandleOnSendData(const OHOS::AAFwk::WantParams &data)
228 {
229     napi_value argv[] = { AppExecFwk::CreateJsWantParams(env_, data) };
230     CallObjectMethod("onData", argv, ARGC_ONE);
231 }
232 
CallJsOnDisconnect()233 void JSUIServiceUIExtConnection::CallJsOnDisconnect()
234 {
235     TAG_LOGI(AAFwkTag::UISERVC_EXT, "called");
236     CallObjectMethod("onDisconnect", nullptr, 0);
237 }
238 
IsJsCallbackObjectEquals(napi_env env,std::unique_ptr<NativeReference> & callback,napi_value value)239 bool JSUIServiceUIExtConnection::IsJsCallbackObjectEquals(napi_env env,
240     std::unique_ptr<NativeReference> &callback, napi_value value)
241 {
242     if (value == nullptr || callback == nullptr) {
243         return callback.get() == reinterpret_cast<NativeReference*>(value);
244     }
245     auto object = callback->GetNapiValue();
246     if (object == nullptr) {
247         TAG_LOGE(AAFwkTag::UISERVC_EXT, "Failed to get object.");
248         return false;
249     }
250     bool result = false;
251     if (napi_strict_equals(env, object, value, &result) != napi_ok) {
252         TAG_LOGE(AAFwkTag::UISERVC_EXT, "Object does not match value.");
253         return false;
254     }
255     return result;
256 }
257 
258 }
259 }
260