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
16 #include <cstring>
17 #include <vector>
18 #include <algorithm>
19
20 #include "interop-logging.h"
21 #include "dynamic-loader.h"
22 #include "koala-types.h"
23
24 // DO NOT USE KOALA INTEROP MECHANISMS IN THIS FILE!
25
26 #ifdef KOALA_JNI
27 #include "jni.h"
28 #endif
29
30 #ifdef KOALA_ETS_NAPI
31 #include "etsapi.h"
32 #endif
33
34 #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS)
35 #include "sys/stat.h"
36 #include "dirent.h"
37 #endif
38
39 #define OHOS_USER_LIBS "/data/storage/el1/bundle/libs"
40 #ifdef KOALA_OHOS_ARM32
41 #define USE_SYSTEM_ARKVM 1
42 #elif KOALA_OHOS_ARM64
43 #define USE_SYSTEM_ARKVM 1
44 #else
45 #define USE_SYSTEM_ARKVM 0
46 #endif
47
48 #if USE_SYSTEM_ARKVM
49 #define SYSTEM_ARK_STDLIB_PATH "/system/etc/etsstdlib.abc"
50 #endif
51
52 void traverseDir(std::string root, std::vector<std::string>& paths, int depth = 0);
53
54 struct VMLibInfo {
55 const char* sdkPath;
56 const char* platform;
57 const char* lib;
58 const char* createVM;
59 };
60
61 #ifdef KOALA_JNI
62 const VMLibInfo javaVMLib = {
63 getenv("JAVA_HOME"),
64 #if defined(KOALA_LINUX) || defined(KOALA_MACOS)
65 "lib/server"
66 #elif KOALA_WINDOWS
67 "bin/server"
68 #else
69 #error "Unknown platform"
70 #endif
71 ,
72 "jvm",
73 "JNI_CreateJavaVM",
74 };
75 #endif
76
77 #ifdef KOALA_ETS_NAPI
78 const VMLibInfo pandaVMLib = {
79 // sdkPath
80 #if defined(KOALA_OHOS)
81 #ifdef KOALA_OHOS_ARM32
82 "/system/lib"
83 #elif KOALA_OHOS_ARM64
84 "/system/lib64"
85 #else
86 OHOS_USER_LIBS
87 #endif
88 #else
89 getenv("PANDA_HOME")
90 #endif
91 ,
92
93 // platform
94 #ifdef KOALA_LINUX
95 #ifdef KOALA_LINUX_ARM64
96 "linux_arm64_host_tools/lib"
97 #else
98 "linux_host_tools/lib"
99 #endif
100 #elif KOALA_MACOS
101 "macos_host_tools/lib"
102 #elif KOALA_WINDOWS
103 "_host_tools/lib"
104 #elif KOALA_OHOS_ARM64
105 "arm64"
106 #elif KOALA_OHOS_ARM32
107 "arm"
108 #else
109 #error "Unknown platform"
110 #endif
111 ,
112
113 // lib
114 "arkruntime"
115 ,
116
117 // createVM
118 "ETS_CreateVM"
119 };
120 #endif
121
122 struct VMInitArgs {
123 int version;
124 int nOptions;
125 void* options;
126 };
127
128 #define JAVA_VM_KIND 1
129 #define PANDA_VM_KIND 2
130 #define ES2PANDA_KIND 3
131
132 struct ForeignVMContext {
133 void* currentVMContext;
134 int32_t (*callSync)(void* vmContext, int32_t callback, int8_t* data, int32_t length);
135 };
136
137 struct VMEntry {
138 int vmKind;
139 void* env;
140 void* app;
141 void* enter;
142 void* emitEvent;
143 void* restartWith;
144 ForeignVMContext foreignVMContext;
145 };
146
147 VMEntry g_vmEntry = {};
148
149 typedef int (*createVM_t)(void** pVM, void** pEnv, void* vmInitArgs);
150 typedef int (*getVMs_t)(void** pVM, int32_t bufLen, int32_t* nVMs);
151
152 #ifdef KOALA_WINDOWS
153 #define DLL_EXPORT __declspec(dllexport)
154 #else
155 #define DLL_EXPORT __attribute__ ((visibility ("default")))
156 #endif
157
loadES2Panda(const char * appClassPath,const char * appLibPath)158 int loadES2Panda(const char* appClassPath, const char* appLibPath) {
159 fprintf(stderr, "native: es2panda %s\n", appClassPath);
160 return 0;
161 }
162
ArkMobileLog(int id,int level,const char * component,const char * fmt,const char * msg)163 static int ArkMobileLog(int id, int level, const char *component, const char *fmt, const char *msg) {
164 LOGE("ArkMobileLog: %" LOG_PUBLIC "s", msg);
165 return 0;
166 }
167
LoadVirtualMachine(KInt vmKind,const char * appClassPath,const char * appLibPath,const ForeignVMContext * foreignVMContext)168 extern "C" DLL_EXPORT KInt LoadVirtualMachine(KInt vmKind, const char* appClassPath, const char* appLibPath, const ForeignVMContext* foreignVMContext) {
169 if (vmKind == ES2PANDA_KIND) {
170 return loadES2Panda(appClassPath, appLibPath);
171 }
172
173 const VMLibInfo* thisVM =
174 #ifdef KOALA_JNI
175 (vmKind == JAVA_VM_KIND) ? &javaVMLib :
176 #endif
177 #ifdef KOALA_ETS_NAPI
178 (vmKind == PANDA_VM_KIND) ? &pandaVMLib :
179 #endif
180 nullptr;
181
182 if (!thisVM) {
183 LOGE("Unknown VM kind: %" LOG_PUBLIC "d\n (possibly %" LOG_PUBLIC "s is compiled without expected flags)", vmKind, __FILE__);
184 return -1;
185 }
186
187 LOGI("Starting VM %" LOG_PUBLIC "d with classpath=%" LOG_PUBLIC "s native=%" LOG_PUBLIC "s", vmKind, appClassPath, appLibPath);
188
189 std::string libPath =
190 #if USE_SYSTEM_ARKVM
191 std::string(thisVM->sdkPath) + "/" + libName(thisVM->lib)
192 #elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS)
193 std::string(thisVM->sdkPath) + "/" + std::string(thisVM->platform) + "/" + libName(thisVM->lib)
194 #elif defined(KOALA_OHOS)
195 std::string(OHOS_USER_LIBS) + "/" + libName(thisVM->lib)
196 #else
197 #error "Library path not specified for this platform"
198 #endif
199 ;
200 void *handle = loadLibrary(libPath);
201 if (!handle) {
202 LOGE("Cannot load library %" LOG_PUBLIC "s: %" LOG_PUBLIC "s\n", libPath.c_str(), libraryError());
203 return -1;
204 }
205
206 createVM_t createVM = (createVM_t)findSymbol(handle, thisVM->createVM);
207 getVMs_t getVMs = (getVMs_t)findSymbol(handle, "ETS_GetCreatedVMs");
208
209 if (!createVM) {
210 LOGE("Cannot find %" LOG_PUBLIC "s\n", thisVM->createVM);
211 return -1;
212 }
213
214 void* vm = nullptr;
215 void* env = nullptr;
216 int32_t nVMs = 0;
217 int result = 0;
218
219 #ifdef KOALA_JNI
220 if (vmKind == JAVA_VM_KIND) {
221 JavaVMInitArgs javaVMArgs;
222 javaVMArgs.version = JNI_VERSION_10;
223 javaVMArgs.ignoreUnrecognized = false;
224 std::vector<JavaVMOption> javaVMOptions;
225 javaVMOptions = {
226 {(char*)strdup((std::string("-Djava.class.path=") + appClassPath).c_str())},
227 {(char*)strdup((std::string("-Djava.library.path=") + appLibPath).c_str())},
228 };
229 javaVMArgs.nOptions = javaVMOptions.size();
230 javaVMArgs.options = javaVMOptions.data();
231 g_vmEntry.vmKind = JAVA_VM_KIND;
232 result = createVM(&vm, &env, &javaVMArgs);
233 }
234 #endif
235
236 #ifdef KOALA_ETS_NAPI
237 if (vmKind == PANDA_VM_KIND) {
238 EtsVMInitArgs pandaVMArgs;
239 pandaVMArgs.version = ETS_NAPI_VERSION_1_0;
240 std::vector<EtsVMOption> etsVMOptions;
241 std::vector<std::string> files;
242 traverseDir(std::string(appClassPath), files);
243 std::sort(files.begin(), files.end());
244 etsVMOptions = {
245 #if USE_SYSTEM_ARKVM
246 {EtsOptionType::ETS_BOOT_FILE, SYSTEM_ARK_STDLIB_PATH},
247 #elif defined(KOALA_OHOS)
248 {EtsOptionType::ETS_BOOT_FILE, (std::string(OHOS_USER_LIBS) + "/" + "etsstdlib.abc").c_str() },
249
250 #elif defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_WINDOWS)
251 {EtsOptionType::ETS_BOOT_FILE, (char*)strdup((std::string(thisVM->sdkPath) + "/ets/etsstdlib.abc").c_str())},
252 #endif
253 };
254 for (const std::string& path : files) {
255 etsVMOptions.push_back({EtsOptionType::ETS_BOOT_FILE, (char*)strdup(path.c_str())});
256 }
257 etsVMOptions.push_back({EtsOptionType::ETS_NATIVE_LIBRARY_PATH, (char*)strdup(std::string(appLibPath).c_str())});
258 etsVMOptions.push_back({EtsOptionType::ETS_VERIFICATION_MODE, "on-the-fly"});
259 etsVMOptions.push_back({EtsOptionType::ETS_NO_JIT, nullptr});
260 etsVMOptions.push_back({EtsOptionType::ETS_MOBILE_LOG, (void*)ArkMobileLog});
261 etsVMOptions.push_back({EtsOptionType::ETS_AOT, nullptr});
262 pandaVMArgs.nOptions = etsVMOptions.size();
263 pandaVMArgs.options = etsVMOptions.data();
264 g_vmEntry.vmKind = PANDA_VM_KIND;
265
266 result = getVMs ? getVMs(&vm, 1, &nVMs) : 0;
267 if (nVMs != 0) {
268 __EtsVM* vmInstance = (__EtsVM*)vm;
269 EtsEnv* pEnv = nullptr;
270 vmInstance->GetEnv(&pEnv, ETS_NAPI_VERSION_1_0);
271 env = static_cast<void*>(pEnv);
272 } else {
273 result = createVM(&vm, &env, &pandaVMArgs);
274 }
275
276 }
277 #endif
278
279 if (result != 0) {
280 LOGE("Error creating a VM of kind %" LOG_PUBLIC "d: %" LOG_PUBLIC "d\n", vmKind, result);
281 return result;
282 }
283 g_vmEntry.env = env;
284 g_vmEntry.foreignVMContext = *foreignVMContext;
285 return 0;
286 }
287
288 struct AppInfo {
289 const char* className;
290 const char* createMethodName;
291 const char* createMethodSig;
292 const char* startMethodName;
293 const char* startMethodSig;
294 const char* enterMethodName;
295 const char* enterMethodSig;
296 const char* emitEventMethodName;
297 const char* emitEventMethodSig;
298 const char* restartWithMethodName;
299 const char* restartWithMethodSig;
300 };
301
302 #ifdef KOALA_JNI
303 const AppInfo javaAppInfo = {
304 "org/koalaui/arkoala/Application",
305 "createApplication",
306 "(Ljava/lang/String;Ljava/lang/String;)Lorg/koalaui/arkoala/Application;",
307 "start",
308 "()J",
309 "enter",
310 "(IIJ)Z",
311 "emitEvent",
312 "(IIII)Ljava/lang/String;",
313 };
314 #endif
315
316 #ifdef KOALA_ETS_NAPI
317 const AppInfo pandaAppInfo = {
318 "@koalaui/arkts-arkui/Application/Application",
319 "createApplication",
320 "Lstd/core/String;Lstd/core/String;Z:L@koalaui/arkts-arkui/Application/Application;",
321 "start",
322 ":J",
323 "enter",
324 "IIJ:Z",
325 "emitEvent",
326 "IIII:Lstd/core/String;",
327 };
328 const AppInfo harnessAppInfo = {
329 "@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication",
330 "createApplication",
331 "Lstd/core/String;Lstd/core/String;Z:L@koalaui/ets-harness/src/EtsHarnessApplication/EtsHarnessApplication;",
332 "start",
333 ":J",
334 "enter",
335 "II:Z",
336 "emitEvent",
337 "IIII:Lstd/core/String;",
338 "restartWith",
339 "Lstd/core/String;:V"
340 };
341 #endif
342
StartApplication(const char * appUrl,const char * appParams)343 extern "C" DLL_EXPORT KNativePointer StartApplication(const char* appUrl, const char* appParams) {
344 const auto isTestEnv = std::string(appUrl) == "EtsHarness";
345 const AppInfo* appInfo =
346 #ifdef KOALA_JNI
347 (g_vmEntry.vmKind == JAVA_VM_KIND) ? &javaAppInfo :
348 #endif
349 #ifdef KOALA_ETS_NAPI
350 (g_vmEntry.vmKind == PANDA_VM_KIND) ? isTestEnv ? &harnessAppInfo : &pandaAppInfo :
351 #endif
352 nullptr;
353
354 if (!appInfo) {
355 LOGE("No appInfo provided for VM kind %" LOG_PUBLIC "d (recompile vmloader.cc with the missing flags)\n", g_vmEntry.vmKind);
356 return nullptr;
357 }
358
359 LOGI("Starting application %" LOG_PUBLIC "s with params %" LOG_PUBLIC "s", appUrl, appParams);
360
361 #ifdef KOALA_JNI
362 if (g_vmEntry.vmKind == JAVA_VM_KIND) {
363 JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
364 jclass appClass = jEnv->FindClass(appInfo->className);
365 if (!appClass) {
366 LOGE("Cannot load main class %s\n", appInfo->className);
367 return nullptr;
368 }
369 jmethodID create = jEnv->GetStaticMethodID(appClass, appInfo->createMethodName, appInfo->createMethodSig);
370 if (!create) {
371 LOGE("Cannot find create method %s\n", appInfo->createMethodName);
372 return nullptr;
373 }
374 auto app = jEnv->NewGlobalRef(jEnv->CallStaticObjectMethod(appClass, create, jEnv->NewStringUTF(appUrl), jEnv->NewStringUTF(appParams)));
375 g_vmEntry.app = app;
376 auto start = jEnv->GetMethodID(appClass, appInfo->startMethodName, appInfo->startMethodSig);
377 if (!start) {
378 LOGE("Cannot find start method \"%s %s\"\n", appInfo->startMethodName, appInfo->startMethodSig);
379 return nullptr;
380 }
381 g_vmEntry.enter = (void*)(jEnv->GetMethodID(appClass, appInfo->enterMethodName, appInfo->enterMethodSig));
382 if (!g_vmEntry.enter) {
383 LOGE("Cannot find enter method %s\n", appInfo->enterMethodName);
384 return nullptr;
385 }
386 g_vmEntry.emitEvent = (void*)(jEnv->GetMethodID(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig));
387 if (!g_vmEntry.emitEvent) {
388 LOGE("Cannot find emitEvent method %s\n", appInfo->emitEventMethodName);
389 return nullptr;
390 }
391 return reinterpret_cast<KNativePointer>(jEnv->CallLongMethod(
392 app, start));
393 }
394 #endif
395 #ifdef KOALA_ETS_NAPI
396 if (g_vmEntry.vmKind == PANDA_VM_KIND) {
397 EtsEnv* etsEnv = (EtsEnv*)g_vmEntry.env;
398 ets_class appClass = etsEnv->FindClass(appInfo->className);
399 if (!appClass) {
400 LOGE("Cannot load main class %" LOG_PUBLIC "s\n", appInfo->className);
401 return nullptr;
402 }
403 ets_method create = etsEnv->GetStaticp_method(appClass, appInfo->createMethodName, appInfo->createMethodSig);
404 if (!create) {
405 LOGE("Cannot find create method %" LOG_PUBLIC "s\n", appInfo->createMethodName);
406 if (etsEnv->ErrorCheck()) {
407 etsEnv->ErrorDescribe();
408 etsEnv->ErrorClear();
409 }
410 return nullptr;
411 }
412 #if defined (KOALA_OHOS_ARM64)
413 auto useNativeLog = true;
414 #else
415 auto useNativeLog = false;
416 #endif
417 auto app = etsEnv->NewGlobalRef(etsEnv->CallStaticObjectMethod(
418 appClass, create,
419 etsEnv->NewStringUTF(appUrl), etsEnv->NewStringUTF(appParams),
420 useNativeLog
421 ));
422 if (!app) {
423 LOGE("createApplication returned null");
424 if (etsEnv->ErrorCheck()) {
425 etsEnv->ErrorDescribe();
426 etsEnv->ErrorClear();
427 }
428 return nullptr;
429 }
430 g_vmEntry.app = (void*)app;
431 auto start = etsEnv->Getp_method(appClass, appInfo->startMethodName, appInfo->startMethodSig);
432 g_vmEntry.enter = (void*)(etsEnv->Getp_method(appClass, appInfo->enterMethodName, nullptr /*appInfo->enterMethodSig */));
433 if (!g_vmEntry.enter) {
434 LOGE("Cannot find enter method %" LOG_PUBLIC "s", appInfo->enterMethodName);
435 if (etsEnv->ErrorCheck()) {
436 etsEnv->ErrorDescribe();
437 etsEnv->ErrorClear();
438 }
439 return nullptr;
440 }
441 g_vmEntry.emitEvent = (void*)(etsEnv->Getp_method(appClass, appInfo->emitEventMethodName, appInfo->emitEventMethodSig));
442 if (!g_vmEntry.emitEvent) {
443 LOGE("Cannot find enter emitEvent %" LOG_PUBLIC "s", appInfo->emitEventMethodSig);
444 if (etsEnv->ErrorCheck()) {
445 etsEnv->ErrorDescribe();
446 etsEnv->ErrorClear();
447 }
448 return nullptr;
449 }
450 if (isTestEnv) {
451 g_vmEntry.restartWith = (void*)(etsEnv->Getp_method(appClass, appInfo->restartWithMethodName, appInfo->restartWithMethodSig));
452 if (!g_vmEntry.restartWith) {
453 LOGE("Cannot find enter restartWith %" LOG_PUBLIC "s", appInfo->restartWithMethodSig);
454 if (etsEnv->ErrorCheck()) {
455 etsEnv->ErrorDescribe();
456 etsEnv->ErrorClear();
457 }
458 return nullptr;
459 }
460 }
461 // TODO: pass app entry point!
462 return reinterpret_cast<KNativePointer>(etsEnv->CallLongMethod((ets_object)(app), start));
463 }
464 #endif
465 return nullptr;
466 }
467
RunApplication(const KInt arg0,const KInt arg1)468 extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) {
469 #ifdef KOALA_JNI
470 if (g_vmEntry.vmKind == JAVA_VM_KIND) {
471 JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
472 auto result = jEnv->CallBooleanMethod(
473 (jobject)(g_vmEntry.app),
474 (jmethodID)(g_vmEntry.enter),
475 (jint)arg0,
476 (jint)arg1,
477 (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext)
478 );
479 if (jEnv->ExceptionCheck()) {
480 jEnv->ExceptionDescribe();
481 jEnv->ExceptionClear();
482 }
483 return result;
484 }
485 #endif
486 #ifdef KOALA_ETS_NAPI
487 if (g_vmEntry.vmKind == PANDA_VM_KIND) {
488 EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
489 if (!g_vmEntry.enter) {
490 LOGE("Cannot find enter method");
491 return -1;
492 }
493 auto result = etsEnv->CallBooleanMethod(
494 (ets_object)(g_vmEntry.app),
495 (ets_method)(g_vmEntry.enter),
496 (ets_int)arg0,
497 (ets_int)arg1,
498 (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext)
499 );
500 if (etsEnv->ErrorCheck()) {
501 LOGE("Calling enter() method gave an error");
502 etsEnv->ErrorDescribe();
503 etsEnv->ErrorClear();
504 }
505 return result;
506 }
507 #endif
508 return 1;
509 }
510
EmitEvent(const KInt type,const KInt target,const KInt arg0,const KInt arg1)511 extern "C" DLL_EXPORT const char* EmitEvent(const KInt type, const KInt target, const KInt arg0, const KInt arg1) {
512 #ifdef KOALA_JNI
513 if (g_vmEntry.vmKind == JAVA_VM_KIND) {
514 JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
515 if (!g_vmEntry.emitEvent) {
516 LOGE("Cannot find emitEvent method");
517 return "-1";
518 }
519 auto rv = (jstring)jEnv->CallObjectMethod(
520 (jobject)(g_vmEntry.app),
521 (jmethodID)(g_vmEntry.emitEvent),
522 (jint)type,
523 (jint)target,
524 (jint)arg0,
525 (jint)arg1
526 );
527 if (jEnv->ExceptionCheck()) {
528 jEnv->ExceptionDescribe();
529 jEnv->ExceptionClear();
530 }
531 const char *result = jEnv->GetStringUTFChars(rv, 0);
532 return result;
533 }
534 #endif
535 #ifdef KOALA_ETS_NAPI
536 if (g_vmEntry.vmKind == PANDA_VM_KIND) {
537 EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
538 if (!g_vmEntry.emitEvent) {
539 LOGE("Cannot find emitEvent method");
540 return "-1";
541 }
542 auto rv = (ets_string)etsEnv->CallObjectMethod(
543 (ets_object)(g_vmEntry.app),
544 (ets_method)(g_vmEntry.emitEvent),
545 (ets_int)type,
546 (ets_int)target,
547 (ets_int)arg0,
548 (ets_int)arg1
549 );
550 if (etsEnv->ErrorCheck()) {
551 LOGE("Calling emitEvent() method gave an error");
552 etsEnv->ErrorDescribe();
553 etsEnv->ErrorClear();
554 }
555 const char *result = etsEnv->GetStringUTFChars(rv, 0);
556 return result;
557 }
558 #endif
559 return "-1";
560 }
561
RestartWith(const char * page)562 extern "C" DLL_EXPORT void RestartWith(const char* page) {
563 #ifdef KOALA_JNI
564 if (g_vmEntry.vmKind == JAVA_VM_KIND) {
565 JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env);
566 if (!g_vmEntry.restartWith) {
567 LOGE("Cannot find restartWith method");
568 return;
569 }
570 jEnv->CallVoidMethod(
571 (jobject)(g_vmEntry.app),
572 (jmethodID)(g_vmEntry.restartWith),
573 jEnv->NewStringUTF(page)
574 );
575 if (jEnv->ExceptionCheck()) {
576 jEnv->ExceptionDescribe();
577 jEnv->ExceptionClear();
578 }
579 }
580 #endif
581 #ifdef KOALA_ETS_NAPI
582 if (g_vmEntry.vmKind == PANDA_VM_KIND) {
583 EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env);
584 if (!g_vmEntry.restartWith) {
585 LOGE("Cannot find restartWith method");
586 return;
587 }
588 etsEnv->CallVoidMethod(
589 (ets_object)(g_vmEntry.app),
590 (ets_method)(g_vmEntry.restartWith),
591 etsEnv->NewStringUTF(page)
592 );
593 if (etsEnv->ErrorCheck()) {
594 LOGE("Calling restartWith() method gave an error");
595 etsEnv->ErrorDescribe();
596 etsEnv->ErrorClear();
597 }
598 }
599 #endif
600 }
601
traverseDir(std::string root,std::vector<std::string> & paths,int depth)602 void traverseDir(std::string root, std::vector<std::string>& paths, int depth) {
603 if (depth >= 50) {
604 return;
605 }
606 #if defined(KOALA_LINUX) || defined(KOALA_MACOS) || defined(KOALA_OHOS)
607 std::string suffix = ".abc";
608 #if defined(KOALA_OHOS)
609 suffix += ".so";
610 #endif
611 DIR* directory = opendir(root.c_str());
612 if (!directory) {
613 LOGE("Cannot open dir %" LOG_PUBLIC "s\n", root.c_str());
614 return;
615 }
616 struct dirent* ent = NULL;
617 struct stat statbuf;
618
619 LOGI("Searching for *%" LOG_PUBLIC "s in %" LOG_PUBLIC "s\n", suffix.c_str(), root.c_str());
620 while ((ent = readdir(directory)) != nullptr) {
621 std::string filename = std::string(ent->d_name);
622 if (filename == "." || filename == "..") {
623 continue;
624 }
625 std::string filepath = root + "/" + filename;
626 int rv = stat(filepath.c_str(), &statbuf);
627 if (rv < 0) continue;
628 if (filepath.size() >= suffix.size() && filepath.substr(filepath.size() - suffix.size()) == suffix && (statbuf.st_mode & S_IFMT) == S_IFREG) {
629 paths.push_back(filepath);
630 }
631 if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
632 traverseDir(filepath, paths, depth + 1);
633 }
634 }
635 closedir(directory);
636 #endif
637 }
638