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