1 /*
2 * Copyright (c) 2022-2024 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 "input_display_bind_helper.h"
17
18 #include <algorithm>
19 #include <cctype>
20 #include <filesystem>
21 #include <fstream>
22 #include <iostream>
23 #include <list>
24 #include <sstream>
25
26 #include "mmi_log.h"
27 #include "parameters.h"
28 #include "util.h"
29
30 #undef MMI_LOG_DOMAIN
31 #define MMI_LOG_DOMAIN MMI_LOG_WINDOW
32 #undef MMI_LOG_TAG
33 #define MMI_LOG_TAG "InputDisplayBindHelper"
34
35 namespace OHOS {
36 namespace MMI {
37 namespace {
38 const std::string FOLD_SCREEN_FLAG = system::GetParameter("const.window.foldscreen.type", "");
39 const std::string INPUT_DEVICE_NAME_CONFIG { "/sys_prod/etc/input/input_device_name.cfg" };
40 const std::string DIRECTORY { "/sys/devices/virtual/input" };
41 const std::string SEPARATOR { "/" };
42 const std::string SUFFIX { "0000:0000" };
43 const std::string INPUT { "input" };
44 const std::string EVENT { "event" };
45 const std::string NAME { "name" };
46 const int32_t DISPLAY_ID_MAIN { 0 };
47 const int32_t DISPLAY_ID_SUB { 5 };
48 }
49
50 namespace fs = std::filesystem;
51
IsDualDisplayFoldDevice()52 static bool IsDualDisplayFoldDevice()
53 {
54 return (!FOLD_SCREEN_FLAG.empty() && FOLD_SCREEN_FLAG[0] == '2');
55 }
56
GetInputDeviceId() const57 int32_t BindInfo::GetInputDeviceId() const
58 {
59 return inputDeviceId_;
60 }
61
GetInputDeviceName() const62 std::string BindInfo::GetInputDeviceName() const
63 {
64 return inputDeviceName_;
65 }
66
GetDisplayId() const67 int32_t BindInfo::GetDisplayId() const
68 {
69 return displayId_;
70 }
71
GetDisplayName() const72 std::string BindInfo::GetDisplayName() const
73 {
74 return displayName_;
75 }
76
IsUnbind() const77 bool BindInfo::IsUnbind() const
78 {
79 return ((inputDeviceId_ == -1) || (displayId_ == -1));
80 }
81
InputDeviceNotBind() const82 bool BindInfo::InputDeviceNotBind() const
83 {
84 return (inputDeviceId_ == -1);
85 }
86
DisplayNotBind() const87 bool BindInfo::DisplayNotBind() const
88 {
89 return (displayId_ == -1);
90 }
91
AddInputDevice(int32_t deviceId,const std::string & deviceName)92 bool BindInfo::AddInputDevice(int32_t deviceId, const std::string &deviceName)
93 {
94 if ((inputDeviceId_ != -1) || !inputDeviceName_.empty()) {
95 return false;
96 }
97 inputDeviceId_ = deviceId;
98 inputDeviceName_ = deviceName;
99 return true;
100 }
101
RemoveInputDevice()102 void BindInfo::RemoveInputDevice()
103 {
104 inputDeviceId_ = -1;
105 inputDeviceName_.clear();
106 }
107
AddDisplay(int32_t id,const std::string & name)108 bool BindInfo::AddDisplay(int32_t id, const std::string &name)
109 {
110 if ((displayId_ != -1) || !displayName_.empty()) {
111 return false;
112 }
113 displayId_ = id;
114 displayName_ = name;
115 return true;
116 }
117
RemoveDisplay()118 void BindInfo::RemoveDisplay()
119 {
120 displayId_ = -1;
121 displayName_.clear();
122 }
123
GetDesc() const124 std::string BindInfo::GetDesc() const
125 {
126 std::ostringstream oss;
127 oss << "InputDevice(id:" << inputDeviceId_ << ",name:" << inputDeviceName_ << "),Display(id:" << displayId_ <<
128 ",name:" << displayName_ << ")";
129 return oss.str();
130 }
131
operator <(const BindInfo & l,const BindInfo & r)132 bool operator < (const BindInfo &l, const BindInfo &r)
133 {
134 if (l.inputDeviceId_ != r.inputDeviceId_) {
135 return (l.inputDeviceId_ < r.inputDeviceId_);
136 }
137 return (l.displayId_ < r.displayId_);
138 }
139
operator <<(std::ostream & os,const BindInfo & r)140 std::ostream &operator << (std::ostream &os, const BindInfo &r)
141 {
142 os << r.inputDeviceName_ << "<=>" << r.displayName_ << std::endl;
143 return os;
144 }
145
operator >>(std::istream & is,BindInfo & r)146 std::istream &operator >> (std::istream &is, BindInfo &r)
147 {
148 std::string line;
149 std::getline(is, line);
150 const std::string delim = "<=>";
151 std::string::size_type pos = line.find(delim);
152 if (pos == std::string::npos) {
153 return is;
154 }
155 r.inputDeviceName_ = line.substr(0, pos);
156 r.displayName_ = line.substr(pos + delim.length());
157 r.inputDeviceId_ = 0;
158 r.displayId_ = 0;
159 return is;
160 }
161
GetDesc() const162 std::string BindInfos::GetDesc() const
163 {
164 int32_t index = 0;
165 std::ostringstream oss;
166 for (const auto &info : infos_) {
167 oss << "index:" << index << "," << info.GetDesc() << std::endl;
168 }
169 return oss.str();
170 }
171
GetInfos() const172 const std::list<BindInfo> &BindInfos::GetInfos() const
173 {
174 return infos_;
175 }
176
GetBindDisplayIdByInputDevice(int32_t inputDeviceId) const177 int32_t BindInfos::GetBindDisplayIdByInputDevice(int32_t inputDeviceId) const
178 {
179 for (const auto &item : infos_) {
180 if (item.GetInputDeviceId() == inputDeviceId) {
181 if (!item.IsUnbind()) {
182 return item.GetDisplayId();
183 }
184 }
185 }
186 return -1;
187 }
188
GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const189 std::string BindInfos::GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const
190 {
191 for (const auto &item : infos_) {
192 if (item.GetInputDeviceId() == inputDeviceId) {
193 if (!item.IsUnbind()) {
194 return item.GetDisplayName();
195 }
196 }
197 }
198 return "";
199 }
200
GetDisplayNameByInputDevice(const std::string & name) const201 std::string BindInfos::GetDisplayNameByInputDevice(const std::string &name) const
202 {
203 for (const auto &item : infos_) {
204 if (item.GetInputDeviceName() == name) {
205 return item.GetDisplayName();
206 }
207 }
208 return "";
209 }
210
GetInputDeviceByDisplayName(const std::string & name) const211 std::string BindInfos::GetInputDeviceByDisplayName(const std::string &name) const
212 {
213 for (const auto &item : infos_) {
214 if (item.GetDisplayName() == name) {
215 return item.GetInputDeviceName();
216 }
217 }
218 return "";
219 }
220
Add(const BindInfo & info)221 bool BindInfos::Add(const BindInfo &info)
222 {
223 auto it = infos_.begin();
224 for (; it != infos_.end(); ++it) {
225 if (info < *it) {
226 break;
227 }
228 }
229 auto it2 = infos_.emplace(it, std::move(info));
230 if (it2 == infos_.end()) {
231 MMI_HILOGE("Duplicate %{public}s", info.GetDesc().c_str());
232 }
233 return true;
234 }
235
UnbindInputDevice(int32_t deviceId)236 void BindInfos::UnbindInputDevice(int32_t deviceId)
237 {
238 auto it = infos_.begin();
239 for (; it != infos_.end(); ++it) {
240 if (it->GetInputDeviceId() == deviceId) {
241 it->RemoveInputDevice();
242 infos_.erase(it);
243 return;
244 }
245 }
246 }
247
UnbindDisplay(int32_t displayId)248 void BindInfos::UnbindDisplay(int32_t displayId)
249 {
250 auto it = infos_.begin();
251 for (; it != infos_.end(); ++it) {
252 if (it->GetDisplayId() == displayId) {
253 it->RemoveDisplay();
254 infos_.erase(it);
255 return;
256 }
257 }
258 }
259
GetUnbindInputDevice(const std::string & displayName)260 BindInfo BindInfos::GetUnbindInputDevice(const std::string &displayName)
261 {
262 auto it = infos_.begin();
263 while (it != infos_.end()) {
264 if (it->InputDeviceNotBind()) {
265 if (it->GetDisplayName() == displayName) {
266 auto info = std::move(*it);
267 infos_.erase(it);
268 return info;
269 }
270 }
271 ++it;
272 }
273 return BindInfo();
274 }
275
GetUnbindDisplay()276 BindInfo BindInfos::GetUnbindDisplay()
277 {
278 auto it = infos_.begin();
279 while (it != infos_.end()) {
280 if (it->DisplayNotBind()) {
281 auto info = std::move(*it);
282 infos_.erase(it);
283 return info;
284 }
285 ++it;
286 }
287 return BindInfo();
288 }
289
GetUnbindDisplay(const std::string & inputDeviceName)290 BindInfo BindInfos::GetUnbindDisplay(const std::string &inputDeviceName)
291 {
292 auto it = infos_.begin();
293 while (it != infos_.end()) {
294 if (it->DisplayNotBind()) {
295 if (it->GetInputDeviceName() == inputDeviceName) {
296 auto info = std::move(*it);
297 infos_.erase(it);
298 return info;
299 }
300 }
301 ++it;
302 }
303 return GetUnbindDisplay();
304 }
305
operator <<(std::ostream & os,const BindInfos & r)306 std::ostream &operator << (std::ostream &os, const BindInfos &r)
307 {
308 const auto &infos = r.GetInfos();
309 for (const auto &info : infos) {
310 if (!info.IsUnbind()) {
311 os << info;
312 }
313 }
314 return os;
315 }
316
operator >>(std::istream & is,BindInfos & r)317 std::istream &operator >> (std::istream &is, BindInfos &r)
318 {
319 while (!is.eof()) {
320 BindInfo info;
321 is >> info;
322 if (info.IsUnbind()) {
323 break;
324 }
325 r.Add(info);
326 }
327 return is;
328 }
329
InputDisplayBindHelper(const std::string bindCfgFile)330 InputDisplayBindHelper::InputDisplayBindHelper(const std::string bindCfgFile)
331 : fileName_(bindCfgFile), infos_(std::make_shared<BindInfos>()), configFileInfos_(std::make_shared<BindInfos>())
332 {}
333
GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const334 std::string InputDisplayBindHelper::GetBindDisplayNameByInputDevice(int32_t inputDeviceId) const
335 {
336 CALL_DEBUG_ENTER;
337 CHKPO(infos_);
338 return infos_->GetBindDisplayNameByInputDevice(inputDeviceId);
339 }
340
AddInputDevice(int32_t id,const std::string & name)341 void InputDisplayBindHelper::AddInputDevice(int32_t id, const std::string &name)
342 {
343 CALL_DEBUG_ENTER;
344 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
345 auto displayName = configFileInfos_->GetDisplayNameByInputDevice(name);
346 BindInfo info = infos_->GetUnbindInputDevice(displayName);
347 info.AddInputDevice(id, name);
348 infos_->Add(info);
349 Store();
350 }
351
RemoveInputDevice(int32_t id)352 void InputDisplayBindHelper::RemoveInputDevice(int32_t id)
353 {
354 CALL_DEBUG_ENTER;
355 MMI_HILOGD("Param: id:%{public}d", id);
356 infos_->UnbindInputDevice(id);
357 }
358
IsDisplayAdd(int32_t id,const std::string & name)359 bool InputDisplayBindHelper::IsDisplayAdd(int32_t id, const std::string &name)
360 {
361 const auto &infos = infos_->GetInfos();
362 for (const auto &info : infos) {
363 if ((info.GetDisplayName() == name) && (info.GetDisplayId() == id)) {
364 return true;
365 }
366 }
367 return false;
368 }
369
GetDisplayIdNames() const370 std::set<std::pair<int32_t, std::string>> InputDisplayBindHelper::GetDisplayIdNames() const
371 {
372 CALL_DEBUG_ENTER;
373 std::set<std::pair<int32_t, std::string>> idNames;
374 const auto &infos = infos_->GetInfos();
375 for (const auto &info : infos) {
376 if (info.GetDisplayId() != -1) {
377 idNames.insert(std::make_pair(info.GetDisplayId(), info.GetDisplayName()));
378 }
379 }
380 return idNames;
381 }
382
AddDisplay(int32_t id,const std::string & name)383 void InputDisplayBindHelper::AddDisplay(int32_t id, const std::string &name)
384 {
385 CALL_DEBUG_ENTER;
386 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
387 auto inputDeviceName = configFileInfos_->GetInputDeviceByDisplayName(name);
388 if (IsDualDisplayFoldDevice()) {
389 std::string deviceName = GetInputDeviceById(id);
390 if (!deviceName.empty()) {
391 inputDeviceName = deviceName;
392 }
393 }
394 BindInfo info = infos_->GetUnbindDisplay(inputDeviceName);
395 info.AddDisplay(id, name);
396 infos_->Add(info);
397 Store();
398 }
399
AddLocalDisplay(int32_t id,const std::string & name)400 void InputDisplayBindHelper::AddLocalDisplay(int32_t id, const std::string &name)
401 {
402 CALL_DEBUG_ENTER;
403 MMI_HILOGD("Param: id:%{public}d, name:%{public}s", id, name.c_str());
404 CHKPV(infos_);
405
406 const auto &infos = infos_->GetInfos();
407 std::vector<std::string> unbindDevices;
408 for (const auto &info : infos) {
409 if (info.DisplayNotBind()) {
410 unbindDevices.push_back(info.GetInputDeviceName());
411 MMI_HILOGI("Unbind InputDevice, id:%{public}d, inputDevice:%{public}s",
412 info.GetInputDeviceId(), info.GetInputDeviceName().c_str());
413 }
414 }
415
416 bool IsStore = false;
417 for (auto &item : unbindDevices) {
418 auto inputDeviceName = item;
419 if (IsDualDisplayFoldDevice()) {
420 std::string deviceName = GetInputDeviceById(id);
421 if (!deviceName.empty()) {
422 inputDeviceName = deviceName;
423 }
424 }
425 BindInfo info = infos_->GetUnbindDisplay(inputDeviceName);
426 info.AddDisplay(id, name);
427 infos_->Add(info);
428 IsStore = true;
429 }
430 if (IsStore) {
431 Store();
432 }
433 unbindDevices.clear();
434 }
435
GetInputDeviceById(int32_t id)436 std::string InputDisplayBindHelper::GetInputDeviceById(int32_t id)
437 {
438 CALL_DEBUG_ENTER;
439 if (id != DISPLAY_ID_MAIN && id != DISPLAY_ID_SUB) {
440 return "";
441 }
442
443 std::string inputNodeName = GetInputNodeNameByCfg(id);
444 if (inputNodeName.empty()) {
445 return "";
446 }
447
448 std::string inputNode = GetInputNode(inputNodeName);
449 if (inputNode.empty()) {
450 return "";
451 }
452
453 std::string inputEvent = inputNode;
454 size_t pos = inputEvent.find(INPUT);
455 if (pos != std::string::npos) {
456 inputEvent.replace(pos, INPUT.length(), EVENT);
457 }
458
459 std::string inputDeviceName;
460 inputDeviceName.append(DIRECTORY).append(SEPARATOR)
461 .append(inputNode).append(SEPARATOR)
462 .append(inputEvent).append(SUFFIX);
463
464 MMI_HILOGI("GetInputDeviceById, id:%{public}d, inputDevice:%{public}s", id, inputDeviceName.c_str());
465 return inputDeviceName;
466 }
467
GetInputNodeNameByCfg(int32_t id)468 std::string InputDisplayBindHelper::GetInputNodeNameByCfg(int32_t id)
469 {
470 CALL_DEBUG_ENTER;
471 std::ifstream file(INPUT_DEVICE_NAME_CONFIG);
472 std::string res;
473 if (file.is_open()) {
474 std::string line;
475 while (getline(file, line)) {
476 const std::string delim = "<=>";
477 size_t pos = line.find(delim);
478 if (pos == std::string::npos) {
479 continue;
480 }
481 std::string displayId = line.substr(0, pos);
482 std::string inputNodeName = line.substr(pos + delim.length());
483 if (!displayId.empty() && !inputNodeName.empty()
484 && std::all_of(displayId.begin(), displayId.end(), ::isdigit)
485 && std::atoi(displayId.c_str()) == id) {
486 res = inputNodeName;
487 break;
488 }
489 }
490 file.close();
491 }
492 if (!res.empty() && (res.back() == '\n' || res.back() == '\r')) {
493 res.pop_back();
494 }
495 return res;
496 }
497
GetContent(const std::string & fileName)498 std::string InputDisplayBindHelper::GetContent(const std::string &fileName)
499 {
500 CALL_DEBUG_ENTER;
501 std::string content;
502 char realPath[PATH_MAX] = {};
503 if (realpath(fileName.c_str(), realPath) == nullptr) {
504 MMI_HILOGE("The realpath return nullptr");
505 return content;
506 }
507 std::ifstream file(realPath);
508 if (file.is_open()) {
509 std::string line;
510 while (getline(file, line)) {
511 content.append(line);
512 }
513 file.close();
514 }
515 return content;
516 }
517
GetInputNode(const std::string & inputNodeName)518 std::string InputDisplayBindHelper::GetInputNode(const std::string &inputNodeName)
519 {
520 CALL_DEBUG_ENTER;
521 std::string inputNode;
522 if (fs::exists(DIRECTORY) && fs::is_directory(DIRECTORY)) {
523 for (const auto& entry : fs::directory_iterator(DIRECTORY)) {
524 std::string node = fs::path(entry.path()).filename();
525 std::string file;
526 file.append(DIRECTORY).append(SEPARATOR)
527 .append(node).append(SEPARATOR)
528 .append(NAME);
529 if (inputNodeName == GetContent(file)) {
530 inputNode = node;
531 break;
532 }
533 }
534 }
535 return inputNode;
536 }
537
RemoveDisplay(int32_t id)538 void InputDisplayBindHelper::RemoveDisplay(int32_t id)
539 {
540 CALL_DEBUG_ENTER;
541 MMI_HILOGD("Param: id:%{public}d", id);
542 infos_->UnbindDisplay(id);
543 }
544
Store()545 void InputDisplayBindHelper::Store()
546 {
547 CALL_DEBUG_ENTER;
548 CHKPV(infos_);
549 char realPath[PATH_MAX] = {};
550 CHKPV(realpath(fileName_.c_str(), realPath));
551 if (!IsValidJsonPath(realPath)) {
552 MMI_HILOGE("file path is invalid");
553 return;
554 }
555 std::ofstream ofs(realPath, std::ios::trunc | std::ios::out | std::ios_base::binary);
556 if (!ofs) {
557 MMI_HILOGE("Open file fail.%{public}s, errno:%{public}d", realPath, errno);
558 return;
559 }
560 ofs << *infos_;
561 ofs.close();
562 }
563
GetDisplayBindInfo(DisplayBindInfos & infos)564 int32_t InputDisplayBindHelper::GetDisplayBindInfo(DisplayBindInfos &infos)
565 {
566 CALL_DEBUG_ENTER;
567 CHKPR(infos_, RET_ERR);
568 for (const auto &item : infos_->GetInfos()) {
569 infos.push_back({
570 .inputDeviceId = item.GetInputDeviceId(),
571 .inputDeviceName = item.GetInputDeviceName(),
572 .displayId = item.GetDisplayId(),
573 .displayName = item.GetDisplayName(),
574 });
575 }
576 return RET_OK;
577 }
578
SetDisplayBind(int32_t deviceId,int32_t displayId,std::string & msg)579 int32_t InputDisplayBindHelper::SetDisplayBind(int32_t deviceId, int32_t displayId, std::string &msg)
580 {
581 CALL_DEBUG_ENTER;
582 MMI_HILOGD("Param: deviceId:%{public}d, displayId:%{public}d", deviceId, displayId);
583 if ((deviceId == -1) || (displayId == -1)) {
584 msg = "The deviceId or displayId is invalid";
585 MMI_HILOGE("%s", msg.c_str());
586 return RET_ERR;
587 }
588 if (infos_ == nullptr) {
589 msg = "Infos_ is nullptr";
590 MMI_HILOGE("%s", msg.c_str());
591 return RET_ERR;
592 }
593
594 BindInfo bindByDevice;
595 BindInfo bindByDisplay;
596 for (const auto &item : infos_->GetInfos()) {
597 if (item.GetInputDeviceId() == deviceId) {
598 bindByDevice = item;
599 }
600 if (item.GetDisplayId() == displayId) {
601 bindByDisplay = item;
602 }
603 }
604 if (bindByDevice.GetInputDeviceId() == -1) {
605 msg = "The deviceId is invalid";
606 MMI_HILOGE("%s", msg.c_str());
607 return RET_ERR;
608 }
609 if (bindByDisplay.GetDisplayId() == -1) {
610 msg = "The displayId is invalid";
611 MMI_HILOGE("%s", msg.c_str());
612 return RET_ERR;
613 }
614
615 if (infos_->GetBindDisplayIdByInputDevice(deviceId) == displayId) {
616 msg = "The input device and display has alread bind";
617 MMI_HILOGE("%s", msg.c_str());
618 return RET_ERR;
619 }
620
621 infos_->UnbindInputDevice(bindByDevice.GetInputDeviceId());
622 infos_->UnbindInputDevice(bindByDisplay.GetInputDeviceId());
623 infos_->UnbindDisplay(bindByDevice.GetDisplayId());
624 infos_->UnbindDisplay(bindByDisplay.GetDisplayId());
625
626 BindInfo info1;
627 info1.AddInputDevice(bindByDevice.GetInputDeviceId(), bindByDevice.GetInputDeviceName());
628 info1.AddDisplay(bindByDisplay.GetDisplayId(), bindByDisplay.GetDisplayName());
629 infos_->Add(info1);
630
631 if ((bindByDevice.GetDisplayId() != -1) && (bindByDisplay.GetInputDeviceId() != -1)) {
632 MMI_HILOGD("Both display id and input device id are invalid");
633 BindInfo info2;
634 info2.AddInputDevice(bindByDisplay.GetInputDeviceId(), bindByDisplay.GetInputDeviceName());
635 info2.AddDisplay(bindByDevice.GetDisplayId(), bindByDevice.GetDisplayName());
636 infos_->Add(info2);
637 return RET_OK;
638 }
639
640 if (bindByDevice.GetDisplayId() != -1) {
641 MMI_HILOGD("The display id is invalid");
642 AddDisplay(bindByDevice.GetDisplayId(), bindByDevice.GetDisplayName());
643 return RET_OK;
644 }
645
646 if (bindByDisplay.GetInputDeviceId() != -1) {
647 MMI_HILOGD("The input device id is invalid");
648 AddInputDevice(bindByDisplay.GetInputDeviceId(), bindByDisplay.GetInputDeviceName());
649 return RET_OK;
650 }
651
652 msg = "Can not reach here";
653 return RET_ERR;
654 }
655
Load()656 void InputDisplayBindHelper::Load()
657 {
658 CALL_DEBUG_ENTER;
659 char realPath[PATH_MAX] = {};
660 CHKPV(realpath(fileName_.c_str(), realPath));
661 if (!IsValidJsonPath(realPath)) {
662 MMI_HILOGE("file path is invalid");
663 return;
664 }
665 std::ifstream ifs(realPath);
666 MMI_HILOGEK("Open file end:%{public}s", realPath);
667 if (!ifs) {
668 MMI_HILOGE("Open file fail.%{public}s, errno:%{public}d", realPath, errno);
669 return;
670 }
671 ifs >> *configFileInfos_;
672 ifs.close();
673 }
674
Dumps() const675 std::string InputDisplayBindHelper::Dumps() const
676 {
677 CALL_DEBUG_ENTER;
678 CHKPO(infos_);
679 std::ostringstream oss;
680 oss << *infos_;
681 return oss.str();
682 }
683 } // namespace MMI
684 } // namespace OHOS
685