1 关键字

napi;XTS;账号子系统;枚举值无效

2 问题描述

测试用例 ActsOsAccountLocalIdSerial_0600 无法跑通过。

3 问题原因

3.1 正常机制

测试用例正常通过后打印日志:

JSApp: app Log: [pass]ActsOsAccountLocalIdSerial_0600 ;

3.2 异常机制

测试用例报错后打印日志:

JSApp: app Log: [error]ActsOsAccountLocalIdSerial_0600 ;

4 解决方案

由于hap应用在使用napi层导出的枚举时,未按正确的值来赋值,导致xts运行时,在napi层不能正常解析参数,返回错误,导致xts执行不过。 正确使用napi导出的枚举值即可正常获取参数,使xts正常通过。

5 定位过程

  1. 用例代码

    image-20230106193300960

  2. 执行xts,查看日志。

    image-20230106194038095

  3. 通过 ActsOsAccountLocalIdSerial_0600 用例名称查找日志,可以看到用例在日志的53719行开始执行,进程ID为 5988

  4. 依次往后查 5988 的日志,可以在日志53749行看到打印出napi层错误信息

    image-20230106194455714

  5. 在日志53757行打印出hap的错误信息

    image-20230106194519005

  6. 在tvos的源代码的 os_account 模块下分别搜索两个错误信息的字符串 Get type failedThe type of arg 2 must be number,可以定位到函数 ParseParaCreateOA 中24,25行

    bool ParseParaCreateOA(napi_env env, napi_callback_info cbInfo, CreateOAAsyncContext *asyncContext)
    {
        size_t argc = ARGS_SIZE_THREE;
        napi_value argv[ARGS_SIZE_THREE] = {0};
        napi_get_cb_info(env, cbInfo, &argc, argv, nullptr, nullptr);
    ​
        if (argc == ARGS_SIZE_THREE) {
            if (!GetCallbackProperty(env, argv[argc - 1], asyncContext->callbackRef, 1)) {
                ACCOUNT_LOGE("Get callbackRef failed");
                std::string errMsg = "The type of arg " + std::to_string(argc) + " must be function";
                AccountNapiThrow(env, ERR_JS_PARAMETER_ERROR, errMsg, asyncContext->throwErr);
                return false;
            }
        }
    ​
        if (!GetStringProperty(env, argv[PARAMZERO], asyncContext->name)) {
            ACCOUNT_LOGE("Get name failed");
            std::string errMsg = "The type of arg 1 must be string";
            AccountNapiThrow(env, ERR_JS_PARAMETER_ERROR, errMsg, asyncContext->throwErr);
            return false;
        }
        int32_t type = 0;
        if (!GetIntProperty(env, argv[PARAMONE], type)) {
            ACCOUNT_LOGE("Get type failed");
            std::string errMsg = "The type of arg 2 must be number";
            AccountNapiThrow(env, ERR_JS_PARAMETER_ERROR, errMsg, asyncContext->throwErr);
            return false;
        }
        asyncContext->type = static_cast<OsAccountType>(type);
        return true;
    }

    在js对应的napi层函数 CreateOsAccount 中12行确实有调用 ParseParaCreateOA

    napi_value CreateOsAccount(napi_env env, napi_callback_info cbInfo)
    {
        CreateOAAsyncContext *createOACB = new (std::nothrow) CreateOAAsyncContext();
        if (createOACB == nullptr) {
            ACCOUNT_LOGE("insufficient memory for createOACB!");
            return WrapVoidToJS(env);
        }
        createOACB->env = env;
        createOACB->callbackRef = nullptr;
        createOACB->throwErr = true;
    ​
        if (!ParseParaCreateOA(env, cbInfo, createOACB)) {
            delete createOACB;
            return nullptr;
        }
    ​
        napi_value result = nullptr;
        if (createOACB->callbackRef == nullptr) {
            napi_create_promise(env, &createOACB->deferred, &result);
        } else {
            napi_get_undefined(env, &result);
        }
    ​
        napi_value resource = nullptr;
        napi_create_string_utf8(env, "CreateOsAccount", NAPI_AUTO_LENGTH, &resource);
    ​
        napi_create_async_work(env, nullptr, resource, CreateOAExecuteCB, CreateOACallbackCompletedCB,
            reinterpret_cast<void *>(createOACB), &createOACB->work);
    ​
        napi_queue_async_work(env, createOACB->work);
        return result;
    }
  7. ParseParaCreateOA 的7 - 14行,获取js调用时第3个参数,判断是callback还是promise调用。

  8. ParseParaCreateOA 的16 - 21行,获取js调用时第1个参数,并判断是否为js的string类型。

  9. ParseParaCreateOA 的22 - 28行,获取js调用时第2个参数,并判断是否为js的number类型。通过错误日志定位,可以发现js调用时第2个参数传入有误导致错误。

  10. 回到hap包的js代码中,可以发现代码第4行调用时,第2个参数传入的值为 osaccount.OsAccountType.Guest

    var osAccountManager = osaccount.getAccountManager();
    console.debug("====>get AccountManager finish====");
    var localId;
    var OsAccountInfo = await osAccountManager.createOsAccount("accountIdSerialB", osaccount.OsAccountType.Guest);
    console.debug("====>create os account OsAccountInfo: " + JSON.stringify(OsAccountInfo));
  11. 传入的值 osaccount.OsAccountType.Guest 为napi导出的枚举值,转到napi层导出枚举值的地方。

    napi_value osAccountType = nullptr;
    napi_create_object(env, &osAccountType);
    ​
    SetEnumProperty(env, osAccountType, OS_ACCOUNT_TYPE_ADMIN, "ADMIN");
    SetEnumProperty(env, osAccountType, OS_ACCOUNT_TYPE_NORMAL, "NORMAL");
    SetEnumProperty(env, osAccountType, OS_ACCOUNT_TYPE_GUEST, "GUEST");
    ​
    napi_value constraintSourceType = nullptr;
    napi_create_object(env, &constraintSourceType);
    SetEnumProperty(env, constraintSourceType, CONSTRAINT_NOT_EXIST, "CONSTRAINT_NOT_EXIST");
    SetEnumProperty(env, constraintSourceType, CONSTRAINT_TYPE_BASE, "CONSTRAINT_TYPE_BASE");
    SetEnumProperty(env, constraintSourceType, CONSTRAINT_TYPE_DEVICE_OWNER, "CONSTRAINT_TYPE_DEVICE_OWNER");
    SetEnumProperty(env, constraintSourceType, CONSTRAINT_TYPE_PROFILE_OWNER, "CONSTRAINT_TYPE_PROFILE_OWNER");
    ​
    napi_property_descriptor exportEnum[] = {
        DECLARE_NAPI_PROPERTY("OsAccountType", osAccountType),
        DECLARE_NAPI_PROPERTY("ConstraintSourceType", constraintSourceType),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(exportEnum) / sizeof(*exportEnum), exportEnum));
  12. 分析代码发现,代码19行导出16 - 17行两个枚举 OsAccountTypeConstraintSourceType ,其中 OsAccountType 包括4 - 6行的3个值:ADMINNORMALGUEST

  13. 此时可以确认hap包js调用时传入的 OsAccountType.Guest 与napi实际导出的值 OsAccountType.GUEST 不匹配而导致报错。

  14. 将hap包js调用时传入的值改为正确的值,即执行成功。

6 知识分享

在hap的开发中,在使用tvos-o提供的js sdk api时,必须与各模块napi导出的方法,枚举,属性以及声明文件*.d.ts等等保存一致,避免出现类似的问题。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐