1 /*
2 * Copyright 2023 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 #include "le_audio_log_history.h"
18
19 #include <bluetooth/log.h>
20
21 #include <cstdint>
22 #include <memory>
23 #include <string>
24
25 #include "common/circular_buffer.h"
26 #include "common/strings.h"
27 #include "main/shim/dumpsys.h"
28 #include "os/log.h"
29 #include "osi/include/osi.h"
30 #include "osi/include/properties.h"
31
32 using namespace bluetooth;
33
34 constexpr size_t kMaxLogSize = 255;
35 constexpr size_t kLeAudioLogHistoryBufferSize = 200;
36
37 class TimestampedStringCircularBuffer
38 : public bluetooth::common::TimestampedCircularBuffer<std::string> {
39 public:
TimestampedStringCircularBuffer(size_t size)40 explicit TimestampedStringCircularBuffer(size_t size)
41 : bluetooth::common::TimestampedCircularBuffer<std::string>(size) {}
42
Push(const std::string & s)43 void Push(const std::string& s) {
44 bluetooth::common::TimestampedCircularBuffer<std::string>::Push(
45 s.substr(0, kMaxLogSize));
46 }
47
48 template <typename... Args>
Push(Args...args)49 void Push(Args... args) {
50 char buf[kMaxLogSize];
51 std::snprintf(buf, sizeof(buf), args...);
52 bluetooth::common::TimestampedCircularBuffer<std::string>::Push(
53 std::string(buf));
54 }
55 };
56
57 class LeAudioLogHistoryImpl;
58 LeAudioLogHistoryImpl* instance;
59
60 constexpr size_t kMaxLogHistoryTagLength = 14;
61 constexpr size_t kMaxLogHistoryMsgLength = 44;
62 const std::string kTimeFormat("%Y-%m-%d %H:%M:%S");
63
64 using Record = bluetooth::common::TimestampedEntry<std::string>;
65
66 class LeAudioLogHistoryImpl : public LeAudioLogHistory {
67 public:
~LeAudioLogHistoryImpl(void)68 ~LeAudioLogHistoryImpl(void) { history_.reset(); }
69
LeAudioLogHistoryImpl(void)70 LeAudioLogHistoryImpl(void) {
71 history_ = std::make_shared<TimestampedStringCircularBuffer>(
72 kLeAudioLogHistoryBufferSize);
73 log::assert_that(history_ != nullptr, "assert failed: history_ != nullptr");
74 history_->Push(std::string("Initialized le_audio history"));
75 }
76
Dump(int fd)77 void Dump(int fd) {
78 #define DUMPSYS_TAG "::le_audio"
79
80 LOG_DUMPSYS_TITLE(fd, DUMPSYS_TAG);
81 if (history_ == nullptr) {
82 return;
83 }
84 std::vector<Record> history = history_->Pull();
85 for (auto& record : history) {
86 time_t then = record.timestamp / 1000;
87 struct tm tm;
88 localtime_r(&then, &tm);
89 auto s2 = bluetooth::common::StringFormatTime(kTimeFormat, tm);
90 LOG_DUMPSYS(fd, " %s.%03u %s", s2.c_str(),
91 static_cast<unsigned int>(record.timestamp % 1000),
92 record.entry.c_str());
93 }
94 #undef DUMPSYS_TAG
95 }
96
AddLogHistory(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg,const std::string & extra)97 void AddLogHistory(const std::string& tag, int group_id,
98 const RawAddress& addr, const std::string& msg,
99 const std::string& extra) {
100 add_logs_history_common(tag, group_id, addr, msg, extra);
101 }
102
AddLogHistory(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg)103 void AddLogHistory(const std::string& tag, int group_id,
104 const RawAddress& addr, const std::string& msg) {
105 AddLogHistory(tag, group_id, addr, msg, std::string());
106 }
107
108 private:
add_logs_history_common(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg,const std::string & extra)109 void add_logs_history_common(const std::string& tag, int group_id,
110 const RawAddress& addr, const std::string& msg,
111 const std::string& extra) {
112 if (history_ == nullptr) {
113 log::error(
114 "LeAudioLogHistory has not been constructed or already destroyed !");
115 return;
116 }
117
118 history_->Push("%-*s GID %-3d %-*s: %-22s %s", kMaxLogHistoryTagLength,
119 tag.substr(0, kMaxLogHistoryTagLength).c_str(), group_id,
120 kMaxLogHistoryMsgLength,
121 msg.substr(0, kMaxLogHistoryMsgLength).c_str(),
122 ADDRESS_TO_LOGGABLE_CSTR(addr), extra.c_str());
123 }
124
125 std::shared_ptr<TimestampedStringCircularBuffer> history_{nullptr};
126 };
127
Get(void)128 LeAudioLogHistory* LeAudioLogHistory::Get(void) {
129 if (instance) {
130 return instance;
131 }
132 instance = new LeAudioLogHistoryImpl();
133 return instance;
134 }
135
DebugDump(int fd)136 void LeAudioLogHistory::DebugDump(int fd) {
137 if (instance) {
138 instance->Dump(fd);
139 }
140 }
141
Cleanup(void)142 void LeAudioLogHistory::Cleanup(void) {
143 if (!instance) {
144 return;
145 }
146 auto ptr = instance;
147 instance = nullptr;
148 delete ptr;
149 }
150