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_vpn_extension_context.h"
17 
18 #include <chrono>
19 #include <cstdint>
20 #include "ability_manager_client.h"
21 #include "ability_runtime/js_caller_complex.h"
22 #include "hilog_wrapper.h"
23 #include "js_extension_context.h"
24 #include "js_error_utils.h"
25 #include "js_data_struct_converter.h"
26 #include "runtime.h"
27 #include "js_runtime.h"
28 #include "js_runtime_utils.h"
29 #include "napi/native_api.h"
30 #include "napi_common_ability.h"
31 #include "napi_common_want.h"
32 #include "napi_common_util.h"
33 #include "napi_remote_object.h"
34 #include "napi_common_start_options.h"
35 #include "start_options.h"
36 #include "hitrace_meter.h"
37 #include "netmgr_ext_log_wrapper.h"
38 #include "ability_business_error/ability_business_error.h"
39 
40 using namespace OHOS::AbilityRuntime;
41 namespace OHOS {
42 namespace NetManagerStandard {
43 namespace {
44 constexpr int32_t INDEX_ZERO = 0;
45 constexpr size_t ARGC_ONE = 1;
46 
47 class StartAbilityByCallParameters {
48 public:
49     int err = 0;
50     sptr<IRemoteObject> remoteCallee = nullptr;
51     std::shared_ptr<CallerCallBack> callerCallBack = nullptr;
52     std::mutex mutexlock;
53     std::condition_variable condition;
54 };
55 
56 static std::map<ConnectionKey, sptr<JSVpnExtensionContext>, key_compare> g_connects;
57 
58 class JsVpnExtensionContext final {
59 public:
JsVpnExtensionContext(const std::shared_ptr<VpnExtensionContext> & context)60     explicit JsVpnExtensionContext(const std::shared_ptr<VpnExtensionContext>& context) : context_(context) {}
61     ~JsVpnExtensionContext() = default;
62 
Finalizer(napi_env env,void * data,void * hint)63     static void Finalizer(napi_env env, void* data, void* hint)
64     {
65         NETMGR_EXT_LOG_D("JsAbilityContext::Finalizer is called");
66         std::unique_ptr<JsVpnExtensionContext>(static_cast<JsVpnExtensionContext*>(data));
67     }
68 
StartVpnExtensionAbility(napi_env env,napi_callback_info info)69     static napi_value StartVpnExtensionAbility(napi_env env, napi_callback_info info)
70     {
71         GET_NAPI_INFO_AND_CALL(env, info, JsVpnExtensionContext, OnStartExtensionAbility);
72     }
73 
StopVpnExtensionAbility(napi_env env,napi_callback_info info)74     static napi_value StopVpnExtensionAbility(napi_env env, napi_callback_info info)
75     {
76         GET_NAPI_INFO_AND_CALL(env, info, JsVpnExtensionContext, OnStopExtensionAbility);
77     }
78 
79 private:
80     std::weak_ptr<VpnExtensionContext> context_;
81     sptr<JsFreeInstallObserver> freeInstallObserver_ = nullptr;
ClearFailedCallConnection(const std::weak_ptr<VpnExtensionContext> & vpnContext,const std::shared_ptr<CallerCallBack> & callback)82     static void ClearFailedCallConnection(
83         const std::weak_ptr<VpnExtensionContext>& vpnContext, const std::shared_ptr<CallerCallBack> &callback)
84     {
85         NETMGR_EXT_LOG_D("clear failed call of startup is called.");
86         auto context = vpnContext.lock();
87         if (context == nullptr || callback == nullptr) {
88             NETMGR_EXT_LOG_E("clear failed call of startup input param is nullptr.");
89             return;
90         }
91 
92         context->ClearFailedCallConnection(callback);
93     }
94 
OnStartExtensionAbility(napi_env env,NapiCallbackInfo & info)95     napi_value OnStartExtensionAbility(napi_env env, NapiCallbackInfo& info)
96     {
97         NETMGR_EXT_LOG_I("StartExtensionAbility");
98         if (info.argc < ARGC_ONE) {
99             NETMGR_EXT_LOG_E("Start extension failed, not enough params.");
100             ThrowTooFewParametersError(env);
101             return CreateJsUndefined(env);
102         }
103         AAFwk::Want want;
104         if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) {
105             ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
106             return CreateJsUndefined(env);
107         }
108 
109         NapiAsyncTask::CompleteCallback complete =
110             [weak = context_, want](napi_env env, NapiAsyncTask& task, int32_t status) {
111                 auto context = weak.lock();
112                 if (!context) {
113                     HILOG_WARN("context is released");
114                     task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
115                     return;
116                 }
117                 auto innerErrorCode = context->StartVpnExtensionAbility(want);
118                 if (innerErrorCode == 0) {
119                     task.Resolve(env, CreateJsUndefined(env));
120                 } else {
121                     task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
122                 }
123             };
124 
125         napi_value lastParam = (info.argc <= ARGC_ONE) ? nullptr : info.argv[ARGC_ONE];
126         napi_value result = nullptr;
127         NapiAsyncTask::ScheduleHighQos("JSVpnExtensionContext::OnStartExtensionAbility",
128             env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
129         return result;
130     }
131 
OnStopExtensionAbility(napi_env env,NapiCallbackInfo & info)132     napi_value OnStopExtensionAbility(napi_env env, NapiCallbackInfo& info)
133     {
134         NETMGR_EXT_LOG_I("StopExtensionAbility");
135         if (info.argc < ARGC_ONE) {
136             NETMGR_EXT_LOG_E("Start extension failed, not enough params.");
137             ThrowTooFewParametersError(env);
138             return CreateJsUndefined(env);
139         }
140         AAFwk::Want want;
141         if (!AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want)) {
142             ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
143             return CreateJsUndefined(env);
144         }
145 
146         NapiAsyncTask::CompleteCallback complete =
147             [weak = context_, want](napi_env env, NapiAsyncTask& task, int32_t status) {
148                 auto context = weak.lock();
149                 if (!context) {
150                     HILOG_WARN("context is released");
151                     task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
152                     return;
153                 }
154                 auto innerErrorCode = context->StopVpnExtensionAbility(want);
155                 if (innerErrorCode == 0) {
156                     task.Resolve(env, CreateJsUndefined(env));
157                 } else {
158                     task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
159                 }
160             };
161 
162         napi_value lastParam = (info.argc <= ARGC_ONE) ? nullptr : info.argv[ARGC_ONE];
163         napi_value result = nullptr;
164         NapiAsyncTask::Schedule("JSVpnExtensionContext::OnStopExtensionAbility",
165             env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
166         return result;
167     }
168 };
169 } // namespace
170 
CreateJsVpnExtensionContext(napi_env env,std::shared_ptr<VpnExtensionContext> context)171 napi_value CreateJsVpnExtensionContext(napi_env env, std::shared_ptr<VpnExtensionContext> context)
172 {
173     NETMGR_EXT_LOG_D("CreateJsVpnExtensionContext");
174     std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = nullptr;
175     if (context) {
176         abilityInfo = context->GetAbilityInfo();
177     }
178     napi_value object = CreateJsExtensionContext(env, context, abilityInfo);
179 
180     std::unique_ptr<JsVpnExtensionContext> jsContext = std::make_unique<JsVpnExtensionContext>(context);
181     napi_wrap(env, object, jsContext.release(), JsVpnExtensionContext::Finalizer, nullptr, nullptr);
182 
183     const char *moduleName = "JsVpnExtensionContext";
184     BindNativeFunction(env, object, "startVpnExtensionAbility", moduleName,
185         JsVpnExtensionContext::StartVpnExtensionAbility);
186     BindNativeFunction(env, object, "stopVpnExtensionAbility", moduleName,
187         JsVpnExtensionContext::StopVpnExtensionAbility);
188     return object;
189 }
190 
JSVpnExtensionContext(napi_env env)191 JSVpnExtensionContext::JSVpnExtensionContext(napi_env env) : env_(env) {}
192 
~JSVpnExtensionContext()193 JSVpnExtensionContext::~JSVpnExtensionContext()
194 {
195     if (jsConnectionObject_ == nullptr) {
196         return;
197     }
198 
199     uv_loop_t *loop = nullptr;
200     napi_get_uv_event_loop(env_, &loop);
201     if (loop == nullptr) {
202         return;
203     }
204 
205     uv_work_t *work = new (std::nothrow) uv_work_t;
206     if (work == nullptr) {
207         return;
208     }
209     work->data = reinterpret_cast<void *>(jsConnectionObject_.release());
210     int ret = uv_queue_work(loop, work, [](uv_work_t *work) {},
211     [](uv_work_t *work, int status) {
212         if (work == nullptr) {
213             return;
214         }
215         if (work->data == nullptr) {
216             delete work;
217             work = nullptr;
218             return;
219         }
220         delete reinterpret_cast<NativeReference *>(work->data);
221         work->data = nullptr;
222         delete work;
223         work = nullptr;
224     });
225     if (ret != 0) {
226         delete reinterpret_cast<NativeReference *>(work->data);
227         work->data = nullptr;
228         delete work;
229         work = nullptr;
230     }
231 }
232 }  // namespace NetManagerStandard
233 }  // namespace OHOS
234