1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "camera_dump.h"
17 
18 #include <sys/time.h>
19 #include <fstream>
20 #include <sstream>
21 #include <cstdio>
22 #include <iostream>
23 #include <cstdlib>
24 #include <regex>
25 
26 #include "camera.h"
27 #include "v1_0/vdi_types.h"
28 
29 using namespace std;
30 
31 namespace OHOS::Camera {
32 const std::string DUMP_PATH = "/data/local/tmp/";
33 const std::string DUMP_CONFIG_PATH = "/data/local/tmp/dump.config";
34 constexpr uint32_t ARGS_MAX_NUM = 5;
35 constexpr uint32_t MAX_USAGE_RATE = 70;
36 constexpr int32_t CHECK_DISKINFO_TIME_MS = 10000;
37 const uint32_t TITLEINFO_ARRAY_SIZE = 200;
38 
39 const char *g_cameraDumpHelp =
40     " Camera manager dump options:\n"
41     "     -h: camera dump help\n"
42     "     -m: start dump metadata\n"
43     "     -b: start dump buffer\n"
44     "     -o: start dump start\n"
45     "     -e: exit all dump\n";
46 
47 std::map<DumpType, bool> g_dumpInfoMap = {
48     {MedataType, false},
49     {BufferType, false},
50     {OpenType, false}
51 };
52 
53 std::map<std::string, std::string> g_dumpToolMap = {
54     {ENABLE_DQ_BUFFER_DUMP, "false"},
55     {ENABLE_UVC_NODE, "false"},
56     {ENABLE_UVC_NODE_CONVERTED, "false"},
57     {ENABLE_EXIF_NODE_CONVERTED, "false"},
58     {ENABLE_FACE_NODE_CONVERTED, "false"},
59     {ENABLE_FORK_NODE_CONVERTED, "false"},
60     {ENABLE_RKFACE_NODE_CONVERTED, "false"},
61     {ENABLE_RKEXIF_NODE_CONVERTED, "false"},
62     {ENABLE_CODEC_NODE_CONVERTED, "false"},
63     {ENABLE_RKCODEC_NODE_CONVERTED, "false"},
64     {ENABLE_STREAM_TUNNEL, "false"},
65     {ENABLE_METADATA, "false"},
66     {PREVIEW_INTERVAL, "1"},
67     {CAPTURE_INTERVAL, "1"}
68 };
69 
~CameraDumper()70 CameraDumper::~CameraDumper()
71 {
72     StopCheckDiskInfo();
73 }
74 
DumpStart()75 bool CameraDumper::DumpStart()
76 {
77     if (!IsDumpOpened(OpenType)) {
78         return false;
79     }
80     std::stringstream mkdirCmd;
81     mkdirCmd << "mkdir -p " << DUMP_PATH;
82     system(mkdirCmd.str().c_str());
83 
84     ReadDumpConfig();
85     return true;
86 }
87 
ReadDumpConfig()88 bool CameraDumper::ReadDumpConfig()
89 {
90     std::stringstream ss;
91     ss << DUMP_CONFIG_PATH;
92     std::ifstream ifs;
93     ifs.open(ss.str(), std::ios::in);
94     if (!ifs) {
95         CAMERA_LOGE("open dump config file <%{public}s> failed, error: %{public}s",
96             ss.str().c_str(), std::strerror(errno));
97         return false;
98     }
99 
100     std::string str;
101     while (!ifs.eof()) {
102         if (ifs >> str) {
103             istringstream istr(str);
104             std::string strTemp;
105             vector<std::string> strVector;
106             while (getline(istr, strTemp, '=')) {
107                 strVector.push_back(strTemp);
108             }
109             g_dumpToolMap[strVector[0]] = strVector[1];
110         }
111     }
112 
113     ifs.close();
114     return true;
115 }
116 
IsDumpCommandOpened(std::string type)117 bool CameraDumper::IsDumpCommandOpened(std::string type)
118 {
119     std::lock_guard<std::mutex> l(dumpStateLock_);
120     if (g_dumpToolMap.find(type) != g_dumpToolMap.end() && g_dumpToolMap[type] == "true") {
121         return true;
122     }
123     return false;
124 }
125 
DumpBuffer(std::string name,std::string type,const std::shared_ptr<IBuffer> & buffer,uint32_t width,uint32_t height)126 bool CameraDumper::DumpBuffer(std::string name, std::string type, const std::shared_ptr<IBuffer>& buffer,
127     uint32_t width, uint32_t height)
128 {
129     if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type) || (buffer == nullptr)) {
130         return false;
131     }
132     uint32_t defaultWidth = (width == 0) ? buffer->GetCurWidth() : width;
133     uint32_t defaultHeight = (height == 0) ? buffer->GetCurHeight() : height;
134     void* srcAddr = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferAddr() : buffer->GetVirAddress();
135     uint32_t size = buffer->GetIsValidDataInSurfaceBuffer() ? buffer->GetSuffaceBufferSize() : buffer->GetSize();
136     const std::string DqBufferName = "DQBuffer";
137     if (name != DqBufferName) {
138         size = buffer->GetEsFrameInfo().size > 0 ? buffer->GetEsFrameInfo().size : size;
139     }
140 
141     std::stringstream ss;
142     std::string fileName;
143     ss << name.c_str() << "_captureId[" << buffer->GetCaptureId() << "]_streamId[" << buffer->GetStreamId() <<
144         "]_width[" << defaultWidth << "]_height[" << defaultHeight;
145 
146     int32_t previewInterval = 1;
147     std::istringstream ssPreview(g_dumpToolMap[PREVIEW_INTERVAL]);
148     ssPreview >> previewInterval;
149 
150     int32_t captureInterval = 1;
151     std::istringstream ssVideo(g_dumpToolMap[CAPTURE_INTERVAL]);
152     ssVideo >> captureInterval;
153 
154     ++dumpCount_;
155     if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_JPEG) {
156         if (dumpCount_ % captureInterval != 0) {
157             return true;
158         }
159         ss << "]_" << GetCurrentLocalTimeStamp();
160         ss >> fileName;
161         fileName += ".jpeg";
162     } else if (buffer->GetEncodeType() == VDI::Camera::V1_0::ENCODE_TYPE_H264) {
163 #ifdef CAMERA_BUILT_ON_USB
164         ss << "]_" << GetCurrentLocalTimeStamp();
165         ss >> fileName;
166         fileName += "_umpVideo.yuv";
167 #else
168         fileName += "dumpVideo.h264";
169 #endif
170     } else {
171         if (dumpCount_ % previewInterval != 0) {
172             return true;
173         }
174 
175         ss << "]_" << GetCurrentLocalTimeStamp();
176         ss >> fileName;
177         fileName += ".yuv";
178     }
179     return SaveDataToFile(fileName.c_str(), srcAddr, size);
180 }
181 
DumpMetadata(std::string name,std::string type,const std::shared_ptr<CameraMetadata> & metadata)182 bool CameraDumper::DumpMetadata(std::string name, std::string type,
183     const std::shared_ptr<CameraMetadata>& metadata)
184 {
185     if (metadata == nullptr) {
186         CAMERA_LOGE("metadata is nullptr");
187         return false;
188     }
189 
190     if (!IsDumpOpened(OpenType) || !IsDumpCommandOpened(type)) {
191         return false;
192     }
193 
194     common_metadata_header_t *data = metadata->get();
195     if (data == nullptr) {
196         CAMERA_LOGE("data is nullptr");
197         return false;
198     }
199     std::string metaStr = FormatCameraMetadataToString(data);
200     if (metaStr.size() == 0) {
201         CAMERA_LOGE("metaStr.size is 0");
202         return true;
203     }
204     std::stringstream ss;
205     ss << GetCurrentLocalTimeStamp() << "_" << name << ".meta";
206 
207     return SaveDataToFile(ss.str().c_str(), metaStr.c_str(), metaStr.size());
208 }
209 
UpdateDumpMode(DumpType type,bool isDump,HdfSBuf * reply)210 void CameraDumper::UpdateDumpMode(DumpType type, bool isDump, HdfSBuf *reply)
211 {
212     std::string upRetStr;
213     {
214         std::lock_guard<std::mutex> l(dumpStateLock_);
215         auto it = g_dumpInfoMap.find(type);
216         if (it != g_dumpInfoMap.end()) {
217             g_dumpInfoMap[type] = isDump;
218             upRetStr += " set dump mode success!\n";
219         }
220     }
221 
222     if (reply != nullptr) {
223         (void)HdfSbufWriteString(reply, upRetStr.c_str());
224     }
225 
226     if (isDump) {
227         StartCheckDiskInfo();
228     } else {
229         StopCheckDiskInfo();
230     }
231 }
232 
IsDumpOpened(DumpType type)233 bool CameraDumper::IsDumpOpened(DumpType type)
234 {
235     std::lock_guard<std::mutex> l(dumpStateLock_);
236     if (g_dumpInfoMap.find(type) != g_dumpInfoMap.end() && g_dumpInfoMap[type]) {
237         return true;
238     }
239     return false;
240 }
241 
SaveDataToFile(const char * fileName,const void * data,uint32_t size)242 bool CameraDumper::SaveDataToFile(const char *fileName, const void *data, uint32_t size)
243 {
244     CAMERA_LOGI("save dump file <%{public}s> begin, size: %{public}d", fileName, size);
245     std::stringstream mkdirCmd;
246     mkdirCmd << "mkdir -p " << DUMP_PATH;
247     system(mkdirCmd.str().c_str());
248 
249     std::stringstream ss;
250     ss << DUMP_PATH << fileName;
251     std::ofstream ofs(ss.str(), std::ios::app);
252 
253     if (!ofs.good()) {
254         CAMERA_LOGE("open dump file <%{public}s> failed, error: %{public}s", ss.str().c_str(), std::strerror(errno));
255         return false;
256     }
257 
258     ofs.write(static_cast<const char *>(data), size);
259     ofs.close();
260 
261     return true;
262 }
263 
GetCurrentLocalTimeStamp()264 uint64_t CameraDumper::GetCurrentLocalTimeStamp()
265 {
266     std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
267         std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
268     auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
269     return static_cast<uint64_t>(tmp.count());
270 }
271 
ShowDumpMenu(HdfSBuf * reply)272 void CameraDumper::ShowDumpMenu(HdfSBuf *reply)
273 {
274     if (reply != nullptr) {
275         (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
276     }
277 }
278 
CameraHostDumpProcess(HdfSBuf * data,HdfSBuf * reply)279 void CameraDumper::CameraHostDumpProcess(HdfSBuf *data, HdfSBuf *reply)
280 {
281     if (data == nullptr || reply == nullptr) {
282         CAMERA_LOGE("%{public}s is nullptr", (data == nullptr) ? "data" : "reply");
283         return;
284     }
285 
286     uint32_t argsNum;
287     if (!HdfSbufReadUint32(data, &argsNum)) {
288         CAMERA_LOGE("read argsNum failed!");
289         return;
290     }
291 
292     if (argsNum <= 0 || argsNum > ARGS_MAX_NUM) {
293         (void)HdfSbufWriteString(reply, g_cameraDumpHelp);
294         return;
295     }
296 
297     for (uint32_t i = 0; i < argsNum; i++) {
298         const char *value = HdfSbufReadString(data);
299         if (value == NULL) {
300             CAMERA_LOGE("arg is invalid i: %{public}u", i);
301             return;
302         }
303         if (strcmp(value, "-m") == 0) {
304             UpdateDumpMode(MedataType, true, reply);
305         } else if (strcmp(value, "-b") == 0) {
306             UpdateDumpMode(BufferType, true, reply);
307         } else if (strcmp(value, "-e") == 0) {
308             UpdateDumpMode(BufferType, false, reply);
309             UpdateDumpMode(MedataType, false, reply);
310         } else if (strcmp(value, "-o") == 0) {
311             UpdateDumpMode(OpenType, true, reply);
312         } else {
313             ShowDumpMenu(reply);
314         }
315     }
316 }
317 
CameraDumpEvent(HdfSBuf * data,HdfSBuf * reply)318 int32_t CameraDumpEvent(HdfSBuf *data, HdfSBuf *reply)
319 {
320     CameraDumper& dumper = CameraDumper::GetInstance();
321     dumper.CameraHostDumpProcess(data, reply);
322     return HDF_SUCCESS;
323 }
324 
CheckDiskInfo()325 void CameraDumper::CheckDiskInfo()
326 {
327     stringstream ss;
328     ss << "df " << DUMP_PATH;
329 
330     FILE *fp = popen(ss.str().c_str(), "r");
331     if (fp == NULL) {
332         CAMERA_LOGE("popen failed, cmd : %{public}s", ss.str().c_str());
333         return;
334     }
335 
336     char titleInfo[TITLEINFO_ARRAY_SIZE] = {0};
337     char resultInfo[TITLEINFO_ARRAY_SIZE] = {0};
338     fgets(titleInfo, sizeof(titleInfo) / sizeof(titleInfo[0]) - 1, fp);
339     fgets(resultInfo, sizeof(resultInfo) / sizeof(resultInfo[0]) - 1, fp);
340 
341     pclose(fp);
342 
343     std::string diskInfoStr(resultInfo);
344     istringstream str(diskInfoStr);
345     string out;
346     std::vector<std::string> infos;
347 
348     while (str >> out) {
349         infos.push_back(out);
350     }
351 
352     std::string userPerStr = infos[4].substr(0, infos[4].length() - 1);
353     uint32_t usePer = std::atoi(userPerStr.c_str());
354     if (usePer >= MAX_USAGE_RATE) {
355         CAMERA_LOGE("dump use disk over the limit, stop dump");
356         std::lock_guard<std::mutex> l(dumpStateLock_);
357         for (auto it = g_dumpInfoMap.begin(); it != g_dumpInfoMap.end(); it++) {
358             it->second = false;
359         }
360     }
361 }
362 
ThreadWorkFun()363 void CameraDumper::ThreadWorkFun()
364 {
365     while (true) {
366         CheckDiskInfo();
367 
368         std::unique_lock<std::mutex> l(terminateLock_);
369         cv_.wait_for(l, std::chrono::milliseconds(CHECK_DISKINFO_TIME_MS),
370             [this]() {
371                 return terminate_;
372             }
373         );
374 
375         if (terminate_) {
376             break;
377         }
378     }
379 }
380 
StartCheckDiskInfo()381 void CameraDumper::StartCheckDiskInfo()
382 {
383     {
384         std::unique_lock<std::mutex> l(terminateLock_);
385         if (terminate_ == false) {
386             CAMERA_LOGD("thread is already start");
387             return;
388         }
389         terminate_ = false;
390     }
391 
392     handleThread_ = std::make_unique<std::thread>([this] { this->ThreadWorkFun(); });
393 }
394 
StopCheckDiskInfo()395 void CameraDumper::StopCheckDiskInfo()
396 {
397     {
398         std::unique_lock<std::mutex> l(terminateLock_);
399         if (terminate_ == true) {
400             CAMERA_LOGD("thread is already stop");
401             return;
402         }
403         terminate_ = true;
404         cv_.notify_one();
405     }
406     if (handleThread_ != nullptr && handleThread_->joinable()) {
407         handleThread_->join();
408         handleThread_ = nullptr;
409     }
410 }
411 
GetInstance()412 CameraDumper& CameraDumper::GetInstance()
413 {
414     static CameraDumper instance_;
415     return instance_;
416 }
417 } // namespace OHOS::Camera
418