• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "android.hardware.tv.hdmi.connection"
18 #include "HdmiConnectionMock.h"
19 #include <android-base/logging.h>
20 #include <fcntl.h>
21 #include <utils/Log.h>
22 
23 using ndk::ScopedAStatus;
24 
25 namespace android {
26 namespace hardware {
27 namespace tv {
28 namespace hdmi {
29 namespace connection {
30 namespace implementation {
31 
serviceDied(void * cookie)32 void HdmiConnectionMock::serviceDied(void* cookie) {
33     ALOGE("HdmiConnectionMock died");
34     auto hdmi = static_cast<HdmiConnectionMock*>(cookie);
35     hdmi->mHdmiThreadRun = false;
36     pthread_join(hdmi->mThreadId, NULL);
37 }
38 
getPortInfo(std::vector<HdmiPortInfo> * _aidl_return)39 ScopedAStatus HdmiConnectionMock::getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) {
40     *_aidl_return = mPortInfos;
41     return ScopedAStatus::ok();
42 }
43 
isConnected(int32_t portId,bool * _aidl_return)44 ScopedAStatus HdmiConnectionMock::isConnected(int32_t portId, bool* _aidl_return) {
45     // Maintain port connection status and update on hotplug event
46     if (portId <= mTotalPorts && portId >= 1) {
47         *_aidl_return = mPortConnectionStatus.at(portId - 1);
48     } else {
49         *_aidl_return = false;
50     }
51 
52     return ScopedAStatus::ok();
53 }
54 
setCallback(const std::shared_ptr<IHdmiConnectionCallback> & callback)55 ScopedAStatus HdmiConnectionMock::setCallback(
56         const std::shared_ptr<IHdmiConnectionCallback>& callback) {
57     if (mCallback != nullptr) {
58         stopThread();
59         mCallback = nullptr;
60     }
61     if (callback != nullptr) {
62         mCallback = callback;
63         mDeathRecipient =
64                 ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
65 
66         AIBinder_linkToDeath(callback->asBinder().get(), mDeathRecipient.get(), this /* cookie */);
67 
68         mInputFile = open(HDMI_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
69         pthread_create(&mThreadId, NULL, __threadLoop, this);
70         pthread_setname_np(mThreadId, "hdmi_loop");
71     }
72     return ScopedAStatus::ok();
73 }
74 
setHpdSignal(HpdSignal signal,int32_t portId)75 ScopedAStatus HdmiConnectionMock::setHpdSignal(HpdSignal signal, int32_t portId) {
76     if (portId > mTotalPorts || portId < 1) {
77         return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
78     }
79     if (!mHdmiThreadRun) {
80         return ScopedAStatus::fromServiceSpecificError(
81                 static_cast<int32_t>(Result::FAILURE_INVALID_STATE));
82     }
83     mHpdSignal.at(portId - 1) = signal;
84     return ScopedAStatus::ok();
85 }
86 
getHpdSignal(int32_t portId,HpdSignal * _aidl_return)87 ScopedAStatus HdmiConnectionMock::getHpdSignal(int32_t portId, HpdSignal* _aidl_return) {
88     if (portId > mTotalPorts || portId < 1) {
89         return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
90     }
91     *_aidl_return = mHpdSignal.at(portId - 1);
92     return ScopedAStatus::ok();
93 }
94 
__threadLoop(void * user)95 void* HdmiConnectionMock::__threadLoop(void* user) {
96     HdmiConnectionMock* const self = static_cast<HdmiConnectionMock*>(user);
97     self->threadLoop();
98     return 0;
99 }
100 
readMessageFromFifo(unsigned char * buf,int msgCount)101 int HdmiConnectionMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
102     if (msgCount <= 0 || !buf) {
103         return 0;
104     }
105 
106     int ret = -1;
107     // Maybe blocked at driver
108     ret = read(mInputFile, buf, msgCount);
109     if (ret < 0) {
110         ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", HDMI_MSG_IN_FIFO, ret);
111         return -1;
112     }
113 
114     return ret;
115 }
116 
printEventBuf(const char * msg_buf,int len)117 void HdmiConnectionMock::printEventBuf(const char* msg_buf, int len) {
118     int i, size = 0;
119     const int bufSize = MESSAGE_BODY_MAX_LENGTH * 3;
120     // Use 2 characters for each byte in the message plus 1 space
121     char buf[bufSize] = {0};
122 
123     // Messages longer than max length will be truncated.
124     for (i = 0; i < len && size < bufSize; i++) {
125         size += sprintf(buf + size, " %02x", msg_buf[i]);
126     }
127     ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
128 }
129 
handleHotplugMessage(unsigned char * msgBuf)130 void HdmiConnectionMock::handleHotplugMessage(unsigned char* msgBuf) {
131     bool connected = ((msgBuf[3]) & 0xf) > 0;
132     int32_t portId = static_cast<uint32_t>(msgBuf[0] & 0xf);
133 
134     if (portId > static_cast<int32_t>(mPortInfos.size()) || portId < 1) {
135         ALOGD("[halimp_aidl] ignore hot plug message, id %x does not exist", portId);
136         return;
137     }
138 
139     ALOGD("[halimp_aidl] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf),
140           (msgBuf[3] & 0xf));
141     mPortConnectionStatus.at(portId - 1) = connected;
142     if (mPortInfos.at(portId - 1).type == HdmiPortType::OUTPUT) {
143         mPhysicalAddress = (connected ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2])));
144         mPortInfos.at(portId - 1).physicalAddress = mPhysicalAddress;
145         ALOGD("[halimp_aidl] hot plug physical address %x", mPhysicalAddress);
146     }
147 
148     if (mCallback != nullptr) {
149         mCallback->onHotplugEvent(connected, portId);
150     }
151 }
152 
threadLoop()153 void HdmiConnectionMock::threadLoop() {
154     ALOGD("[halimp_aidl] threadLoop start.");
155     unsigned char msgBuf[MESSAGE_BODY_MAX_LENGTH];
156     int r = -1;
157 
158     // Open the input pipe
159     while (mHdmiThreadRun && mInputFile < 0) {
160         usleep(1000 * 1000);
161         mInputFile = open(HDMI_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
162     }
163     ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
164 
165     while (mHdmiThreadRun) {
166         memset(msgBuf, 0, sizeof(msgBuf));
167         // Try to get a message from dev.
168         // echo -n -e '\x04\x83' >> /dev/cec
169         r = readMessageFromFifo(msgBuf, MESSAGE_BODY_MAX_LENGTH);
170         if (r <= 1) {
171             // Ignore received ping messages
172             continue;
173         }
174 
175         printEventBuf((const char*)msgBuf, r);
176 
177         if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
178             handleHotplugMessage(msgBuf);
179         }
180     }
181 
182     ALOGD("[halimp_aidl] thread end.");
183 }
184 
HdmiConnectionMock()185 HdmiConnectionMock::HdmiConnectionMock() {
186     ALOGE("[halimp_aidl] Opening a virtual HDMI HAL for testing and virtual machine.");
187     mCallback = nullptr;
188     mPortInfos.resize(mTotalPorts);
189     mPortConnectionStatus.resize(mTotalPorts);
190     mHpdSignal.resize(mTotalPorts);
191     mPortInfos[0] = {.type = HdmiPortType::OUTPUT,
192                      .portId = static_cast<uint32_t>(1),
193                      .cecSupported = true,
194                      .arcSupported = false,
195                      .eArcSupported = false,
196                      .physicalAddress = mPhysicalAddress};
197     mPortConnectionStatus[0] = false;
198     mHpdSignal[0] = HpdSignal::HDMI_HPD_PHYSICAL;
199     mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
200 }
201 
stopThread()202 void HdmiConnectionMock::stopThread() {
203     if (mCallback != nullptr) {
204         ALOGE("[halimp_aidl] HdmiConnectionMock shutting down.");
205         mCallback = nullptr;
206         mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
207         mHdmiThreadRun = false;
208         pthread_join(mThreadId, NULL);
209     }
210 }
211 
~HdmiConnectionMock()212 HdmiConnectionMock::~HdmiConnectionMock() {
213     stopThread();
214 }
215 
216 }  // namespace implementation
217 }  // namespace connection
218 }  // namespace hdmi
219 }  // namespace tv
220 }  // namespace hardware
221 }  // namespace android
222