1 /*
2 * Copyright (C) 2025 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 #include <ani.h>
16 #include <fcntl.h>
17 #include <securec.h>
18 #include <sys/stat.h>
19 #include <filesystem>
20 #include <iostream>
21 #include <regex>
22 #include <string>
23 #include <system_error>
24
25 #include "constant.h"
26 #include "class.h"
27 #include "log.h"
28 #include "memory.h"
29 #include "ani_utils.h"
30 #include "ani_task.h"
31 #include "ani_js_initialize.h"
32 #include "request_common.h"
33
34 using namespace OHOS;
35 using namespace OHOS::Request;
36 using namespace OHOS::AniUtil;
37
38 template<>
TryConvertArray(std::vector<ani_ref> & value)39 bool UnionAccessor::TryConvertArray<ani_ref>(std::vector<ani_ref> &value)
40 {
41 ani_double length;
42 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
43 return false;
44 }
45
46 for (int i = 0; i < int(length); i++) {
47 ani_ref ref;
48 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
49 return false;
50 }
51 value.push_back(ref);
52 }
53 return true;
54 }
55
ThrowBusinessError(ani_env * env,int errCode,std::string && errMsg)56 static void ThrowBusinessError(ani_env *env, int errCode, std::string&& errMsg)
57 {
58 REQUEST_HILOGI("into ThrowBusinessError.");
59 static const char *errorClsName = "L@ohos/base/BusinessError;";
60 ani_class cls {};
61 if (env->FindClass(errorClsName, &cls) != ANI_OK) {
62 REQUEST_HILOGE("find class BusinessError %{public}s failed", errorClsName);
63 return;
64 }
65 ani_method ctor;
66 if (env->Class_FindMethod(cls, "<ctor>", ":V", &ctor) != ANI_OK) {
67 REQUEST_HILOGE("find method BusinessError.constructor failed");
68 return;
69 }
70 ani_object errorObject;
71 if (env->Object_New(cls, ctor, &errorObject) != ANI_OK) {
72 REQUEST_HILOGE("create BusinessError object failed");
73 return;
74 }
75 ani_double aniErrCode = static_cast<ani_double>(errCode);
76 ani_string errMsgStr;
77 if (env->String_NewUTF8(errMsg.c_str(), errMsg.size(), &errMsgStr) != ANI_OK) {
78 REQUEST_HILOGE("convert errMsg to ani_string failed");
79 return;
80 }
81 if (env->Object_SetFieldByName_Double(errorObject, "code", aniErrCode) != ANI_OK) {
82 REQUEST_HILOGE("set error code failed");
83 return;
84 }
85 if (env->Object_SetPropertyByName_Ref(errorObject, "message", errMsgStr) != ANI_OK) {
86 REQUEST_HILOGE("set error message failed");
87 return;
88 }
89 env->ThrowError(static_cast<ani_error>(errorObject));
90 return;
91 }
92
InitConfig(ani_env * env,ani_object object,Config & config)93 static ExceptionError InitConfig(ani_env *env, ani_object object, Config &config)
94 {
95 std::shared_ptr<OHOS::AbilityRuntime::Context> context = nullptr;
96 ExceptionError error = { .code = E_OK };
97 context = JsInitialize::GetContext(env, object);
98 if (context == nullptr) {
99 REQUEST_HILOGE("context == null");
100 error.code = E_PARAMETER_CHECK;
101 error.errInfo = "Parameter verification failed, Get context fail";
102 return error;
103 }
104 auto applicationInfo = context->GetApplicationInfo();
105 if (applicationInfo == nullptr) {
106 REQUEST_HILOGE("ApplicationInfo == null");
107 error.code = E_OTHER;
108 error.errInfo = "ApplicationInfo is null";
109 return error;
110 }
111 config.bundleType = static_cast<u_int32_t>(applicationInfo->bundleType);
112 config.bundleName = context->GetBundleName();
113 config.version = Version::API10;
114 bool ret = JsInitialize::CheckFilePath(context, config, error);
115 if (!ret) {
116 REQUEST_HILOGE("error info is: %{public}s", error.errInfo.c_str());
117 }
118 return error;
119 }
120
IsArray(ani_env * env,ani_object aniData)121 static bool IsArray(ani_env *env, ani_object aniData)
122 {
123 ani_double length;
124 if (ANI_OK != env->Object_GetPropertyByName_Double(aniData, "length", &length)) {
125 return false;
126 }
127 return true;
128 }
129
IsInstanceOf(ani_env * env,const std::string & cls_name,ani_object obj)130 ani_boolean OHOS::AniUtil::IsInstanceOf(ani_env *env, const std::string &cls_name, ani_object obj)
131 {
132 ani_class cls;
133 if (ANI_OK != env->FindClass(cls_name.c_str(), &cls)) {
134 return ANI_FALSE;
135 }
136
137 ani_boolean ret;
138 env->Object_InstanceOf(obj, cls, &ret);
139 return ret;
140 }
141
GetDownloadData(ani_env * env,Config & aniConfig,ani_object aniData)142 static bool GetDownloadData(ani_env *env, Config &aniConfig, ani_object aniData)
143 {
144 UnionAccessor unionAccessor(env, aniData);
145 if (unionAccessor.IsInstanceOf("Lstd/core/String;")) {
146 aniConfig.data = AniStringUtils::ToStd(env, static_cast<ani_string>(aniData));
147 }
148 return true;
149 }
150
ProcessDatas(ani_env * env,Config & aniConfig,ani_object aniData)151 static bool ProcessDatas(ani_env *env, Config &aniConfig, ani_object aniData)
152 {
153 UnionAccessor unionAccessor(env, aniData);
154 if (aniConfig.action == Action::DOWNLOAD) {
155 return GetDownloadData(env, aniConfig, aniData);
156 }
157 if (aniConfig.action != Action::UPLOAD) {
158 return false;
159 }
160
161 std::vector<ani_ref> arrayDoubleValues = {};
162 if (!unionAccessor.TryConvertArray<ani_ref>(arrayDoubleValues) || arrayDoubleValues.empty()) {
163 return false;
164 }
165
166 for (uint16_t i = 0; i < arrayDoubleValues.size(); i++) {
167 ani_object data = static_cast<ani_object>(arrayDoubleValues[i]);
168 ani_ref nameRef;
169 if (ANI_OK != env->Object_GetPropertyByName_Ref(data, "name", &nameRef)) {
170 REQUEST_HILOGE("Object_GetFieldByName_Ref name from data Faild");
171 return false;
172 }
173 auto name = AniStringUtils::ToStd(env, static_cast<ani_string>(nameRef));
174
175 ani_ref valueRef;
176 if (ANI_OK != env->Object_GetPropertyByName_Ref(data, "value", &valueRef)) {
177 REQUEST_HILOGE("Object_GetFieldByName_Ref value from data Faild");
178 return false;
179 }
180 if (IsInstanceOf(env, "Lstd/core/String;", static_cast<ani_object>(valueRef))) {
181 FormItem form;
182 form.name = name;
183 form.value = AniStringUtils::ToStd(env, static_cast<ani_string>(valueRef));
184 aniConfig.forms.push_back(form);
185 continue;
186 }
187 if (IsInstanceOf(env, "L@ohos/request/request/agent/FileSpec;", static_cast<ani_object>(valueRef))) {
188 FileSpec file;
189 if (!JsInitialize::Convert2FileSpec(env, static_cast<ani_object>(valueRef), name, file)) {
190 REQUEST_HILOGE("Convert2FileSpec failed");
191 return false;
192 }
193 aniConfig.files.push_back(file);
194 continue;
195 }
196 if (!IsArray(env, static_cast<ani_object>(valueRef))) {
197 return false;
198 }
199 if (!JsInitialize::Convert2FileSpecs(env, static_cast<ani_object>(valueRef), name, aniConfig.files)) {
200 return false;
201 }
202 }
203 return true;
204 }
205
SetConfigInfo(ani_env * env,Config & aniConfig,ani_object config)206 static bool SetConfigInfo(ani_env *env, Config &aniConfig, ani_object config)
207 {
208 ani_ref url;
209 if (ANI_OK != env->Object_GetPropertyByName_Ref(config, "url", &url)) {
210 REQUEST_HILOGI("Failed to get property named type");
211 return false;
212 }
213 auto urlStr = AniStringUtils::ToStd(env, static_cast<ani_string>(url));
214 REQUEST_HILOGI("urlStr: %{public}s", urlStr.c_str());
215 aniConfig.url = urlStr;
216 ani_ref aniAction;
217 if (ANI_OK != env->Object_GetPropertyByName_Ref(config, "action", &aniAction)) {
218 REQUEST_HILOGI("Failed to get property named type");
219 return false;
220 }
221 EnumAccessor actionAccessor(env, static_cast<ani_enum_item>(aniAction));
222 expected<Action, ani_status> actionExpected = actionAccessor.To<Action>();
223 if (!actionExpected) {
224 return false;
225 }
226 Action action = actionExpected.value();
227 aniConfig.action = action;
228 REQUEST_HILOGI("vibrateInfo.type: %{public}d", action);
229
230 aniConfig.overwrite = true;
231
232 ani_ref aniMethod;
233 if (env->Object_GetPropertyByName_Ref(config, "method", &aniMethod) == ANI_OK && aniMethod != nullptr) {
234 auto method = AniStringUtils::ToStd(env, static_cast<ani_string>(aniMethod));
235 aniConfig.method = method;
236 }
237
238 ani_ref aniSaveas;
239 if (env->Object_GetPropertyByName_Ref(config, "saveas", &aniSaveas) == ANI_OK && aniSaveas != nullptr) {
240 auto saveas = AniStringUtils::ToStd(env, static_cast<ani_string>(aniSaveas));
241 aniConfig.saveas = saveas;
242 }
243
244 ani_ref aniData;
245 if (env->Object_GetPropertyByName_Ref(config, "data", &aniData) == ANI_OK && aniData != nullptr) {
246 bool ret = ProcessDatas(env, aniConfig, static_cast<ani_object>(aniData));
247 if (!ret) {
248 REQUEST_HILOGE("ProcessDatas data error.");
249 return ret;
250 }
251 }
252 return true;
253 }
254
Create(ani_env * env,ani_object object,ani_object config)255 static ani_object Create([[maybe_unused]] ani_env *env, ani_object object, ani_object config)
256 {
257 REQUEST_HILOGI("Create Start");
258 ani_object nullobj{};
259 if (object == nullptr) {
260 REQUEST_HILOGE("context == null");
261 return nullobj;
262 }
263 if (config == nullptr) {
264 REQUEST_HILOGE("config == null");
265 return nullobj;
266 }
267
268 Config aniConfig{};
269 aniConfig.saveas = "default.txt";
270 if (!SetConfigInfo(env, aniConfig, config)) {
271 REQUEST_HILOGE("Failed to SetConfigInfo.");
272 return nullobj;
273 }
274
275 ExceptionError err = InitConfig(env, object, aniConfig);
276 if (err.code != E_OK) {
277 REQUEST_HILOGE("err.code : %{public}d, err.errInfo : %{public}s", err.code, err.errInfo.c_str());
278 ThrowBusinessError(env, err.code, std::move(err.errInfo));
279 return nullobj;
280 }
281
282 AniTask *task = AniTask::Create(env, aniConfig);
283 if (task == nullptr) {
284 REQUEST_HILOGE("AniTask::Create task == nullptr!");
285 return nullobj;
286 }
287
288 auto taskImpl = AniObjectUtils::Create(env, "L@ohos/request/request;", "Lagent;", "LTaskImpl;");
289
290 NativePtrWrapper wrapper(env, taskImpl);
291 wrapper.Wrap<AniTask>(task);
292 return taskImpl;
293 }
294
StartSync(ani_env * env,ani_object object)295 static void StartSync([[maybe_unused]] ani_env *env, ani_object object)
296 {
297 REQUEST_HILOGI("Enter Start");
298 if (env == nullptr) {
299 return;
300 }
301 NativePtrWrapper wrapper(env, object);
302 auto task = wrapper.Unwrap<AniTask>();
303 if (task == nullptr) {
304 REQUEST_HILOGE("task is nullptr");
305 return;
306 }
307 task->Start(env);
308 }
309
OnSync(ani_env * env,ani_object object,ani_string response,ani_object callback)310 static void OnSync([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object,
311 ani_string response, ani_object callback)
312 {
313 REQUEST_HILOGI("Enter On");
314
315 ani_ref callbackRef = nullptr;
316 env->GlobalReference_Create(reinterpret_cast<ani_ref>(callback), &callbackRef);
317 auto responseEvent = AniStringUtils::ToStd(env, static_cast<ani_string>(response));
318 NativePtrWrapper wrapper(env, object);
319 auto task = wrapper.Unwrap<AniTask>();
320 if (task == nullptr) {
321 REQUEST_HILOGE("task is nullptr");
322 return;
323 }
324 task->On(env, responseEvent, callbackRef);
325 }
326
ANI_Constructor(ani_vm * vm,uint32_t * result)327 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
328 {
329 REQUEST_HILOGI("Enter ANI_Constructor Start");
330 ani_env *env;
331 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
332 REQUEST_HILOGI("Unsupported ANI_VERSION_1");
333 return ANI_ERROR;
334 }
335
336 static const char *namespaceName = "L@ohos/request/request;";
337 ani_namespace request;
338 if (ANI_OK != env->FindNamespace(namespaceName, &request)) {
339 REQUEST_HILOGI("Not found '%{public}s'", namespaceName);
340 return ANI_ERROR;
341 }
342
343 static const char *agentNamespaceName = "Lagent;";
344 ani_namespace agent;
345 if (ANI_OK != env->Namespace_FindNamespace(request, agentNamespaceName, &agent)) {
346 REQUEST_HILOGI("Not found '%{public}s'", agentNamespaceName);
347 return ANI_ERROR;
348 }
349 std::array nsMethods = {
350 ani_native_function {"createSync", nullptr, reinterpret_cast<void *>(Create)},
351 };
352
353 if (ANI_OK != env->Namespace_BindNativeFunctions(agent, nsMethods.data(), nsMethods.size())) {
354 REQUEST_HILOGI("Cannot bind native methods to '%{public}s'", namespaceName);
355 return ANI_ERROR;
356 };
357
358 static const char *requestclsName = "LTaskImpl;";
359 ani_class requestClass;
360 if (ANI_OK != env->Namespace_FindClass(agent, requestclsName, &requestClass)) {
361 REQUEST_HILOGI("Not found class %{public}s", requestclsName);
362 return ANI_NOT_FOUND;
363 }
364
365 std::array methods = {
366 ani_native_function {"startSync", nullptr, reinterpret_cast<void *>(StartSync)},
367 ani_native_function {"onSync", nullptr, reinterpret_cast<void *>(OnSync)},
368 };
369
370 if (ANI_OK != env->Class_BindNativeMethods(requestClass, methods.data(), methods.size())) {
371 REQUEST_HILOGI("Cannot bind native methods to %{public}s", requestclsName);
372 return ANI_ERROR;
373 }
374
375 auto cleanerCls = TypeFinder(env).FindClass(agent, "LCleaner;");
376 NativePtrCleaner(env).Bind(cleanerCls.value());
377
378 *result = ANI_VERSION_1;
379 return ANI_OK;
380 }
381