合作咨询
我们的专家服务团队将竭诚为您提供专业的合作咨询服务
DV Engine整体上开发步骤包含三部分,其中UX要求包含基于安全考虑的隐私声明,连接鉴权和可信设备管理,以及对于设备名称的展示,北向接口是由DV Engine实现,三方厂商来进行调用,进行初始化,南向接口是由DV Engine进行定义,三方厂商来实现,最终由DV Engine SDK在解析接收到手机侧发送过来的命令后进行调用,完成整个虚拟化过程。

Camera/Mic业务:
1. 当发现连接完设备后,应用程序可以调用手机侧DV Engine接口使能虚拟Camera/Mic服务。
2. 应用程序通过标准android接口,打开虚拟Camera/Mic,建立数据通道。
3. 设备侧DV Engine SDK接收到打开虚拟Camera的消息,通知设备厂商打开Camera,获取视频流。
4. 设备侧厂商推送视频流,DV Engine SDK将视频流发送到手机侧。
5. 应用程序通过标准android接口,获取到虚拟Camera/Mic数据流进行播放。
虚拟Speaker业务和上述业务流程类似,只不过是手机侧的数据流发送到设备侧在设备侧进行播放,不再进行详述。
手机连接设备后必须给用户明确的提示,设备有显示屏的需要展示已经连接的手机名称,包括但不限于声音、指示灯或者文字等。
手机连接设备后必须提供用户通过相机操作主动断开DV Engine的能力,包括但不限于通过开关、菜单或者按键的选择。
设备必须提供可以开启或者关闭DV Engine功能的入口,包括但不限于开关、菜单或者按键选择,对于DV Engine默认开关状态,需要在产品资料中说明。
设备应具备随时中断正在使用中的DV Engine通信能力,包括但不限于通过开关、菜单或者按键关闭,用户一旦选择中断,设备应立即响应生效。
首次连接时,输入连接码鉴权。
非首次连接时,无需输入连接码鉴权,使用已经鉴权过的设备信息进行鉴权。
连接码是随机值,没有设置项。
首次连接设备,通过连接码进行鉴权。
如果设备带有显示屏,则在设备上显示连接码。
如果设备不带有显示屏,且必须在私密空间内(例如:车内),可以用设备语音播报连接码。
内容包含标题和设备连接码,在设备上全屏显示。
展示六位数字连接码,为界面的最核心元素。
连接此设备的手机名称。
如果设备不带有显示屏,且必须在私密空间内(例如:车内),播报内容必须设备连接码,清晰的提示用户连接码内容。例如:连接码:xxxxxx。
如果设备不带有显示屏,且必须在私密空间内(例如:车内),播报内容应包含引导语提示用户此时将要播报连接码。例如:请在XX设备上输入。
1. 信任列表(必选)
DV Engine信任列表采用白名单方式。
2. 可信设备管理
设备提供展示可信设备信息的能力,包括但不限于设备名称信息。
设备提供可以删除可信设备的入口,包括但不限于开关、菜单等。
删除可信设备时需弹窗提醒用户确认是否删除。
上文中涉及的设备名称展示,建议显示完整手机名称,至少显示手机名称的前8个字节。
DV_AGENT_HANDLE g_dvagentHandle = NULL;
bool g_disconnectState = false;
/* DVAgent设备状态上报 */
void DeviceChange(DV_AGENT_HANDLE handle, const char *deviceId, uint32_t idLen, int state)
{
/* Asynchronously process 106 state. */
switch (state) {
case DVAGENT_DEVICE_DETECT_DISCONNECT:
/* when receive this state, DVAgentDestoryServiceHandle and recreate the handle in another thread */
g_disconnectState = true;
break;
case DVAGENT_DEVICE_CONNECTED:
/* you can do something when recv the connected status */
break;
default:
break;
}
return;
}
/* DVAgent服务状态上报 */
void DeviceServiceChange(DV_AGENT_HANDLE handle, const char *serviceId, uint32_t idLen, int state)
{
return;
}
/* 接收来自DVAgent的数据 */
void DataReceive(DV_AGENT_HANDLE handle, int dataType, char *data, uint32_t dataLen)
{
return;
}
/* 接收来自DVAgent鉴权认证过程中的PIN码并显示 */
void PinCode(const char *pinCode, uint32_t codeLen)
{
return;
} int32_t GetSecureFileSize(const char *filename)
{
FILE *fp;
int32_t size = 0;
if (filename == NULL) {
printf("GetSecureFileSize param null.\n");
return -1;
}
printf("Enter GetSecureFileSize, fileName:%s\n", filename);
char filenameInBox[256] = {0};
sprintf(filenameInBox, "%s/%s", HICHAIN_FILE_PATH, filename);
fp = fopen(filenameInBox, "rb");
if (fp == NULL) {
printf("fopen null.\n");
return 0;
}
if (fseek(fp, 0L, SEEK_END) != 0) {
printf("fseek error.\n");
fclose(fp);
return -1;
}
size = ftell(fp);
fclose(fp);
printf("Leave GetSecureFileSize, size:%d\n", size);
return size;
}
int32_t ReadSecureFile(const char *filename, uint32_t offset, uint8_t *buf, uint32_t len)
{
FILE *fp;
int32_t size = 0;
(void)offset;
if (filename == NULL || buf == NULL) {
printf("ReadSecureFile param null.\n");
return -1;
}
printf("ReadSecureFile, fileName:%s, len:%d\n", filename, len);
char filenameInBox[256] = {0};
sprintf(filenameInBox, "%s/%s", HICHAIN_FILE_PATH, filename);
fp = fopen(filenameInBox, "rb");
if (fp == NULL) {
printf("fopen null.\n");
return 0;
}
size = fread(buf, sizeof(uint8_t), (size_t)len, fp);
fclose(fp);
printf("ReadSecureFile, size:%d\n", size);
return size;
}
int32_t WriteSecureFile(const char *filename, uint32_t offset, uint8_t *buf, uint32_t len)
{
(void)offset;
FILE *fp;
size_t size;
if (filename == NULL || buf == NULL) {
printf("WriteSecureFile param null.\n");
return -1;
}
printf("WriteSecureFile, filename:%s, len:%d\n", filename, len);
char filenameInBox[256] = {0};
sprintf(filenameInBox, "%s/%s", HICHAIN_FILE_PATH, filename);
fp = fopen(filenameInBox, "wb");
if (fp == NULL) {
printf("fopen null.\n");
return 0;
}
size = fwrite(buf, sizeof(uint8_t), (uint32_t)len, fp);
fflush(fp);
fclose(fp);
printf("WriteSecureFile, size:%d\n", size);
if (size != len) {
printf("WriteSecureFile error.\n");
return -1;
}
return 0;
}
int32_t RemoveSecureFile(const char *filename)
{
printf("RemoveSecureFile\n");
return 0;
} int32_t DeviceLogHeaderFormat(char *logInfo, size_t length)
{
struct timeval tv;
struct timezone tz;
struct tm p;
int result;
gettimeofday(&tv, &tz);
localtime_r(&tv.tv_sec, &p);
result = sprintf_s(logInfo, length - 1, "%02d-%02d %02d:%02d:%02d.%03ld", 1 + p.tm_mon,
p.tm_mday, p.tm_hour, p.tm_min, p.tm_sec, tv.tv_usec / 1000L);
return result;
}
void DeviceLogDebug(const char *tag, const char *funcName, const char *format, ...)
{
char logInfo[LOG_BUFFER_LEN + 1] = "";
uint32_t len = 0;
va_list stVaList;
va_start(stVaList, format);
int32_t ret = memset_s(logInfo, LOG_BUFFER_LEN, 0, LOG_BUFFER_LEN);
if (ret != 0) {
return;
}
uint32_t result = DeviceLogHeaderFormat(logInfo, LOG_BUFFER_LEN);
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = sprintf_s(logInfo + len, LOG_BUFFER_LEN - len, "[%s-%s-%s]", tag, funcName, "Debug");
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = vsprintf_s(logInfo + len, LOG_BUFFER_LEN - len, (const char *)format, stVaList);
if (result < 0) {
va_end(stVaList);
return;
}
printf("%s\n", logInfo);
va_end(stVaList);
return;
}
void DeviceLogInfo(const char *tag, const char *funcName, const char *format, ...)
{
char logInfo[LOG_BUFFER_LEN + 1] = "";
uint32_t len = 0;
va_list stVaList;
va_start(stVaList, format);
int32_t ret = memset_s(logInfo, LOG_BUFFER_LEN, 0, LOG_BUFFER_LEN);
if (ret != 0) {
return;
}
uint32_t result = DeviceLogHeaderFormat(logInfo, LOG_BUFFER_LEN);
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = sprintf_s(logInfo + len, LOG_BUFFER_LEN - len, "[%s-%s-%s]", tag, funcName, "Info");
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = vsprintf_s(logInfo + len, LOG_BUFFER_LEN - len, (const char *)format, stVaList);
if (result < 0) {
va_end(stVaList);
return;
}
printf("%s\n", logInfo);
va_end(stVaList);
return;
}
void DeviceLogWarning(const char *tag, const char *funcName, const char *format, ...)
{
char logInfo[LOG_BUFFER_LEN + 1] = "";
uint32_t len = 0;
va_list stVaList;
va_start(stVaList, format);
int32_t ret = memset_s(logInfo, LOG_BUFFER_LEN, 0, LOG_BUFFER_LEN);
if (ret != 0) {
return;
}
uint32_t result = DeviceLogHeaderFormat(logInfo, LOG_BUFFER_LEN);
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = sprintf_s(logInfo + len, LOG_BUFFER_LEN - len, "[%s-%s-%s]", tag, funcName, "Warn");
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = vsprintf_s(logInfo + len, LOG_BUFFER_LEN - len, (const char *)format, stVaList);
if (result < 0) {
va_end(stVaList);
return;
}
printf("%s\n", logInfo);
va_end(stVaList);
return;
}
void DeviceLogError(const char *tag, const char *funcName, const char *format, ...)
{
char logInfo[LOG_BUFFER_LEN + 1] = "";
uint32_t len = 0;
va_list stVaList;
va_start(stVaList, format);
int32_t ret = memset_s(logInfo, LOG_BUFFER_LEN, 0, LOG_BUFFER_LEN);
if (ret != 0) {
return;
}
uint32_t result = DeviceLogHeaderFormat(logInfo, LOG_BUFFER_LEN);
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = sprintf_s(logInfo + len, LOG_BUFFER_LEN - len, "[%s-%s-%s]", tag, funcName, "Error");
if (result < 0) {
va_end(stVaList);
return;
}
len += result;
result = vsprintf_s(logInfo + len, LOG_BUFFER_LEN - len, (const char *)format, stVaList);
if (result < 0) {
va_end(stVaList);
return;
}
printf("%s\n", logInfo);
va_end(stVaList);
return;
} #define MAINLOOP_SLEEP 5000
void InitDvAgentMac(uint8_t *mac, uint32_t len)
{
if (mac == NULL) {
return;
}
uint32_t i = 0;
for (; i < len; i++) {
mac[i] = TEST_MACBYTE;
}
}
void DoSomethingWhenChanged()
{
if (g_disconnectState) {
printf("disconnect the device change\n");
DVAgentDestoryServiceHandle(g_dvagentHandle);
g_dvagentHandle = DVAgentCreateServiceHandle();
g_disconnectState = false;
}
}
int main()
{
DVAgentConfig dvAgentConfig = { { 0 } };
dvAgentConfig.deviceInfo.vendorID = TEST_VENDOR_ID;
dvAgentConfig.deviceInfo.connectAllowType = 0x3;
/* BLE P2P init brMac, CoAP init wifiMac. Pick one of two. */
InitDvAgentMac(dvAgentConfig.deviceInfo.brMac, DVAGENT_BR_MAC_LEN);
char *deviceID = "BB000DDD92A0A2A346F0B531F278AF06E370F86932CCAFCCC892D68D350F80F8";
char *deviceName = "SampleDevice";
dvAgentConfig.deviceInfo.deviceId = deviceID;
dvAgentConfig.deviceInfo.idLen = strlen(deviceID);
dvAgentConfig.deviceInfo.deviceName = deviceName;
dvAgentConfig.deviceInfo.nameLen = strlen(deviceName);
DVAgentInit(&dvAgentConfig);
DVAgentListener listener = { 0 };
listener.DeviceChange = DeviceChange;
listener.DeviceServiceChange = DeviceServiceChange;
listener.DataReceive = DataReceive;
listener.PinCode = PinCode;
listener.GetSecureFileSize = GetSecureFileSize;
listener.ReadSecureFile = ReadSecureFile;
listener.WriteSecureFile = WriteSecureFile;
listener.RemoveSecureFile = RemoveSecureFile;
listener.StartScanQRCode = StartScanQRCode;
listener.StopScanQRCode = StopScanQRCode;
listener.logGroup.logD = DeviceLogDebug;
listener.logGroup.logE = DeviceLogError;
listener.logGroup.logI = DeviceLogInfo;
listener.logGroup.logW = DeviceLogWarning;
DVAgentRegisterListener(&listener);
g_dvagentHandle = DVAgentCreateServiceHandle();
while (1) {
usleep(MAINLOOP_SLEEP);
DoSomethingWhenChanged();
}
return 0;
} 靠近发现连接采用BLE发现。DV Engine SDK通过BLE扫描手机广播,解析后回复反向广播,手机通过解析反向广播内容显示设备,用户根据设备名字选择设备,点击连接建立BLE连接通道。
DV SDK与BLE driver(Linux南向)交互时序图

DV Engine SDK通过BLE通道进行信道协商。在信道协商完成后创建GO,将GC信息经由BLE通道发送到手机。手机在收到设备的GC信息后连接设备,当手机连接GO成功后,WLAN Mac层链路打通。
DV Engine SDK与WLAN driver(Linux南向实现)交互时序图

DV Engine需要采集智能设备的设备类型、设备支持的服务能力及设备能力的状态(当前是否占用)。DV Engine采集智能设备的设备类型、设备支持的服务能力及设备能力的状态,智能设备厂商需实现对应libdmsdpdevice.so,完成获取设备类型、设备服务能力及设备能力状态等接口,供DV Engine调用。
将智能设备上的Camera硬件虚拟化,通过DV Engine的分布式能力,使用Camera硬件。此类接口用于定义DV Engine对智能设备Camera硬件的调用接口,智能设备厂商需实现对应的libdmsdpcamerahandler.so。如果智能设备厂商不具备Camera硬件或者不需要将Camera硬件虚拟化,不需要关注该接口,默认不实现此so即可。
将智能设备上的Speaker和Mic硬件虚拟化,通过DV Engine的分布式能力,使用Speaker和Mic硬件。此类接口用于定义DV Engine对智能设备Audio(Speaker/Mic)硬件的调用接口,智能设备厂商需实现对应的libdmsdpaudiohandler.so。如果智能设备厂商不具备Audio(Speaker/Mic)硬件或者不需要将Audio(Speaker/Mic)硬件虚拟化,不需要关注该接口,默认不实现此so即可。
本章节提供了编解码接口定义,智能设备厂商根据业务选择性集成实现。对于音视频硬件编解码的调用接口,接口需要智能设备厂商在对应的so中实现。智能设备厂商不提供功能实现,不需要实现对应so即可。