/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2012-2021. All rights reserved. * Description: hidl interface of DTV */ #define LOG_TAG "Hwdtv" #include "Hwdtv.h" #include #include #include #include #include #include #include #include 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 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 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 Hwdtv::setPlugins(const hidl_string& path) { ALOGE("Hwdtv setPlugins"); registerPlugin(path.c_str()); return 0; } Return Hwdtv::getCapBuffer(uint32_t id, getCapBuffer_cb _hidl_cb) { ALOGE("Hwdtv getCapBuffer id = %u", id); hidl_memory mBuffer = {}; _hidl_cb(mBuffer); return Void(); } Return Hwdtv::hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb) { const char* data = request.c_str(); int length = static_cast(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(parcelReply.data()), parcelReply.dataSize()); _hidl_cb(ret, inputStr); return Void(); } Return Hwdtv::disconnect() { return Void(); } Return Hwdtv::onObjectDeath(uint64_t cookie) { Mutex::Autolock autoLock(mNotifyLock); IHwdtvCallback* cb = reinterpret_cast((uintptr_t)cookie); std::vector>::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 Hwdtv::setCallback(const sp& 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 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(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(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