/* * Copyright (C) 2014-present 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. */ #include #include #include #include #include #include #include "dbus_defines.h" #define LOG_MODULE_TAG "TV" #define LOG_CLASS_TAG "TvClient" #include "CTvLog.h" #include "TvClient.h" static sd_bus *gs_signal_bus = nullptr; bool tvclient_preset_signal_bus(sd_bus *bus) { if (nullptr == bus) { LOGE("invalid parameter\n"); return false; } if (nullptr != gs_signal_bus) { LOGE("signal bus already set\n"); return false; } gs_signal_bus = bus; return true; } static int gs_method_call_pipe[2]; static std::vector mTvClientObserver; static int SendEvent(CTvEvent &event) { for (const auto &o : mTvClientObserver) { if (o != nullptr) { o->onTvClientEvent(event); LOGD("send event for %p count TvClientObserver!\n", o); } } return 0; } static int signalCallback(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { const char *memb = sd_bus_message_get_member(msg); if (strcmp(memb, TV_SERVICE_SIGNAL_SIGNAL_DETECTED) == 0) { TvEvent::SignalDetectEvent e; int r = sd_bus_message_read(msg, "iiiiiu", &e.mSourceInput, &e.mFmt, &e.mTrans_fmt, &e.mStatus, &e.mDviFlag, &e.mhdr_info); if (r < 0) return r; SendEvent(e); } else if (strcmp(memb, TV_SERVICE_SIGNAL_SOURCE_CONNECTED) == 0) { TvEvent::SourceConnectEvent e; int r = sd_bus_message_read(msg, "ii", &e.mSourceInput, &e.connectionState); if (r < 0) return r; SendEvent(e); } else if (strcmp(memb, TV_SERVICE_SIGNAL_DV_ALLM_CHANGED) == 0) { TvEvent::SignalDvAllmEvent e; int r = sd_bus_message_read(msg, "iii", &e.allm_mode, &e.it_content, &e.cn_type); if (r < 0) return r; SendEvent(e); } else if (strcmp(memb, TV_SERVICE_SIGNAL_VRR_CHANGED) == 0) { TvEvent::SignalVrrEvent e; int r = sd_bus_message_read(msg, "i", &e.cur_vrr_status); if (r < 0) return r; SendEvent(e); } else if (strcmp(memb, TV_SERVICE_SIGNAL_QMS_CHANGED) == 0) { TvEvent::QMSEvent e; int r = sd_bus_message_read(msg, "iii", &e.qms_en, &e.qms_fps, &e.qms_base_fps); if (r < 0) return r; SendEvent(e); } else if (strcmp(memb, TV_SERVICE_SIGNAL_PIP_SIGNAL_DETECTED) == 0) { TvEvent::PipSignalDetectEvent e; int r = sd_bus_message_read(msg, "iiiiiu", &e.mSourceInput, &e.mFmt, &e.mTrans_fmt, &e.mStatus, &e.mDviFlag, &e.mhdr_info); if (r < 0) return r; SendEvent(e); } else { LOGE("invalid signal received!\n"); } return 0; } static void bus_cleanup(sd_bus *bus, sd_event *event) { if (event) { sd_event_exit(event, 0); sd_event_unref(event); event = nullptr; } if (bus) { sd_bus_unref(bus); bus = nullptr; } } using DeferCallbackHandler = std::function; struct defer_callback_data { sd_bus *bus; sd_event *event; const DeferCallbackHandler *handler; defer_callback_data(sd_bus *b, sd_event *e, const DeferCallbackHandler *h) : bus(b), event(e), handler(h) {} }; static int defer_callback(sd_event_source *, void *userdata) { auto data = static_cast(userdata); (*data->handler)(data->bus, data->event); delete data; return 0; } static void init_run_bus(DeferCallbackHandler callback) { std::thread t([=]() { sd_bus *bus = nullptr; sd_event *event = nullptr; int r = sd_bus_default_system(&bus); if (r < 0) { LOGE("Failed to open system bus: %s\n", strerror(-r)); return; } r = sd_event_default(&event); if (r < 0) { LOGE("Failed to create event: %s\n", strerror(-r)); sd_bus_unref(bus); return; } r = sd_bus_attach_event(bus, event, 0); if (r < 0) { LOGE("Failed to attach event: %s\n", strerror(-r)); sd_event_unref(event); sd_bus_unref(bus); return; } if (callback) { // Add a defer event r = sd_event_add_defer(event, nullptr, defer_callback, new defer_callback_data(bus, event, &callback)); if (r < 0) { LOGE("Failed to add defer event: %s\n", strerror(-r)); sd_event_unref(event); sd_bus_unref(bus); return; } } sd_event_loop(event); bus_cleanup(bus, event); }); t.detach(); } using onCallPrepareHandler = std::function; using onCallDoneHandler = std::function; struct MethodCallRequest { const char *method; onCallPrepareHandler *callPrepare; onCallDoneHandler *onCallDone; MethodCallRequest() : method(nullptr), callPrepare(nullptr), onCallDone(nullptr) {} MethodCallRequest(const char *m, onCallPrepareHandler *cp, onCallDoneHandler *ocd) : method(m), callPrepare(cp), onCallDone(ocd) {} }; static int on_call_done(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { auto handler = static_cast(userdata); const sd_bus_error *err = sd_bus_message_get_error(m); if (err) { LOGE("Failed to call: %s, %s(%s)\n", strerror(-sd_bus_error_get_errno(err)), err->name, err->message); return (*handler)(nullptr); } return (*handler)(m); } static int method_call_handler(sd_event_source *, int fd, uint32_t /*revents*/, void *userdata) { sd_bus *bus = (sd_bus *)userdata; MethodCallRequest req; int len = read(fd, &req, sizeof(req)); if (len != sizeof(req)) { LOGE("%d read, %d bytes expected\n", len, sizeof(req)); } else { if (req.method == nullptr) return 0; sd_bus_message *msg = nullptr; if (sd_bus_message_new_method_call( bus, &msg, TV_SERVICE_DBUS_SERVICE, TV_SERVICE_DBUS_OBJECT, TV_SERVICE_DBUS_INTERFACE, req.method) < 0) return 0; if (req.callPrepare && !(*req.callPrepare)(msg)) { sd_bus_message_unref(msg); msg = nullptr; return 0; } int r; if ((r = sd_bus_call_async(bus, nullptr, msg, on_call_done, req.onCallDone, 0)) < 0) { LOGE("Failed to async call: %s (%s)\n", req.method, strerror(-r)); (*req.onCallDone)(nullptr); } if (msg) { sd_bus_message_unref(msg); msg = nullptr; } } return 0; } static sd_bus_message * call_method(const char *member, std::function packer) { std::promise b; std::future f = b.get_future(); onCallPrepareHandler prepare( [=](sd_bus_message *msg) -> bool { return packer(msg); }); onCallDoneHandler done([&b](sd_bus_message *m) -> int { sd_bus_message_ref(m); b.set_value(m); return 0; }); MethodCallRequest req(member, packer ? &prepare : nullptr, &done); if (write(gs_method_call_pipe[1], &req, sizeof(req)) != sizeof(req)) { LOGE("Failed to write to pipe: %s\n", strerror(errno)); return nullptr; } sd_bus_message *msg = f.get(); return msg; } #define _cleanup_(x) __attribute__((__cleanup__(x))) #define _cleanup_bus_message_unref_ _cleanup_(sd_bus_message_unrefp) int TvClient::StartTv(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("StartTv", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::StopTv(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("StopTv", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::SwitchSource(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SwitchSource", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } tv_source_input_t TvClient::GetCurrentSource() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSource", nullptr); tv_source_input_t source; int r = sd_bus_message_read(m, "i", &source); if (r < 0) { return SOURCE_INVALID; } return source; } int TvClient::SetVdinWorkMode(vdin_work_mode_t vdinWorkMode) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetVdinWorkMode", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", vdinWorkMode); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::SetEdidVersion(tv_source_input_t source, int edidVer) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetEdidVersion", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "ii", source, edidVer); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetEdidVersion(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetEdidVersion", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::SetEdidData(tv_source_input_t source, char *dataBuf) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetEdidData", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append_array(msg, 'y', dataBuf, 256); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetEdidData(tv_source_input_t source, char *dataBuf) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetEdidData", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); const void *ptr; size_t size = 0; int ret = 0; int r = sd_bus_message_read(m, "i", &ret); if (r < 0) { return -1; } r = sd_bus_message_read_array(m, 'y', &ptr, &size); if (r < 0) { return -1; } memcpy(dataBuf, ptr, size > 256 ? 256 : size); return ret; } int TvClient::GetHdmiSPDInfo(tv_source_input_t source, char *dataBuf, size_t datalen) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiSPDInfo", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); const void *ptr; size_t size = 0; int ret = 0; int r = sd_bus_message_read(m, "i", &ret); if (r < 0) { return -1; } r = sd_bus_message_read_array(m, 'y', &ptr, &size); if (r < 0) { return -1; } memcpy(dataBuf, ptr, size > datalen ? datalen : size); return ret; } int TvClient::GetCurrentSourceFrameHeight() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceFrameHeight", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetCurrentSourceFrameWidth() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceFrameWidth", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetCurrentSourceFrameFps() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceFrameFps", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetCurrentSourceColorDepth() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceColorDepth", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } tvin_aspect_ratio_t TvClient::GetCurrentSourceAspectRatio() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceAspectRatio", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return (tvin_aspect_ratio_t)0; } return (tvin_aspect_ratio_t)result; } tvin_color_fmt_t TvClient::GetCurrentSourceColorFormat() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceColorFormat", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return (tvin_color_fmt_t)0; } return (tvin_color_fmt_t)result; } int TvClient::SetCurrentSourceColorRange(tvin_color_range_t range_mode) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method( "SetCurrentSourceColorRange", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", range_mode); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } tvin_color_range_t TvClient::GetCurrentSourceColorRange() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceColorRange", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return (tvin_color_range_t)0; } return (tvin_color_range_t)result; } tvin_line_scan_mode_t TvClient::GetCurrentSourceLineScanMode() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceLineScanMode", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return (tvin_line_scan_mode_t)0; } return (tvin_line_scan_mode_t)result; } int TvClient::GetSourceConnectStatus(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetSourceConnectStatus", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::SetEdidBoostOn(int bBoostOn) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetEdidBoostOn", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", bBoostOn); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::GetCurrentSourceAllmInfo(tvin_latency_s *info) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentSourceAllmInfo", nullptr); int r = sd_bus_message_read(m, "yyiy", &info->allm_mode, &info->it_content, &info->cn_type, &info->fmm_flag); if (r < 0) { return -1; } return 0; } int TvClient::SetHdmiAllmEnabled(bool enable) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetHdmiAllmEnabled", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "b", enable); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } bool TvClient::GetHdmiAllmEnabled() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiAllmEnabled", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } bool TvClient::GetHdmiVrrSupport() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiVrrSupport", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } int TvClient::SetHdmiVrrEnabled(bool enable) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetHdmiVrrEnabled", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "b", enable); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } bool TvClient::GetHdmiVrrEnabled() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiVrrEnabled", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } vdin_vrr_mode_t TvClient::GetHdmiVrrMode() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiVrrMode", nullptr); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return (vdin_vrr_mode_t)0; } return (vdin_vrr_mode_t)result; } bool TvClient::GetHdmiQmsSupport() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiQmsSupport", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } int TvClient::SetHdmiQmsEnabled(bool enable) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetHdmiQmsEnabled", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "b", enable); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } bool TvClient::GetHdmiQmsEnabled() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiQmsEnabled", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } int TvClient::GetCurrentStdInfo(struct tvin_std_s *info) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetCurrentStdInfo", nullptr); int r = sd_bus_message_read(m, "iiyyy", &info->source, &info->edid_version, &info->allm_enabled, &info->vrr_enabled, &info->qms_enabled); if (r < 0) { return -1; } return 0; } int TvClient::SetAudioDecoderId(int channel, int id) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetAudioDecoderId", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "ii", channel, id); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::SetAvAudioModeEnabled (bool enable) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetAvAudioModeEnabled", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "b", enable); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::StartTvInPIP(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("StartTvInPIP", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } int TvClient::StopTvInPIP(tv_source_input_t source) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("StopTvInPIP", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "i", source); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } bool TvClient::GetHdmiDlgSupport() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiDlgSupport", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } int TvClient::SetHdmiDlgEnabled(bool enable) { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("SetHdmiDlgEnabled", [&](sd_bus_message *msg) -> bool { int r = sd_bus_message_append(msg, "b", enable); if (r < 0) { return false; } return true; }); int result; int r = sd_bus_message_read(m, "i", &result); if (r < 0) { return -1; } return result; } bool TvClient::GetHdmiDlgEnabled() { _cleanup_bus_message_unref_ sd_bus_message *m = call_method("GetHdmiDlgEnabled", nullptr); int result; int r = sd_bus_message_read(m, "b", &result); if (r < 0) { return false; } return result; } TvClient::TvClient() { init_tv_logging(); if (gs_signal_bus) { int r = sd_bus_match_signal( gs_signal_bus, nullptr, TV_SERVICE_DBUS_SERVICE, TV_SERVICE_DBUS_OBJECT, TV_SERVICE_DBUS_INTERFACE, nullptr, signalCallback, nullptr); if (r < 0) { LOGE("Failed to match signal: %s\n", strerror(-r)); } } else { init_run_bus([](sd_bus *bus, sd_event *) { int r = sd_bus_match_signal( bus, nullptr, TV_SERVICE_DBUS_SERVICE, TV_SERVICE_DBUS_OBJECT, TV_SERVICE_DBUS_INTERFACE, nullptr, signalCallback, nullptr); if (r < 0) { LOGE("Failed to match signal: %s\n", strerror(-r)); } }); } std::promise b; std::future f = b.get_future(); init_run_bus([&](sd_bus *bus, sd_event *event) { if (pipe(gs_method_call_pipe) < 0) { LOGE("pipe() failed: %s\n", strerror(errno)); return; } sd_event_add_io(event, nullptr, gs_method_call_pipe[0], POLLIN, method_call_handler, bus); try { b.set_value(true); } catch (const std::future_error &e) { LOGE("%s\n", e.what()); } }); f.get(); } TvClient::~TvClient() { LOGD("%s: TvClient has been destructed.\n", __FUNCTION__); } int TvClient::setTvClientObserver(TvClientIObserver *observer) { if (observer != nullptr) { mTvClientObserver.emplace_back(observer); LOGD("%s: mTvClientObserver[%p] has been register.\n", __FUNCTION__, observer); } else { LOGD("%s: observer is NULL.\n", __FUNCTION__); } return 0; } TvClient *TvClient::GetInstance() { static std::shared_ptr inst_; if (inst_.get() == nullptr) { inst_ = std::shared_ptr(new TvClient(), [](TvClient *ptr) { delete ptr; }); } return inst_.get(); } void TvClient::Release() {}