1 /*
2  * Copyright (c) 2022-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 "environment_callback.h"
17 
18 #include "hilog_tag_wrapper.h"
19 #include "hitrace_meter.h"
20 #include "js_data_struct_converter.h"
21 #include "js_runtime_utils.h"
22 
23 namespace OHOS {
24 namespace AbilityRuntime {
JsEnvironmentCallback(napi_env env)25 JsEnvironmentCallback::JsEnvironmentCallback(napi_env env)
26     : env_(env)
27 {
28 }
29 
30 int32_t JsEnvironmentCallback::serialNumber_ = 0;
31 
CallConfigurationUpdatedInner(const std::string & methodName,const AppExecFwk::Configuration & config,const std::map<int32_t,std::shared_ptr<NativeReference>> & callbacks)32 void JsEnvironmentCallback::CallConfigurationUpdatedInner(const std::string &methodName,
33     const AppExecFwk::Configuration &config, const std::map<int32_t, std::shared_ptr<NativeReference>> &callbacks)
34 {
35     TAG_LOGD(AAFwkTag::APPKIT, "methodName = %{public}s", methodName.c_str());
36     for (auto &callback : callbacks) {
37         if (!callback.second) {
38             TAG_LOGE(AAFwkTag::APPKIT, " Invalid jsCallback");
39             return;
40         }
41 
42         auto obj = callback.second->GetNapiValue();
43         if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
44             TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object");
45             return;
46         }
47 
48         napi_value method = nullptr;
49         napi_get_named_property(env_, obj, methodName.data(), &method);
50         if (method == nullptr) {
51             TAG_LOGE(AAFwkTag::APPKIT, "Failed to get %{public}s from object",
52                 methodName.data());
53             return;
54         }
55 
56         napi_value argv[] = { CreateJsConfiguration(env_, config) };
57         napi_call_function(env_, obj, method, ArraySize(argv), argv, nullptr);
58     }
59 }
60 
OnConfigurationUpdated(const AppExecFwk::Configuration & config)61 void JsEnvironmentCallback::OnConfigurationUpdated(const AppExecFwk::Configuration &config)
62 {
63     std::weak_ptr<JsEnvironmentCallback> thisWeakPtr(shared_from_this());
64     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>(
65         [thisWeakPtr, config, callbacks = callbacks_, callbacksSync = callbacksSync_]
66         (napi_env env, NapiAsyncTask &task, int32_t status) {
67             std::shared_ptr<JsEnvironmentCallback> jsEnvCallback = thisWeakPtr.lock();
68             if (jsEnvCallback) {
69                 jsEnvCallback->CallConfigurationUpdatedInner("onConfigurationUpdated", config, callbacks);
70                 jsEnvCallback->CallConfigurationUpdatedInner("onConfigurationUpdated", config, callbacksSync);
71             }
72         }
73     );
74     napi_ref callback = nullptr;
75     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
76     NapiAsyncTask::Schedule("JsEnvironmentCallback::OnConfigurationUpdated",
77         env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
78 }
79 
CallMemoryLevelInner(const std::string & methodName,const int level,const std::map<int32_t,std::shared_ptr<NativeReference>> & callbacks)80 void JsEnvironmentCallback::CallMemoryLevelInner(const std::string &methodName, const int level,
81     const std::map<int32_t, std::shared_ptr<NativeReference>> &callbacks)
82 {
83     TAG_LOGD(AAFwkTag::APPKIT, "methodName = %{public}s", methodName.c_str());
84     for (auto &callback : callbacks) {
85         if (!callback.second) {
86             TAG_LOGE(AAFwkTag::APPKIT, "Invalid jsCallback");
87             return;
88         }
89 
90         auto obj = callback.second->GetNapiValue();
91         if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
92             TAG_LOGE(AAFwkTag::APPKIT, "Failed to get object");
93             return;
94         }
95 
96         napi_value method = nullptr;
97         napi_get_named_property(env_, obj, methodName.data(), &method);
98         if (method == nullptr) {
99             TAG_LOGE(AAFwkTag::APPKIT, "Failed to get %{public}s from object", methodName.data());
100             return;
101         }
102 
103         napi_value argv[] = { CreateJsValue(env_, level) };
104         napi_call_function(env_, obj, method, ArraySize(argv), argv, nullptr);
105     }
106 }
107 
OnMemoryLevel(const int level)108 void JsEnvironmentCallback::OnMemoryLevel(const int level)
109 {
110     std::weak_ptr<JsEnvironmentCallback> thisWeakPtr(shared_from_this());
111     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>(
112         [thisWeakPtr, level, callbacks = callbacks_, callbacksSync = callbacksSync_]
113         (napi_env env, NapiAsyncTask &task, int32_t status) {
114             std::shared_ptr<JsEnvironmentCallback> jsEnvCallback = thisWeakPtr.lock();
115             if (jsEnvCallback) {
116                 jsEnvCallback->CallMemoryLevelInner("onMemoryLevel", level, callbacks);
117                 jsEnvCallback->CallMemoryLevelInner("onMemoryLevel", level, callbacksSync);
118             }
119         }
120     );
121     napi_ref callback = nullptr;
122     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
123     NapiAsyncTask::Schedule("JsEnvironmentCallback::OnMemoryLevel",
124         env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
125 }
126 
Register(napi_value jsCallback,bool isSync)127 int32_t JsEnvironmentCallback::Register(napi_value jsCallback, bool isSync)
128 {
129     if (env_ == nullptr) {
130         return -1;
131     }
132     int32_t callbackId = serialNumber_;
133     if (serialNumber_ < INT32_MAX) {
134         serialNumber_++;
135     } else {
136         serialNumber_ = 0;
137     }
138     napi_ref ref = nullptr;
139     napi_create_reference(env_, jsCallback, 1, &ref);
140     if (isSync) {
141         callbacksSync_.emplace(callbackId, std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref)));
142     } else {
143         callbacks_.emplace(callbackId, std::shared_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref)));
144     }
145     return callbackId;
146 }
147 
UnRegister(int32_t callbackId,bool isSync)148 bool JsEnvironmentCallback::UnRegister(int32_t callbackId, bool isSync)
149 {
150     TAG_LOGD(AAFwkTag::APPKIT, "callbackId : %{public}d", callbackId);
151     if (isSync) {
152         auto it = callbacksSync_.find(callbackId);
153         if (it == callbacksSync_.end()) {
154             TAG_LOGE(AAFwkTag::APPKIT, "callbackId: %{public}d is not in callbacksSync_", callbackId);
155             return false;
156         }
157         TAG_LOGD(AAFwkTag::APPKIT, "callbacksSync_.callbackId : %{public}d", it->first);
158         return callbacksSync_.erase(callbackId) == 1;
159     }
160     auto it = callbacks_.find(callbackId);
161     if (it == callbacks_.end()) {
162         TAG_LOGE(AAFwkTag::APPKIT, "callbackId: %{public}d is not in callbacks_", callbackId);
163         return false;
164     }
165     TAG_LOGD(AAFwkTag::APPKIT, "callbacks_.callbackId : %{public}d", it->first);
166     return callbacks_.erase(callbackId) == 1;
167 }
168 
IsEmpty() const169 bool JsEnvironmentCallback::IsEmpty() const
170 {
171     return callbacks_.empty() && callbacksSync_.empty();
172 }
173 }  // namespace AbilityRuntime
174 }  // namespace OHOS