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 "js_root_scene_session.h"
17 #include "session_manager/include/scene_session_manager.h"
18
19 #include "context.h"
20 #include <js_runtime_utils.h>
21 #include "window_manager_hilog.h"
22
23 #include "js_scene_utils.h"
24 #include "singleton_container.h"
25 #include "dms_reporter.h"
26 #include "common/include/session_permission.h"
27
28 namespace OHOS::Rosen {
29 using namespace AbilityRuntime;
30 namespace {
31 constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, HILOG_DOMAIN_WINDOW, "JsRootSceneSession" };
32 const std::string PENDING_SCENE_CB = "pendingSceneSessionActivation";
33 } // namespace
34
JsRootSceneSession(napi_env env,const sptr<RootSceneSession> & rootSceneSession)35 JsRootSceneSession::JsRootSceneSession(napi_env env, const sptr<RootSceneSession>& rootSceneSession)
36 : env_(env), rootSceneSession_(rootSceneSession)
37 {
38 taskScheduler_ = std::make_shared<MainThreadScheduler>(env);
39 }
40
Create(napi_env env,const sptr<RootSceneSession> & rootSceneSession)41 napi_value JsRootSceneSession::Create(napi_env env, const sptr<RootSceneSession>& rootSceneSession)
42 {
43 napi_value objValue = nullptr;
44 napi_create_object(env, &objValue);
45 if (objValue == nullptr) {
46 WLOGFE("[NAPI]Object is null!");
47 return NapiGetUndefined(env);
48 }
49
50 auto jsRootSceneSession = std::make_unique<JsRootSceneSession>(env, rootSceneSession);
51 napi_wrap(env, objValue, jsRootSceneSession.release(), JsRootSceneSession::Finalizer, nullptr, nullptr);
52
53 const char* moduleName = "JsRootSceneSession";
54 BindNativeFunction(env, objValue, "loadContent", moduleName, JsRootSceneSession::LoadContent);
55 BindNativeFunction(env, objValue, "on", moduleName, JsRootSceneSession::RegisterCallback);
56 return objValue;
57 }
58
Finalizer(napi_env env,void * data,void * hint)59 void JsRootSceneSession::Finalizer(napi_env env, void* data, void* hint)
60 {
61 WLOGD("Finalizer.");
62 std::unique_ptr<JsRootSceneSession>(static_cast<JsRootSceneSession*>(data));
63 }
64
RegisterCallback(napi_env env,napi_callback_info info)65 napi_value JsRootSceneSession::RegisterCallback(napi_env env, napi_callback_info info)
66 {
67 WLOGD("RegisterCallback.");
68 JsRootSceneSession* me = CheckParamsAndGetThis<JsRootSceneSession>(env, info);
69 return (me != nullptr) ? me->OnRegisterCallback(env, info) : nullptr;
70 }
71
LoadContent(napi_env env,napi_callback_info info)72 napi_value JsRootSceneSession::LoadContent(napi_env env, napi_callback_info info)
73 {
74 WLOGD("LoadContent.");
75 JsRootSceneSession* me = CheckParamsAndGetThis<JsRootSceneSession>(env, info);
76 return (me != nullptr) ? me->OnLoadContent(env, info) : nullptr;
77 }
78
NapiIsCallable(napi_env env,napi_value value)79 bool NapiIsCallable(napi_env env, napi_value value)
80 {
81 bool result = false;
82 napi_is_callable(env, value, &result);
83 return result;
84 }
85
OnRegisterCallback(napi_env env,napi_callback_info info)86 napi_value JsRootSceneSession::OnRegisterCallback(napi_env env, napi_callback_info info)
87 {
88 size_t argc = 4;
89 napi_value argv[4] = {nullptr};
90 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
91 if (argc < ARGC_TWO) {
92 WLOGFE("[NAPI]Argc is invalid: %{public}zu", argc);
93 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM),
94 "Input parameter is missing or invalid"));
95 return NapiGetUndefined(env);
96 }
97 std::string cbType;
98 if (!ConvertFromJsValue(env, argv[0], cbType)) {
99 WLOGFE("[NAPI]Failed to convert parameter to callbackType");
100 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM),
101 "Input parameter is missing or invalid"));
102 return NapiGetUndefined(env);
103 }
104 napi_value value = argv[1];
105 if (value == nullptr || !NapiIsCallable(env, value)) {
106 WLOGFE("[NAPI]Invalid argument");
107 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM),
108 "Input parameter is missing or invalid"));
109 return NapiGetUndefined(env);
110 }
111 if (IsCallbackRegistered(env, cbType, value)) {
112 return NapiGetUndefined(env);
113 }
114 if (rootSceneSession_ == nullptr) {
115 WLOGFE("[NAPI]root session is nullptr");
116 napi_throw(env, CreateJsError(
117 env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM), "Root scene session is null!"));
118 return NapiGetUndefined(env);
119 }
120
121 rootSceneSession_->SetPendingSessionActivationEventListener([this](SessionInfo& info) {
122 this->PendingSessionActivation(info);
123 });
124 std::shared_ptr<NativeReference> callbackRef;
125 napi_ref result = nullptr;
126 napi_create_reference(env, value, 1, &result);
127 callbackRef.reset(reinterpret_cast<NativeReference*>(result));
128 {
129 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsRootSceneSession set jsCbMap[%s]", cbType.c_str());
130 std::unique_lock<std::shared_mutex> lock(jsCbMapMutex_);
131 jsCbMap_[cbType] = callbackRef;
132 }
133 WLOGFD("[NAPI]Register end, type = %{public}s", cbType.c_str());
134 return NapiGetUndefined(env);
135 }
136
OnLoadContent(napi_env env,napi_callback_info info)137 napi_value JsRootSceneSession::OnLoadContent(napi_env env, napi_callback_info info)
138 {
139 WLOGD("[NAPI]OnLoadContent");
140 size_t argc = 4;
141 napi_value argv[4] = {nullptr};
142 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
143 if (argc < ARGC_TWO) {
144 WLOGFE("[NAPI]Argc is invalid: %{public}zu", argc);
145 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM),
146 "Input parameter is missing or invalid"));
147 return NapiGetUndefined(env);
148 }
149 std::string contentUrl;
150 napi_value context = argv[1];
151 napi_value storage = argc < 3 ? nullptr : argv[2];
152 if (!ConvertFromJsValue(env, argv[0], contentUrl)) {
153 WLOGFE("[NAPI]Failed to convert parameter to content url");
154 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_INVALID_PARAM),
155 "Input parameter is missing or invalid"));
156 return NapiGetUndefined(env);
157 }
158
159 if (context == nullptr) {
160 WLOGFE("[NAPI]Failed to get context object");
161 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_STATE_ABNORMALLY)));
162 return NapiGetUndefined(env);
163 }
164 void* pointerResult = nullptr;
165 napi_unwrap(env, context, &pointerResult);
166 auto contextNativePointer = static_cast<std::weak_ptr<Context>*>(pointerResult);
167 if (contextNativePointer == nullptr) {
168 WLOGFE("[NAPI]Failed to get context pointer from js object");
169 napi_throw(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_STATE_ABNORMALLY)));
170 return NapiGetUndefined(env);
171 }
172 auto contextWeakPtr = *contextNativePointer;
173 SceneSessionManager::GetInstance().SetRootSceneContext(contextWeakPtr);
174
175 std::shared_ptr<NativeReference> contentStorage = nullptr;
176 if (storage != nullptr) {
177 napi_ref ref = nullptr;
178 napi_create_reference(env, storage, 1, &ref);
179 contentStorage = std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
180 }
181
182 NapiAsyncTask::CompleteCallback complete = [rootSceneSession = rootSceneSession_,
183 contentUrl, contextWeakPtr, contentStorage](napi_env env, NapiAsyncTask& task, int32_t status) {
184 if (rootSceneSession == nullptr) {
185 WLOGFE("[NAPI]rootSceneSession is nullptr");
186 task.Reject(env, CreateJsError(env, static_cast<int32_t>(WSErrorCode::WS_ERROR_STATE_ABNORMALLY)));
187 return;
188 }
189 napi_value nativeStorage = contentStorage ? contentStorage->GetNapiValue() : nullptr;
190 rootSceneSession->LoadContent(contentUrl, env, nativeStorage, contextWeakPtr.lock().get());
191 };
192 napi_value lastParam = nullptr, result = nullptr;
193 NapiAsyncTask::Schedule("JsRootSceneSession::OnLoadContent", env,
194 CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
195 return result;
196 }
197
IsCallbackRegistered(napi_env env,const std::string & type,napi_value jsListenerObject)198 bool JsRootSceneSession::IsCallbackRegistered(napi_env env, const std::string& type, napi_value jsListenerObject)
199 {
200 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsRootSceneSession::IsCallbackRegistered[%s]", type.c_str());
201 std::shared_lock<std::shared_mutex> lock(jsCbMapMutex_);
202 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
203 WLOGFI("[NAPI]Method %{public}s has not been registered", type.c_str());
204 return false;
205 }
206
207 for (auto iter = jsCbMap_.begin(); iter != jsCbMap_.end(); ++iter) {
208 bool isEquals = false;
209 napi_strict_equals(env, jsListenerObject, iter->second->GetNapiValue(), &isEquals);
210 if (isEquals) {
211 WLOGFE("[NAPI]Method %{public}s has already been registered", type.c_str());
212 return true;
213 }
214 }
215 return false;
216 }
217
GetJSCallback(const std::string & functionName) const218 std::shared_ptr<NativeReference> JsRootSceneSession::GetJSCallback(const std::string& functionName) const
219 {
220 HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "JsRootSceneSession::GetJSCallback[%s]", functionName.c_str());
221 std::shared_ptr<NativeReference> jsCallBack = nullptr;
222 std::shared_lock<std::shared_mutex> lock(jsCbMapMutex_);
223 auto iter = jsCbMap_.find(functionName);
224 if (iter == jsCbMap_.end()) {
225 TLOGE(WmsLogTag::DEFAULT, "%{public}s callback not found!", functionName.c_str());
226 } else {
227 jsCallBack = iter->second;
228 }
229 return jsCallBack;
230 }
231
PendingSessionActivationInner(std::shared_ptr<SessionInfo> sessionInfo)232 void JsRootSceneSession::PendingSessionActivationInner(std::shared_ptr<SessionInfo> sessionInfo)
233 {
234 napi_env& env_ref = env_;
235 auto task = [sessionInfo, jsCallBack = GetJSCallback(PENDING_SCENE_CB), env_ref]() {
236 if (!jsCallBack) {
237 TLOGE(WmsLogTag::WMS_LIFE, "[NAPI]jsCallBack is nullptr");
238 return;
239 }
240 if (sessionInfo == nullptr) {
241 TLOGE(WmsLogTag::WMS_LIFE, "[NAPI]sessionInfo is nullptr");
242 return;
243 }
244 napi_value jsSessionInfo = CreateJsSessionInfo(env_ref, *sessionInfo);
245 if (jsSessionInfo == nullptr) {
246 TLOGE(WmsLogTag::WMS_LIFE, "[NAPI]jsSessionInfo is nullptr");
247 return;
248 }
249 napi_value argv[] = {jsSessionInfo};
250 TLOGI(WmsLogTag::WMS_LIFE, "pend active success, id:%{public}d",
251 sessionInfo->persistentId_);
252 napi_call_function(env_ref, NapiGetUndefined(env_ref),
253 jsCallBack->GetNapiValue(), ArraySize(argv), argv, nullptr);
254 };
255 taskScheduler_->PostMainThreadTask(task, "PendingSessionActivationInner");
256 }
257
GetRealCallerSessionId(const sptr<SceneSession> & sceneSession)258 static int32_t GetRealCallerSessionId(const sptr<SceneSession>& sceneSession)
259 {
260 int32_t realCallerSessionId = SceneSessionManager::GetInstance().GetFocusedSessionId();
261 if (realCallerSessionId == sceneSession->GetPersistentId()) {
262 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]caller is self, switch to self caller.");
263 realCallerSessionId = sceneSession->GetSessionInfo().callerPersistentId_;
264 }
265 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]caller session: %{public}d.", realCallerSessionId);
266 return realCallerSessionId;
267 }
268
PendingSessionActivation(SessionInfo & info)269 void JsRootSceneSession::PendingSessionActivation(SessionInfo& info)
270 {
271 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]bundleName %{public}s, moduleName %{public}s, abilityName %{public}s, "
272 "appIndex %{public}d, reuse %{public}d", info.bundleName_.c_str(), info.moduleName_.c_str(),
273 info.abilityName_.c_str(), info.appIndex_, info.reuse);
274 sptr<SceneSession> sceneSession = GenSceneSession(info);
275 if (sceneSession == nullptr) {
276 TLOGE(WmsLogTag::WMS_LIFE, "sceneSession is nullptr");
277 return;
278 }
279
280 if (info.want != nullptr) {
281 bool isNeedBackToOther = info.want->GetBoolParam(AAFwk::Want::PARAM_BACK_TO_OTHER_MISSION_STACK, false);
282 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]session: %{public}d isNeedBackToOther: %{public}d",
283 sceneSession->GetPersistentId(), isNeedBackToOther);
284 if (isNeedBackToOther) {
285 info.callerPersistentId_ = GetRealCallerSessionId(sceneSession);
286 VerifyCallerToken(info);
287 } else {
288 info.callerPersistentId_ = INVALID_SESSION_ID;
289 }
290
291 auto focusedOnShow = info.want->GetBoolParam(AAFwk::Want::PARAM_RESV_WINDOW_FOCUSED, true);
292 sceneSession->SetFocusedOnShow(focusedOnShow);
293
294 std::string continueSessionId = info.want->GetStringParam(Rosen::PARAM_KEY::PARAM_DMS_CONTINUE_SESSION_ID_KEY);
295 if (!continueSessionId.empty()) {
296 info.continueSessionId_ = continueSessionId;
297 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]continueSessionId from ability manager: %{public}s",
298 continueSessionId.c_str());
299 }
300
301 // app continue report for distributed scheduled service
302 if (info.want->GetIntParam(Rosen::PARAM_KEY::PARAM_DMS_PERSISTENT_ID_KEY, 0) > 0) {
303 TLOGI(WmsLogTag::WMS_LIFE, "[NAPI]continue app with persistentId: %{public}d", info.persistentId_);
304 SingletonContainer::Get<DmsReporter>().ReportContinueApp(true, static_cast<int32_t>(WSError::WS_OK));
305 }
306 } else {
307 sceneSession->SetFocusedOnShow(true);
308 }
309
310 sceneSession->SetSessionInfo(info);
311 std::shared_ptr<SessionInfo> sessionInfo = std::make_shared<SessionInfo>(info);
312 auto task = [this, sessionInfo]() {
313 PendingSessionActivationInner(sessionInfo);
314 };
315 sceneSession->PostLifeCycleTask(task, "PendingSessionActivation", LifeCycleTaskType::START);
316 if (info.fullScreenStart_) {
317 sceneSession->NotifySessionFullScreen(true);
318 }
319 }
320
VerifyCallerToken(SessionInfo & info)321 void JsRootSceneSession::VerifyCallerToken(SessionInfo& info)
322 {
323 auto callerSession = SceneSessionManager::GetInstance().GetSceneSession(info.callerPersistentId_);
324 if (callerSession != nullptr) {
325 TLOGI(WmsLogTag::WMS_SCB,
326 "update isCalledRightlyByCallerId result is from :%{public}d to false", info.isCalledRightlyByCallerId_);
327 info.isCalledRightlyByCallerId_ = false;
328 }
329 }
330
GenSceneSession(SessionInfo & info)331 sptr<SceneSession> JsRootSceneSession::GenSceneSession(SessionInfo& info)
332 {
333 sptr<SceneSession> sceneSession;
334 if (info.persistentId_ == INVALID_SESSION_ID) {
335 auto result = SceneSessionManager::GetInstance().CheckIfReuseSession(info);
336 if (result == BrokerStates::BROKER_NOT_START) {
337 WLOGE("[NAPI] The BrokerStates is not opened");
338 return nullptr;
339 }
340
341 if (info.reuse || info.isAtomicService_) {
342 if (SceneSessionManager::GetInstance().CheckCollaboratorType(info.collaboratorType_)) {
343 sceneSession = SceneSessionManager::GetInstance().FindSessionByAffinity(
344 info.sessionAffinity);
345 } else {
346 ComparedSessionInfo compareSessionInfo = { info.bundleName_, info.moduleName_, info.abilityName_,
347 info.appIndex_, info.windowType_, info.isAtomicService_ };
348 sceneSession = SceneSessionManager::GetInstance().GetSceneSessionByName(compareSessionInfo);
349 }
350 }
351 if (sceneSession == nullptr) {
352 WLOGFI("GetSceneSessionByName return nullptr, RequestSceneSession");
353 sceneSession = SceneSessionManager::GetInstance().RequestSceneSession(info);
354 if (sceneSession == nullptr) {
355 WLOGFE("RequestSceneSession return nullptr");
356 return sceneSession;
357 }
358 }
359 info.persistentId_ = sceneSession->GetPersistentId();
360 sceneSession->SetSessionInfoPersistentId(sceneSession->GetPersistentId());
361 sceneSession->SetDefaultDisplayIdIfNeed();
362 } else {
363 sceneSession = SceneSessionManager::GetInstance().GetSceneSession(info.persistentId_);
364 if (sceneSession == nullptr) {
365 WLOGFE("GetSceneSession return nullptr");
366 sceneSession = SceneSessionManager::GetInstance().RequestSceneSession(info);
367 if (sceneSession == nullptr) {
368 WLOGFE("retry RequestSceneSession return nullptr");
369 return sceneSession;
370 }
371 info.persistentId_ = sceneSession->GetPersistentId();
372 sceneSession->SetSessionInfoPersistentId(sceneSession->GetPersistentId());
373 }
374 }
375 sceneSession->SetSessionInfoProcessOptions(info.processOptions);
376 return sceneSession;
377 }
378 } // namespace OHOS::Rosen
379