1.概述
1.1 文档目的
本文档旨在提供给集成灵云SDK的开发者,作为授权部分的开发指南,降低开发者对灵云SDK的集成难度。
1.2 授权介绍
互联网的逐渐普及和日新月异的发展极大地改变了我们工作和生活的方式,软件的保护和授权也应该与互联网技术相结合,为保护捷通知识产权、防止灵云SDK被恶意利用、同时也能更好的服务客户、方便管理等,采用授权机制进行管理,同时,为满足不同场景的不同需求,灵云SDK提供云端授权、本地授权、线程授权等多种授权方式。
1.3 名词解释
- 永久 : 由于灵云 SDK 实现限制(time_t字段值限制),关于永久的说明,实际时间为2037-12-31。永久授权和永久使用都以此时间作为限定,超过此值后的行为不进行考虑。
- udid : 用户设备标识,必须保证唯一。
- 授权服务期限 : 指授权文件约定的能力服务时间期限,过期后无法继续使用灵云能力。
- 授权文件起始日期、截止日期 : 指授权文件允许被使用的区间范围,第一次使用授权文件时必须在区间范围内,否则按授权文件无效处理。
2. SDK 授权机制
2.1 授权方式种类
灵云 SDK 授权方式共有五种
- 云端授权
- 本地预授权
- 本地永久授权
- 本地批量授权
- 线程授权
不同授权方式可满足不同用户需求,如离线工作、多终端、多线程等。
2.2 授权方式使用顺序
云端授权、本地预授权、本地永久授权均为能力授权,灵云 SDK 在检查授权时顺序为云端授权->本地永久授权->本地预授权,一种授权读取成功后,不会再读取后面的授权,例如本地永久授权读取成功时,将不会再读取本地预授权。本地无授权文件存在时,尝试云端抓取一次授权,如果抓取失败,则授权失败。
3. 授权方式说明
3.1 各种授权方式说明
授权方式 | 授权文件 | 获取方式 | 生效时间 | 截止时间 | 绑定信息 | 详细说明 |
---|---|---|---|---|---|---|
云端授权 | HCI_AUTH | 新装应用在首次运行时 会自动从云端下载 |
下载到本地 后立即生效 |
后台控制,默认 6 个月 | udidType udid appKey developerKey |
用户删除已下载授权文件或授权文件过期时,会自动从云端更新,须确保设备处于联网状态,后续运行时,若本地有此授权文件并且授权未过期,可以离线使用。此授权会绑定设备udid,详细信息请参考3.2节udid说明。 |
本地预授权 | HCI_BASIC_AUTH | 联系捷通华声获取 | 制作后生效 | 分为绝对过期时间 (即某一日期后失效) 和相对过期时间 (即X天后失效)两种。 |
appKey deveoperKey |
适用于无法联网的设备,离线使用,此授权方式不绑定设备信息,同一账号授权可多终端使用。 |
本地永久授权 | HCI_AUTH_FOREVER | 联系捷通华声获取 | 制作后生效 | 2037-12-31 24:00:00 | appKey deveoperKey |
适用于无法联网的设备,离线使用,此授权方式不绑定设备信息,同一账号授权可多终端使用。永久授权限制激活期限,比如合同约定是一年,则一年内所有出货终端永久可用。 |
本地批量授权 | HCI_AUTH HCI_USER_INFO |
联系捷通华声获取 | 制作后生效 | 后台控制,默认6个月 | appKey deveoperKey |
此授权可用于多台终端设备,申请此授权时须提供要使用此授权的所有设备的udid。 |
线程授权 | HCI_LICENSE 或 HCI_LICENSE_BATCH |
联系捷通华声获取 | 制作后生效 | 永久有效 | udidType udid appKey developerKey |
灵云SDK默认windows、linux最大线程数为2(hwr能力为5),android、ios最大线程数为256,如有多线程使用需求,可申请此种授权,线程授权与设备信息绑定,HCI_LISCENSE只可在一台设备使用,HCI_LICENSE_BATCH可在多台设备上使用,申请时须提供要使用此授权的所有设备的udid。 线程授权不可单独使用,须和上面四种之一组合使用。 |
3.2 udid说明
udid 在常用平台可通过配置项来设置,在调用 hci_init
接口时,增加配置项:udidType=XXX,不同平台可取配置如下:
平台 | 可选配置 | 对应udidType 查日志时使用 |
默认配置 |
---|---|---|---|
windows | macaddress uuid |
0 1 |
macaddress |
android | androidid imei wifiid randomnumber serial |
10 11 12 14 15 |
androidid |
ios | macaddress udid uuid openudid |
20 21 22 23 |
openudid |
linux | macaddress uuid |
30 31 |
macaddress |
wince | uuid 3G serial no |
40 1001 |
uuid |
除以上通用配置外,灵云 SDK 额外提供两种更为灵活的绑定方式:
绑定信息 | 获取方式 | udidType | 详细说明 |
---|---|---|---|
可唯一标识设备的序列号 | 联系捷通华声定制SDK | 10001 | 客户提供获取设备唯一序列号的方法及示例代码,由捷通华声评估可行后提供定制版SDK |
加密锁序列号 | 联系捷通华声定制SDK及加密锁 | 10001 | 捷通华声可定制加密锁及绑定此加密锁的SDK,此方法不与设备绑定,可在任意插入此加密锁的设备上使用 |
3.3 云端授权的自动更新与手动更新
对于新装应用、用户删除已下载授权、已有云端授权但授权时间已过期,SDK 会自动从云端更新授权,此时需确保设备联网,其他情况下想要更新授权时,可通过调用 hci_check_auth
接口手动更新授权,或者删除原有授权文件重新下载授权。
4.授权相关操作
4.1 初始化接口自动获取云端授权
调用 hci_init
初始化接口时,若读取本地授权失败,则会自动尝试从云端获取授权,应用层指定授权文件保存路径,须保证此路径可读写。同时可在config参数中指定授权文件绑定的设备udid类型。
Android代码
// 授权文件路径 默认在文件缓存 String authDirPath = this.getFilesDir().getAbsolutePath(); // 前置条件:无 InitParam initparam = new InitParam(); // 授权文件所在路径,此项必填 initparam.addParam(InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authDirPath); // 灵云云服务的接口地址,此项必填 initparam.addParam(InitParam.AuthParam.PARAM_KEY_CLOUD_URL, "test.api.hcicloud.com:8888"); // 开发者Key,此项必填,由捷通华声提供 initparam.addParam(InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, "7dafh5eb2dcdb5a1ab78af0000000000"); // 应用Key,此项必填,由捷通华声提供 initparam.addParam(InitParam.AuthParam.PARAM_KEY_APP_KEY, "60665666"); // 配置日志参数 String sdcardState = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(sdcardState)) { String sdPath = Environment.getExternalStorageDirectory() .getAbsolutePath(); String packageName = this.getPackageName(); String logPath = sdPath + File.separator + "sinovoice" + File.separator + packageName + File.separator + "log" + File.separator; // 日志文件地址 File fileDir = new File(logPath); boolean exit= fileDir.exists(); if (!exit) { fileDir.mkdirs(); } // 日志的路径,可选,如果不传或者为空则不生成日志 initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_LEVEL, "5"); initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logPath); } // 初始化 errocode可从开发手册查询到 int errCode = HciCloudSys.hciInit(initparam.getStringConfig(), this);
C++代码
// SYS初始化 HCI_ERR_CODE err_code = HCI_ERR_NONE; //配置串是由"字段=值"的形式给出的一个字符串,多个字段之间以','隔开。字段名不分大小写。 string init_config = ""; init_config += "appKey=" + account_info->app_key(); //灵云应用序号 init_config += ",developerKey=" + account_info->developer_key(); //灵云开发者密钥 init_config += ",cloudUrl=" + account_info->cloud_url(); //灵云云服务的接口地址 init_config += ",authpath=" + account_info->auth_path(); //授权文件所在路径,保证可写 init_config += ",logfilepath=" + account_info->logfile_path(); //日志的路径 init_config += ",loglevel=5"; //日志的等级 init_config += ",logfilesize=512"; //日志文件的大小 init_config += ",udidType=macaddress"; //绑定udid类型 //其他配置使用默认值,不再添加,如果想设置可以参考开发手册 err_code = hci_init( init_config.c_str() ); if( err_code != HCI_ERR_NONE ) { printf( "hci_init return (%d:%s)\n", err_code, hci_get_error_info(err_code) ); return -1; } printf( "hci_init success\n" );
iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; // appkey, developerKey, cloudUrl, authPath为必填项。 // 其他可配置信息请查询灵云SDK开发手册hci_init函数说明。 NSString *appkey = @""; // 开发者社区申请所得 NSString *developerKey = @""; // 开发者社区申请所得 NSString *cloudUrl = @""; // 开发者社区申请所得 NSString *authPath = @""; // 授权文件所在路径,一般设置为沙盒Document路径 NSString *config = [NSString stringWithFormat: @"appkey=%@,developerKey=%@,cloudUrl=%@,authPath=%@", appkey, developerKey, cloudUrl, authPath]; errCode = hci_init(config.UTF8String); if (errCode != HCI_ERR_NONE && errCode != HCI_ERR_SYS_ALREADY_INIT) { NSLog(@"hci_init failed, error: %@", errCode); }else{ NSLog(@"hci_init success"); }
4.2 获取授权过期时间及更新授权
云端授权方式新增能力或者因其他原因损坏时,可以通过调用 hci_check_auth
接口主动更新授权文件。
开发者可以使用 hci_get_auth_expire_time()
获取当前授权过期时间,当此函数返回错误或者授权过期时间已经快到了或者已经过期的时候, 再调用 hci_check_auth()
函数到云端下载授权文件。例如,下面的示例会在授权过期后检测并下载新的授权。
- Android代码
// 获取授权
private int checkAuthAndUpdateAuth() {
// 获取系统授权到期时间
int initResult;
AuthExpireTime objExpireTime = new AuthExpireTime();
initResult = HciCloudSys.hciGetAuthExpireTime(objExpireTime);
if (initResult == HciErrorCode.HCI_ERR_NONE) {
// 显示授权日期,如用户不需要关注该值,此处代码可忽略
Date date = new Date(objExpireTime.getExpireTime() * 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.CHINA);
Log.i(TAG, "expire time: " + sdf.format(date));
if (objExpireTime.getExpireTime() * 1000 > System.currentTimeMillis()) {
// 已经成功获取了授权,并且距离授权到期有充足的时间(>7天)
Log.i(TAG, "checkAuth success");
return initResult;
}
}
// 获取过期时间失败或者已经过期
initResult = HciCloudSys.hciCheckAuth();
if (initResult == HciErrorCode.HCI_ERR_NONE) {
Log.i(TAG, "checkAuth success");
return initResult;
} else {
Log.e(TAG, "checkAuth failed: " + initResult);
return initResult;
}
}
C++代码
bool checkAuthAndUpdateAuth() { int64 nExpireTime; int64 nCurTime = (int64)time( NULL ); //获取授权过期时间 HCI_ERR_CODE errCode = hci_get_auth_expire_time( &nExpireTime ); if( errCode == HCI_ERR_NONE ) { if( nExpireTime < nCurTime )// 已经过期 { errCode = hci_check_auth(); //更新授权文件 if( errCode == HCI_ERR_NONE ) return true; else return false; } else { return true; } } else if( errCode == HCI_ERR_SYS_AUTHFILE_INVALID )//授权文件无效 { errCode = hci_check_auth(); //更新授权文件 if( errCode == HCI_ERR_NONE ) return true; else return false; } else { return false; } }
iOS代码
bool checkAuthAndUpdateAuth() { //获取过期时间 int64 expireTime; int64 currentTime = (int64)time(NULL); HCI_ERR_CODE errCode = hci_get_auth_expire_time(&expireTime); if (errCode == HCI_ERR_NONE) { if (expireTime < currentTime) { errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ //没有过期 NSLog(@"auth can use continue"); return YES; } } //读取授权文件出现错误 else if(errCode == HCI_ERR_SYS_AUTHFILE_INVALID) { errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; } else { NSLog(@"check auth failed, error: %@", errCode); return NO; } } else { NSLog(@"check auth failed, error: %@", errCode); return NO; } }
4.3 获取已授权能力列表
可通过调用 hci_get_capability_list()
接口获取已授权的能力列表,接口参数说明:pszHciType 所查询的灵云能力(例如 HWR, TTS, OCR, ASR),多种能力用';'隔开, 如果为 NULL 或者空串,表示全部查询;psCapabilityList 结果缓冲区指针,存放返回的能力列表。使用完毕后,应该使用 hci_free_capability_list()
释放
Android代码
// 创建能力结果对象 CapabilityResult sCapabilityList = new CapabilityResult(); // 获取所有能力的描述信息 int nRet = HciCloudSys.hciGetCapabilityList(null, sCapabilityList); // 输出能力列表 for ( int nItemIndex = 0; nItemIndex < sCapabilityList.getCapabilityList().size(); nItemIndex ++ ) { // 资源文件名 strFileName= sCapabilityList.getCapabilityList.get(nItemIndex).getFileName(); Log.d(tag, "capKey: " + strFileName); }
C++代码
// 获取一下授权中全部可用能力列表 CAPABILITY_LIST capbility_list; HCI_ERR_CODE errCode = hci_get_capability_list( NULL, &capbility_list); if (errCode != HCI_ERR_NONE) { printf("hci_get_capability_list failed return %d \n",errCode); return false; } // 判断传入的capbility_array 中的能力是否在可用能力列表中 bool is_capkey_enable = false; for (size_t capbility_index = 0; capbility_index < capbility_list.uiItemCount; capbility_index++) { if (capkey == string(capbility_list.pItemList[capbility_index].pszCapKey)) { is_capkey_enable = true; break; } } // 释放可用能力列表 hci_free_capability_list(&capbility_list);
iOS代码
// 获取一下授权中全部可用能力列表 CAPABILITY_LIST capbility_list; HCI_ERR_CODE errCode = hci_get_capability_list( NULL, &capbility_list); if (errCode != HCI_ERR_NONE) { NSLog("hci_get_capability_list failed return %d ",errCode); return NO; } // 判断传入的capbility_array 中的能力是否在可用能力列表中 BOOL is_capkey_enable = NO; for (size_t capbility_index = 0; capbility_index < capbility_list.uiItemCount; capbility_index++) { if (capkey == string(capbility_list.pItemList[capbility_index].pszCapKey)) { is_capkey_enable = YES; break; } } // 释放可用能力列表 hci_free_capability_list(&capbility_list);
4. FAQ
Q: 授权失败,返回 8 号错误:HCI_ERR_SERVICE_CONNECT_FAILED,如何解决? 通用有两种情况导致此问题。
- 本地预授权、永久授权等因为某种原因无法继续使用,如确定本地原有 HCI_BASIC_AUTH 和 HCI_AUTH_FOREVER 文件,请联系技术支持解决。
- 本地无授权文件,尝试访问云端检查授权,由于无法连接灵云服务器,导致此问题。请在浏览器输入 cloudUrl:8888,如 api.hcicloud.com:8888,如果返回如下结果,说明服务器链接正常,授权端口可以正常访问。
然后再输入cloudUrl:8880,做同样的检查,如果返回如下结果,说明服务器链接正常,识别(合成、翻译...)端口可以正常访问。
Q: 授权失败,返回112号错误,HCI_ERR_SYS_CHECKAUTH_RESPONSE_FAILED,如何解决? 通用有三种情况导致此问题:
- 获取授权时,appkey,devkey 等信息有错误,需要检查此处是否填写了错误的appkey,devkey。(常见的是将appkey,devkey填反,需注意appkey字串较短,为8位的字符串)
- 检查授权时,需求的 capkey,云端账号无对应 capkey。请检查开发者社区,appkey 对应应用是否勾选了使用的 capkey。如果是测试域,可以选中对应应用,点击修改能力,添加、更新能力。如果是商用域,需要联系捷通华声技术支持处理,后台添加对应能力。云端勾选新的 capkey 后的刷新时间是 10 分钟,请耐心等待。
- 由于终端超限导致的服务端不再返回授权。此种情况发生概率最大,且在 Android 平台较为常见。(Android 平台默认绑定的 androidid,刷机后会作为新终端处理,终端数消耗快),如果您希望使用其他不会改变的标识作为设备的唯一id,请参考udid说明
Q: 集成SDK后,总是返回12号错误:CAPKEY NOT FOUND。这个是什么原因? 出现此问题,首先需要检查能力初始化时,授权文件里是否有对应的 capkey 信息。请先调用
hci_get_capability_list
接口,检查授权文件中是否有需要使用的 capkey,如果没有对应的能力授权,请参考如下方法处理。- 如果是联网,云端授权,请检查开发者社区,appkey 对应应用是否勾选了使用的 capkey。如果是测试域,可以选中对应应用,点击修改能力,添加、更新能力。如果是商用域,需要联系捷通华声技术支持处理,后台添加对应能力。云端勾选新的 capkey 后的刷新时间是 10 分钟,请耐心等待。
- 如果是提供的本地授权文件,如本地预授权、本地永久授权,此时应联系捷通华声技术支持。协助提供授权相关的绑定信息(如 appkey,devkey,cloudurl,udidtype,udid),或提供 hci.log 日志文件。由技术支持协助解密、查看授权文件是否有对应能力,并协助提供新的本地授权文件。 授权更新后,需要将本地原有的授权文件、HCI_USERINFO 删除,然后再尝试重新运行 SDK。
Q: 增加新能力时,应该如何更新授权? 本地预授权、本地永久授权、本地批量授权,请联系捷通华声更换授权文件,云端授权方式可手动删除 HCI_AUTH 文件或者主动调用 hci_check_auth 更新授权文件。
Q: 我们机器上原来有两块网卡,因为后面我们准备拆除无线网卡模块,所以我禁用了无线网卡,只留下了有线网卡。现在发现怎么都无法使用,应该如何解决? 我们的云端授权,默认是绑定 udid 的。在 Windows 系统上,默认绑定的是 MAC 地址。造成此问题是由于原先您是用无线网卡去我们云端取的授权,udid 是无线网卡的 macaddress。现在您将无线网卡禁用,导致我们缓存在本地的 HCI_AUTH,找不到对应的udid,所以无法继续使用。目前有两种解决方案:
- 您删除本地的 HCI_AUTH 和 HCI_USERINFO,重新用有线网卡联网激活。(推荐)
- 将有线网卡的 mac 地址改为原来无线网卡的 mac 地址
Q: 我在 Windows 平台,运行你们的身份证识别 SDK,总是提示 17 号错误:SESSION TOO MUCH。此问题应该如何解决? Windows,Linux 平台 SDK 默认提供 2 线授权。您期望同时开启的 session 数如果超过2线,就会返回此错误。请联系技术支持提供线程授权。此外,如果在 sessionstart 后,没有进行 sessionstop 操作,则已经使用完毕的线程不会被回收,也可能会导致此错误。
Q: 我用你们提供的终端信息采集工具,获取了设备udid。然后使用您提供的授权,无法使用。这是为什么? 经检查是存在多网卡的情况,我们的终端信息采集工具默认只采集了活动单网卡的 madaddress,请将您机器的所有网卡的 macaddress 提供给技术支持,我们根据您的这一情况,重新制作授权文件。
Q: 我们的应用场景无法联网,你们是否可以提供不联网的SDK授权方式? 可以,我们支持提供本地预授权(HCI_BASIC_AUTH)、本地永久授权(HCI_AUTH_FOREVER)、本地批量授权(HCI_AUTH+HCI_USER_INFO)的方式,将本地授权文件,组入代码authpath定义的授权路径下,即可实现不联网的使用。如需提供本地授权,请联系我们商务。
Q: 我们的授权文件已经使用很长一段时间了,我想知道授权文件里都有哪些能力的授权,是否方便告诉我? 您可以先调用
hci_get_capability_list
接口,检查是否有对应的能力授权。如不清楚如何调用接口,请协助提供授权文件,和授权相关的绑定信息(如appkey,devkey,cloudurl,udidtype,udid),将相关信息反馈给我们技术支持,由我们技术支持为您解密授权文件,查看可用能力。Q: 我云端有账号,后来工程又组入了你们提供的本地授权文件,我想请问如果我的工程里同时存在这两种授权,那么我的授权信息以哪个为准? 根据 SDK 默认的授权加载顺序,云端授权(HCI_AUTH)>本地授权(HCI_BASIC_AUTH,HCI_AUTH_FOREVER),所以如果本地同时存在云端、本地授权,会先读云端授权,云端授权不存在再尝试读取本地授权。无授权文件存在,或授权均不可用的情况下,SDK会尝试调用一次云端获取授权的checkauth()接口,如果无法获得授权,则授权过程失败。例如: 本地同时存在HCI_AUTH(云端授权),本地授权(HCI_BASIC_AUTH)的场景。云端授权文件可正常读取,但其中没有tts能力授权,本地授权文件中有tts能力授权。此时用户若使用tts能力时,云端授权存在,但不可用,此场景下,不会再读取本地授权文件。SDK会自动尝试执行checkauth()方法,从云端获取授权。 建议本地只存在一种授权文件,这样结构比较清晰,也方便排查问题。