/* * Copyright (C) 2014-2026 Amlogic, Inc. All rights reserved. * * All information contained herein is Amlogic confidential. * * This software is provided to you pursuant to Software License Agreement * (SLA) with Amlogic Inc ("Amlogic"). This software may be used * only in accordance with the terms of this agreement. * * Redistribution and use in source and binary forms, with or without * modification is strictly prohibited without prior written permission from * Amlogic. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "SubtitleServer" // #include #include "SubtitleServer.h" #include #include #include "MemoryLeakTrackUtil.h" #include "SubtitleLog.h" #include "Watchdog.h" using android::CallStack; using namespace std::chrono_literals; static constexpr auto kTimeout = 10s; SubtitleLogLevel gSubtitleLogLevel = SUB_LOG_INFO; SubtitleServer *SubtitleServer::sInstance = nullptr; SubtitleServer *SubtitleServer::Instance() { if (!sInstance) sInstance = new SubtitleServer(); return sInstance; } const static int FIRST_SESSION_ID = 0x1F230000; const static int LAST_SESSION_ID = 0x7FFFFFFF; static pthread_once_t threadFlag = PTHREAD_ONCE_INIT; SubtitleServer::SubtitleServer() { pthread_once(&threadFlag, [] { SubtitleSignalHandler::instance().installSignalHandlers(); }); // mDeathRecipient =new DeathRecipient(this); // mFallbackPlayStarted = false; // mFallbackCallback = nullptr; // Here, we create this messageQueue, it automatically start a thread to pump // decoded data/event from parser, and call CallbackHandler to send to remote side. mMessageQueue = new AndroidCallbackMessageQueue(new ClientMessageHandlerImpl(this)); mCurSessionId = FIRST_SESSION_ID; mOpenCalled = false; mLastOpenType = OpenType::TYPE_APPSDK; mLastOpenTime = -1; } std::shared_ptr SubtitleServer::getSubtitleServiceLocked(int sId) { SUBTITLE_LOGI("getSubtitleServiceLocked, sId= %d", sId); if (sId >= FIRST_SESSION_ID && sId != mCurSessionId) { SUBTITLE_LOGE("Error: %d is not the running session %d", sId, mCurSessionId); return nullptr; } return mServiceClients[0]; } std::shared_ptr SubtitleServer::getSubtitleService(int sId) { android::AutoMutex _l(mLock); return getSubtitleServiceLocked(sId); } // Methods from ISubtitleServer follow. // void SubtitleServer::openConnection(openConnection_cb _hidl_cb) { int SubtitleServer::openConnection() { SUBTITLE_LOGI_FUNC_IN(); Watchdog watchdog(kTimeout); int sessionId = -1; { // TODO: consider to support multi-session cases. // // In single client case, try closing the running connect before open a new connection. if (mServiceClients.find(0) != mServiceClients.end() && mServiceClients[0]) { SUBTITLE_LOGE("session already existed, close current connection"); userDataClose(0); close(0); closeConnection(0); } android::AutoMutex _l(mLock); sessionId = ++mCurSessionId; if (sessionId == LAST_SESSION_ID) { sessionId = mCurSessionId = FIRST_SESSION_ID; } mServiceClients[0] = std::make_shared(); for (size_t i = 0; i < mServiceClients.size(); ++i) { SUBTITLE_LOGI("client %zu-%zu, sId=%d: %p", i, mServiceClients.size(), sessionId, mServiceClients[i].get()); } } return sessionId; //_hidl_cb(Result::OK, sessionId); // return Void(); } Result SubtitleServer::closeConnection(int32_t sId) { Watchdog watchdog(kTimeout); SUBTITLE_LOGI("sId=%d", sId); // Only support 1 client by now. if (mServiceClients.empty()) { return Result::FAIL; } mServiceClients.erase(0); return Result::OK; // TODO: too simple here! need more condition. // hide(sId); // for standalone play, need hide! // SUBTITLE_LOGI("need hide here"); android::AutoMutex _l(mLock); int clientSize = mServiceClients.size(); SUBTITLE_LOGI("clientSize=%d", clientSize); #if SUPPORT_MULTI for (auto it = mServiceClients.begin(); it != mServiceClients.end();) { if (it->first == sId) { mServiceClients.erase(it); return Result::OK; } it++; } #else if (!mServiceClients.empty()) { for (auto it = mServiceClients.begin(); it != mServiceClients.end();) { if (it->first == sId) { SUBTITLE_LOGI("closeConnection for sessionId %d", sId); mServiceClients.erase(it); break; } else { it++; } } } #endif return Result::FAIL; } Result SubtitleServer::open(int32_t sId, int32_t ioType, OpenType openType, const native_handle_t *handle) { // Update log level updateSubtitleLogLevel(); int currentLevel = (int)*getLogLevel(); SUBTITLE_LOGI("currentLevel=%d", currentLevel); Watchdog watchdog(kTimeout); android::AutoMutex _l(mLock); std::shared_ptr ss = getSubtitleServiceLocked(sId); SUBTITLE_LOGI("ss=%p ioType=%d openType:%d", ss.get(), ioType, openType); std::vector fds; int idxSubId = -1; // int dupFd = -1; // fd will auto closed when destruct hidl_handle, dump one. int demuxId = -1; if (handle != nullptr && handle->numFds >= 1) { for (int i = 0; i < handle->numFds; i++) { int fd = handle->data[i]; fds.push_back(::dup(fd)); } if (handle->numInts > 0) { idxSubId = handle->data[handle->numFds]; } } auto now = systemTime(SYSTEM_TIME_MONOTONIC); const int64_t diff200ms = 200000000LL; SUBTITLE_LOGI("mOpenCalled: %d mLastOpenType=%d, mLastOpenTime=%" PRId64 ", now=%" PRId64, mOpenCalled, mLastOpenType, mLastOpenTime, now); if (ss != nullptr) { // because current we test middleware player(e.g CTC) with VideoPlayer, it know nothing // about middleware calling, if ctc called, close VideoPlayer's request, use CTC's request. if (mOpenCalled && openType == OpenType::TYPE_MIDDLEWARE) { if ((openType != mLastOpenType && (now - mLastOpenTime) < diff200ms) || mLastOpenTime == -1) { bool r = ss->stopSubtitle(); /* ss->stopFmqReceiver(); if (mDataMQ) { std::unique_ptr removeDataMQ(std::move(mDataMQ)); mDataMQ.reset(nullptr); }*/ } } // TODO: need revise, fix the value. if ((ioType & 0xff) == E_SUBTITLE_DEMUX) { demuxId = ioType >> 16; ioType = ioType & 0xff; SUBTITLE_LOGI("mOpenCalled : demux id= %d, ioType =%d\n", demuxId, ioType); ss->setDemuxId(demuxId); } bool r = ss->startSubtitle(fds, idxSubId, (SubtitleIOType)ioType, mMessageQueue.get()); mOpenCalled = true; mLastOpenType = openType; mLastOpenTime = now; return (r ? Result::OK : Result::FAIL); } // if (dupFd != -1) close(dupFd); SUBTITLE_LOGI("no valid ss, Should not enter here!"); return Result::FAIL; } Result SubtitleServer::close(int32_t sId) { Watchdog watchdog(kTimeout); std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { bool r = ss->stopSubtitle(); /* ss->stopFmqReceiver(); { android::AutoMutex _l(mLock); if (mDataMQ) { std::unique_ptr removeDataMQ(std::move(mDataMQ)); mDataMQ.reset(nullptr); } mOpenCalled = false; }*/ return (r ? Result::OK : Result::FAIL); } return Result::FAIL; } Result SubtitleServer::resetForSeek(int32_t sId) { Watchdog watchdog(kTimeout); std::shared_ptr ss = getSubtitleService(sId); if (ss != nullptr) { bool r = ss->resetForSeek(); return (r ? Result::OK : Result::FAIL); } return Result::FAIL; } Result SubtitleServer::updateVideoPos(int32_t sId, int64_t pos) { std::shared_ptr ss = getSubtitleService(sId); if (ss != nullptr) { bool r = ss->updateVideoPosAt(pos); return (r ? Result::OK : Result::FAIL); } return Result::FAIL; } // void SubtitleServer::getTotalTracks(int32_t sId, getTotalTracks_cb _hidl_cb) { int SubtitleServer::getTotalTracks(int32_t sId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { SUBTITLE_LOGI("total=%d", ss->totalSubtitles()); //_hidl_cb(Result::OK, ss->totalSubtitles()); return ss->totalSubtitles(); } else { // _hidl_cb(Result::FAIL, 1);//-1); return -1; } // return Void(); } // void SubtitleServer::getType(int32_t sId, getType_cb _hidl_cb) { int SubtitleServer::getType(int32_t sId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { SUBTITLE_LOGI("subType=%d", ss->subtitleType()); //_hidl_cb(Result::OK, ss->subtitleType()); return ss->subtitleType(); } else { //_hidl_cb(Result::FAIL, 1); return -1; } // return Void(); } // void SubtitleServer::getLanguage(int32_t sId, getLanguage_cb _hidl_cb) { std::string SubtitleServer::getLanguage(int32_t sId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { return ss->currentLanguage(); } else { return std::string("No lang"); } } Result SubtitleServer::setStartTimeStamp(int32_t sId, int32_t startTime) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->setStartTimeStamp(startTime); } return Result{}; } Result SubtitleServer::setSubType(int32_t sId, int32_t type) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p subType=%d", ss.get(), type); if (ss != nullptr) { ss->setSubType(type); } return Result{}; } Result SubtitleServer::setSubPid(int32_t sId, int32_t pid) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p pid=%d", ss.get(), pid); if (ss != nullptr) { ss->setSubPid(pid); } return Result{}; } Result SubtitleServer::setPageId(int32_t sId, int32_t pageId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->setSubPageId(pageId); } return Result{}; } Result SubtitleServer::setAncPageId(int32_t sId, int32_t ancPageId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p setAncPageId=%d", ss.get(), ancPageId); if (ss != nullptr) { ss->setSubAncPageId(ancPageId); } return Result{}; } Result SubtitleServer::setSecureLevel(int32_t sId, int32_t flag) { std::shared_ptr ss = getSubtitleService(flag); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->setSecureLevel(flag); } return Result{}; } Result SubtitleServer::setChannelId(int32_t sId, int32_t channelId) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->setChannelId(channelId); } return Result{}; } Result SubtitleServer::setClosedCaptionVfmt(int32_t sId, int32_t vfmt) { std::shared_ptr ss = getSubtitleService(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->setClosedCaptionVfmt(vfmt); } return Result{}; } int SubtitleServer::getSubtitleStatus(int32_t sId, int type, int cmd) { std::shared_ptr ss = getSubtitleService(sId); if (ss != nullptr) { SUBTITLE_LOGI("type:%d cmd:%d teltextStatus=%d", type, cmd, ss->getSubtitleStatus(type, cmd)); return ss->getSubtitleStatus(type, cmd); } else { return -1; } } Result SubtitleServer::ttControl(int32_t sId, int cmd, int magazine, int pageNo, int regionId, int param) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } SUBTITLE_LOGI("ss=%p cmd=%d", ss.get(), cmd); bool r = ss->ttControl(cmd, magazine, pageNo, regionId, param); return r ? Result::OK : Result::FAIL; } Result SubtitleServer::ttGoHome(int32_t sId) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } ss->ttGoHome(); return Result::OK; } Result SubtitleServer::ttNextPage(int32_t sId, int32_t dir) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } ss->ttNextPage(dir); return Result::OK; } Result SubtitleServer::ttNextSubPage(int32_t sId, int32_t dir) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } ss->ttNextSubPage(dir); return Result::OK; } Result SubtitleServer::ttGotoPage(int32_t sId, int32_t pageNo, int32_t subPageNo) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } SUBTITLE_LOGE(" ttGotoPage pageNo=%d, subPageNo=%d", pageNo, subPageNo); ss->ttGotoPage(pageNo, subPageNo); return Result::OK; } Result SubtitleServer::setPipId(int32_t sId, int32_t mode, int32_t id) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } if ((PIP_PLAYER_ID != mode) && (PIP_MEDIASYNC_ID != mode)) { return Result::FAIL; } SUBTITLE_LOGI(" setPipId mode=%d, id=%d", mode, id); ss->setPipId(mode, id); return Result::OK; } Result SubtitleServer::setSubTranslationLanguage(int32_t sId, const std::string &srcLang, const std::string &dstLang) { std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } auto ret = ss->setSubTranslationLanguage(srcLang, dstLang); return ret ? Result::OK : Result::FAIL; } Result SubtitleServer::userDataOpen(int32_t sId) { Watchdog watchdog(kTimeout); SUBTITLE_LOGI_FUNC_IN(); std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } ss->userDataOpen(mMessageQueue.get()); return Result::OK; } Result SubtitleServer::userDataClose(int32_t sId) { Watchdog watchdog(kTimeout); SUBTITLE_LOGI_FUNC_IN(); std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } ss->userDataClose(); return Result::OK; } #if 0 void SubtitleServer::prepareWritingQueue(int32_t sId, int32_t size, prepareWritingQueue_cb _hidl_cb) { Watchdog watchdog(kTimeout); auto sendError = [&_hidl_cb](Result result) { _hidl_cb(result, DataMQ::Descriptor()); }; SUBTITLE_LOGI_FUNC_IN(); android::AutoMutex _l(mLock); // Create message queues. if (mDataMQ) { SUBTITLE_LOGE("the client attempts to call prepareForWriting twice"); } else { std::unique_ptr tempDataMQ(new DataMQ(size, true /* EventFlag */)); if (!tempDataMQ->isValid()) { SUBTITLE_LOGE(!tempDataMQ->isValid(), "data MQ is invalid"); sendError(Result::FAIL); return Void(); } mDataMQ = std::move(tempDataMQ); } //TODO: create new thread for fetch the client wrote data. std::unique_ptr tempReader(new FmqReaderImpl(mDataMQ.get())); std::shared_ptr ss = getSubtitleServiceLocked(sId); SUBTITLE_LOGI("ss=%p", ss.get()); if (ss != nullptr) { ss->startFmqReceiver(std::move(tempReader)); } else { SUBTITLE_LOGE("Error! cannot get subtitle service through ID:%d(%x)", sId, sId); } _hidl_cb(Result::OK, *mDataMQ->getDesc()); return Void(); } #endif void SubtitleServer::setCallback(const sp &callback, ConnectType type) { Watchdog watchdog(kTimeout); android::AutoMutex _l(mLock); if (callback != nullptr) { int cookie = -1; int clientSize = mCallbackClients.size(); for (int i = 0; i < clientSize; i++) { if (mCallbackClients[i] == nullptr) { SUBTITLE_LOGI(", client index:%d had died, this id give the new client", i); cookie = i; mCallbackClients[i] = callback; break; } } if (cookie < 0) { cookie = clientSize; mCallbackClients[clientSize] = callback; } /* Return linkResult = callback->linkToDeath(mDeathRecipient, cookie); bool linkSuccess = linkResult.isOk() ? static_cast(linkResult) : false; if (!linkSuccess) { SUBTITLE_LOGE("Couldn't link death recipient for cookie: %d", cookie); } */ SUBTITLE_LOGI("cookie:%d, client size:%d", cookie, (int)mCallbackClients.size()); } return; } void SubtitleServer::setFallbackCallback(const sp &callback, ConnectType type) { Watchdog watchdog(kTimeout); android::AutoMutex _l(mLock); mFallbackCallback = callback; return; } void SubtitleServer::removeCallback(const sp &callback) { Watchdog watchdog(kTimeout); android::AutoMutex _l(mLock); if (callback != nullptr) { // Remove, if fallback callback. /* if (mFallbackCallback != nullptr && mFallbackCallback.get() == callback.get()) { mFallbackCallback->unlinkToDeath(mDeathRecipient); mFallbackCallback = nullptr; } */ int clientSize = mCallbackClients.size(); SUBTITLE_LOGI("[removeCallback] remove:%p clientSize=%d", callback.get(), clientSize); for (auto it = mCallbackClients.begin(); it != mCallbackClients.end();) { /*if (it->second != nullptr) { SUBTITLE_LOGI("[removeCallback] %p", (it->second).get()); it->second->unlinkToDeath(mDeathRecipient); }*/ it = mCallbackClients.erase(it); } } return; } // This only valid for global fallback display. Result SubtitleServer::show(int32_t sId) { Watchdog watchdog(kTimeout); #ifdef ANDROID SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); mFallbackPlayStarted = true; parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SHOW; parcel.bodyInt.resize(0); sendUiEvent(parcel); // Can send display Data to subtitle service now. return Result{}; #else std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } bool ret = ss->show(); return ret ? Result::OK : Result::FAIL; #endif } Result SubtitleServer::hide(int32_t sId) { Watchdog watchdog(kTimeout); #ifdef ANDROID SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); mFallbackPlayStarted = false; parcel.msgType = (int)FallthroughUiCmd::CMD_UI_HIDE; parcel.bodyInt.resize(0); sendUiEvent(parcel); return Result{}; #else std::shared_ptr ss = getSubtitleService(sId); if (ss == nullptr) { return Result::FAIL; } bool ret = ss->hide(); return ret ? Result::OK : Result::FAIL; #endif } #if 0 /*CMD_UI_SHOW = 0, CMD_UI_SET_IMGRATIO, CMD_UI_SET_SUBTITLE_DIMENSION, CMD_UI_SET_SURFACERECT*/ Result SubtitleServer::setTextColor(int32_t sId, int32_t color) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_TEXTCOLOR; parcel.bodyInt.resize(1); parcel.bodyInt[0] = color; sendUiEvent(parcel); return Result {}; } Result SubtitleServer::setTextSize(int32_t sId, int32_t size) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_TEXTSIZE; parcel.bodyInt.resize(1); parcel.bodyInt[0] = size; sendUiEvent(parcel); return Result {}; } Result SubtitleServer::setGravity(int32_t sId, int32_t gravity) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_GRAVITY; parcel.bodyInt.resize(1); parcel.bodyInt[0] = gravity; sendUiEvent(parcel); return Result {}; } Result SubtitleServer::setTextStyle(int32_t sId, int32_t style) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_TEXTSTYLE; parcel.bodyInt.resize(1); parcel.bodyInt[0] = style; sendUiEvent(parcel); return Result {}; } Result SubtitleServer::setPosHeight(int32_t sId, int32_t yOffset) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_POSHEIGHT; parcel.bodyInt.resize(1); parcel.bodyInt[0] = yOffset; sendUiEvent(parcel); return Result {}; } Result SubtitleServer::setImgRatio(int32_t sId, float ratioW, float ratioH, int32_t maxW, int32_t maxH) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_IMGRATIO; parcel.bodyInt.resize(4); parcel.bodyInt[0] = (int)ratioW; parcel.bodyInt[1] = (int)ratioH; parcel.bodyInt[2] = maxW; parcel.bodyInt[3] = maxH; sendUiEvent(parcel); return Result {}; } void SubtitleServer::getSubDimension(int32_t sId, getSubDimension_cb _hidl_cb) { // TODO implement return Void(); } Result SubtitleServer::setSubDimension(int32_t sId, int32_t width, int32_t height) { // TODO implement return Result {}; } Result SubtitleServer::setSurfaceViewRect(int32_t sId, int32_t x, int32_t y, int32_t w, int32_t h) { Watchdog watchdog(kTimeout); SubtitleHidlParcel parcel; android::AutoMutex _l(mLock); parcel.msgType = (int)FallthroughUiCmd::CMD_UI_SET_SURFACERECT; parcel.bodyInt.resize(4); parcel.bodyInt[0] = x; parcel.bodyInt[1] = y; parcel.bodyInt[2] = w; parcel.bodyInt[3] = h; sendUiEvent(parcel); return Result {}; } #endif void SubtitleServer::sendSubtitleDisplayNotify(SubtitleHidlParcel &event) { android::AutoMutex _l(mLock); SUBTITLE_LOGI("onEvent event:%d, client size:%zu", event.msgType, mCallbackClients.size()); if (mFallbackPlayStarted && mFallbackCallback != nullptr) { // enabled fallback display and fallback displayer started, then mFallbackCallback->notifyDataCallback(event); // SUBTITLE_LOGE(!r.isOk(), "Error, call notifyDataCallback failed! client died?"); return; } for (int i = 0; i < mCallbackClients.size(); i++) { if (mCallbackClients[i] != nullptr) { SUBTITLE_LOGI(", xxxxxxxxxxxxxcc client cookie:%d notifyCallback", i); mCallbackClients[i]->notifyDataCallback(event); // SUBTITLE_LOGE(!r.isOk(), "Error, call notifyDataCallback failed! client died?"); } } } void SubtitleServer::sendSubtitleEventNotify(SubtitleHidlParcel &event) { android::AutoMutex _l(mLock); if (mFallbackPlayStarted && mFallbackCallback != nullptr) { mFallbackCallback->eventNotify(event); // SUBTITLE_LOGE(!r.isOk(), "Error, call eventNotify failed! client died?"); } int clientSize = mCallbackClients.size(); SUBTITLE_LOGI("onEvent event:%d, client size:%d", event.msgType, clientSize); // check has valid client or not. bool hasEventClient = false; if (clientSize > 0) { for (int i = 0; i < clientSize; i++) { if (mCallbackClients[i] != nullptr) { hasEventClient = true; break; } } } // send DisplayEvent to clients if (hasEventClient > 0) { for (int i = 0; i < clientSize; i++) { if (mCallbackClients[i] != nullptr) { SUBTITLE_LOGI(", client cookie:%d notifyCallback", i); mCallbackClients[i]->eventNotify(event); // SUBTITLE_LOGE(!r.isOk(), "Error, call notifyDataCallback failed! client died?"); } } } else { // No client connected, try fallback display. /*if (mFallbackDisplayClient != nullptr) { if (ENABLE_LOG_PRINT) SUBTITLE_LOGI("fallback display event:%d, client size:%d", parcel.msgType, clientSize); mFallbackDisplayClient->notifyDisplayCallback(parcel); }*/ } } void SubtitleServer::sendUiEvent(SubtitleHidlParcel &event) { // if (!mFallbackPlayStarted) { // SUBTITLE_LOGE("UI event request not proceed, do you called uiShow()?"); // return; // } if (mFallbackCallback == nullptr) { SUBTITLE_LOGE("Error, no default fallback display registered!"); return; } mFallbackCallback->uiCommandCallback(event); } bool SubtitleServer::isClosed(int32_t sId) { return mServiceClients.find(sId) == mServiceClients.end(); } Result SubtitleServer::registerOnDataCB(int32_t sId) { auto ss = getSubtitleService(sId); SUBTITLE_LOGI("sId=%d", sId); if (!ss) { SUBTITLE_LOGE("didn't find service sId=%d", sId); return Result::FAIL; } ss->registerOnDataCB(); return Result::OK; } Result SubtitleServer::setDvbCountryCode(int32_t sId, int countryCode) { auto ss = getSubtitleService(sId); SUBTITLE_LOGI("sId=%d, countryCode = %d", sId, countryCode); if (!ss) { SUBTITLE_LOGE("didn't find service sId=%d, ", sId); return Result::FAIL; } char iso3166CountryCode[4] = {0}; iso3166CountryCode[0] = (char)((countryCode >> 16) & 0xFF); iso3166CountryCode[1] = (char)((countryCode >> 8) & 0xFF); iso3166CountryCode[2] = (char)(countryCode & 0xFF); iso3166CountryCode[3] = '\0'; bool ret = ss->setDvbCountryCode(std::string(iso3166CountryCode)); return ret ? Result::OK : Result::FAIL; } /* void SubtitleServer::debug(const hidl_handle& handle, const hidl_vec& options) { SUBTITLE_LOGI_FUNC_IN(); if (handle != nullptr && handle->numFds >= 1) { int fd = handle->data[0]; std::vector args; for (size_t i = 0; i < options.size(); i++) { args.push_back(options[i]); } dump(fd, args); fsync(fd); } return Void(); } void SubtitleServer::dump(int fd, const std::vector& args) { android::Mutex::Autolock lock(mLock); SUBTITLE_LOGI_FUNC_IN(); int len = args.size(); for (int i = 0; i < len; i ++) { std::string debugInfoAll("-a"); std::string debugLevel("-l"); std::string dumpraw("--dumpraw"); std::string dumpmemory("-m"); std::string help("-h"); if (args[i] == debugInfoAll) { //print all subtitle server runtime status(include subtitle ui) //we need write an api to get information from subtitle ui process: dprintf(fd, "\n"); } else if (args[i] == debugLevel) { if (i + 1 < len) { std::string levelStr(args[i+1]); int level = atoi(levelStr.c_str()); //setLogLevel(level); dprintf(fd, "Setting log level to %d.\n", level); break; } } else if (args[i] == dumpmemory) { if (!android::dumpMemoryAddresses(fd)) { dprintf(fd, "Cannot get malloc information!\n"); dprintf(fd, "Please executed this first[need su]:\n\n"); dprintf(fd, " setprop libc.debug.malloc.options backtrace\n"); dprintf(fd, " stop subtitleserver\n"); dprintf(fd, " start subtitleserver\n"); dprintf(fd, " setprop libc.debug.malloc.options \"\"\n\n\n"); } return; } else if (args[i] ==dumpraw) { mDumpMaps |= (1 << SUBTITLE_DUMP_SOURCE); dprintf(fd, "dump raw data source enabled.\n"); return; } else if (args[i] == help) { dprintf(fd, "subtitle server hwbinder service use to control the ctc/apk channel to subtitle ui \n" "usage: \n" "lshal debug vendor.amlogic.hardware.subtitleserver@1.0::ISubtitleServer/default -l value \n" "-a: dump all subtitle server and subtitle ui related information \n" "-l: set subtitle server debug level \n" "-m: dump memory alloc info [need restart subtitle with libc.debug.malloc.options=backtrace]" "-h: help \n"); return; } } //dump client: dprintf(fd, "\n\n HIDL Service: \n"); dprintf(fd, "--------------------------------------------------------------------------------------\n\n"); dprintf(fd, "Subtitile Clients:\n"); if (mFallbackCallback != nullptr) { dprintf(fd, " FallbackDisplayCallback: (%p)%s\n", mFallbackCallback.get(), toString(mFallbackCallback).c_str()); } int clientSize = mCallbackClients.size(); dprintf(fd, " EventCallback: count=%d\n", clientSize); for (int i = 0; i < clientSize; i++) { dprintf(fd, " %d: (%p)%s\n", i, mCallbackClients[i].get(), mCallbackClients[i]==nullptr ? "null" : toString(mCallbackClients[i]).c_str()); } // TODO: travsel each service. std::shared_ptr ss = getSubtitleServiceLocked(0); if (ss != nullptr) { ss->dump(fd); } } */ bool SubtitleServer::ClientMessageHandlerImpl::onSubtitleDisplayNotify(SubtitleHidlParcel &event) { SUBTITLE_LOGE("CallbackHandlerImpl onSubtitleDataEvent"); mSubtitleServer->sendSubtitleDisplayNotify(event); return false; } bool SubtitleServer::ClientMessageHandlerImpl::onSubtitleEventNotify(SubtitleHidlParcel &event) { SUBTITLE_LOGE("CallbackHandlerImpl onSubtitleDataEvent"); mSubtitleServer->sendSubtitleEventNotify(event); return false; } #if 0 void SubtitleServer::handleServiceDeath(uint32_t cookie) { SUBTITLE_LOGI_FUNC_IN(); { android::AutoMutex _l(mLock); /*if (cookie == FALLBACK_DISPLAY_COOKIE) { mFallbackDisplayClient->unlinkToDeath(mDeathRecipient); mFallbackDisplayClient = nullptr; } else */{ mCallbackClients[cookie]->unlinkToDeath(mDeathRecipient); mCallbackClients[cookie] = nullptr; } } // TODO: handle which client exited // currently, only support 1 subs //mSubtitleService->stopSubtitle(); //close(); } void SubtitleServer::DeathRecipient::serviceDied( uint64_t cookie, const ::android::wp<::android::hidl::base::V1_0::IBase>& who) { SUBTITLE_LOGE("subtitleserver daemon client died cookie:%d", (int)cookie); ::android::sp<::android::hidl::base::V1_0::IBase> s = who.promote(); SUBTITLE_LOGE("subtitleserver daemon client died who:%p", s.get()); if (s != nullptr) { auto r = s->interfaceDescriptor([&](const hidl_string &types) { SUBTITLE_LOGE("subtitleserver daemon client, who=%s", types.c_str()); }); if (!r.isOk()) { SUBTITLE_LOGE("why?"); } } uint32_t type = static_cast(cookie); mSubtitleServer->handleServiceDeath(type); } #endif // Methods from ::android::hidl::base::V1_0::IBase follow. // ISubtitleServer* HIDL_FETCH_ISubtitleServer(const char* /* name */) { // return new SubtitleServer(); // } // // } // namespace implementation // } // namespace V1_0 // } // namespace subtitleserver // } // namespace hardware // } // namespace amlogic // } // namespace vendor