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