You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
12 KiB

/*
* Copyright (c) Hisilicon Technologies Co., Ltd. 2012-2021. All rights reserved.
* Description: hidl interface of DTV
*/
#define LOG_TAG "Hwdtv"
#include "Hwdtv.h"
#include <hardware/hardware.h>
#include <log/log_main.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <cinttypes>
#include <dlfcn.h>
#include <android/hidl/memory/1.0/IMemory.h>
#include <securec.h>
const SVR_S32 DTV_COST_TIME = 1000;
namespace vendor {
namespace huanglong {
namespace hardware {
namespace hwdtv {
namespace V1_0 {
namespace implementation {
using ::vendor::huanglong::hardware::hwdtv::V1_0::IHwdtv;
using ::vendor::huanglong::hardware::hwdtv::V1_0::IHwdtvCallback;
using ::android::hardware::hidl_string;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::String8;
using ::android::hardware::hidl_memory;
using ::android::hidl::memory::V1_0::IMemory;
#define DTV_LIB_DIR "/vendor/lib/"
#define CHECK_DOFUNC(func) \
do { \
int32_t ret = 0; \
ret = func; \
if (ret != SVR_SUCCESS) { \
ALOGE("[%s] strncpy_s failed!", #func); \
}; \
} while (0)
#define CHECK_DOFUNC_SECURE_RETURN(func) \
do { \
int32_t ret = 0; \
ret = func; \
if (ret != EOK) { \
ALOGE("[%s] strncpy_s failed!", #func); \
return SVR_FAILURE; \
}; \
} while (0)
class DtvDeathRecipient : public hidl_death_recipient {
public:
explicit DtvDeathRecipient(const sp<Hwdtv> hDtv) : mHwDtv(hDtv)
{
}
~DtvDeathRecipient()
{
}
virtual void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>&)
{
// handle death notify
ALOGE("serviceDied cookie = 0x%" PRIu64, cookie);
mHwDtv->onObjectDeath(cookie);
}
private:
sp<Hwdtv> mHwDtv;
};
Hwdtv::Hwdtv() : mDeathRecipient(new DtvDeathRecipient(this))
{
ALOGW("Hwdtv constructor");
setPlugins("dtv-live://plugin.libdol_dtvplg");
}
// Methods from ::vendor::huanglong::hardware::Hwdtv::V1_0::IHwdtv follow.
Return<int32_t> Hwdtv::setPlugins(const hidl_string& path)
{
ALOGE("Hwdtv setPlugins");
registerPlugin(path.c_str());
return 0;
}
Return<void> Hwdtv::getCapBuffer(uint32_t id, getCapBuffer_cb _hidl_cb)
{
ALOGE("Hwdtv getCapBuffer id = %u", id);
hidl_memory mBuffer = {};
_hidl_cb(mBuffer);
return Void();
}
Return<void> Hwdtv::hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb)
{
const char* data = request.c_str();
int length = static_cast<int>(request.size());
android::Parcel parcelReq;
android::Parcel parcelReply;
parcelReq.write(data, length);
parcelReq.setDataPosition(0);
int32_t ret = invoke(parcelReq, &parcelReply);
hidl_string inputStr(reinterpret_cast<const char *>(parcelReply.data()), parcelReply.dataSize());
_hidl_cb(ret, inputStr);
return Void();
}
Return<void> Hwdtv::disconnect()
{
return Void();
}
Return<void> Hwdtv::onObjectDeath(uint64_t cookie)
{
Mutex::Autolock autoLock(mNotifyLock);
IHwdtvCallback* cb = reinterpret_cast<IHwdtvCallback *>((uintptr_t)cookie);
std::vector<sp<IHwdtvCallback>>::iterator itr = mCallbacks.begin();
while (itr != mCallbacks.end()) {
if (*itr == cb) {
ALOGD("delete callback");
itr = mCallbacks.erase(itr);
} else {
itr++;
}
}
ALOGD("after delete callback count = %u", mCallbacks.size());
return Void();
}
Return<int32_t> Hwdtv::setCallback(const sp<IHwdtvCallback>& callback)
{
ALOGD("Hwdtv setCallback");
if (callback == NULL) {
ALOGE("callback is NULL!\n");
return SVR_FAILURE;
}
Mutex::Autolock autoLock(mNotifyLock);
uint64_t cookie = (uint64_t)(uintptr_t)(callback.get());
if (!callback->linkToDeath(mDeathRecipient, cookie)) {
ALOGE("setCallback Failed to register death notification");
return -1;
}
mCallbacks.push_back(callback);
return 0;
}
Return<int32_t> Hwdtv::registerPlugin(const SVR_CHAR* pcUrl)
{
DOL_DTV_PLUGIN_S *pPlugin = NULL;
SVR_CHAR acRealLibPath[PATH_MAX];
if (pcUrl == NULL) {
return SVR_FAILURE;
}
Mutex::Autolock lock(mLock);
SVR_CHAR *p = NULL;
SVR_CHAR acLibName[DTV_PLUGIN_PATH_LENGTH];
SVR_CHAR acLibPath[DTV_PLUGIN_PATH_LENGTH];
SVR_CHAR acTmp[DTV_PLUGIN_PATH_LENGTH];
SVR_U32 u32Length = 0;
const SVR_U32 suffixLen = 4; // 4 is length for ".so\0"
CHECK_DOFUNC(memset_s(acTmp, sizeof(acTmp), 0, sizeof(acTmp)));
CHECK_DOFUNC_SECURE_RETURN(strncpy_s(acTmp, sizeof(acTmp), pcUrl, sizeof(acTmp) - 1));
while ((p = strrchr(acTmp, '.')) != NULL) {
CHECK_DOFUNC(memset_s(acLibName, sizeof(acLibName), 0, sizeof(acLibName)));
CHECK_DOFUNC(memset_s(acLibPath, sizeof(acLibPath), 0, sizeof(acLibPath)));
u32Length = strlen(p + 1);
if (u32Length > (DTV_PLUGIN_PATH_LENGTH - suffixLen)) {
ALOGE("%s plugin path too long", __FUNCTION__);
break;
}
CHECK_DOFUNC_SECURE_RETURN(strncpy_s(acLibName, sizeof(acLibName), p + 1, u32Length));
acLibName[DTV_PLUGIN_PATH_LENGTH - suffixLen] = '\0';
SVR_S32 len = snprintf_s(acLibPath, sizeof(acLibPath), sizeof(acLibPath) - 1, "%s%s%s",
(DTV_LIB_DIR), acLibName, ".so");
if (len < 0) {
ALOGE("[%s] strncpy_s failed!", __FUNCTION__);
return SVR_FAILURE;
}
acLibPath[DTV_PLUGIN_PATH_LENGTH - 1] = '\0';
ALOGD("enter setPlugin libName = %s", acLibPath);
*p = '\0';
}
CHECK_DOFUNC(memset_s(acRealLibPath, sizeof(acRealLibPath), 0, sizeof(acRealLibPath)));
if (strlen(acLibPath) > (DTV_PLUGIN_PATH_LENGTH - 1) || NULL == realpath(acLibPath, acRealLibPath)) {
ALOGE("url is %s error no 0x%x\n", acRealLibPath, errno);
return SVR_FAILURE;
}
SVR_U32 urlLength = strlen(acRealLibPath);
if (urlLength >= DTV_PLUGIN_PATH_LENGTH) {
ALOGE("URL length over %d error.", DTV_PLUGIN_PATH_LENGTH);
return SVR_FAILURE;
}
ALOGD("regist DTV plugin %s", acRealLibPath);
for (SVR_U32 i = 0; i < mPluginList.size(); i++) {
if (strcmp(mPluginList[i]->acLibName, acRealLibPath) == 0) {
ALOGI("plugin[%s] already opened ", acRealLibPath);
return SVR_SUCCESS;
}
}
SVR_S32 ret;
do {
pPlugin = reinterpret_cast<DOL_DTV_PLUGIN_S *>(malloc(sizeof(DOL_DTV_PLUGIN_S)));
if (pPlugin == SVR_NULL) {
ALOGE("[%s], malloc() failed\n", __FUNCTION__);
return SVR_FAILURE;
}
ret = memcpy_s(pPlugin->acLibName, sizeof(pPlugin->acLibName), acRealLibPath, urlLength + 1);
if (ret != EOK) {
ALOGE("memcpy_s failed, ret: %d\n", ret);
free(pPlugin);
pPlugin = SVR_NULL;
return SVR_FAILURE;
}
pPlugin->pHandle = dlopen(acRealLibPath, RTLD_NOW);
if (!pPlugin->pHandle) {
ALOGE("dlopen(%s) failed, err: %s\n", acRealLibPath, dlerror());
free(pPlugin);
pPlugin = SVR_NULL;
return SVR_FAILURE;
}
pPlugin->fnCreateInstance = (PFN_CREATE_PLUGIN)dlsym(pPlugin->pHandle, "createInstance");
pPlugin->fnDestroyInstance = (PFN_DESTROY_PLUGIN)dlsym(pPlugin->pHandle, "destroyInstance");
if ((pPlugin->fnCreateInstance == SVR_NULL) || (pPlugin->fnDestroyInstance == SVR_NULL)) {
ALOGE("dlsym err: %s\n", dlerror());
dlclose(pPlugin->pHandle);
free(pPlugin);
pPlugin = SVR_NULL;
return SVR_FAILURE;
}
pPlugin->pInstance = pPlugin->fnCreateInstance();
if (pPlugin->pInstance == SVR_NULL) {
ALOGD("Create %s Instance failed \n", acRealLibPath);
dlclose(pPlugin->pHandle);
free(pPlugin);
pPlugin = SVR_NULL;
return SVR_FAILURE;
}
ALOGD("Create Instance OK \n");
pPlugin->pInstance->regMsgHandler(this);
mPluginList.add(pPlugin);
} while (0);
return SVR_SUCCESS;
}
status_t Hwdtv::notify(SVR_S32 s32Msg, SVR_S32 s32Ext1, SVR_S32 s32Ext2, SVR_S32 s32Ext3,
const Parcel* pParcelObj, Parcel* pParcelReply)
{
SVR_S32 i = 0;
Mutex::Autolock autoLock(mNotifyLock);
SVR_S32 s32Len = (SVR_S32)mCallbacks.size();
ALOGD("notify: callback count = %d", s32Len);
hidl_string inputStr = nullptr;
if (pParcelObj != NULL && pParcelObj->dataSize() > 0) {
hidl_string tmpInputStr((const char *)pParcelObj->data(), pParcelObj->dataSize());
inputStr = tmpInputStr;
}
for (i = 0; i < s32Len; i++) {
if (mCallbacks[i] != nullptr) {
String8 retReply;
auto cb = [&](hidl_string strReply) {
retReply = String8(strReply.c_str(), strReply.size());
};
if (!mCallbacks[i]->ping().isOk()) {
ALOGE("NotifyCb ping failed...");
continue;
}
auto _hidl_err = mCallbacks[i]->hwNotify(s32Msg, s32Ext1, s32Ext2, s32Ext3, inputStr, cb);
if (!_hidl_err.isOk() || _hidl_err.isDeadObject()) {
ALOGE("hwNotify failed, client is nok or isDeadObject!\n");
continue;
}
if (pParcelReply != NULL) {
pParcelReply->write(retReply.c_str(), retReply.size());
pParcelReply->setDataPosition(0);
}
}
}
return 0;
}
inline status_t Hwdtv::invoke(const android::Parcel& request, android::Parcel* reply)
{
SVR_BOOL invokeFlag = SVR_FALSE;
Mutex::Autolock lock(mLock);
SVR_U32 pluginCount = mPluginList.size();
if (pluginCount == 0) {
ALOGE("invoke the PluginList is TD_NULL\n");
return PLUGIN_NOT_IMPLIMENT;
}
// read DTV_INTERFACE_NAME
#if (PLATFORM_SDK_VERSION < 29) // 29 for AndroidQ
(SVR_VOID)request.readInt32();
(SVR_VOID)request.readString16();
#elif (PLATFORM_SDK_VERSION == 29)
// writeInterfaceToken is changed from P to Q
(SVR_VOID)request.readInt32();
(SVR_VOID)request.readInt32();
(SVR_VOID)request.readString16();
#elif (PLATFORM_SDK_VERSION == 31)
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
(SVR_VOID)request.readInt64();
#endif
size_t requestPos = request.dataPosition();
SVR_U32 cmd = static_cast<SVR_U32>(request.readInt32());
for (SVR_U32 i = 0; i < pluginCount; i++) {
/* CMD must be between in [startCmd,endCmd] */
if ((cmd >= mPluginList[i]->pInstance->getStartCmd()) &&
(cmd <= mPluginList[i]->pInstance->getEndCmd())) {
struct timespec tsStart, tsEnd;
SVR_U32 timeMs = 0;
(SVR_VOID)clock_gettime(CLOCK_MONOTONIC, &tsStart);
request.setDataPosition(requestPos);
if (cmd != 0x901 && cmd != 0x903 && cmd != 0x905) { // 0x901~905 is getTime cmd
ALOGD("+++before cmd =0x%x", cmd);
}
mPluginList[i]->pInstance->invoke(request, reply);
if (cmd != 0x901 && cmd != 0x903 && cmd != 0x905) { // 0x901~905 is getTime cmd
ALOGD("---after cmd =0x%x", cmd);
}
invokeFlag = SVR_TRUE;
(SVR_VOID)clock_gettime(CLOCK_MONOTONIC, &tsEnd);
/* 1000 for s and 1000000 for ns */
timeMs = (SVR_U32)((tsEnd.tv_sec - tsStart.tv_sec) * 1000 + (tsEnd.tv_nsec - tsStart.tv_nsec) / 1000000);
if (timeMs > DTV_COST_TIME) {
ALOGE("DTV command %#x cost too much time %d ms\n", cmd, timeMs);
}
break;
}
}
if (invokeFlag == SVR_FALSE && (reply != NULL)) {
reply->writeInt32(SVR_FAILURE);
}
return ((invokeFlag == SVR_TRUE) ? (status_t)SVR_SUCCESS : (status_t)PLUGIN_NOT_IMPLIMENT);
}
IHwdtv *HIDL_FETCH_IHwdtv(const char *)
{
return new Hwdtv();
}
} // namespace implementation
} // namespace V1_0
} // namespace hwdtv
} // namespace hardware
} // namespace huanglong
} // namespace vendor