1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
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 <algorithm>
17 #include <codecvt>
18 #include <string>
19 #include <memory>
20 #include <malloc.h>
21 #include <parameters.h>
22
23 #include "ani_util.h"
24 #include "application_context.h"
25 #include "context.h"
26 #include "directory_ex.h"
27 #include "file_ex.h"
28 #include "hiappevent_util.h"
29 #include "hidebug_native_interface.h"
30 #include "hilog/log.h"
31 #include "iservice_registry.h"
32 #include "refbase.h"
33 #include "system_ability_definition.h"
34
35 using namespace OHOS;
36 using namespace OHOS::HiviewDFX;
37
38 namespace {
39 #undef LOG_DOMAIN
40 #define LOG_DOMAIN 0xD002D0A
41 #undef LOG_TAG
42 #define LOG_TAG "HiDebug_ANI"
43
44 constexpr int MAX_TAGS_ARRAY_LENGTH = 40;
45 enum ErrorCode {
46 PERMISSION_ERROR = 201,
47 PARAMETER_ERROR = 401,
48 VERSION_ERROR = 801,
49 SYSTEM_ABILITY_NOT_FOUND = 11400101,
50 HAVA_ALREADY_TRACE = 11400102,
51 WITHOUT_WRITE_PERMISSON = 11400103,
52 SYSTEM_STATUS_ABNORMAL = 11400104,
53 NO_CAPTURE_TRACE_RUNNING = 11400105,
54 };
55 }
56
IsArrayForAniValue(ani_env * env,ani_object param,ani_int & arraySize)57 static bool IsArrayForAniValue(ani_env *env, ani_object param, ani_int &arraySize)
58 {
59 ani_boolean isArray = ANI_FALSE;
60 ani_class cls = nullptr;
61 ani_static_method isArrayMethod = nullptr;
62 if (env->FindClass("Lescompat/Array;", &cls) != ANI_OK ||
63 env->Class_FindStaticMethod(cls, "isArray", "Lstd/core/Object;:Z", &isArrayMethod) != ANI_OK ||
64 env->Class_CallStaticMethod_Boolean(cls, isArrayMethod, &isArray, param) != ANI_OK ||
65 isArray == ANI_FALSE) {
66 return false;
67 }
68
69 ani_double length = 0;
70 if (env->Object_GetPropertyByName_Double(param, "length", &length) != ANI_OK) {
71 return false;
72 }
73 arraySize = static_cast<ani_int>(length);
74 return true;
75 }
76
GetDumpParam(ani_env * env,ani_object argsAni,std::vector<std::u16string> & args)77 static bool GetDumpParam(ani_env *env, ani_object argsAni, std::vector<std::u16string> &args)
78 {
79 ani_int arraySize = 0;
80 if (!IsArrayForAniValue(env, argsAni, arraySize)) {
81 HILOG_ERROR(LOG_CORE, "Get input args failed.");
82 return false;
83 }
84 for (ani_int i = 0; i < arraySize; i++) {
85 ani_ref aniValue = nullptr;
86 if (env->Object_CallMethodByName_Ref(argsAni, "$_get", "I:Lstd/core/Object;", &aniValue, i) != ANI_OK) {
87 HILOG_ERROR(LOG_CORE, "get_element -> Get input args failed.");
88 return false;
89 }
90 std::string strValue;
91 if (AniUtil::ParseAniString(env, static_cast<ani_string>(aniValue), strValue) != ANI_OK) {
92 HILOG_ERROR(LOG_CORE, "get_value -> Get input args failed.");
93 return false;
94 }
95 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> strCnv;
96 args.push_back(strCnv.from_bytes(strValue));
97 }
98 return true;
99 }
100
GetTraceParam(ani_env * env,ani_array_double tagsAni,std::vector<uint64_t> & tags)101 static bool GetTraceParam(ani_env *env, ani_array_double tagsAni, std::vector<uint64_t> &tags)
102 {
103 ani_size arraySize = 0;
104 if (env->Array_GetLength(static_cast<ani_array>(tagsAni), &arraySize) != ANI_OK) {
105 HILOG_ERROR(LOG_CORE, "Get input tags size failed.");
106 return false;
107 }
108 if (arraySize > static_cast<ani_size>(MAX_TAGS_ARRAY_LENGTH)) {
109 HILOG_ERROR(LOG_CORE, "The length of tags array exceeds the limit.");
110 return false;
111 }
112 ani_double *aniValues = new ani_double[arraySize];
113 if (env->Array_GetRegion_Double(tagsAni, 0, arraySize, aniValues) != ANI_OK) {
114 HILOG_ERROR(LOG_CORE, "Get input tags value failed.");
115 delete []aniValues;
116 return false;
117 }
118 for (ani_size i = 0; i < arraySize; i++) {
119 tags.push_back(static_cast<uint64_t>(aniValues[i]));
120 }
121 delete []aniValues;
122 return true;
123 }
124
GetPss(ani_env * env)125 ani_object GetPss(ani_env *env)
126 {
127 ani_object pss = nullptr;
128 auto nativeMemInfoOption = HidebugNativeInterface::GetInstance().GetAppNativeMemInfo(false);
129 AniUtil::ToAniBigInt(env, nativeMemInfoOption ? static_cast<uint64_t>(nativeMemInfoOption->pss) : 0, pss);
130 return pss;
131 }
132
GetSharedDirty(ani_env * env)133 ani_object GetSharedDirty(ani_env *env)
134 {
135 ApiInvokeRecorder apiInvokeRecorder("getSharedDirty");
136 ani_object sharedDirty = nullptr;
137 auto nativeMemInfoOption = HidebugNativeInterface::GetInstance().GetAppNativeMemInfo(false);
138 AniUtil::ToAniBigInt(env,
139 nativeMemInfoOption ? static_cast<uint64_t>(nativeMemInfoOption->sharedDirty) : 0, sharedDirty);
140 return sharedDirty;
141 }
142
GetPrivateDirty(ani_env * env)143 ani_object GetPrivateDirty(ani_env *env)
144 {
145 ApiInvokeRecorder apiInvokeRecorder("getPrivateDirty");
146 ani_object privateDirtyValue = nullptr;
147 auto nativeMemInfoOption = HidebugNativeInterface::GetInstance().GetAppNativeMemInfo(false);
148 AniUtil::ToAniBigInt(env, nativeMemInfoOption ? nativeMemInfoOption->privateDirty : 0, privateDirtyValue);
149 return privateDirtyValue;
150 }
151
GetCpuUsage(ani_env * env)152 ani_double GetCpuUsage(ani_env *env)
153 {
154 ApiInvokeRecorder apiInvokeRecorder("getCpuUsage");
155 return static_cast<ani_double>(HidebugNativeInterface::GetInstance().GetCpuUsage());
156 }
157
GetNativeHeapSize(ani_env * env)158 ani_object GetNativeHeapSize(ani_env *env)
159 {
160 struct mallinfo mi = mallinfo();
161 ani_object nativeHeapSize = nullptr;
162 AniUtil::ToAniBigInt(env, uint64_t(mi.uordblks + mi.fordblks), nativeHeapSize);
163 return nativeHeapSize;
164 }
165
GetNativeHeapAllocatedSize(ani_env * env)166 ani_object GetNativeHeapAllocatedSize(ani_env *env)
167 {
168 ApiInvokeRecorder apiInvokeRecorder("getNativeHeapAllocatedSize");
169 struct mallinfo mi = mallinfo();
170 ani_object nativeHeapAllocatedSize = nullptr;
171 AniUtil::ToAniBigInt(env, uint64_t(mi.uordblks), nativeHeapAllocatedSize);
172 return nativeHeapAllocatedSize;
173 }
174
GetNativeHeapFreeSize(ani_env * env)175 ani_object GetNativeHeapFreeSize(ani_env *env)
176 {
177 ApiInvokeRecorder apiInvokeRecorder("getNativeHeapFreeSize");
178 struct mallinfo mi = mallinfo();
179 ani_object nativeHeapFreeSize = nullptr;
180 AniUtil::ToAniBigInt(env, uint64_t(mi.fordblks), nativeHeapFreeSize);
181 return nativeHeapFreeSize;
182 }
183
GetServiceDump(ani_env * env,ani_double serviceIdAni,ani_double fdAni,ani_object argsAni)184 static void GetServiceDump(ani_env *env,
185 ani_double serviceIdAni, ani_double fdAni, ani_object argsAni)
186 {
187 ApiInvokeRecorder apiInvokeRecorder("getServiceDump");
188 int serviceAbilityId = static_cast<int>(serviceIdAni);
189 int fd = static_cast<int>(fdAni);
190 std::vector<std::u16string> args;
191 if (!GetDumpParam(env, argsAni, args)) {
192 std::string paramErrorMessage = "The parameter check failed.";
193 AniUtil::ThrowErrorMessage(env, paramErrorMessage, ErrorCode::PARAMETER_ERROR);
194 return;
195 }
196 sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
197 if (!sam) {
198 return;
199 }
200 sptr<IRemoteObject> sa = sam->CheckSystemAbility(serviceAbilityId);
201 if (sa == nullptr) {
202 HILOG_ERROR(LOG_CORE, "no this system ability.");
203 std::string idErrorMessage = "ServiceId invalid. The system ability does not exist.";
204 AniUtil::ThrowErrorMessage(env, idErrorMessage, ErrorCode::SYSTEM_ABILITY_NOT_FOUND);
205 return;
206 }
207 int dumpResult = sa->Dump(fd, args);
208 HILOG_INFO(LOG_CORE, "Dump result: %{public}d", dumpResult);
209 }
210
GetVss(ani_env * env)211 ani_object GetVss(ani_env *env)
212 {
213 ApiInvokeRecorder apiInvokeRecorder("getVss");
214 ani_object vss = nullptr;
215 auto vssInfoOption = HidebugNativeInterface::GetInstance().GetVss();
216 AniUtil::ToAniBigInt(env, vssInfoOption ? vssInfoOption.value() : 0, vss);
217 return vss;
218 }
219
GetSystemCpuUsage(ani_env * env)220 static ani_double GetSystemCpuUsage(ani_env *env)
221 {
222 ApiInvokeRecorder apiInvokeRecorder("getSystemCpuUsage");
223 auto cpuUsageOptional = HidebugNativeInterface::GetInstance().GetSystemCpuUsage();
224 if (!cpuUsageOptional) {
225 std::string paramErrorMessage = "The status of the system CPU usage is abnormal.";
226 AniUtil::ThrowErrorMessage(env, paramErrorMessage, ErrorCode::SYSTEM_STATUS_ABNORMAL);
227 return 0;
228 }
229 return static_cast<ani_double>(cpuUsageOptional.value());
230 }
231
ConvertThreadCpuUsageToEts(ani_env * env,ani_class cls,uint32_t threadIdValue,double cpuUsageValue)232 static ani_object ConvertThreadCpuUsageToEts(ani_env *env, ani_class cls, uint32_t threadIdValue, double cpuUsageValue)
233 {
234 ani_method ctorMethod = nullptr;
235 ani_object obj = nullptr;
236 if (env->Class_FindMethod(cls, "<ctor>", ":V", &ctorMethod) != ANI_OK ||
237 env->Object_New(cls, ctorMethod, &obj) != ANI_OK) {
238 return AniUtil::CreateUndefined(env);
239 }
240 AniUtil::SetNamedPropertyNumber(env, obj, "threadId", static_cast<double>(threadIdValue));
241 AniUtil::SetNamedPropertyNumber(env, obj, "cpuUsage", cpuUsageValue);
242 return obj;
243 }
244
ConvertThreadCpuUsageMapToEts(ani_env * env,const std::map<uint32_t,double> & threadMap)245 static ani_array_ref ConvertThreadCpuUsageMapToEts(ani_env *env, const std::map<uint32_t, double> &threadMap)
246 {
247 ani_class cls = nullptr;
248 ani_size aniSize = static_cast<ani_size>(threadMap.size());
249 ani_array_ref result = nullptr;
250 if (env->FindClass("L@ohos/hidebug/hidebug/ThreadCpuUsageImpl;", &cls) != ANI_OK ||
251 env->Array_New_Ref(static_cast<ani_type>(cls), aniSize, nullptr, &result) != ANI_OK) {
252 return result;
253 }
254 ani_size idx = 0;
255 for (const auto& [threadId, cpuUsage] : threadMap) {
256 ani_object obj = ConvertThreadCpuUsageToEts(env, cls, threadId, cpuUsage);
257 env->Array_Set_Ref(result, idx, static_cast<ani_ref>(obj));
258 idx++;
259 }
260 return result;
261 }
262
GetAppThreadCpuUsage(ani_env * env)263 ani_array GetAppThreadCpuUsage(ani_env *env)
264 {
265 ApiInvokeRecorder apiInvokeRecorder("getAppThreadCpuUsage");
266 std::map<uint32_t, double> threadMap = HidebugNativeInterface::GetInstance().GetAppThreadCpuUsage();
267 return ConvertThreadCpuUsageMapToEts(env, threadMap);
268 }
269
GetAppNativeMemInfo(ani_env * env)270 ani_object GetAppNativeMemInfo(ani_env *env)
271 {
272 ApiInvokeRecorder apiInvokeRecorder("getAppNativeMemInfo");
273 auto nativeMemInfoOption = HidebugNativeInterface::GetInstance().GetAppNativeMemInfo();
274 if (!nativeMemInfoOption) {
275 nativeMemInfoOption.emplace();
276 }
277
278 ani_class cls = nullptr;
279 ani_method ctorMethod = nullptr;
280 ani_object memInfo = nullptr;
281 if (env->FindClass("L@ohos/hidebug/hidebug/NativeMemInfoImpl;", &cls) != ANI_OK ||
282 env->Class_FindMethod(cls, "<ctor>", ":V", &ctorMethod) != ANI_OK ||
283 env->Object_New(cls, ctorMethod, &memInfo) != ANI_OK) {
284 return AniUtil::CreateUndefined(env);
285 }
286 AniUtil::SetNamedPropertyBigInt(env, memInfo, "pss", nativeMemInfoOption->pss);
287 AniUtil::SetNamedPropertyBigInt(env, memInfo, "rss", nativeMemInfoOption->rss);
288 AniUtil::SetNamedPropertyBigInt(env, memInfo, "sharedDirty", nativeMemInfoOption->sharedDirty);
289 AniUtil::SetNamedPropertyBigInt(env, memInfo, "privateDirty", nativeMemInfoOption->privateDirty);
290 AniUtil::SetNamedPropertyBigInt(env, memInfo, "sharedClean", nativeMemInfoOption->sharedClean);
291 AniUtil::SetNamedPropertyBigInt(env, memInfo, "privateClean", nativeMemInfoOption->privateClean);
292 AniUtil::SetNamedPropertyBigInt(env, memInfo, "vss", nativeMemInfoOption->vss);
293 return memInfo;
294 }
295
GetSystemMemInfo(ani_env * env)296 ani_object GetSystemMemInfo(ani_env *env)
297 {
298 ApiInvokeRecorder apiInvokeRecorder("getSystemMemInfo");
299 auto sysMemOption = HidebugNativeInterface::GetInstance().GetSystemMemInfo();
300 if (!sysMemOption) {
301 sysMemOption.emplace();
302 }
303
304 ani_class cls = nullptr;
305 ani_method ctorMethod = nullptr;
306 ani_object sysMemInfo = nullptr;
307 if (env->FindClass("L@ohos/hidebug/hidebug/SystemMemInfoImpl;", &cls) != ANI_OK ||
308 env->Class_FindMethod(cls, "<ctor>", ":V", &ctorMethod) != ANI_OK ||
309 env->Object_New(cls, ctorMethod, &sysMemInfo) != ANI_OK) {
310 return AniUtil::CreateUndefined(env);
311 }
312
313 AniUtil::SetNamedPropertyBigInt(env, sysMemInfo, "totalMem", static_cast<uint64_t>(sysMemOption->totalMem));
314 AniUtil::SetNamedPropertyBigInt(env, sysMemInfo, "freeMem", static_cast<uint64_t>(sysMemOption->freeMem));
315 AniUtil::SetNamedPropertyBigInt(env, sysMemInfo, "availableMem", static_cast<uint64_t>(sysMemOption->availableMem));
316 return sysMemInfo;
317 }
318
StartAppTraceCapture(ani_env * env,ani_array_double tagsAni,ani_enum_item flagAni,ani_double limitSizeAni)319 ani_string StartAppTraceCapture(ani_env *env,
320 ani_array_double tagsAni, ani_enum_item flagAni, ani_double limitSizeAni)
321 {
322 ApiInvokeRecorder apiInvokeRecorder("startAppTraceCapture");
323 ani_string result = nullptr;
324 uint32_t traceFlag = 0;
325 uint32_t limitSize = static_cast<uint32_t>(limitSizeAni);
326 std::vector<uint64_t> tags;
327 if (AniUtil::ParseAniEnum(env, flagAni, traceFlag) != ANI_OK || !GetTraceParam(env, tagsAni, tags)) {
328 std::string paramErrorMessage = "Invalid argument";
329 apiInvokeRecorder.SetErrorCode(ErrorCode::PARAMETER_ERROR);
330 AniUtil::ThrowErrorMessage(env, paramErrorMessage, ErrorCode::PARAMETER_ERROR);
331 return nullptr;
332 }
333 uint64_t tag = std::accumulate(tags.begin(), tags.end(), 0ull, [](uint64_t a, uint64_t b) { return a | b; });
334 std::string file;
335 auto ret = HidebugNativeInterface::GetInstance().StartAppTraceCapture(tag, traceFlag, limitSize, file);
336 if (ret == TRACE_SUCCESS) {
337 env->String_NewUTF8(file.c_str(), file.size(), &result);
338 return result;
339 }
340 if (ret == TRACE_INVALID_ARGUMENT) {
341 std::string errorMessage = "Invalid argument";
342 apiInvokeRecorder.SetErrorCode(ErrorCode::PARAMETER_ERROR);
343 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::PARAMETER_ERROR);
344 }
345 if (ret == TRACE_CAPTURED_ALREADY) {
346 std::string errorMessage = "Capture trace already enabled.";
347 apiInvokeRecorder.SetErrorCode(ErrorCode::HAVA_ALREADY_TRACE);
348 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::HAVA_ALREADY_TRACE);
349 }
350 if (ret == TRACE_NO_PERMISSION) {
351 std::string errorMessage = "No write permission on the file.";
352 apiInvokeRecorder.SetErrorCode(ErrorCode::WITHOUT_WRITE_PERMISSON);
353 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::WITHOUT_WRITE_PERMISSON);
354 }
355 std::string errorMessage = "Abnormal trace status.";
356 apiInvokeRecorder.SetErrorCode(ErrorCode::SYSTEM_STATUS_ABNORMAL);
357 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::SYSTEM_STATUS_ABNORMAL);
358 return nullptr;
359 }
360
StopAppTraceCapture(ani_env * env)361 void StopAppTraceCapture(ani_env *env)
362 {
363 ApiInvokeRecorder apiInvokeRecorder("stopAppTraceCapture");
364 auto ret = HidebugNativeInterface::GetInstance().StopAppTraceCapture();
365 if (ret == TRACE_ABNORMAL) {
366 std::string errorMessage = "The status of the trace is abnormal";
367 apiInvokeRecorder.SetErrorCode(ErrorCode::SYSTEM_STATUS_ABNORMAL);
368 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::SYSTEM_STATUS_ABNORMAL);
369 return;
370 }
371 if (ret == NO_TRACE_RUNNING) {
372 std::string errorMessage = "No capture trace running";
373 apiInvokeRecorder.SetErrorCode(ErrorCode::NO_CAPTURE_TRACE_RUNNING);
374 AniUtil::ThrowErrorMessage(env, errorMessage, ErrorCode::NO_CAPTURE_TRACE_RUNNING);
375 return;
376 }
377 }
378
GetGraphicsMemorySync(ani_env * env)379 ani_double GetGraphicsMemorySync(ani_env *env)
380 {
381 ApiInvokeRecorder apiInvokeRecorder("getGraphicsMemorySync");
382 std::optional<int32_t> result = HidebugNativeInterface::GetInstance().GetGraphicsMemory();
383 if (result) {
384 return static_cast<ani_double>(result.value());
385 }
386 constexpr const char* errMsg = "Failed to get the application memory due to a remote exception";
387 AniUtil::ThrowErrorMessage(env, errMsg, ErrorCode::SYSTEM_STATUS_ABNORMAL);
388 apiInvokeRecorder.SetErrorCode(ErrorCode::SYSTEM_STATUS_ABNORMAL);
389 return 0;
390 }
391
ANI_Constructor(ani_vm * vm,uint32_t * result)392 ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
393 {
394 ani_env *env = nullptr;
395 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
396 return ANI_ERROR;
397 }
398 ani_namespace nameSpace = nullptr;
399 if (ANI_OK != env->FindNamespace("L@ohos/hidebug/hidebug;", &nameSpace)) {
400 return ANI_ERROR;
401 }
402 std::array methods = {
403 ani_native_function {"getPss", ":Lescompat/BigInt;", reinterpret_cast<void *>(GetPss)},
404 ani_native_function {"getSharedDirty", ":Lescompat/BigInt;", reinterpret_cast<void *>(GetSharedDirty)},
405 ani_native_function {"getPrivateDirty", ":Lescompat/BigInt;", reinterpret_cast<void *>(GetPrivateDirty)},
406 ani_native_function {"getCpuUsage", ":D", reinterpret_cast<void *>(GetCpuUsage)},
407 ani_native_function {"getServiceDump", "DDLescompat/Array;:V", reinterpret_cast<void *>(GetServiceDump)},
408 ani_native_function {"getNativeHeapSize", ":Lescompat/BigInt;", reinterpret_cast<void *>(GetNativeHeapSize)},
409 ani_native_function {"getNativeHeapAllocatedSize", ":Lescompat/BigInt;",
410 reinterpret_cast<void *>(GetNativeHeapAllocatedSize)},
411 ani_native_function {"getNativeHeapFreeSize", ":Lescompat/BigInt;",
412 reinterpret_cast<void *>(GetNativeHeapFreeSize)},
413 ani_native_function {"getVss", ":Lescompat/BigInt;", reinterpret_cast<void *>(GetVss)},
414 ani_native_function {"getAppThreadCpuUsage", ":[L@ohos/hidebug/hidebug/ThreadCpuUsage;",
415 reinterpret_cast<void *>(GetAppThreadCpuUsage)},
416 ani_native_function {"getSystemCpuUsage", ":D", reinterpret_cast<void *>(GetSystemCpuUsage)},
417 ani_native_function {"getAppNativeMemInfo", ":L@ohos/hidebug/hidebug/NativeMemInfo;",
418 reinterpret_cast<void *>(GetAppNativeMemInfo)},
419 ani_native_function {"getSystemMemInfo", ":L@ohos/hidebug/hidebug/SystemMemInfo;",
420 reinterpret_cast<void *>(GetSystemMemInfo)},
421 ani_native_function {"startAppTraceCapture", "[DL@ohos/hidebug/hidebug/TraceFlag;D:Lstd/core/String;",
422 reinterpret_cast<void *>(StartAppTraceCapture)},
423 ani_native_function {"stopAppTraceCapture", ":V", reinterpret_cast<void *>(StopAppTraceCapture)},
424 ani_native_function {"getGraphicsMemorySync", ":D", reinterpret_cast<void *>(GetGraphicsMemorySync)},
425 };
426 if (ANI_OK != env->Namespace_BindNativeFunctions(nameSpace, methods.data(), methods.size())) {
427 return ANI_ERROR;
428 }
429 *result = ANI_VERSION_1;
430 return ANI_OK;
431 }
432